「requestsとBeautifulSoupで書いたのに、肝心のデータだけ空っぽで取得できない」——スクレイピングを自分で書いてみた人の多くがぶつかる壁です。原因の大半は、対象サイトがJavaScriptでデータを後から描画する動的サイトだったり、ログインしないと見えないページだったりすることにあります。この記事では、動的サイトの攻略・ログイン必須サイトの突破・隠しAPIの見つけ方・大量データ取得を安全に設計する方法まで、実装コード付きで解説します。あわせて、個人で対応するには限界がある規約・法令面の注意点も整理します。

動的サイトのスクレイピング|JavaScriptレンダリング対策

requests + BeautifulSoupで取れない理由

requestsで取得できるのは「サーバーが最初に返すHTML」だけです。ReactやVue製のサイト、無限スクロールの一覧ページなどは、ブラウザがJavaScriptを実行したにデータが挿入されるため、requestsのレスポンスには空の枠しか入っていません。この場合は、実際にブラウザを動かしてレンダリング結果を取得する必要があります。

Selenium・Playwrightの使い分け

  • Playwright:起動が速く、待機処理(要素が表示されるまで自動で待つ)が組み込みで安定。新規案件では第一候補
  • Selenium:歴史が長く情報量が多い。既存資産や特殊なブラウザ操作が必要な場合に選択
  • requests-html / httpx:軽量だが複雑なJS実行には非対応。単純な遅延読み込み程度なら候補

Playwrightでの基本形は以下の通りです。

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://example.com/items")
    page.wait_for_selector(".item-card")  # 描画完了を待つ
    items = page.query_selector_all(".item-card")
    for item in items:
        print(item.inner_text())
    browser.close()

ポイントはwait_for_selectorで「要素が出るまで待つ」ことです。固定のtime.sleepに頼ると、回線速度によって取りこぼしたり、逆に無駄に待って処理が遅くなったりします。

ログイン必須サイトの攻略|セッション維持とCookie管理

requests.Session()でログイン状態を維持する

会員限定ページを取得するには、ログインリクエストで得たCookieを以降のリクエストにも引き継ぐ必要があります。requests.Session()を使うとCookieの管理が自動化されます。

import requests

session = requests.Session()
session.post("https://example.com/login", data={
    "email": "user@example.com",
    "password": "xxxxx",
})
res = session.get("https://example.com/mypage/orders")
print(res.text)

2段階認証・CAPTCHAは個人対応が難しい領域

SMS認証やreCAPTCHAが挟まるサイトは、Cookie維持だけでは突破できません。突破用のサービスを組み込む手法も存在しますが、多くのサイトの利用規約で自動化ツールでのログインを明確に禁止しています。ここは技術力より先に「規約上やってよいか」を確認すべき領域です(詳しくは後述の規約・法令セクション参照)。

隠しAPIの見つけ方|DevToolsのNetworkタブ活用術

HTMLを解析する前にAPIを探す

多くのサイトは裏側でJSON形式のAPIを呼び出してデータを取得しています。このAPIを直接叩けば、ブラウザ操作もHTML解析も不要になり、取得速度・安定性ともに大幅に向上します。手順は次の通りです。

  1. Chrome DevToolsを開き「Network」タブを選択
  2. フィルタを「Fetch/XHR」に絞る
  3. 対象ページを操作(検索・ページ送りなど)してリクエストを発生させる
  4. 該当するリクエストのURL・Headers・Responseを確認
  5. 同じヘッダー(Cookie・Authorization等)を付けてrequestsで再現する
import requests

headers = {
    "User-Agent": "Mozilla/5.0 ...",
    "Authorization": "Bearer xxxxx",
}
res = requests.get("https://example.com/api/v1/items?page=1", headers=headers)
data = res.json()

APIが見つかれば実装コストは劇的に下がる

HTML構造は運営側のデザイン変更でいつ壊れるか分かりませんが、JSON APIは構造が安定している場合が多く、保守コストも下がります。ただしAPIキーの認証方式やレート制限、リクエストごとの署名(Signature)付与などサイトごとの実装差が大きく、「見つけたのに再現できない」ケースも珍しくありません。実装の勘所はスクレイピング実装テクニック【2026年8月最新】でも詳しく取り上げています。

大量データ取得の設計|レート制限・リトライ・並列化

ライブラリの選び方

ライブラリ

特徴

向いている用途

requests

同期・シンプル・情報量が多い

数百件程度の小規模取得

httpx

requests互換API+非同期対応

中規模〜大規模の移行しやすい選択肢

aiohttp

非同期専用・高スループット

数万件規模の並列取得

指数バックオフでリトライを実装する

大量取得では一時的なエラー(429・503など)が必ず発生します。即座に再試行せず、待機時間を倍々に増やす「指数バックオフ」で相手サーバーへの負荷を抑えつつ安定して取得します。

import time
import requests

def fetch_with_backoff(url, max_retries=5):
    for i in range(max_retries):
        res = requests.get(url)
        if res.status_code == 200:
            return res
        wait = 2 ** i
        time.sleep(wait)
    raise RuntimeError("取得に失敗しました")

プロキシローテーションは「最終手段」

アクセス頻度が高いとIP単位でブロックされることがあり、プロキシを切り替える手法も存在します。ただし相手サーバーの規約で明確に禁止されている場合、プロキシでの回避はブロック逃れとみなされるリスクが高く、まずはリクエスト間隔を空ける・並列数を抑えるといった相手に配慮した設計を優先すべきです。

規約・法令面の注意点|見落とすと事業リスクになる4項目

項目

内容

robots.txt

法的拘束力はないが、クロール可否の意思表示。無視すると規約違反の根拠にされやすい

利用規約(ToS)

「自動化アクセス禁止」の明記があれば、技術的に可能でも契約違反。アカウント停止・損害賠償請求の対象になり得る

著作権法

取得したデータの再配布・二次利用は著作権侵害になり得る。社内分析目的での取得と、公開・販売目的では扱いが異なる

不正アクセス禁止法

認証を突破してのアクセスは対象になり得る。ログイン必須サイトの取得は特に慎重な判断が必要

個人や社内の一部メンバーがこれらを毎回正しく判断しながら実装・運用し続けるのは、想像以上に負荷が高い作業です。動的サイトの解析、API仕様の再現、レート制限の調整、規約確認までを継続的にメンテナンスするコストは、外部委託の方が結果的に安くつくケースが多くあります。

この記事に関連するアイテム

実際に使うものを選ぶ際の参考にどうぞ。

※ 本サイトはAmazonアソシエイト・プログラムの参加者です。紹介リンクを経由してご購入いただいた場合、手数料を受け取ることがあります。

まとめ

スクレイピングは「requestsで固定ページを取る」段階から一歩進むと、動的サイトのレンダリング待ち、ログインセッションの維持、隠しAPIの解析、大量取得時のレート制限対応など、専門知識が必要な工程が一気に増えます。さらに規約・法令面の判断を誤ると事業リスクに直結するため、自社で継続運用するにはそれなりの体制が必要です。設計から実装・運用まで任せたい場合は、専門業者への委託も選択肢に入れてみてください。

業務自動化・スクレイピングの導入をご検討の方はnashiまでお問い合わせください。https://nashi-portfolio.netlify.app