スクレイピング時にcaptchaを避ける方法(ルールを守ったまま)
スクレイピング中のcaptchaのほとんどは、自動化されたトラフィックに見えるアクセスへの反応です。もっとも安価な対策は、captchaを引き起こさないことです。そのためには、まず正規のデータソースを探し、サイトのルールで許可されている範囲を確認し、リクエストの数を減らして間隔を広げ、同じページを二度取得しないことが必要です。captchaを解くのは、避けられない場合の最後の手段であり、基本の計画ではありません。
まず公式APIやフィードを確認する
スクレイピングされることの多いサイトの多くは、captchaなしで同じデータを取得する方法をすでに提供しています。公開APIやデータエクスポート、RSSやサイトマップのフィード、パートナープログラムなどです。公式のデータソースのほうが信頼性が高く、予告なしに変わりにくく、サイトの運営者がどのように利用されるかを決められます。次のようなものを探してください。
- フッターにある開発者向けまたはAPIのセクション
- ページの一覧を得るための
sitemap.xml - ページの
<head>にあるRSSまたはAtomのリンク - 一括エクスポート用の「データ」または「ダウンロード」のページ
必要なデータがこれらでまかなえるなら、それを使ってください。HTMLをまったくリクエストしなければ、captchaのほとんどは避けられます。
robots.txtと要求されている間隔を守る
robots.txtには、自動化されたクライアントが取得してよいパスと、場合によってはリクエスト間で待つべき時間が書かれています。Pythonの標準ライブラリで読み込めます。
import time
from urllib.robotparser import RobotFileParser
import requests
USER_AGENT = "ExampleBot/1.0 (+https://example.com/bot)"
parser = RobotFileParser()
parser.set_url("https://example.com/robots.txt")
parser.read()
delay = parser.crawl_delay(USER_AGENT) or 5 # 保守的に5秒をデフォルトにする
urls = [
"https://example.com/products?page=1",
"https://example.com/products?page=2",
]
last_request = 0.0
for url in urls:
if not parser.can_fetch(USER_AGENT, url):
print("skipped by robots.txt:", url)
continue
wait = delay - (time.monotonic() - last_request)
if wait > 0:
time.sleep(wait)
resp = requests.get(url, headers={"User-Agent": USER_AGENT}, timeout=30)
last_request = time.monotonic()
print(url, resp.status_code)
robots.txtがパスを禁止している場合、正直な答えは、そのパスには触れないことか、運営者に相談することです。ブラウザを使って迂回しても、サイトが求めた内容は変わりません。
速度を落として、リクエストを分散する
一定の控えめなペースであればチャレンジが出ることはほとんどありませんが、バーストは大抵チャレンジを引き起こします。次のような習慣で大きく変わります。
- サイトごとにリクエストのペースを1つ決め、全速力で走らせずにそのペースを守る。
- タイミングが完全に規則的にならないよう、ランダムなジッターを加える。
- 429やcaptchaページが出たら、しばらく止めてバックオフする。
- サイトの負荷が気になるなら、ジョブは混雑していない時間帯に実行する。
キャッシュして再取得しない
自分がしなかったリクエストは、サイトが処理しなくてよいリクエストです。取得したページは、取得日時とボディのハッシュとともに保存し、内容が変わった可能性が高いときだけ再取得してください。If-Modified-SinceやETagヘッダーによる条件付きリクエストに対応しているサーバーなら、「変更なし」を安価に返してもらえます。
セッションを再利用し、通常のヘッダーを送る
リクエストの間でCookieを保持するセッションは、毎回まっさらに始めるスクリプトより、訪問者に近い振る舞いになります。サイトごとにrequests.Session()を使い、ボットであることと連絡先が分かる、明確で正直なUser-Agentを送ってください。ルールを回避するためにブラウザを装ってはいけません。サイトの内容を表示するのにJavaScriptが必要な場合は、そのページだけブラウザツールを使い、同じレート制限を守ってください。
それでもcaptchaが出る場合
どれだけ注意深く振る舞っても、captchaが表示されるサイトはあります。コードでそれを検知し、ページを叩き続けるのではなく、そのジョブを止めてください。検知と、PopularCaptchaに対する任意の解決については、Webスクレイピングのcaptchaで扱っています。
クロールを開始前に計画する
毎日すべてを取得するクロールは、captchaを引き起こす最も簡単な方法です。作業を先に計画してください。
- 優先順位を付ける。 頻繁に変わるページと、めったに変わらないページを決めます。頻繁に変わるものは頻繁にクロールし、残りは稀にクロールします。
- サイトマップの日付を使う。 多くのサイトマップには
lastmodの値があります。前回の訪問以降に変わっていないURLはスキップします。 - 増分クロールにする。 URLごとに最後に成功した取得を保存し、失敗したときは最初のページからやり直すのではなく、そこから再開します。
- 負荷を分散する。 大きなリストはバッチに分け、一度にまとめて実行せず、一日を通して実行します。
このような計画はリクエスト数を減らします。それが手元で最も効く手段です。
計画は、短い設定ファイルか表として書き出してください。サイト、セクション、更新間隔、そしてそれぞれの1日のリクエスト予算を書きます。予算を使い切ったら、次の時間枠まで作業を止めます。予算をガイドラインではなく厳格な上限として扱うことが、クローラーがcaptchaを引き起こすトラフィックパターンへ流れていくのを防ぎます。サイトが成長するとトラフィックも増えることがあるため、数週間ごとに予算を見直してください。前の四半期には穏当だったものが、今は穏当でないかもしれません。
責任あるスクレイピング
- 開始前にrobots.txtと利用規約を確認し、書かれていることを守ってください。
- リクエストの頻度は低く保ち、サイトが負荷を感じているサインがあれば止めてください。
- 持つ理由のない個人データは収集せず、必要がなくなったら削除してください。
- 著作権のあるコンテンツを大量に再掲載しないでください。分析、比較、自分のプロダクトのためにデータを使い、適切な場合は出典を明記してください。
- より多くの量が必要なら、サイトの運営者に連絡してください。フィードを提供してくれるかもしれません。