PythonでWebスクレイピング — 2026年完全ガイド
PythonでWebスクレイピングを基礎から解説。requests + BeautifulSoup、ページネーション、JavaScriptページ向けPlaywright、そしてスクレイピングAPIが自作に勝る場面まで。
価格、リスティング、検索結果、レビューなど、Web ページ上にしかないデータが必要なのに、ダウンロードボタンはどこにもありません。これまでに開いたチュートリアルは、デモサイトを対象にした「Hello World」レベルで止まるか、間に実行できるコードが何もないままフレームワークの構成図へ飛んでしまうかのどちらかです。その間にも、実際のターゲットは 403 を返し、すべてを JavaScript で描画し、スクリプトがようやく動いた翌週にはマークアップを変えてしまいます。
このガイドは、その道のり全体を 1 か所にまとめたものです。まず実行できる最初のスクレイパー、次にページネーションとマナー、続いて Playwright での JavaScript ページ、そして本番スクレイパーがなぜ壊れるのかについての正直な説明と、壊れたときにどうするかまで。以下のコードブロックはすべて、公開デモサイトに対してそのまま動きます。scraping するのは公開データのみで、DIY が正しいツールでなくなる境界も明確にお伝えします。
2026 年の Python スクレイピング・スタック
Web scraping のデフォルト言語がいまも Python なのは、必要な 3 つのツールが成熟していて、ドキュメントが充実し、pip install 一発で手に入るからです。
| 層 | ツール | 使うべき場面 |
|---|---|---|
| 静的 HTML | requests + beautifulsoup4 | データがページソースにある(View Source で見える) |
| JavaScript ページ | playwright | ブラウザがスクリプトを実行した後にだけデータが現れる |
| 保護されたサイト / 大規模 | スクレイピング API | サイトにブロックされる、またはメンテナンスコストがデータの価値を上回る |
2026 年半ば時点の最新バージョンは、requests 2.x、BeautifulSoup 4.15(2026 年 6 月リリース)、Playwright for Python 1.61 です。プロジェクトを健全に保つルールはこうです: ページに合う最小のツールを使う。静的 HTML にヘッドレスブラウザを向けるのは、同じ結果のために 50× のリソースを浪費することですし、React アプリに requests を向けても空の div が返ってくるだけです。
最初の層をインストールすれば準備完了です。
pip install requests beautifulsoup4
クイックスタート: 15 行で書く最初のスクレイパー
まさにこの練習のために作られたデモ書店 books.toscrape.com を scraping します。タイトル・価格・在庫状況を持つ 1,000 冊の本が並んでいます。パターンは「取得、パース、選択」です。
import requests
from bs4 import BeautifulSoup
url = "https://books.toscrape.com/"
resp = requests.get(url, timeout=10)
resp.raise_for_status() # crash loudly on 4xx/5xx instead of parsing an error page
soup = BeautifulSoup(resp.text, "html.parser")
books = []
for card in soup.select("article.product_pod"):
books.append({
"title": card.h3.a["title"],
"price": card.select_one("p.price_color").get_text(strip=True),
"in_stock": "In stock" in card.select_one("p.availability").get_text(),
})
print(f"Scraped {len(books)} books")
print(books[0])
# {'title': 'A Light in the Attic', 'price': '£51.77', 'in_stock': True}
ここで重みを支えているのは 3 つです。
resp.raise_for_status()— 初心者はこれを省き、実際には 404 ページだったものに対して BeautifulSoup が「何も見つけられなかった」理由を 1 時間かけてデバッグする羽目になります。soup.select(...)は CSS セレクタを受け取ります。ブラウザの DevTools で試すのと同じものです。要素を右クリックして「検証」を選び、Python を 1 行書く前にそこでセレクタを組み立てましょう。この工程 — 雑然としたマークアップを名前付きフィールドに変える作業 — が HTML パースであり、スクレイパーのコードの大半(そして将来の故障の大半)はここに集中します。- 最初から構造化された出力に。 dict のリストなら、CSV・JSON・DataFrame への変換は 1 行です。エクスポートは次のセクションで組み込みます。
BeautifulSoup は select よりずっと奥が深く、属性フィルタ付きの find_all、ツリーのナビゲーション、壊れたマークアップの処理などがあります。API の全体像は BeautifulSoup チュートリアルで扱っています。このガイドは、最初のページが動いた後に現れる問題へ進みます。
レベル 2: ページネーションとマナー
1 ページだけならデモです。実際のデータセットはページネーションにまたがります。そしてループでページをリクエストした瞬間、あなたはボットのトラフィックパターンになります。つまり、scraping のエチケットが問われ始めるのもここからです。
import time
import requests
from bs4 import BeautifulSoup
BASE = "https://books.toscrape.com/catalogue/page-{}.html"
HEADERS = {"User-Agent": "book-price-research/1.0 ([email protected])"}
books = []
for page in range(1, 6): # first 5 of 50 pages
resp = requests.get(BASE.format(page), headers=HEADERS, timeout=10)
if resp.status_code == 404:
break # walked off the end of the catalogue
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
for card in soup.select("article.product_pod"):
books.append({
"title": card.h3.a["title"],
"price": card.select_one("p.price_color").get_text(strip=True),
})
time.sleep(1.0) # ~1 request/second
print(f"{len(books)} books across 5 pages")
良き市民と迷惑者を分ける習慣は次のとおりです。
- 自分を絞る。
time.sleep(1.0)は形式ではなく下限です。毎秒 1 リクエストなら気づきもしないサイトでも、毎秒 50 なら確実に気づきます。サーバーはレート制限で自衛します。429 レスポンスは丁寧な警告であり、その次に来る 403 は丁寧ではありません。 - まず robots.txt を確認する。
https://the-site.com/robots.txtを取得し、Disallowルールを尊重しましょう。Python 組み込みのurllib.robotparserが評価してくれます。法律ではありませんが、無視すれば寛容なサイトを敵対的なサイトに変える最短ルートですし、法的な立ち位置も悪くなります。(この話題について: 公開データの scraping は合法であり得ますが、データの種類・規約・管轄区域によって変わります — is web scraping legal in 2026 をご覧ください。) - User-Agent で正直に名乗る。 連絡が取れる手段を添え、429 を尊重してタイトなループで再試行するのではなく指数的にバックオフしましょう。
- 公開ページだけを scraping する。 ログインの向こう側にあるもの、個人的なもの、サイトの規約が明確に禁じているものには手を出さない。
使える形で保存する
dict を集めてきたので、エクスポートは標準ライブラリの領分です。シンプルなケースに pandas は要りません。
import csv
with open("books.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["title", "price"])
writer.writeheader()
writer.writerows(books)
データセットが小さい今のうちに身につけたい習慣が 2 つあります。第一に、入口でクリーニングすること。"£51.77" は 3 本先のスクリプトではなく、scraping した時点で float に変換しましょう。今日の float(price.lstrip("£")) が、後日のロケール解析バグを防ぎます。第二に、再パースするかもしれないものは生の HTML を残しておくこと。ディスクは安く、来月になって星評価も必要だったと気づいたとき、保存済みページの再パースはサイトの再クロールに勝ります。出力先がデータベースではなく誰かのデスクのスプレッドシートなら、整形・複数シート・定期リフレッシュまで含めた完全なパイプラインは scrape a website to Excel にあります。
レベル 3: Playwright で JavaScript ページを扱う
クイックスタートのパターンをモダンな React や Vue のサイトに向けると、空っぽの殻が返ってきます。HTML はほぼ空で届き、その後 JavaScript が中身を埋めるからです。requests はその JavaScript を決して実行しません。手早い診断法: View Source にデータがなく、描画後のページにはある場合、ヘッドレスブラウザが必要です。
2026 年、Python でのデフォルトは Playwright です。ブラウザを同梱し、コンテンツを賢く待ってくれます。
pip install playwright
playwright install chromium
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://quotes.toscrape.com/js/", timeout=30000)
page.wait_for_selector("div.quote") # block until JS has rendered the data
quotes = []
for q in page.locator("div.quote").all():
quotes.append({
"text": q.locator("span.text").inner_text(),
"author": q.locator("small.author").inner_text(),
})
browser.close()
print(f"{len(quotes)} quotes rendered by JavaScript")
このターゲット — quotes.toscrape.com/js/ — は空のページを配信し、すべての引用をクライアントサイドで組み立てるので、まさに論点を証明してくれます。requests は 0 件、Playwright は 10 件取得します。鍵となる行は wait_for_selector で、壊れやすい sleep(5) して祈るアプローチを、本当に必要な要素を明示的に待つ処理に置き換えます。
ただしコストは現実のものです。ヘッドレスブラウザのセッションは、requests なら数 MB で済んだところを数百 MB の RAM を使い、実行速度は 1 桁遅くなります。経験則は 2 つ。データが本当に生の HTML にないときだけ Playwright に手を伸ばすこと、そしてまず Network タブを確認すること。「JavaScript サイト」の多くは、requests で直接呼べるきれいな JSON エンドポイントからデータを取得しています。本当にブラウザが必要になったら、セレクタ・待機・スケーリングを web scraping with Playwright で深掘りしています。古いスタックを維持しているなら Selenium もまだ十分に機能し、15 年分の Stack Overflow の回答という蓄積があります。両者の比較は Selenium web scraping をご覧ください。
正直な話: 本番スクレイパーはなぜ壊れるのか
ここまでの内容はすべて動きます。デモサイトの上だけでなく、本当に動きます。requests と BeautifulSoup と cron ジョブで何年も回り続けている社内ツールや研究プロジェクトはたくさんあります。しかし「デモサイト相手なら手元で動く」と「商用ターゲット相手に毎晩動き続ける」の間には壁があり、これをはっきり書いておく価値があります。scraping プロジェクトの多くが実際に息絶えるのはここだからです。
アンチボットシステムはコンテンツより先に応答します。 Cloudflare、DataDome、Akamai とその同類は、商用サイトのかなりの割合の前に立っています。すべてのリクエストをスコアリングし、Python クライアントはそのスコアにすぐ落ちます。最初のリクエストで 403、パターンができれば 429、そして requests には実行できない JavaScript チャレンジ。これはエッジケースではなく、2026 年の EC・旅行・不動産・検索ターゲットのデフォルトの構えです。
あなたの IP にはレピュテーションがあり、それは悪いものです。 データセンター IP — クラウド Wi-Fi 上のラップトップ、AWS のボックス、CI ランナー — はカタログ化され、あらかじめフラグが立っています。そこを越えるには、ローテーティングプロキシプールを通してリクエストごとに切り替わるレジデンシャルまたはモバイル IP が必要になり、つまりプロキシベンダー、GB 単位の課金、そして面倒を見るべきインフラの新カテゴリが増えます。
ヘッドレスブラウザでさえ正体を暴かれます。 アンチボットベンダーは TLS ハンドシェイク、canvas レンダリング、インストール済みフォントなど数十のシグナルを調べてデバイス署名 — ブラウザフィンガープリンティング — を計算しますが、素の Playwright には見分けのつく署名があります。パッチ済みビルドやステルスプラグインは存在するものの、これは終わりのないトレッドミルです。ベンダーが更新し、パッチが遅れ、あなたのスクレイパーはある火曜日に静かに失敗し始めます。
レイアウト変化は静かな殺し屋です。 サイトがリデザインを出し、p.price_color が存在しなくなっても、スクレイパーはクラッシュしません。誰かがダッシュボードの異変に気づくまで、空のフィールドをパイプラインに流し続けます。あなたが書くセレクタはどれも、相手側が署名していない、バージョン管理もされていない契約なのです。
合計すると、おなじみのパターンになります。書くのに週末 1 回で済んだスクレイパーが、今ではプロキシの請求書、セレクタの修理、ブロックの調査に毎週数時間を食っている。この保守税 — 足りない Python スキルではなく — こそが DIY の本当のコストであり、web scraping vs API で正直に集計しています。DIY を続けたいときに実際に効く対抗策は scraping sites that block bots をご覧ください。
API という道: 1 回の呼び出しがスタックを置き換える
その壁と戦い続ける代わりに、スタック全体 — プロキシ、ブラウザ、フィンガープリント、パーサー — を、フルタイムで代わりに戦ってくれる Web スクレイピング API に渡すという選択肢があります。実際の Amazon 検索がこちらです。構造化 JSON が返り、ブラウザなし、プロキシプールなし、パーサーなし。
import requests
resp = requests.get(
"https://api.crawlora.net/api/v1/amazon/search",
params={"k": "mechanical keyboard"},
headers={"x-api-key": "YOUR_API_KEY"},
)
resp.raise_for_status()
for item in resp.json()["data"]:
print(item["title"], item["price"], item["rating"])
クイックスタートで学んだのと同じ requests ライブラリです。スキルはそのまま移行します。変わったのは、あなたが何に責任を持つかです。
- HTML ではなく構造化フィールド。 レスポンスは正規化された JSON —
title、price、rating、asin、review_count— なので、Amazon がリデザインしても、あなたが気づく前に直すのは Crawlora のパーサーであって、あなたのものではありません。 - アンチボットとの戦いはキーの向こう側で行われます。 プロキシローテーション、レンダリング、リトライ、フィンガープリントの維持はベンダーのフルタイムの仕事です。
- 課金は成功時のみ。 課金対象は成功した 2xx レスポンスだけです。難しいターゲットの scraping には失敗がつきものだからこそ、これが効いてきます。
- 無料プランはカード不要で月 2,000 クレジット。 何かにお金を払う前に、そのデータが構築する価値のあるものか確かめるには十分です。
同じパターンが数十のプラットフォームをカバーします。Google Search、TikTok、Zillow、GitHub、アプリストアなどで、それぞれ API ドキュメントにエンドポイントが文書化され、Python サンプルにはすべてのエンドポイント分のコピペ可能な Python があります。スクレイパーの出力先がスプレッドシートではなく LLM エージェントなら、同じエンドポイントがホスト型 MCP サーバーとして公開されています(MCP インテグレーションを見る)。エージェント + scraping のパターンはそれ自体が 1 つのテーマで、web scraping with AI で扱っています。
公平を期すと、API にはリクエスト単価があり、カバーするのは対応プラットフォームだけで、パイプラインにベンダーが 1 社入ります。DIY にはそのどれもありません。だからこそ、正解はたいてい両者のミックスなのです。
DIY vs スクレイピング API: どう決めるか
| 要素 | DIY(requests / Playwright) | スクレイピング API |
|---|---|---|
| 初期コスト | 無料 — すべてオープンソース | 無料プランの後はリクエストごとのクレジット |
| 保護されていないサイト | 勝ち — 速い・無料・完全なコントロール | 動くが、不要な保護に料金を払うことになる |
| 保護されたサイト(Cloudflare など) | プロキシ + ステルス + 絶え間ない保守 | 勝ち — キーの裏側で処理済み |
| ロングテール / ニッチ / 社内サイト | 勝ち — すべてのサイトをカバーできる API はない | 対応プラットフォームのみ |
| メンテナンス | 自分の仕事: セレクタ、プロキシ、ブロック | ベンダーの仕事: パーサーとブロック解除 |
| 出力 | パースしたものすべて — 完全カスタム | プラットフォームごとに正規化された JSON |
| 学習価値 | 勝ち — スタック全体を理解できる | 面白い部分が抽象化されて見えない |
正直な線引きはこうです: DIY はコスト、ニッチターゲットのカバー、コントロールで勝ち、API は保護されたプラットフォーム、メンテナンス、データ到達までの時間で勝ちます。 多くのチームは結局、簡単なターゲットと社内ターゲットは自分で scraping し、敵対的で価値の高いプラットフォームは API 経由に振り分けています。
どちらを本番に載せるにしても、その前にこのチェックリストを確認してください。
- データは公開されているか。robots.txt とサイトの規約は、やろうとしていることを許可しているか?
- データは生の HTML にあるか(requests + BeautifulSoup)、それとも JavaScript で描画されるか(Playwright)?
- 丁寧なレートに絞り、タイムアウト、raise_for_status、429 への指数バックオフを備えているか?
- scraping したフィールドを検証(非空・妥当な型)して、レイアウト変化が静かにではなく大きな音を立てて失敗するようにしているか?
- 保守税 — プロキシ、セレクタ修理、ブロック対応 — をスクレイピング API のリクエスト単価と比較したか?
- 保護されたターゲットについて: API 呼び出し 1 回で、インフラなしに同じデータが取れるか試したか?
次のステップ
これで 2026 年の全体地図が手に入りました。静的ページには requests と BeautifulSoup、JavaScript が邪魔をするなら Playwright、本物のマナー習慣、そして本番の壁とそれを抜ける API の扉を見据えたクリアな視点です。最良の次の一歩は、本当に関心のあるターゲットに対してクイックスタートを走らせることです。1 時間もあれば、プロジェクトにスタックのどの層が必要か分かるでしょう。そして最初のリクエストが 403 で返ってきても、いま目の前にあるものが何か正確に分かっていますし、どちらの道を選んでも解決済みの問題です。プロキシの請求書と比べたくなったら、料金ページにクレジットの計算があります。
壁はスキップ — 自作スクレイパーと並べて API を試す
Amazon、Google、TikTok などから構造化 JSON。マネージドなプロキシ・レンダリング・リトライ、成功時のみ課金。月 2,000 クレジット無料、カード不要。
関連記事:
よくある質問
Python での Web スクレイピングは合法ですか?
公開データの scraping は合法であり得ますが、収集する内容、サイトの利用規約、管轄区域によって変わります。公開ページにとどまり、ログインの先には触れず、個人データを避け、robots.txt を尊重し、リクエストレートを丁寧に保ちましょう。判例は公開データの scraping に概ね好意的ですが、これは背景情報であって法的助言ではありません。自分のユースケースの詳細は必ず確認してください。
Web スクレイピングに最適な Python ライブラリは何ですか?
ページ次第です。静的 HTML には requests + BeautifulSoup が標準の組み合わせで、シンプルで速く、ドキュメントも充実しています。JavaScript で描画されるページには、2026 年のデフォルトであるヘッドレスブラウザライブラリの Playwright。多数のページをクロールするなら、Scrapy がスケジューリングとパイプラインを加えてくれます。経験則は「scraping するページに合う最小のツールを使う」です。
Python は Web スクレイピングに向いていますか?
はい。Python が最も人気のある scraping 言語なのは、エコシステムが仕事のあらゆる層をカバーしているからです。HTTP には requests、HTML パースには BeautifulSoup、ブラウザ自動化には Playwright、結果のクリーニングとエクスポートには pandas。動くスクレイパーは約 15 行のコードで書け、scraping のチュートリアル、Stack Overflow の回答、API のサンプルのほとんどが Python を最初に提供しています。
JavaScript を使うサイトはどうやって scraping しますか?
View Source にデータがなく、描画後のページには見える場合、素の requests は空の殻しか返しません。Playwright for Python を使いましょう。ヘッドレス Chromium を起動し、page.goto を呼び、必要な要素を wait_for_selector で待ってからテキストを抽出します。あわせて、まずブラウザの Network タブを確認してください。JavaScript サイトの多くは直接呼べる JSON エンドポイントからデータを読み込んでおり、ブラウザを動かすよりはるかに安上がりです。
scraping 中にブロックされないためにはどうすればいいですか?
おおよそ毎秒 1 リクエストに絞り、正直な User-Agent を設定し、robots.txt を尊重し、429 レスポンスには指数的にバックオフします。Cloudflare などのアンチボットシステムの背後にあるサイトでは、それでも足りないことが多くあります。データセンター IP はあらかじめフラグ付けされ、ヘッドレスブラウザはフィンガープリントされるからです。その時点での選択肢は、ローテーティングレジデンシャルプロキシ + ステルスパッチか、ブロック解除を代行してくれるスクレイピング API です。
BeautifulSoup と Scrapy の違いは何ですか?
BeautifulSoup はパースライブラリです。自分で取得した HTML を渡すと、CSS セレクタでフィールドを抽出するのを助けてくれます。Scrapy は本格的なクローリングフレームワークで、リクエスト、並行処理、リンクの追跡、スロットリング、エクスポートパイプラインを管理します。スクリプトや小さなジョブには requests + BeautifulSoup を、サイト全体で数千ページをクロールするなら Scrapy を検討しましょう。
Python スクレイパーを書く代わりに API を使えますか?
はい、対応プラットフォームであれば可能です。Crawlora のようなスクレイピング API は、Amazon 検索、Google Search、TikTok などのドキュメント化されたエンドポイントを公開しており、x-api-key ヘッダー付きの requests.get 呼び出し 1 回で正規化された JSON を返します。プロキシ・レンダリング・リトライはサーバー側で処理されます。課金は成功時のみ(2xx レスポンスだけが課金対象)で、無料プランはカード不要の月 2,000 クレジットです。どの API もカバーしないニッチサイトでは、依然として DIY が勝ちます。
