ChatGPTやClaude Codeに「Seleniumでスクレイピングして」と頼むと、今もwebdriver.Chrome(executable_path=...)やwebdriver_managerのChromeDriverManager().install()を使ったコードが出てきます。executable_pathはSelenium 4.10で削除されたので、このコードは今のSeleniumでは起動しません。この記事では、AIにServiceとSelenium Managerを使う書き方へ直させる指示文を実物で載せます。あわせて、Chromeが自動更新されてドライバと合わなくなっても毎日止まらない置き場所まで説明します。
AIが今も出すexecutable_pathとChromeDriverManagerの何が古いのか
2つの変更点:4.6のSelenium Managerと4.10のexecutable_path削除
以前は、Chromeを操作するための中継プログラム(ChromeDriver。Chromeとバージョンを揃える必要がある)を自分で用意する必要がありました。そのパスをexecutable_pathで渡すか、外部ライブラリのwebdriver_managerにダウンロードさせるのが定番でした。生成AIはこの時期のコードを大量に学習しているので、今もこの形を出しがちです。
Selenium側では次の2つが変わっています。
- Selenium 4.6以降:Selenium本体にSelenium Managerが入りました。ドライバのパスを指定しなければ、インストール済みのChromeに合うドライバを自動で取得します。
- Selenium 4.10:
webdriver.Chrome()のexecutable_path引数が削除されました。パスを指定する場合はServiceオブジェクトに渡します。
AIのコードをそのまま新しいSeleniumで動かすと、次のエラーで起動直後に止まります。引数そのものが無くなったので、ドライバを入れ直しても解決しません。
TypeError: WebDriver.__init__() got an unexpected keyword argument 'executable_path'新旧の書き方の比較
項目 | 以前の書き方(AIが出しがち) | 今の書き方 |
|---|---|---|
ドライバの用意 |
| 何もしない(Selenium Managerが自動取得) |
ドライバの渡し方 |
|
|
要素の取得 |
|
|
待ち方 |
|
|
Chrome更新時 | ドライバが古いまま残り、不一致で起動しなくなる | 次回の起動時に合うドライバを取り直す |
実行環境の確認
python3 -V
pip install -U selenium requests
pip show seleniumpip show seleniumのVersion:が4.10以上なら、executable_pathは使えません。4.6より古い場合はSelenium Managerも入っていないので、まず-Uで上げてください。対応するPythonのバージョンは、SeleniumのPyPIページに書かれています。webdriver-managerは今の書き方では不要です。新しく入れる必要はありません。
ChatGPT・Claude Codeへの指示文の実物:最初の指示→問題点→直させる指示
最初の指示と、出てきたコード
Seleniumの書き方を知らない人が出しそうな、よくある指示です。
Pythonで https://example.com/news のニュース一覧から
記事タイトルを全部取り出すスクリプトを書いてください。
JavaScriptで表示されるページなのでSeleniumを使ってください。
失敗したらSlackに通知してください。返ってくるコードは、たとえば次のような形です。
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
import requests
import time
WEBHOOK_URL = "https://hooks.example.com/xxxx"
try:
driver = webdriver.Chrome(executable_path=ChromeDriverManager().install())
driver.get("https://example.com/news")
time.sleep(5)
titles = driver.find_elements_by_css_selector("article h2 a")
for t in titles:
print(t.text)
except:
passこのコードで自分で確認すべき5か所
コードを読み慣れていなくても、次の語を検索すれば見つけられます。エディタの検索機能かgrepを使ってください。
grep -nE "executable_path|webdriver_manager|ChromeDriverManager|find_element_by_|except:|except Exception: *pass|hooks\.|api_key *=|token *=" scrape.pyexecutable_path=をwebdriver.Chrome()に直接渡している:Selenium 4.10以降は起動しません。ChromeDriverManager().install():外部ライブラリに頼る古い方法です。今はSelenium Managerがドライバを用意します。find_element(s)_by_xxx:今のSeleniumには無いメソッドです。Byを使う形に直します。except: pass:最も危険な箇所です。失敗しても何も出さずに正常終了するので、cronやsystemdからは「成功」に見えます。通知の処理までtryの中にあり、そもそも通知が飛びません。- WebhookのURLやAPIキーをコードに直書きしている:GitHubに上げたり人に渡したりした時点で漏れます。環境変数から読む形にします。
アクセス間隔が無いことも確認してください。1ページだけなら問題になりにくいです。しかしAIに「全ページ取って」と頼むと、待ち時間なしでページ送りを連打するループが出てきます。
AIのコードを点検せずに使う危うさは、最近の調査でも裏付けられています。2026年10月21日に公表された調査では、AlibabaのQwen3-Coderが生成したWebアプリのうち、米防衛企業向けと指定した場合に70%でサイバー攻撃が成立したと報告されました。Anthropicも2026年9月23日のブログで、AI駆動開発のボトルネックはコード生成ではなく、その後の検証・修正・展開に移っていると指摘しています。Claude Codeでは2026年8月14日から、Pro・Max・TeamのセッションでAutoモードがデフォルトになりました。各アクションの確認が不要なぶん、古いコードがそのまま実行されてから失敗に気づく場面も増えます。
直させる指示文
「エラーが出ました」だけを貼るより、使うバージョンと、使ってほしくない書き方を名指しするほうが確実に直ります。
次の条件で書き直してください。
- Seleniumは4.10以上を使う(pip show selenium で確認済み)。
executable_path は webdriver.Chrome() に渡さない。
- webdriver_manager は使わない。ドライバのパスは指定せず、
Selenium Manager に自動取得させる(webdriver.Chrome(options=options) の形)。
- 要素の取得は find_element(By.CSS_SELECTOR, ...) を使う。
- time.sleep で表示を待たず、WebDriverWait で要素が出るまで待つ。
- ヘッドレスで動かす。
- except: pass は使わない。失敗したらログに残し、Slackに通知し、
終了コード1で終了する。成功したら0で終了する。
- 取得件数が0件なら失敗として扱う。
- 複数ページを取るときは1ページごとに3秒以上空ける。
- SlackのWebhook URLはコードに書かず、環境変数 SLACK_WEBHOOK_URL から読む。
- 起動時に Chrome と ChromeDriver のバージョンをログに出す。
- driver.quit() は必ず finally で呼ぶ。Serviceとselenium Managerで書き直した、毎日動かせるコード
直った後のコード全体
import logging
import os
import sys
import time
import requests
from selenium import webdriver
from selenium.common.exceptions import TimeoutException, WebDriverException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
TARGET_URLS = [
"https://example.com/news?page=1",
"https://example.com/news?page=2",
]
ITEM_SELECTOR = "article h2 a"
INTERVAL_SECONDS = 3 # ページ間の待ち時間(相手サーバーへの配慮)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger(__name__)
def build_driver():
options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,2000")
# ドライバのパスは書かない。Selenium Manager がChromeに合うものを用意する
driver = webdriver.Chrome(options=options)
driver.set_page_load_timeout(30)
return driver
def fetch_titles(driver, url):
driver.get(url)
WebDriverWait(driver, 15).until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, ITEM_SELECTOR))
)
elements = driver.find_elements(By.CSS_SELECTOR, ITEM_SELECTOR)
return [e.text.strip() for e in elements if e.text.strip()]
def notify(message):
url = os.environ.get("SLACK_WEBHOOK_URL")
if not url:
log.warning("SLACK_WEBHOOK_URL が未設定のため通知を省略")
return
try:
requests.post(url, json={"text": message}, timeout=10)
except requests.RequestException as e:
log.error("通知に失敗: %s", e)
def main():
driver = None
try:
driver = build_driver()
caps = driver.capabilities
log.info(
"Chrome %s / ChromeDriver %s",
caps.get("browserVersion"),
caps.get("chrome", {}).get("chromedriverVersion", "").split(" ")[0],
)
titles = []
for i, url in enumerate(TARGET_URLS):
if i > 0:
time.sleep(INTERVAL_SECONDS)
titles.extend(fetch_titles(driver, url))
if not titles:
raise RuntimeError("0件だった。ページの構造が変わった可能性がある")
for t in titles:
print(t)
log.info("%d件取得", len(titles))
return 0
except (TimeoutException, WebDriverException, RuntimeError) as e:
log.exception("取得に失敗")
notify(f"スクレイピング失敗: {type(e).__name__}: {e}")
return 1
finally:
if driver is not None:
driver.quit()
if __name__ == "__main__":
sys.exit(main())ブロックごとの説明
build_driver():ここが今回の主役です。webdriver.Chrome(options=options)にはドライバのパスを渡していません。パスが無いと、SeleniumはSelenium Managerを呼んでChromeのバージョンを調べ、合うドライバを用意します。--headless=newは画面を出さずにChromeを動かす指定です。set_page_load_timeout(30)を入れておくと、応答しないページで永久に待ち続けずに済みます。- バージョンのログ出力:
driver.capabilitiesから、実際に起動したChromeとChromeDriverのバージョンを取り出して記録します。後で止まったとき、Chromeの更新が原因かどうかをログだけで判断できます。 fetch_titles():time.sleep(5)の決め打ちをやめ、WebDriverWaitで要素が現れるまで最大15秒待ちます。表示が速い日は速く終わります。15秒経っても出なければTimeoutExceptionになり、失敗として扱われます。- ページ送りの
time.sleep(INTERVAL_SECONDS):2ページ目以降は3秒空けてから開きます。表示待ちではなく、相手のサーバーに配慮するための待ち時間です。 - 0件チェック:サイトの構造が変わってセレクタ(取り出す場所の指定)が外れても、Seleniumはエラーを出しません。空のリストが返るだけです。0件を失敗にしないと、「毎日成功しているのに何も取れていない」状態に気づけません。
notify():Webhook URLは環境変数から読みます。通知自体が失敗しても、本体の結果を握りつぶさないよう別にtryで囲みます。return 0/return 1とsys.exit():終了コードで成否を外に伝えます。cronやsystemdはこれを見て成功・失敗を記録します。finally: driver.quit():失敗してもChromeのプロセスを必ず閉じます。閉じ忘れると、毎日の実行のたびにChromeが残り、メモリを食いつぶします。
パスを指定したいときだけServiceを使う
社内の決まりで特定のドライバを使う場合など、パスを固定したいときはServiceに渡します。
from selenium.webdriver.chrome.service import Service
service = Service(executable_path="/home/username/bin/chromedriver")
driver = webdriver.Chrome(service=service, options=options)ただしこの形では、Chromeが更新されるたびにドライバを自分で入れ替える必要があります。毎日動かすスクリプトでは、パスを書かずSelenium Managerに任せるほうが止まりにくくなります。
それでもドライバ不一致が起きるときに見る場所
- 古いchromedriverがPATH上に残っている:昔手で入れたドライバがPATH(コマンドを探す場所の一覧)にあると、それが使われて不一致が再発することがあります。
which chromedriverで確認し、不要なら削除します。 - ネットワークに出られない:Selenium Managerは初回やChrome更新後にドライバを取得するため、外部へ通信できる必要があります。取得したドライバは
/home/username/.cache/selenium/にキャッシュされます。 - コードのどこかに
ChromeDriverManagerが残っている:AIが一部だけ直して、別のファイルに古い書き方を残すことがあります。前の節のgrepをプロジェクト全体にかけてください。
通信の一時的な失敗に備えて再試行を入れたい場合は、method_whitelist削除と429再試行|26年10月で、AIが出しがちな古い再試行の書き方と今の書き方を扱っています。
取得先サイトへの配慮:robots.txt・利用規約・アクセス間隔
Seleniumは本物のブラウザを動かすため、画像やスクリプトも含めて読み込みます。requestsで1ファイル取るより、相手のサーバーへの負荷は大きくなります。2025年12月13日のQiita記事でも、知的財産権、robots.txt、利用規約、サーバー負荷への配慮の重要性が強調されています。最低限、次を守ってください。
- 利用規約を読む:自動取得を禁止しているサイトは対象にしません。公式APIがあるなら、そちらを使います。
- robots.txtを確認する:クローラー(自動巡回プログラム)に取得してほしくない場所が書かれています。
- 間隔を空ける:1ページごとに数秒空け、取得するのは必要なページだけにします。毎日1回で足りるものを1時間おきに回さないようにします。
- ブロックされたら止める:アクセスを拒否されたときにUser-Agentを偽装したりIPを変えたりして回避するのは、相手の意思に反する行為です。ここでは扱いません。
robots.txtの確認は標準ライブラリだけでできます。AIにも「取得前にrobots.txtを確認する処理を入れて」と頼めば、この形が出てきます。
from urllib.robotparser import RobotFileParser
rp = RobotFileParser("https://example.com/robots.txt")
rp.read()
for url in TARGET_URLS:
if not rp.can_fetch("*", url):
log.warning("robots.txtで許可されていないため取得しない: %s", url)can_fetch()がFalseを返したURLは取得対象から外します。rp.crawl_delay("*")で待ち時間が指定されていれば、INTERVAL_SECONDSをそれ以上にしてください。
Chromeの自動更新でも止めずに動かすには:手元PC・共有サーバーcron・VPSの比較
コードが直っても、置き場所を間違えると毎日は動きません。Seleniumのスクリプトでは、Chromeが入れられるかとChromeが更新されたときに追従できるかが分かれ目になります。
3つの置き場所が止まる条件
置き場所 | 向いている場合 | 止まる条件 |
|---|---|---|
手元のPC | 平日の日中だけ動けばよい、試しに数日回したい | スリープ・シャットダウン中は実行されない。ノートPCのフタを閉じると止まる。OSの更新による再起動でも止まる |
共有レンタルサーバーのcron |
| Chromeを自分で入れられないことが多く、Seleniumはそもそも動かないことが多い。メモリやプロセス数の制限で途中で強制終了されることもある |
VPS(自分専用の仮想サーバー) | Seleniumを毎日決まった時刻に動かしたい | Chromeの更新後にSelenium Managerがドライバを取得できない(通信制限)、ディスクやメモリが足りない、契約・支払いが切れる |
Seleniumを毎日止めずに動かすなら、結論はVPSです。共有サーバーのcronで足りるのは、JavaScriptを使わないページをrequestsで取る場合だけです。手元のPCは、閉じている日に動かないことを許容できるときに限ります。
VPSでの定期実行:systemd timerの例
VPSにChromeを入れると、OSの更新に合わせてChromeも上がります。パスを書かないコードにしておけば、更新後の最初の実行でSelenium Managerが合うドライバを取り直すので、手で入れ替える作業は要りません。
定期実行は、cronよりsystemd timerのほうが失敗の記録が残り、扱いやすくなります。
# /etc/systemd/system/scrape.service
[Unit]
Description=daily scrape
[Service]
Type=oneshot
User=username
WorkingDirectory=/home/username/scrape
EnvironmentFile=/home/username/scrape/.env
ExecStart=/home/username/scrape/.venv/bin/python scrape.py
TimeoutStartSec=900# /etc/systemd/system/scrape.timer
[Unit]
Description=daily scrape timer
[Timer]
OnCalendar=*-*-* 07:00:00
Persistent=true
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl enable --now scrape.timer
systemctl list-timers scrape.timer
journalctl -u scrape.service -n 50EnvironmentFile:.envにSLACK_WEBHOOK_URL=...の1行を書き、chmod 600で自分だけが読めるようにします。Gitで管理しているなら.gitignoreに必ず入れてください。TimeoutStartSec=900:Chromeが固まっても15分で打ち切ります。打ち切られた回は失敗として記録されます。Persistent=true:サーバーが止まっていて実行時刻を逃した場合、起動時に1回まとめて実行されます。- 終了コード:スクリプトが
1を返すと、systemctl status scrape.serviceにfailedと表示されます。except: passのままだと、ここが毎日successになります。
OnCalendarの時刻はサーバーのタイムゾーンで解釈されます。VPSは初期設定がUTCのことが多く、日本時間の7時に動かしたつもりが16時に動く、ということが起きます。タイムゾーンの指定方法はutcnow非推奨とcronの日本時間指定|26年10月にまとめています。
止まったときにAIへ渡す情報
それでも止まった日は、journalctlの出力からChrome・ChromeDriverのバージョンの行とエラーの行を切り出し、次のように渡します。
Selenium 4.10以上、webdriver_managerは使わずSelenium Managerに任せる構成で、
毎日systemd timerで動かしているスクリプトが今朝失敗しました。
ログを貼ります。executable_pathやChromeDriverManagerを使う修正は提案しないでください。
原因の候補と、どの行をどう直すかだけを示してください。
(ここにログ)最初に「使わない書き方」を伝えておかないと、AIは学習時点の解決策(ドライバを手で入れ直す、webdriver_managerを入れる)に戻りがちです。
関連する選択肢
毎日決まった時刻にスクリプトを動かすだけなら、共有のレンタルサーバーでも足ります。cronが使えるプランを選べば、自分でOSを管理する必要はありません。
※ 広告を含みます(A8.net)。リンク経由でお申し込みがあった場合、手数料を受け取ることがあります。
まとめ
ChatGPTやClaude Codeが出すSeleniumのコードには、今もwebdriver.Chrome(executable_path=...)やChromeDriverManager().install()が残っています。executable_pathはSelenium 4.10で削除され、4.6以降はSelenium Managerがドライバを自動取得します。AIに直させるときは、「4.10以上を使う」「webdriver_managerは使わない」「パスは指定しない」と、使う版と使わない書き方を名指しします。あわせてexcept: pass・0件の素通り・アクセス間隔の欠如・Webhook URLの直書きの4点をgrepで自分で確認してください。毎日動かすならChromeを入れられて通信もできるVPSに置き、systemd timerと終了コードで失敗を記録します。そうすれば、Chromeの自動更新があっても止まりません。