MENU

【実践】SeleniumでPythonスクレイピング!サンプルコードあり

当ページのリンクには広告(PR)が含まれていることがあります。
【実践】SeleniumでPythonスクレイピング!サンプルコードあり
お悩み女子

BS+Requestで取得できない要素を取得したいんだよね。

なべくん

その悩み、Seleniumで解決できます。

ウェブサイトの特定の場所をクリックしたり、入力フォームにテキストを入力したりとBeautiful Soup+Requestで実現できない操作を実現するのがSeleniumです。

この記事では、PythonとSeleniumを組み合わせることで、動的なウェブサイトから効率的にデータを抽出する方法を基礎から応用まで体系的に解説します。

本記事は、スクレイピングを推奨・助長する目的で執筆しておりませんが、教育目的としてログイン画面を突破したり複数のページ遷移したり自動化を実現するためにも、ぜひ本記事を参考にしてください。

細かい前置きはいいから早く実践に行きたいという方は以下のリンクから実践のフェーズに移動できます。
>>Seleniumのスクレイピング実践へ移動する

目次

Pythonスクレイピング 動的サイトを制するSeleniumの力

手作業でのデータ収集にうんざりしていませんか。

JavaScriptで動的にコンテンツが生成されるウェブサイトや、ログインが必要な複雑なページからのデータ抽出は、従来のスクレイピング手法では非常に難しいものです。

そんな悩みを解決するのがSeleniumの強力な機能です。

ここでは、「従来のスクレイピングでは抽出できない情報」とは何かを具体的に掘り下げ、「JavaScriptで動的に生成されるコンテンツ」の正体を明らかにします。

さらに「なぜSeleniumはウェブブラウザを操作できるのか」という仕組みと、他のスクレイピングライブラリである「Requests・BeautifulSoupとの使い分けと利点」について詳しく解説しますね。

PythonとSeleniumを組み合わせることで、これまでアクセスできなかったウェブサイトの情報も効率的に取得し、あなたのデータ収集業務を劇的に変えることができます。

従来のスクレイピングでは抽出できない情報

「従来のスクレイピング」とは、主にRequestsライブラリでHTMLを取得し、BeautifulSoupで解析する方法を指します。

この方法では、ウェブサーバーから送られてくる静的なHTMLコンテンツは問題なく取得できますが、ログイン後のページや、クリックしないと表示されない隠れたコンテンツ、Ajaxで後から読み込まれるデータなど、ユーザーのアクションによって変化する情報は取得が非常に困難でした。

例えば、オンラインショッピングサイトで商品をカートに入れた後のページや、特定の検索フィルターを適用した後の結果ページなど、手動で操作が必要な場面での情報取得は、まさに一筋縄ではいかない領域だと感じていましたかと。

お悩み女子

なんで頑張ってコード書いても、クリックしないと見れない情報は取れないんだろう?

なべくん

その悩み、Seleniumが解決できます。

つまり、RequestsとBeautifulSoupだけでは、ウェブページの「見える情報」全てを捉えることは難しく、動的な要素が多い現代のウェブサイトでは限界があると言えるでしょう。

JavaScriptで動的に生成されるコンテンツ

「JavaScriptで動的に生成されるコンテンツ」とは、ウェブサイトが表示された後にJavaScriptというプログラミング言語が実行されることで、内容や見た目が変化したり、新たな要素が追加されたりするウェブページのことを指します。

例えば、Googleの検索結果ページで下にスクロールすると、次々と新しい検索結果が表示される「無限スクロール」や、グラフや地図などのインタラクティブな要素が、まさにこの動的生成コンテンツの代表例です。

これらのコンテンツは、ブラウザがJavaScriptコードを実行してから初めて表示されるため、従来のスクレイピングでは「ただの空白」としてしか認識されませんでした。

実際、わたしも初めて動的サイトにぶつかった時、「これじゃ全くデータが取れないじゃないか!」と頭を抱えました。

お悩み女子

スクロールしないと出てこない情報、どうやって集めたらいいの?

なべくん

リアルなブラウザ操作をシミュレーションすることで対応可能です。

動的に生成されるコンテンツは、ユーザー体験を豊かにする一方で、スクレイピングにとっては大きな壁となる要素だと理解しておく必要があります。

なぜSeleniumはウェブブラウザを操作できるのか

Seleniumがウェブブラウザを操作できる理由は、「WebDriver」という専用のドライバーを介して、実際のウェブブラウザ(Google ChromeやFirefoxなど)を直接コントロールするためです。

このWebDriverは、まるで人間がブラウザを触るかのように、ウェブページを開いたり、ボタンをクリックしたり、テキストを入力したりといった操作を、あなたのプログラムからの指示で忠実に実行します。

例えば、あなたはコードで「このテキストボックスに文字を入力して」と指示を出すだけで、WebDriverが実際のブラウザ画面上でその通りに文字を打ち込み、そして「このログインボタンを押して」と命令すれば、ログイン操作も自動で実行されます。

これにより、JavaScriptで動的に表示されるコンテンツも、ブラウザが完全にレンダリング(表示)した状態で情報を取り出せるのが大きな強みです。

お悩み女子

仮想ブラウザって何だろう?実際に画面は見えないけど本当に動いているの?

なべくん

ブラウザを裏で動かし、まるであなたが手で操作しているかのようにウェブサイトに働きかけます。

SeleniumとWebDriverの組み合わせによって、通常のブラウザが処理できること全てがスクレイピングの対象となり、非常に多くのウェブサイトからデータを効率良く収集できるのです。

Requests・BeautifulSoupとの使い分けと利点

Requests、BeautifulSoup、そしてSeleniumは、それぞれ異なる得意分野を持つPythonのスクレイピングライブラリであり、ウェブサイトの特性に応じて適切に使い分けることが効率的なデータ収集の鍵となります。

RequestsはHTTPリクエストを送信してHTMLを取得するライブラリで、非常に高速な処理が可能です。

BeautifulSoupは取得したHTMLを解析し、目的の要素を抽出するのに特化しています。

対してSeleniumは、JavaScriptで動的に生成されるコンテンツや、ログイン、ボタンクリックといった「ブラウザ操作」が必要な場面で真価を発揮します。

処理速度はRequestsより遅くなりますが、より複雑なウェブサイトに対応できます。

お悩み女子

どのライブラリを使えばいいのか、毎回迷ってしまうのはなぜだろう?

なべくん

ウェブサイトの動きに合わせて最適なツールを選ぶのが、データ収集の効率を最大化する秘訣です。

これら3つのツールそれぞれの特性を理解し、ウェブサイトの挙動を見極めて使い分けることで、あなたはあらゆるタイプのウェブサイトから必要な情報を、より確実に、そして効率良く抽出できるようになるでしょう。

5ステップでPythonスクレイピングを習得!Selenium実践ガイド

重要なのは、PythonのSeleniumを使えば、動的なウェブサイトからのデータ抽出も怖くない、という最強のスキルが手に入る点です。

環境準備からスタートし、基本操作重要機能、そして高度なテクニックを習得、最終的に実用的なデータ活用まで、Pythonを使ったSeleniumスクレイピングの全てをマスターできる5つのステップを順に解説します。

これらのステップを一つずつ実践すれば、あなたのデータ収集業務は格段に効率的になるでしょう。

ステップ1 環境準備 SeleniumとWebDriverのセットアップ

Seleniumを使ったウェブスクレイピングを始める上で最も重要なのは、正確な環境準備です。

ここでいう「Selenium」はウェブブラウザを自動操作する強力なライブラリを指し、「WebDriver」はその命令を各ブラウザに伝えるための通訳のようなプログラムを意味します。

最初に「pip install selenium」でPythonのライブラリをインストールし、次に使用するウェブブラウザ(例えばGoogle Chrome)のバージョンに合わせた専用のChromeDriverをダウンロードしてパスを設定する作業が必要です。

わたしも初めてSeleniumを使うとき、WebDriverのバージョン違いで何時間も悩んだ経験がありますが、たった一つのバージョン不一致が大きなエラーに繋がることがあります。

必要なツールを正確に設定する手順は以下のとおりです。

お悩み女子

あれ、WebDriverってブラウザごとに違うんでしたっけ?ちゃんと合わせないと動かないんですかね?

なべくん

そうです、WebDriverは使うブラウザの種類だけでなく、そのブラウザのバージョンにも厳密に合わせる必要があるんです

このステップで確実な基盤を築くことが、これからのスクレイピング成功の鍵を握っています。

ステップ2 基本操作 ブラウザ制御と要素の特定

Seleniumの真骨頂は、まるであなたが手で操作するように、ウェブブラウザをプログラムで制御できる点にあります。

「要素の特定」とは、ウェブページ上のテキストボックス、ボタン、画像など、操作したい一つ一つの部品を見つけ出す作業のことで、driver.get()で特定のURLを開き、find_element_by_id()find_element_by_css_selector()などのメソッドを使って要素を特定し、send_keys()でテキストを入力したり、click()でボタンを押したりします。

主な要素検索メソッドをまとめたものです。

特にJavaScriptによって後から読み込まれるコンテンツを取得するには、目的の要素が完全に表示されるまで数秒から10秒程度の「待機処理」を組み込むのが非常に重要で、待機処理を適切に行わないと「要素が見つかりません」といったエラーに頻繁に遭遇することになります。

お悩み女子

ウェブページの要素を特定するのって、どれを使えば一番確実なんですか?毎回Chromeの開発者ツールで確認しないといけないのかな?

なべくん

一番確実なのはCSSセレクタ、より複雑な場合はXpathを使うとよしなにことが進みます。

要素の特定は、基本的に開発者ツールで確認するのが一般的。開発者ツールの使い方をマスターすれば、多くのウェブサイトから効率的にデータを収集できるようになります。

ステップ3 重要機能 ログイン自動化と複数ページ遷移

Seleniumがデータサイエンティストにとって「重要機能」と呼ばれるゆえんは、ログインが必要なサイトや複数のページにわたる複雑な情報も自動で収集できることにあるでしょう。

これまでのRequestsとBeautifulSoupだけでは難しかった課題を、Seleniumはスマートに解決してくれます。

例えば、あなたの勤める会社のBIツールへのログインを自動化して毎日特定のレポートをダウンロードしたり、ウェブサイトの「次へ」ボタンを自動でクリックして数千ページにわたる顧客レビューを抽出するといった業務も実現できます。

seleniumで使える機能のおもな活用方法は以下のとおりです。

お悩み女子

ヘッドレスモードって、本当に画面が表示されないんですか?ちゃんと動いてるのか確認するのに不安になりますね…

なべくん

ヘッドレスモードでは画面は表示されませんが、ログ出力やスクリーンショット機能で内部的な動作状況を確認することができます

ヘッドレスモードはテスト環境のときはオフにしておき、目視でちゃんと期待通りの動作をすることを確認してからヘッドレスモードをオンにされることをおすすめします。

これらの機能を活用することで、あなたのデータ収集業務の幅は格段に広がり、自動化による恩恵を最大限に受け取ることができるはずです。

ステップ4 高度なテクニック スクロールと外部ライブラリ連携

無限スクロールや特定の情報を効率よく抽出するためには、Seleniumの「高度なテクニック」である画面スクロールの自動化や、他のPythonライブラリとの連携が不可欠です。

たとえば、InstagramやAmazonのようなスクロールするたびにコンテンツが表示されるサイトでは、ページを最下部までスクロールする処理を自動で繰り返すことで、全ての投稿データを漏らさず取得できます。

さらに、Seleniumで取得したHTML情報を「BeautifulSoup」に渡せば、複雑なタグ構造の中から必要なデータ(例: 商品名、価格)を効率的に解析できますし、「Requests」ライブラリを使えば、ウェブスクレイピングで見つけた画像のURLを直接ダウンロードし、ローカルに保存することも可能です。

以下に他のライブラリとの連携の役割を示します。

お悩み女子

他のライブラリと連携するのって、コードが複雑になりそう…何かメリットはあるんですか?

なべくん

はい、Seleniumでブラウザ操作を完結させ、他のライブラリでデータ解析やダウンロードを分担することで、コードの可読性が上がり、処理も効率化できるメリットがあります

高速な処理ではなくなる反面、seleniumと他のライブラリーを組み合わせることでBS+Requestsではできなかったスクレイピング処理が実現できます。

ステップ5 実用的なデータ活用 CSVファイルへの保存

これまで収集した膨大なデータを単なる情報の羅列で終わらせず、「実用的なデータ活用」へと繋げるための最終ステップが、CSVファイルへの保存です。

データをCSV形式で出力することで、ExcelやBIツール、さらにはPandasやNumPyといったPythonのデータ分析ライブラリで簡単に扱うことが可能になります。

Pythonの標準ライブラリであるcsvモジュールを使えば、スクレイピングで取得したタイトルやURL、価格といった様々な種類のデータを約10行程度のコードで整然とした表形式で保存できます。

ファイル名を日付で自動生成したり、エンコーディングエラー(文字化け)を回避するencoding='utf-8'errors='ignore'を指定する工夫も忘れずに取り入れましょう。

CSVファイルへの保存手順は以下のとおりです。

お悩み女子

CSVに保存した後、そのデータをどう活用するんですか?ただ保存するだけじゃなくて、もっといい方法があるんですかね?

なべくん

保存したCSVデータは、高度なデータ分析や機械学習モデルの構築に利用できますよ。

今回はCSVに保存して活用することを例に挙げましたが、実務的には取得したデータを整形してメールでレポーティングしたり、SQL DBなどに保存して再利用したりとさまざまです。

次によく遭遇するエラーと解消方法について見ていきましょう。

Pythonスクレイピング Seleniumでエラー遭遇時!トラブル解決術

お悩み女子

エラーを解決するのに時間がかかって疲れた。

なべくん

私自身も遭遇したエラーと解決方法を紹介します。

PythonでのSeleniumスクレイピングは非常に強力なツールですが、残念ながら一発できれいにコードをアウトプットするのはまれで以下のようなエラーの壁を何度もトライアンドエラーを繰り返すのが一般的です。

ありがちなエラー
  • 要素が見つからないNoSuchElementException
  • WebDriverとブラウザのバージョン不一致
  • ウェブサイトからのブロック

上記の主要なエラーとその解決方法についてそれぞれ見ていきましょう。さらに、「堅牢なプログラムのための例外処理」を実装する方法も詳しく紹介します。

要素が見つからない時の対処法 NoSuchElementException

「NoSuchElementException」とは、Seleniumが指定されたウェブページ上で目的の要素を見つけられなかったときに発生するエラーです。

また以下のようにStacktraceをログに出力することもあります。

- ERROR - 処理中に予期せぬエラーが発生しました: Message: 
Stacktrace:
#0 0x5c043598301a <unknown>
#1 0x5c0435422a70 <unknown>
#2 0x5c0435474907 <unknown>
#3 0x5c0435474b01 <unknown>
#4 0x5c04354c2d54 <unknown>
#5 0x5c043549a40d <unknown>
#6 0x5c04354c014f <unknown>
#7 0x5c043549a1b3 <unknown>
#8 0x5c043546659b <unknown>
#9 0x5c0435467971 <unknown>
#10 0x5c04359481eb <unknown>
#11 0x5c043594bf39 <unknown>
#12 0x5c043592f2c9 <unknown>
#13 0x5c043594cae8 <unknown>
#14 0x5c0435913baf <unknown>
#15 0x5c04359700a8 <unknown>
#16 0x5c0435970286 <unknown>
#17 0x5c0435981ff6 <unknown>
#18 0x7e172a867ac3 <unknown>
お悩み女子

どうすればウェブページ上の要素を正確に見つけられるの?

なべくん

適切な待機処理と正確なセレクタ指定が鍵を握ります。

遷移するページが複数になる場合、そもそもユーザー側の期待した通りのページ遷移ではないケースや広告や認証クッキーバナーが挟まることもよくあるため、以下の点に配慮するのがおすすめです。

  • どのようなページ遷移をするのか?
  • スマホやPCで出現要素やページ構成が変化しないか?
  • プライベートモードでアクセスしてクッキーバナーや広告がはさまらないか?

検証には、DevツールのElementsタブを使ってHTML構造を正確に理解することで、エラーは解決できます。

上記の確認が済んだら、以下のような点にも注意が必要です。

WebDriverとブラウザのバージョン不一致対策

WebDriverとは、Seleniumがブラウザを自動操作するための仲介役」のようなもので、ブラウザとWebDriverは、それぞれバージョンが厳密に一致している必要があります

Google Chromeの場合、以下のコードのように自動的に更新する記述をしてあれば問題ありませんが、ローカル環境でスクレイピングをしている場合、下記のように記述しても更新されないことがあります。

from selenium import webdriver

# この行を実行するだけで、Selenium Managerが
# 自動的に適切なChromeDriverをダウンロードまたは更新します。
driver = webdriver.Chrome()

# これ以降の処理
driver.get("https://www.google.com")
print(driver.title)
driver.quit()

わたしも過去にブラウザが自動更新された後にエラーの沼にハマったことがあり、以下のコードで手動アップデートをしたことが何度とあります。

# seleniumをアップデートするコマンド
pip install --upgrade selenium

しかし、ローカル環境で動かしている場合、手動アップデートでもchromeDriverが更新されないことがあるので、以下のリンクから最新版のChromeDriverを手動でダウンロードして置き換えることをおすすめします。
参考:最新版Chromeダウンロード先

使用しているブラウザのバージョンに合わせたWebDriverを常に最新の状態に保つことで、安定した動作を確保できます。

ウェブサイトからのブロック回避と効果的な対策

ウェブサイトからの「ブロック」とは、スクレイピングがウェブサイトのサーバーに過度な負荷をかけたり、不審なアクセスと判断されたりした場合にサーバー側のシステム判定または手動でブロックされ、IPアドレスを一時的または永久にアクセス禁止にされることです。

例えば、短時間に何百回、何千回とリクエストを送るとほぼ確実にブロックされますし、海外からのアクセスをそもそもブロックする機能を備えているサーバーもあります。

お悩み女子

ブロックされないためにはどうしたらいいの?

なべくん

適切な間隔とランダム性を加えることが効果的です

上記の対策を組み合わせることで、ウェブサイトからのブロックリスクを大幅に減らし、安定したスクレイピング実行が可能です。

プロキシ利用については、有料・無料どちらも使用できるプロキシがあるので、予算や利用目的に応じて検討してみるのがおすすめです。興味があれば、筑波大学で研究されている無料VPN「VPN Gate」を参考にするのも良いかと。

堅牢なPythonプログラムのための例外処理

「例外処理」とは、プログラムの実行中に発生するエラー(例外)を予測し、適切に対応するための仕組みです。

Pythonではtry-except構文がこれにあたります。

ウェブスクレイピングでは、ネットワークの瞬断や予期せぬウェブサイトの変更・長期間スクレイピングによるメモリ不足など、20以上の多様な例外が発生する可能性があります。

お悩み女子

エラーの種類によって処理を変える必要はあるの?

なべくん

特定の例外に対応した処理と予期せぬ例外に備える処理を組み合わせるのが理想的です。

例外処理を適切に組み込むことで、エラー発生時にもプログラムが途中で停止せず、より安定して信頼性の高いスクレイピングツールを構築できます。

【実践】Seleniumを使ってPython公式サイトをスクレイピングしてみる

なべくん

実際にSeleniumを使ってスクレイピングしてみましょう。

喜ぶ女子

待ってました。

サンプルサイト
  • サイト名:Python公式サイト
  • サイトURL:https://www.python.org/
  • 利用規約:https://www.python.org/about/legal/
  • robots.txt:https://www.python.org/robots.txt

Pythonの公式サイトをサンプルとして使わせていただき、実際にスクレイピングをしてみましょう。

  1. robots.txt・利用規約を確認する
  2. 取得する要素を確認する
  3. スクリプトを書き、検証する

各スクレイピング手順について、それぞれ見ていきましょう。

実践手順1. robots.txt・利用規約を確認する

まずは、スクレイピングが可能かどうか利用規約robots.txtを確認します。

# Directions for robots.  See this URL:
# http://www.robotstxt.org/robotstxt.html
# for a description of the file format.

User-agent: HTTrack
User-agent: puf
User-agent: MSIECrawler
Disallow: /

# The Krugle web crawler (though based on Nutch) is OK.
User-agent: Krugle
Allow: /
Disallow: /~guido/orlijn/
Disallow: /webstats/

# No one should be crawling us with Nutch.
User-agent: Nutch
Disallow: /

# Hide old versions of the documentation and various large sets of files.
User-agent: *
Disallow: /~guido/orlijn/
Disallow: /webstats/

以上のrobots.txtと利用規約から以下のことが読み取れます。

  • /~guido/orlijn/ と /webstats/ はスクレイピング禁止
  • 以下の名前を持つクローラーはすべてのページへアクセス拒否
    HTTrack, puf, MSIECrawler, Nutch
  • 利用規約に明示的なスクレイピング禁止は記載されていない

特定のページを除けば、常識の範囲内でスクレイピングができることが確認できます。

実践手順2. 取得する要素を確認する

今回はPythonでSeleniumを使用してスクレイピングするということなので、Pythonの公式サイトから投稿されている記事のタイトルと投稿日を取得していく流れは、以下のとおりです。

取得の流れ
  • Python公式サイトにアクセスする
  • Newsのナビメニューをクリックする
  • 新着記事一覧からmoreをクリックする
  • 記事一覧の投稿日と記事タイトルを取得する
  • CSVに保存してダウンロードする

要素を確認する際に注意しておきたいのがPCとスマホやモバイルなどの端末による見え方やページ遷移先が異なる場合があります。(今回はその典型例)

Seleniumでスクレイピングをする際は、PCのブラウザーからアクセスすることになるので、開発者ツールをそのつど閉じてからページ遷移させることが必要です。

お悩み女子

いちいち開発者ツールを閉じるの面倒くさい。

なべくん

PCとモバイルで取得要素の名称自体が変わることもあるので、面倒でも開発者ツールを閉じましょう。

実践手順3. Pythonスクリプトを書き、検証する

今回は、ウェブスクレイピングをすることが目的であるため、開発環境に関しては言及しません。したがって、誰でもほぼ同じ動作環境が再現できるGoogle Colaboratory(通称:Colab)でコードの実装を行います。

Python公式にアクセスして新着ニュースを取得するコードは下記の通り。

# 必要なライブラリをインストールします
# Google Colab環境の場合はpipの前に!を付けるのがお作法です
!pip install selenium
!pip install webdriver-manager

# ==============================================================================
# セクション1: 環境構築(Google Colab環境で実行)
# ==============================================================================
import logging
import os
import sys

# 意図したフォーマットでログ出力する
for handler in logging.root.handlers[:]:
    logging.root.removeHandler(handler)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Google Colab環境であるかを判定
IS_COLAB = "google.colab" in sys.modules

try:
    if IS_COLAB:
        logging.info(">>> Google Colab環境を検出しました。環境構築を開始します。")
        # Chromeのインストール
        if not os.path.exists("/usr/bin/google-chrome-stable"):
            get_ipython().system('apt-get update -qq && apt-get install -y wget gnupg google-chrome-stable -qq')
        # 必要なPythonライブラリのインストール
        get_ipython().system('pip install selenium webdriver-manager pandas beautifulsoup4 lxml -q')
        logging.info(">>> 環境構築が正常に完了しました。")
    else:
        logging.info(">>> ローカル環境とみなし、環境構築をスキップします。")
except Exception as e:
    logging.error(f"--- 環境構築中にエラーが発生しました: {e} ---")
    raise SystemExit("環境構築に失敗したため、処理を中断します。")


# ==============================================================================
# セクション2: ライブラリのインポート
# ==============================================================================
import time
import pandas as pd
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# Colabでファイルをダウンロードするために必要
if IS_COLAB:
    from google.colab import files

# --- WebDriverのセットアップ ---
options = Options()
options.add_argument('--headless') # ブラウザを画面に表示しないモード
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 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36') 
# ユーザーエージェントをPCに指定し、モバイルサイトへの意図しないリダイレクトを防ぐ

service = Service(ChromeDriverManager().install())
driver = None

# ==============================================================================
# セクション3: メイン処理
# ==============================================================================

try:
    # 1. WebDriverのインスタンスを生成
    driver = webdriver.Chrome(service=service, options=options)
    logging.info("WebDriverのセットアップが完了しました。")

    # 待機時間設定 (最大30秒)
    wait = WebDriverWait(driver, 30)

    # 2. https://www.python.org/ にアクセスする
    driver.get("https://www.python.org/")
    logging.info(f"'{driver.title}' にアクセスしました。")

    # 3. ニュースをクリックする
    logging.info("「News」リンクをクリックします...")
    # 要素がクリックされるのを防ぐオーバーレイなどを回避するため、JavaScriptでクリック
    news_link_locator = (By.XPATH, '//*[@id="news"]/a')
    news_link_element = wait.until(EC.presence_of_element_located(news_link_locator))
    driver.execute_script("arguments[0].click();", news_link_element)
    
    # 4. https://www.python.org/blogs/ にページ遷移したことを確認
    wait.until(EC.url_contains('/blogs/'))
    logging.info(f"ニュース一覧ページ ({driver.current_url}) に遷移しました。")

    # 5. "more" をクリックする
    logging.info("「more」リンクをクリックします...")
    more_link_locator = (By.XPATH, '//*[@id="content"]/div/section/div/div[1]/div/p/a')
    more_link_element = wait.until(EC.element_to_be_clickable(more_link_locator))
    driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", more_link_element) # 要素を画面中央に表示
    time.sleep(1) # スクロール待機
    more_link_element.click()

    # 6. https://blog.python.org/ または https://pythoninsider.blogspot.com/ に遷移するのを待つ
    wait.until(
        lambda d: 'blog.python.org' in d.current_url or 'pythoninsider.blogspot.com' in d.current_url
    )
    logging.info(f"ブログサイト ({driver.current_url}) に正常に遷移しました。")

    # サイトの種類(PC版かモバイル版か)を判定
    is_mobile_site = 'pythoninsider.blogspot.com' in driver.current_url
    if is_mobile_site:
        logging.info(">>> モバイル版ブログサイトのスクレイピングを開始します。")
    else:
        logging.info(">>> PC版ブログサイトのスクレイピングを開始します。")

    # 7. ブログの記事タイトルおよび投稿日を取得する
    news_data = []
    MAX_PAGES = 10 # 最大10ページまで取得

    for i in range(MAX_PAGES):
        logging.info(f"--- ページ {i + 1} のデータを取得します ---")

        # 記事コンテナが表示されるまで待機
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".blog-posts")))

        html = driver.page_source
        soup = BeautifulSoup(html, 'lxml')
        
        page_articles_found = 0
        
        # PCサイトとモバイルサイトで構造が異なるため、処理を分岐
        if is_mobile_site:
            # --- モバイルサイトのスクレイピングロジック ---
            # 日付ごとにグループ化されているコンテナを取得
            post_containers = soup.select(".blog-posts > div.date-posts")
            for container in post_containers:
                date_tag = container.select_one("div.date-header span")
                date = date_tag.get_text(strip=True) if date_tag else "日付不明"
                
                # コンテナ内の記事をすべて取得
                articles = container.select("div.mobile-post-outer")
                for article in articles:
                    title_tag = article.select_one("a > h3.post-title")
                    if title_tag:
                        title = title_tag.get_text(strip=True)
                        # 重複がないかチェックしてリストに追加
                        if title and not any(d['タイトル'] == title for d in news_data):
                            logging.info(f"  >> 発見: [投稿日: {date}] [タイトル: {title}]")
                            news_data.append({'投稿日': date, 'タイトル': title})
                            page_articles_found += 1
        else:
            # --- PCサイトのスクレイピングロジック ---
            # 日付ごとにグループ化されているコンテナを取得
            post_containers = soup.select(".blog-posts > div.date-outer")
            for container in post_containers:
                date_tag = container.select_one("h2.date-header span")
                date = date_tag.get_text(strip=True) if date_tag else "日付不明"

                # コンテナ内の記事をすべて取得
                articles = container.select("div.post-outer")
                for article in articles:
                    title_tag = article.select_one("h3.post-title a")
                    if title_tag:
                        title = title_tag.get_text(strip=True)
                        # 重複がないかチェックしてリストに追加
                        if title and not any(d['タイトル'] == title for d in news_data):
                            logging.info(f"  >> 発見: [投稿日: {date}] [タイトル: {title}]")
                            news_data.append({'投稿日': date, 'タイトル': title})
                            page_articles_found += 1

        logging.info(f"このページから新たに {page_articles_found} 件の記事を取得しました。")
        
        # 新しい記事が1件もなければ、最終ページとみなしてループを抜ける
        if page_articles_found == 0 and i > 0:
            logging.info("新たな記事がなかったため、最終ページと判断し、処理を終了します。")
            break

        # 次のページへ遷移 (最後のページでなければ)
        if i < MAX_PAGES - 1:
            try:
                # サイトによって「次のページ」ボタンのセレクタが違う
                if is_mobile_site:
                    next_button_locator = (By.ID, "blog-pager-older-link")
                else:
                    next_button_locator = (By.CSS_SELECTOR, "a.older-posts")
                
                # ボタンをクリックして次ページへ
                older_posts_button = driver.find_element(*next_button_locator)
                older_posts_button.click()
                logging.info(f"次のページ({i + 2}ページ目)へ遷移します...")
                time.sleep(2) # ページ遷移と読み込みのための待機
            except Exception:
                logging.info("「Older Posts」ボタンが見つかりませんでした。ここで取得を終了します。")
                break
    
    logging.info(f"合計 {len(news_data)} 件のニュースを取得しました。")

    # 8. CSVファイルに投稿日、記事タイトルをCSVファイルに出力し、ダウンロードする
    if news_data:
        df = pd.DataFrame(news_data, columns=['投稿日', 'タイトル'])
        csv_filename = 'python_blog_news.csv'
        df.to_csv(csv_filename, index=False, encoding='utf-8-sig')
        logging.info(f"取得したデータを'{csv_filename}'に保存しました。")
        
        # Google Colab環境の場合、ファイルをダウンロード
        if IS_COLAB:
            files.download(csv_filename)
            logging.info("ファイルのダウンロードが開始されました。")
    else:
        logging.warning("取得できたニュースがなかったため、CSVファイルは作成されませんでした。")

except Exception as e:
    logging.error(f"処理中に予期せぬエラーが発生しました: {e}", exc_info=True)
    if driver:
        # エラー発生時のスクリーンショットを保存
        error_filename = 'error_screenshot.png'
        driver.save_screenshot(error_filename)
        logging.info(f"エラー発生時のスクリーンショットを '{error_filename}' として保存しました。")
        if IS_COLAB:
            files.download(error_filename)
finally:
    if driver:
        driver.quit()
        logging.info("WebDriverを終了しました。")
エラーやクラッシュを防ぐためのTRY処理の補足説明

実行したいメインの処理を確実に実行するためにtry: の中にメイン処理を書きます。
ウェブスクレイピングは、以下のような様々な理由で失敗する可能性があります。

  • ネットワークの問題: インターネット接続が不安定、またはサイトが一時的にダウンしている。
  • サイトの仕様変更: 昨日まで存在したボタンのIDやクラス名が、今日になったら変更されている。
  • タイミングの問題: ページの読み込みが遅く、探している要素がまだ表示されていない。
  • 予期せぬポップアップ: 広告やクッキーの同意画面が表示され、クリックしたい要素を覆い隠してしまう。

上記の問題が起きると、Seleniumは「要素が見つかりません (NoSuchElementException)」などのエラー(例外)を発生させます。tryブロックがない場合、このエラーが発生した瞬間にプログラムは停止し、赤いエラーメッセージを大量に表示して終了してしまいます。

tryブロック内でエラーが発生すると、プログラムは即座にexceptブロックにジャンプします。これは、プログラムがクラッシュするのを防ぐセーフティネットの役割を果たします。

exceptブロックでは、以下のような「後処理」を行います。

  • エラーの記録: logging.error()を使って、どんなエラー(e)が起きたのかを記録します。これにより、後から「なぜ失敗したのか」を調査する手がかりになります。
  • 証拠保全: driver.save_screenshot()でエラー発生時の画面を画像として保存します。「サイトのレイアウトが変わっていた」「広告が表示されていた」など、視覚的に原因を特定するのに非常に役立ちます。
  • ユーザーへの通知: ユーザーに「エラーが発生しました」と親切に伝えることができます。

exceptがないと、ただクラッシュするだけで、何が原因だったのか分からずじまいになってしまいます。

finallyブロックは、tryブロックの処理が成功しようが、exceptブロックでエラーが捕捉されようが、必ず最後に実行されるという非常に重要なブロックです。

今回のコードで最も重要な役割は driver.quit() の実行です。

  • webdriver.Chrome() で起動したブラウザは、Pythonスクリプトとは別のプロセスとして動いています。
  • もしスクリプトがエラーで終了したり、正常に終了した場合でも、driver.quit() を呼び出さないと、ブラウザのプロセスがメモリ内に残り続けてしまいます
  • これが何度も繰り返されると、PCのメモリやCPUをどんどん消費し、動作が重くなる原因になります(ゾンビプロセス)。

finallyブロックに driver.quit() を書いておくことで、どんな状況でも確実にブラウザを閉じてリソースを解放することが保証されます。

上記のコードのようにtry….except….finallyとすることでクラッシュを防ぎ、安定的に動作させるコードの鉄則とも言える作法です。

なべくん

except(エラー処理)の組み立て方が重要です。

参考までに以下のリンクにGoogle Colabのサンプルコードを置いておくので参考にしてください。
参考:Google Colab

よくある質問(FAQ)

ローカルで使用しているseleniumが適切に動作せずにエラーばっか吐きます。

WebDriverと使用しているChromeDriverのバージョンが適切に合致していない可能性があります。以下の公式リンクより最新版のChromeDriverをダウンロードして使用されることをおすすめします。
参考:最新版ChromeDriverダウンロード

Seleniumでスクレイピングを行う際に、特に注意すべき法的なポイントはありますか?

PythonのSeleniumを使ってウェブスクレイピングを行う際は、いくつか法的に注意すべき点があります。

まず、最も重要なのは対象となるウェブサイトの利用規約(ToS)を確認することです。

多くのサイトで、自動プログラムによるアクセスやデータ収集を禁止している場合があります。

また、著作権のあるコンテンツの無断複製や配布は違法行為にあたります。

さらに、個人情報保護法に抵触しないよう、個人情報を収集する際は細心の注意を払う必要があります。

サーバーに過度な負荷をかけないよう、リクエスト間隔に適切な遅延(sleep)を設けるといった、ウェブサイトへの配慮も大切だとわたしは考えています。

毎回ブラウザとWebDriverのバージョンを手動で合わせるのが大変なのですが、自動化する方法はありますか?

はい、ブラウザとWebDriverのバージョンを自動で管理できる便利なライブラリがあります。

webdriver-manager」がその一つです。

このライブラリを使うと、あなたの環境にインストールされているChromeなどのブラウザのバージョンに合わせて、対応するWebDriverを自動でダウンロードし、パスを設定してくれます。

これにより、手動でWebDriverのバージョンを気にする手間を省き、スクレイピング環境のセットアップを大幅に簡素化できます。

インストールはpip install webdriver-managerで簡単にできます。

ログインに二段階認証(2FA)があるWebサイトでも、Seleniumでログイン処理を自動化できますか?

二段階認証が設定されているウェブサイトのログイン自動化は、非常に困難なケースが多いです。

Seleniumはブラウザ操作を自動化できますが、スマートフォンのアプリで生成される認証コードやSMSで送られてくる認証コードを自動で読み取る機能は基本的に持っていません。

そのため、現時点では二段階認証を完全に自動化する方法は現実的ではありません。

ただし、一度手動でログインしてセッション情報(クッキー)を保存し、そのセッションをSeleniumに引き継いでログイン状態を維持するといったアプローチで対応できる場合もあります。

無限スクロールのサイトから全てのデータを確実に取得するには、どのようにコードを書けば良いですか?

無限スクロールのサイトから全てのデータを確実に取得するには、スクロール操作とデータ読み込みの待機を繰り返すロジックを実装します。

具体的には、driver.execute_script(&quot;window.scrollTo(0, document.body.scrollHeight);&quot;)を使ってページの最下部までスクロールします。

スクロール後、新しいコンテンツが読み込まれるのを待つために、time.sleep()で数秒の静的待機を入れるか、WebDriverWaitexpected_conditionsを使って、特定の要素が表示されるまで待つ「明示的待機」を組み込みます。

このスクロールと待機、要素取得のループ処理を、新しいコンテンツがなくなるまで繰り返すことで、全てのデータを取得できます。

RequestsやBeautifulSoupだけで対応できるスクレイピングと比べて、Seleniumはどのくらい処理速度が遅いのでしょうか?

Seleniumは実際のブラウザを起動し、JavaScriptの実行や画像・CSSのレンダリングも行うため、RequestsやBeautifulSoupを使った静的なスクレイピングと比較して、処理速度が数十倍から数百倍遅くなる可能性があります。

RequestsはHTTPリクエストとレスポンスの処理に特化しており、余計なオーバーヘッドがないため非常に高速です。

Seleniumはブラウザを操作する分、複雑な処理が可能になるメリットがある一方、速度は犠牲になる点だと理解しておきましょう。

そのため、SeleniumはJavaScriptで動的に生成されるコンテンツや、ログイン、クリック操作が必要な場面に限定して使用することが推奨されています。

収集したCSVデータを、Pythonを使ってデータ分析に活かすための具体的なステップを教えてください。

Pythonで収集したCSVデータを分析に活かすには、主に以下のステップで進めます。

まず、強力なデータ分析ライブラリである「Pandas」を使ってCSVファイルをデータフレームとして読み込みます。

次に、データ型が適切か確認し、欠損値の処理、重複データの削除、不要な列の削除など、データの前処理を行います。

その後、MatplotlibやSeabornといった可視化ライブラリを用いてデータの傾向をグラフで可視化します。

さらに進んで、Scikit-learnなどの機械学習ライブラリを使い、データのパターンを分析して予測モデルを構築するといった高度な分析も可能です。

あなたのデータ分析スキルと組み合わせることで、CSVデータから新たな洞察やビジネス価値を生み出すことができるのです。

この記事を書いた人

Watanabeのアバター Watanabe サイト運営者

2020年よりブログ開始。
SEOが思いのほか性にあっていたようで現在に至る。
モットーは「勝率の高い選択をする」
AIは活用するが吉、最後は人間が息を吹き込む。
アートと科学を追求し、日々精進。
―――
収益:6~7桁をウゴウゴ。
サイト:ペラサイト~中規模サイトまで運営中。
案件:1000円以上の案件をメインに取組中。
打ち手:ブラックSEO~ホワイトSEOまで
―――

目次