質問するログイン新規登録
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

selenium

Selenium(セレニウム)は、ブラウザをプログラムで作動させるフレームワークです。この原理を使うことにより、ブラウザのユーザーテストなどを自動化にすることができます。

Q&A

0回答

559閲覧

Ruby スクレイピング

hiii__rookii

総合スコア0

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

selenium

Selenium(セレニウム)は、ブラウザをプログラムで作動させるフレームワークです。この原理を使うことにより、ブラウザのユーザーテストなどを自動化にすることができます。

0グッド

0クリップ

投稿2025/10/07 07:22

0

0

実現したいこと

NHKニュースからスクレイピングを行い、タイトル、日時、本文を取得する。

rubyでNHKニュースのスクレイピングを行っています。タイトル、日時、本文の情報を取得するというものです。本文について、途中までは情報を取得できるのですが、途中から末尾に  ”.......” という感じで全文を取得できないという問題が起きてしまいます。なにがいけないのでしょうか? 

該当のソースコード

require 'nokogiri'
require 'selenium-webdriver'
require 'time'
require 'uri'

class ArticlesController < ApplicationController
def index
@articles = Article.order(created_at: :desc)
end

def create
url = params.dig(:article, :url)

unless url.to_s.include?('nhk') flash[:alert] = "NHKのニュースURLを入力してください。" redirect_to root_path and return end driver = nil begin # --- Selenium Chrome 設定 --- options = Selenium::WebDriver::Chrome::Options.new options.add_argument('--headless=new') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--disable-gpu') options.add_argument('--window-size=1920,1080') options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/117.0.0.0 Safari/537.36") options.binary = '/usr/bin/google-chrome' service = Selenium::WebDriver::Service.chrome(path: '/usr/local/bin/chromedriver') driver = Selenium::WebDriver.for(:chrome, options: options, service: service) driver.manage.timeouts.page_load = 40 # --- ページ取得後に「続きを読む」ボタンをクリック --- # --- ページ取得 --- driver.get(url) begin more_button = driver.find_element(xpath: '//button[contains(text(),"続きを読む")]') driver.execute_script("arguments[0].click();", more_button) sleep 1 # 展開待ち rescue Selenium::WebDriver::Error::NoSuchElementError

ボタンがない場合はスルー

end wait = Selenium::WebDriver::Wait.new(timeout: 20) wait.until { driver.find_element(css: 'div.esl7kn2s._1i1d7sh0') } # --- ページを最下部までスクロール(描画完了を促す) --- 100.times do driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") sleep (rand(2.0..10.0)) end # --- 本文取得(Seleniumで描画後のDOMから直接) --- #article_elements = driver.find_elements(css: 'div.esl7kn2s._1i1d7sh0 p._1i1d7sh2') #content = article_elements.map { |el| el.text.strip }.reject(&:empty?).join("\n\n") content = driver.execute_script(<<~JS) const article = document.querySelector('div.esl7kn2s._1i1d7sh0'); if (!article) return ''; const ps = Array.from(article.querySelectorAll('p._1i1d7sh2')); return ps.map(p => p.innerText.trim()).filter(Boolean).join("\\n\\n"); JS # --- 日時取得 --- time_tag_element = driver.find_element(css: 'time[datetime]') rescue nil datetime_str = time_tag_element&.attribute('datetime') published_at = datetime_str ? Time.iso8601(datetime_str).in_time_zone('Asia/Tokyo') : nil # --- タイトル取得(NokogiriでOK) --- doc = Nokogiri::HTML.parse(driver.page_source) title = doc.at_css('h1')&.text&.strip || "タイトル不明" # --- 保存 --- Article.create!( url: url, title: title, published_at: published_at, content: content ) flash[:notice] = "記事を保存しました。" rescue => e flash[:alert] = "スクレイピング中にエラーが発生しました: #{e.message}" puts "[ERROR] #{e.message}" ensure driver&.quit end redirect_to root_path

end
end

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

meg_

2025/10/09 08:07

> NHKニュースからスクレイピングを行い、タイトル、日時、本文を取得する。 「NHKニュース」とはNHK Oneサービスの一部のことでしょうか?そうであれば利用規約で「情報の全部または一部を、転載、複写、複製」することは禁止されていますが、特別に許可を得られているという事でしょうか?
hiii__rookii

2025/10/10 13:36

勉強用で非公開で行っていたのですが、それでも規約違反になりますか? 無知で申し訳ありません
meg_

2025/10/11 04:48

> 勉強用で非公開で行っていたのですが、それでも規約違反になりますか? 質問の対象サイトが不明のため「NHK One」の利用規約についてコメントさせていただきました。私の質問について回答ありませんが「NHK One」で合っていたのでしょうか??サイトが異なれば利用規約も異なるかと思います。 詳細についてはそのサイトの運営者にご確認ください。
hiii__rookii

2025/10/11 05:05

NHKOneです
meg_

2025/10/12 04:45

転載すれば著作権法違反、コピーすれば利用規約違反になる可能背があります。違反した場合にどうなるかについては専門家にお尋ねください。サービスが使えなくなると困るのでコードの検証はできませんでした。 > 勉強用で非公開で行っていたのですが、それでも規約違反になりますか? 「勉強用」であれば他のサイト(練習用のサイトも存在します)で行った方が良いのではないでしょうか? 別件ですが、質問のコードのレイアウトが一部崩れています。
hiii__rookii

2025/10/14 00:27

色々教えていただきありがとうございます
otn

2025/10/14 14:23

具体的なURLが書かれてないので、何ともですが、 質問1:手動で"続きを読む"をクリックすると続きが読めるが、お書きのコードでクリックすると続きが読めるのでなくxxxxとなる ということでしょうか? そうなら、「xxxxとなる」の部分を書きましょう。ブラウザ画面の描写やエラーメッセージなど。「全文を取得できない」は情報量がほぼゼロです。 質問2:普通は、クリックは more_button.click ですが、execute_script にした経緯は何でしょうか? 「more_button.clickだと~~~~となるので、その対応としてexecute_scriptとした」など。 アドバイス: rescue節は削除しましょう。例外捕捉は、「あらかじめ想定した例外」にし対しての後処理や再トライなどを行う目的で使います。 想定していない例外はデバッグが必要なので、e.messageだけでなく、処理系の出す長いメッセージがあったほうがデバッグがしやすいです。このコードだと、例外発生個所の情報を捨ててますね。 例外発生時点dのブラウザ画面を見たいなら(これもデバッグに有効)、例外捕捉してそこで待ってもいいですが。ここまで書いて、気づきましたが、headlessでデバッグしてるんですか????
FKM

2025/10/16 00:18 編集

スクレイピングによる著作権云々は、まるで質問の趣旨とズレているので一旦置いといて。 スクレイピングという操作は、いわばループ操作によってWEB上のテキストを非同期操作で取得する制御なので、まずは作業環境のメモリを確認する必要があります。またそれによるメモリオーバーを防ぐためにプログラム上でタイムアウト制御している場合もありますが、今回は何件までなら正常にとれて、何件からはとれなくなるのかは判別してみたほうがいいです(追跡テスト及び切り分けテスト)。 また、スクレイピングは外部サーバの負担にもなる場合があるので、外部側からアクセス制限をかけている場合もあります。もし著作権違反あるいは不正アクセスとみなし、NHKが制限をかけたのではないかと疑う場合は、NHK以外で、スクレイピング可能なサイトでも同様の問題が起きるかどうかもテストしたほうがいいです(対照テスト)。
meg_

2025/10/15 11:02

> sleep 1 # 展開待ち 待ち時間は十分でしょうか? > # --- ページを最下部までスクロール(描画完了を促す) --- 最下部までスクロール完了していますか? ネットワークの状態によっては想定通りの動きとならないことも考えられます。 > ここまで書いて、気づきましたが、headlessでデバッグしてるんですか????(otnさんのコメントから抜粋) ヘッドレスモードだとスクレイピング上手く行かないこともあり得ます。サイト側から拒否?される可能性も考えられるかと思います。
hiii__rookii

2025/10/18 02:39

みなさんのアドバイスを参考にコードを書き直してみました。 class ArticlesController < ApplicationController require 'selenium-webdriver' def index @articles = Article.all.order(created_at: :desc) end def create url = params[:article][:url] options = Selenium::WebDriver::Chrome::Options.new #options.add_argument('--headless') # GUIなしで起動 options.add_argument('--disable-gpu') driver = Selenium::WebDriver.for(:chrome, options: options) begin driver.navigate.to url wait = Selenium::WebDriver::Wait.new(timeout: 180) wait.until { driver.execute_script("return document.querySelectorAll('p._1i1d7sh2').length") > 3 } begin read_more = driver.find_element(css: 'button[aria-label="続きを読む"]') driver.execute_script("arguments[0].click();", read_more) sleep 3 rescue Selenium::WebDriver::Error::NoSuchElementError end last_height = driver.execute_script("return document.body.scrollHeight") loop do driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") sleep rand(3.5..5.5) new_height = driver.execute_script("return document.body.scrollHeight") break if new_height == last_height last_height = new_height end # 全段落を取得して結合 paragraphs = driver.find_elements(css: 'div._1i1d7sh0 p._1i1d7sh2') content = paragraphs.map(&:text).join("\n\n") # タイトルと公開日時も取得 title_element = driver.find_element(css: 'h1._1j6dito2') title = title_element.text time_element = driver.find_element(css: 'time') published_at = Time.parse(time_element.attribute('datetime')) # DB 保存 Article.create!( url: url, title: title, published_at: published_at, content: content ) redirect_to root_path, notice: '記事を保存しました。' ensure driver.quit end end end このコードの実行の結果以下のエラーが出てしまいました。 Started GET "/" for 127.0.0.1 at 2025-10-18 11:22:43 +0900 ActiveRecord::SchemaMigration Load (1.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC Processing by ArticlesController#index as HTML Rendering layout layouts/application.html.erb Rendering articles/index.html.erb within layouts/application Article Exists? (0.8ms) SELECT 1 AS one FROM "articles" LIMIT $1 [["LIMIT", 1]] ↳ app/views/articles/index.html.erb:46 Article Load (2.6ms) SELECT "articles".* FROM "articles" ORDER BY "articles"."created_at" DESC ↳ app/views/articles/index.html.erb:51 Rendered articles/index.html.erb within layouts/application (Duration: 33.5ms | Allocations: 12903) Rendered layout layouts/application.html.erb (Duration: 127.7ms | Allocations: 50886) Completed 200 OK in 174ms (Views: 123.4ms | ActiveRecord: 16.0ms | Allocations: 64883) Started POST "/articles" for 127.0.0.1 at 2025-10-18 11:22:46 +0900 Processing by ArticlesController#create as TURBO_STREAM Parameters: {"authenticity_token"=>"[FILTERED]", "article"=>{"url"=>"https://news.web.nhk/newsweb/na/na-k10014947461000"}, "commit"=>"実行"} Completed 500 Internal Server Error in 181883ms (ActiveRecord: 0.0ms | Allocations: 415875) Selenium::WebDriver::Error::TimeoutError (timed out after 180 seconds): 
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.29%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問