ChatGPTやClaude Codeに「毎日価格を記録するスクリプトを書いて」と頼んだのに、動かすと find_element_by_xpath で止まる。そんな経験はありませんか。生成AIは今でもSelenium 3系の古い書き方を出してくることがあります。この記事では、AIに書かせたSeleniumの価格監視コードのどこが危ういかを見分ける方法と、By.XPATH・Selenium Manager・headless指定の今の書き方に直させる指示文を実物で示します。最後に、そのスクリプトを毎日止めずに動かす置き場所まで扱います。

AIが今も出すSelenium 3系の書き方と、4系で動かない3箇所

2026年9月時点でも、ChatGPT(9月4日公開のGPT-6 Astra)やClaude Code(9月22日にClaude Opus 5.5が既定モデルになった)は新しいモデルに入れ替わっています。それでも、ネット上に大量に残っている古いサンプルコードに引きずられることがあります。スクレイピングでとくに多いのが次の3つです。

AIが出しがちな書き方

今の状態

今の書き方

driver.find_element_by_xpath(...)

Selenium 4.3で削除

driver.find_element(By.XPATH, ...)

webdriver.Chrome(executable_path="...")

Selenium 4.10で削除

パスを渡さず webdriver.Chrome(options=options)

chromedriverを手動でダウンロードして置く

不要

Selenium同梱のSelenium Managerがドライバを用意する

エラーを見るのは「なぜ書き換えが要るか」を知るためだけ

古い書き方のまま最新のSeleniumで動かすと、次のように止まります。エラーそのものを追いかける必要はありません。「そのメソッドや引数がもう存在しない」という意味だと分かれば十分です。

AttributeError: 'WebDriver' object has no attribute 'find_element_by_xpath'
TypeError: WebDriver.__init__() got an unexpected keyword argument 'executable_path'

手元に入っているSeleniumのバージョンは pip show selenium で確かめられます。表示された Version: が4.10以上なら、上の表の左列の書き方はどれも動きません。

chromedriver手動管理が危ない理由

chromedriver(Chromeを外から操作するための中継プログラム)を手動で置く方式は、Chromeの自動更新でバージョンがずれると動かなくなります。毎日動かす監視スクリプトでは「ある朝から黙って止まる」原因の代表です。今のSeleniumでは、ドライバのパスを渡さなければSelenium Managerが合うドライバを自動で用意します。AIが webdriver-manager という別ライブラリを勧めてくることもありますが、Selenium Managerが使えるなら追加する必要はありません。

ChatGPT・Claude Codeへの指示文を実物で:最初の依頼から直るまで

最初の指示(よくある頼み方)

Pythonで、ECサイトの商品ページから価格を取得して
CSVに毎日記録するスクリプトを書いてください。
Seleniumを使ってください。価格が下がったらSlackに通知したいです。

出てきたコード(問題のある例)

from selenium import webdriver

options = webdriver.ChromeOptions()
options.headless = True
driver = webdriver.Chrome(executable_path="/home/username/chromedriver", options=options)

WEBHOOK_URL = "https://hooks.example.com/services/XXXXXXXX"

urls = ["https://example.com/items/1001", "https://example.com/items/1002"]
for url in urls:
    try:
        driver.get(url)
        price = driver.find_element_by_xpath("//span[@class='price']").text
        with open("prices.csv", "a") as f:
            f.write(f"{url},{price}\n")
    except:
        pass

driver.quit()

自分で見つけられる確認ポイント5つ

コードを読み慣れていなくても、次の語を検索(Ctrl+F)するだけで見つけられます。

  1. find_element_by_ が含まれているか:あれば古い書き方です。By. を使う形に直させます。
  2. executable_path や chromedriver のファイルパスがあるか:あればSelenium 4.10以降では動きません。
  3. time.sleep がループの中にあるか:無ければ複数ページへ間隔なしにアクセスしています。相手のサーバーへの負荷になります。
  4. except: の直後が pass になっていないか:失敗しても何も記録されず、正常終了したように見えます。毎日動かす用途では一番危険な書き方です。
  5. https://hooks. や KEY = のような値がコードに直書きされていないか:通知用URLやAPIキーをコードに書くと、ファイルを共有した時点で漏れます。環境変数から読む形に直させます。

ほかに、options.headless = True という書き方も今のSeleniumでは使いません。headless(画面を出さずにブラウザを動かす)モードは options.add_argument("--headless=new") で指定します。

直させる指示文

「エラーを直して」とだけ頼むと、AIは古い書き方のまま部分的に継ぎ足すことがあります。何を使って書くかを先に指定するのがコツです。

次の条件で書き直してください。
1. Selenium 4の書き方にする。find_element_by_* は使わず、
   from selenium.webdriver.common.by import By を使って find_element(By.XPATH, ...) で書く
2. executable_path と chromedriver のパス指定は削除する。
   ドライバはSelenium Managerに任せる
3. headless は options.add_argument("--headless=new") で指定する
4. 要素の取得は WebDriverWait で最大15秒待つ
5. ページを開く間隔を10秒空ける
6. except: pass は禁止。失敗はログに残し、1件でも失敗したら
   終了コード1で終わる
7. 通知先URLはコードに書かず、環境変数 PRICE_WATCH_WEBHOOK から読む
8. 価格は数字だけを取り出して整数にする
各ブロックが何をしているか、コメントで短く説明してください。

AIが条件を読み落としたときに備えて、返ってきたコードを上の確認ポイント5つでもう一度検索してください。AIに先に不明点を質問させてから書かせる方法は ChatGPTに先に質問させるプロンプト2026年9月版 でも扱っています。

By.XPATH・Selenium Manager・--headless=newで書いた価格取得コード

実行環境

  • Python 3系(Seleniumが対応するPythonのバージョンはPyPIのseleniumのページで確認してください。手元は python3 -V で確認できます)
  • パッケージ:pip install selenium requests
  • Google Chrome本体がインストールされていること(Selenium Managerが用意するのはドライバ側です)
  • Seleniumは4.10以上を前提にします。古い版が入っている環境では pip install -U selenium で上げ、pip show selenium で確認します

仮想環境(プロジェクト専用のPython環境)を作っておくと、後でcronから呼ぶときにパスがはっきりします。

mkdir -p /home/username/price_watch && cd /home/username/price_watch
python3 -m venv .venv
.venv/bin/pip install selenium requests

直った後のコード

import csv
import logging
import os
import sys
import time
from datetime import datetime

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

# --- 設定 ---
TARGETS = [
    ("商品A", "https://example.com/items/1001"),
    ("商品B", "https://example.com/items/1002"),
]
PRICE_XPATH = "//span[@class='price']"
INTERVAL_SEC = 10
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
OUT_CSV = os.path.join(BASE_DIR, "prices.csv")
WEBHOOK_URL = os.environ.get("PRICE_WATCH_WEBHOOK")

logging.basicConfig(
    filename=os.path.join(BASE_DIR, "price_watch.log"),
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
)


# --- ブラウザの起動 ---
def make_driver():
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--window-size=1280,2000")
    return webdriver.Chrome(options=options)


# --- 1ページから価格を取る ---
def fetch_price(driver, url):
    driver.get(url)
    el = WebDriverWait(driver, 15).until(
        EC.presence_of_element_located((By.XPATH, PRICE_XPATH))
    )
    digits = "".join(ch for ch in el.text if ch.isdigit())
    if not digits:
        raise ValueError(f"価格の数字が読めません: {el.text!r}")
    return int(digits)


# --- 通知(未設定なら何もしない) ---
def notify(message):
    if not WEBHOOK_URL:
        return
    try:
        requests.post(WEBHOOK_URL, json={"text": message}, timeout=10)
    except requests.RequestException as e:
        logging.warning("通知に失敗: %s", e)


# --- 本体 ---
def main():
    rows, failures = [], []
    driver = make_driver()
    try:
        for i, (name, url) in enumerate(TARGETS):
            if i:
                time.sleep(INTERVAL_SEC)
            try:
                price = fetch_price(driver, url)
                rows.append([datetime.now().isoformat(timespec="seconds"), name, price])
                logging.info("%s: %d円", name, price)
            except (TimeoutException, ValueError, WebDriverException) as e:
                logging.error("%s の取得に失敗: %s", name, e)
                failures.append(name)
    finally:
        driver.quit()

    with open(OUT_CSV, "a", newline="", encoding="utf-8") as f:
        csv.writer(f).writerows(rows)

    if failures:
        notify(f"価格取得に失敗: {', '.join(failures)}")
        return 1
    return 0


if __name__ == "__main__":
    sys.exit(main())

ブロックごとの説明

  • 設定:監視する商品とURL、価格の場所を示すXPath、アクセス間隔をまとめています。CSVとログの保存先はスクリプトのある場所を基準にしているので、cronのように作業ディレクトリが違う起動方法でも同じ場所に出ます。通知先URLは os.environ.get で環境変数から読むだけで、コードには書きません。
  • ブラウザの起動:webdriver.Chrome(options=options) にドライバのパスを渡していないのがポイントです。ここでSelenium Managerが合うドライバを用意します。--headless=new で画面なしで動かします。
  • 1ページから価格を取る:find_element(By.XPATH, ...) 相当の探し方を WebDriverWait で包み、最大15秒待ちます。JavaScriptで後から価格が描かれるページでも、表示されるまで待ってから読みます。「¥12,800」のような表記から数字だけを抜き出し、読めなければ例外にします。
  • 通知:環境変数が無ければ何もしません。通知自体が失敗してもスクリプトは止めず、ログに残すだけにしています。
  • 本体:2件目以降は10秒空けてからアクセスします。失敗した商品は名前を控えておき、finally で必ずブラウザを閉じます(閉じ忘れると毎日Chromeが溜まってメモリを使い切ります)。1件でも失敗したら通知し、終了コード1で終わります。

最後の終了コードが、次の章の「止まったことに気づけるか」を左右します。AIの元のコードは except: pass だったため、サイトの構造が変わって価格が1件も取れなくても「正常終了」していました。

取得先サイトへの配慮:robots.txt・利用規約・アクセス間隔

AIはこの部分をほぼ書いてくれません。自分で確認してから動かしてください。

  • robots.txt:https://example.com/robots.txt のようにサイトのルートに置かれています。監視したいページのパスが Disallow に入っていれば、そのページは自動取得の対象にしません。
  • 利用規約:「自動的な手段によるアクセスの禁止」が書かれているサイトもあります。その場合はスクレイピングをやめ、公式APIや価格通知機能が無いかを探します。
  • アクセス間隔:価格の定点観測なら1日1回で十分なことがほとんどです。1回の実行の中でも、上のコードのように1ページごとに間隔を空けます。監視する商品を増やすときも、並列化して一気に取りに行く書き方はさせないでください。
  • ブロックされたら止める:アクセスを拒否されたときに、身元を偽ったり制限を回避したりする方向でAIに直させるのはやめてください。拒否されたのはサイト側の意思表示です。

2026年3月31日に総務省と経済産業省が公表した「AI事業者ガイドライン(第1.2版)」でも、外部に影響を与える操作の前に人間の判断を挟む(Human-in-the-Loop)考え方が明記されました。AIに書かせたスクリプトを外部サイトに向けて動かす前に、上の確認を人が行うのはこの考え方に沿った運用です。

毎日止めずに回す置き場所:手元PC・共有サーバーのcron・VPS

コードが直っても、置き場所を間違えると「気づいたら先週から止まっていた」になります。それぞれ止まる条件が違います。

置き場所

向いている条件

止まる条件

手元のPC

まず1〜2週間試す段階。PCを毎日決まった時間に起動している

スリープ・電源オフ・フタを閉じる。OS更新で再起動した後に予約が動かない

共有レンタルサーバーのcron

requestsだけで取れる静的なページ。Chromeが要らない

Chrome本体を入れられない環境ではSeleniumが起動できない。実行時間やメモリの上限で打ち切られる

VPS

Seleniumでheadless Chromeを毎日動かす。監視対象が今後増える

自分で入れたChromeのメモリ不足。OS更新後にChromeが入れ替わる。料金の支払い停止

Seleniumを使うならVPSが基本

価格がJavaScriptで描かれるページでSeleniumが必要なら、Chrome本体を自分でインストールできる環境が前提になります。共有レンタルサーバーはcronが使えても、この点で詰まることが多いです。逆に、価格がHTMLにそのまま入っているページなら、AIに「Seleniumを使わずrequestsとBeautifulSoupで書き直して」と頼めば共有サーバーのcronで十分に回ります。ブラウザを動かさないぶん、相手のサーバーへの負荷も軽くなります。

cronで毎日9時に動かす例

cronはログイン時の環境変数を引き継がないので、Pythonは仮想環境のフルパスで呼び、通知先もcrontabの中で渡します。crontab -e で次の2行を足します。

PRICE_WATCH_WEBHOOK=ここに通知先URL
0 9 * * * cd /home/username/price_watch && /home/username/price_watch/.venv/bin/python price_watch.py >> cron.log 2>&1

crontabは他人に見せないファイルですが、通知先URLを置くのはここだけにし、スクリプト本体には書かないでください。

VPSならsystemd timerで「逃した回」も拾う

VPSが再起動などで止まっていた時間帯の分を、起動後に実行し直させたいならsystemd timerが便利です。Persistent=true がその指定です。

# /home/username/.config/systemd/user/price-watch.service
[Service]
Type=oneshot
WorkingDirectory=/home/username/price_watch
EnvironmentFile=/home/username/price_watch/.env
ExecStart=/home/username/price_watch/.venv/bin/python price_watch.py

# /home/username/.config/systemd/user/price-watch.timer
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true

[Install]
WantedBy=timers.target
chmod 600 /home/username/price_watch/.env
systemctl --user daemon-reload
systemctl --user enable --now price-watch.timer
loginctl enable-linger username

.env には PRICE_WATCH_WEBHOOK=... の1行だけを書き、権限を600にして本人以外が読めないようにします。loginctl enable-linger を入れないと、ログアウトした時点でユーザー単位のtimerが止まります。スクリプトが終了コード1で終われば、systemctl --user status price-watch.service に失敗として残ります。ログが増えすぎていないかの点検方法は journalctl --disk-usageでログ点検2026 を参考にしてください。

「動いている」を毎週1回確かめる

  • prices.csv の最終行の日付が今日になっているか
  • price_watch.log に ERROR が続いていないか(サイトのHTML構造が変わるとXPathが外れて毎日失敗します)
  • 失敗が続いたら、エラーの行とページのHTMLの該当部分をAIに渡し、「XPathだけを直して。他の書き方は変えないで」と範囲を絞って頼む

関連する選択肢

毎日決まった時刻にスクリプトを動かすだけなら、共有のレンタルサーバーでも足ります。cronが使えるプランを選べば、自分でOSを管理する必要はありません。

レンタルサーバー エックスサーバー

※ 広告を含みます(A8.net)。リンク経由でお申し込みがあった場合、手数料を受け取ることがあります。

まとめ:AIの古いSeleniumを直して、毎日動く形にする

ChatGPTやClaude Codeが新しいモデルに替わっても、スクレイピングのコードでは find_element_by_xpath(Selenium 4.3で削除)や executable_path(4.10で削除)、chromedriverの手動配置といった古い書き方が出てくることがあります。直させるときは「エラーを直して」ではなく、By.XPATH・Selenium Manager・--headless=new・WebDriverWait・アクセス間隔・例外を握りつぶさない・通知先は環境変数と、使う書き方を先に指定するのが近道です。返ってきたコードは find_element_by_・executable_path・except:・time.sleep・直書きのURLを検索して自分で確かめます。動かす前にrobots.txtと利用規約を確認し、1日1回・ページごとに間隔を空けて取得します。置き場所は、Seleniumが要るならChromeを入れられるVPSでsystemd timer、静的ページならrequestsに書き換えて共有サーバーのcron、手元PCは試運転までと考えると止まりにくくなります。