BeautifulSoupによるウェブスクレイピング: 静的HTMLから動的ページへ
Expert in Web Scraping Technologies
TL;DR:
- BeautifulSoupはHTMLを解析します; JavaScriptを実行しません。 レスポンスボディには選択したいレコードがすでに含まれている必要があります。
- RequestsとBeautifulSoupは静的ページの最短ルートです。 取得、検証、解析、正規化、エクスポートを1つのPythonプロセスで行います。
- CSSセレクターは入れ子のカードに実用的です。 各レコード内のスコープフィールドセレクターを設定し、値が行を越えて移動しないようにします。
- 動的ページには異なる取得手段が必要です。 まず内部JSONエンドポイントを確認し、ブラウザの実行が必要な場合はレンダリングされたHTMLを使用します。
- BeautifulSoupはレンダリング後も有用です。 ユニバーサルスクレイピングAPIはHTMLを返すことができ、BeautifulSoupは解析とJSON出力の所有権を保ち続けます。
BeautifulSoupのウェブスクレイピングは、ページ取得とHTML解析を別の作業として扱うと最も効果的です。Requestsは表現を取得します。BeautifulSoupはその表現をナビゲート可能なツリーに変換します。どちらの操作も、JavaScript依存のコンテンツが表示されたことを証明するものではありません。
このチュートリアルでは、静的スクレイパーを構築し、制限付きのページネーションを追加し、JavaScriptページでの空のシェル問題を示し、管理されたレンダリングステップの後に同じBeautifulSoupパーサーを保持します。
静的HTTP、内部JSON、またはレンダリングされたHTML?
セレクターを書く前に取得経路を選択してください。
| ページ動作 | 最適な初回経路 | BeautifulSoupの役割 |
|---|---|---|
| レコードが初期HTMLに存在する | Requests | レスポンスを直接解析 |
| ページが公開JSONリクエストからレコードを読み込む | その安定したエンドポイントへのRequests | 必要な場合のみ埋め込まれたHTMLフィールドを解析 |
| JavaScriptの後に必要なコンテンツが現れる | 管理されたレンダラーまたはブラウザ | 返されたレンダリングされたHTMLを解析 |
| ワークフローがクリック、フォーム、またはセッションアクションを必要とする | ブラウザ自動化 | スナップショットまたは最終HTMLを解析 |
ソースを表示もしくはRequestsのレスポンスを検査し、ブラウザのElementsパネルだけでなく確認してください。ブラウザはJavaScript後のDOMを表示し、Requestsはサーバーのレスポンスを見ることができます。
前提条件
Python、仮想環境、および公開対象ページへのアクセス許可が必要です。実行可能な例は、Pythonの組み込みHTMLパーサーを使用してrequestsとbeautifulsoup4を使用します。
Beautiful Soupのドキュメントは、パーサーの選択とCSSセレクターをカバーしています。Requestsクイックスタートは、レスポンスの処理とステータスチェックを文書化しています。
BeautifulSoupとRequestsをインストール
孤立した環境を作成し、スクリプトがインポートする正確なパッケージをインストールします:
bash
python3 -m venv .venv
. .venv/bin/activate
python -m pip install beautifulsoup4 requests
ネットワークコードを追加する前に、インポートが解決されていることを確認します:
bash
python -c "import bs4, requests; print(bs4.__version__, requests.__version__)"
静的HTMLページをスクレイピング
公共のQuotes to Scrape静的ページは、すべての引用カードをレスポンスHTMLに配置します。パーサーは、各.quoteレコード内のテキスト、著者、タグのスコープを設定します。
python
import json
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
url = "https://quotes.toscrape.com/page/1/"
response = requests.get(url, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
records = []
for card in soup.select(".quote"):
records.append({
"text": card.select_one(".text").get_text(strip=True),
"author": card.select_one(".author").get_text(strip=True),
"tags": [tag.get_text(strip=True) for tag in card.select(".tag")],
"source_url": response.url,
})
next_link = soup.select_one("li.next a")
next_url = urljoin(response.url, next_link["href"]) if next_link else None
print(json.dumps({
"count": len(records),
"first": records[0],
"next_url": next_url,
}, indent=2))
パーサーは3つの有用なことを行います: 完全なカードを最初に選択し、カードスコープ内にすべてのフィールドを保持し、ソースURLを保存します。そのソースURLは、後のセレクターやページネーションの問題を再現可能にします。
find_all対CSSセレクター
BeautifulSoupはメソッドベースの検索とCSS選択の両方を提供します。
find()は、名前または属性によって最初の一致タグを返します。find_all()は、すべての一致タグを返します。select_one()は、最初のCSSセレクターマッチを返します。select()は、すべてのCSSセレクターマッチを返します。
CSSセレクターは、入れ子のカードレイアウトに対して簡潔です。メソッドベースの検索は、1つのタグと1つの属性を一致させるときに明確になることがあります。プロジェクトのスタイルを1つ選び、レコードコンテナの下に子フィールドのスコープを設定します。
プレゼンテーションクラスに結び付けられた長いセレクターのチェーンは避けてください。ページがそれらを公開する場合、耐久性のある属性、セマンティック要素、安定したURLパターン、または内部JSONキーを好むべきです。
制限付きページネーションを追加
ページネーションには、ターゲットに明確な次のリンクが現在ある場合でも停止条件が必要です。このスクリプトは2つの静的ページを収集し、リンクが消えると早期に停止します。
python
import json
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
url = "https://quotes.toscrape.com/page/1/"
rows = []
for _ in range(2):
response = requests.get(url, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
for card in soup.select(".quote"):
rows.append({
"text": card.select_one(".text").get_text(strip=True),
"author": card.select_one(".author").get_text(strip=True),
"source_url": response.url,
})
next_link = soup.select_one("li.next a")
if not next_link:
break
url = urljoin(response.url, next_link["href"])
print(json.dumps({"count": len(rows), "last": rows[-1]}, indent=2))
最大ページ数、訪問したURLのセット、安定したレコードキーを使用して、パターンを大きなサイトに適用する前に設定してください。並行作業を小さく保ち、サイトのポリシーを尊重してください。
Scrapelessでスクレイピングを始めましょう
Scrapelessでウェブスクレイピングと自動化ワークフローを強化しましょう!
今日登録して**$5の無料クレジット**を取得しましょう — クレジットカードは不要です。Scrapeless Dashboardで今すぐ無料クレジットを請求しましょう。
なぜBeautifulSoupは空の動的ページを返すのか
JavaScriptバージョンの同じデモは、直接リクエストにHTMLシェルを返します。引用カードは、ブラウザがページスクリプトを実行した後に添付されるため、soup.select('.quote')は初期レスポンスでレコードを見つけられません。
この違いは、短い診断で確認できます:
python
import requests
from bs4 import BeautifulSoup
url = "https://quotes.toscrape.com/js/"
response = requests.get(url, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
print({
"status": response.status_code,
"title": soup.title.get_text(strip=True),
"quote_cards": len(soup.select(".quote")),
})
ステータス200は、サーバーが成功したHTTPレスポンスを返したことを意味しますが、表現にビジネスレコードが含まれていることを意味するわけではありません。 HTTPセマンティクス標準はステータスの動作を定義し、スクレーパーはページのアイデンティティと必要なセレクターを検証しなければなりません。
レンダラーを選択する前に、ブラウザの開発者ツールでFetch/XHRのリクエストを確認してください。安定したパブリックJSONレスポンスは、レンダリングされたDOMよりも小さく、検証が容易な場合があります。プライベートなクレデンシャル、制限されたエンドポイント、または自分が持っていない認証に依存するリクエストは再現しないでください。
BeautifulSoupが止まる場所
BeautifulSoupは解析で止まります。ページスクリプトを実行したり、コントロールをクリックしたり、ブラウザの実行環境を維持したり、HTMLがパーサーに届く前に発生する取得の問題を解決したりすることはありません。
Scrapeless Universal Scraping APIは、公開されたJavaScriptページのレンダリングされたHTMLを返すことができます。その後、BeautifulSoupはローカルHTMLに使用されるのと同じセレクターで返された文字列を解析できます。
注意: 以下のクラウドリクエストには、リーダー所有の
SCRAPELESS_API_KEYが必要です。文書化されたリクエストの形状は確認済みですが、クレデンシャルが制限された呼び出しはこの環境では実行されませんでした。
python
import os
import requests
from bs4 import BeautifulSoup
response = requests.post(
"https://api.scrapeless.com/api/v2/unlocker/request",
headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"]},
json={
"actor": "unlocker.webunlocker",
"proxy": {"country": "ANY"},
"input": {
"url": "https://quotes.toscrape.com/js/",
"jsRender": {
"enabled": True,
"waitUntil": "domcontentloaded",
"response": {"type": "html"}
}
}
},
timeout=60
)
response.raise_for_status()
payload = response.json()
if payload.get("code") != 200:
raise RuntimeError("Rendered HTML was not returned")
soup = BeautifulSoup(payload["data"], "html.parser")
records = [
{
"text": card.select_one(".text").get_text(strip=True),
"author": card.select_one(".author").get_text(strip=True),
}
for card in soup.select(".quote")
]
print({"count": len(records), "first": records[0] if records else None})
Universal Scraping APIドキュメントは、JavaScriptレンダリングとレスポンスオプションを定義します。レンダラーの構成は取得層に保持し、解析器は保存されたHTMLフィクスチャでテスト可能なままとします。
JSONを保存する前に検証する
パーサーは、成功として空のファイルをエクスポートするのではなく、間違ったページを拒否すべきです。検証してください:
- 最終URLが期待されるホストとルートに一致する;
- タイトルまたはH1が意図されたページを識別する;
- 必要なカードセレクターが存在する;
- 各受け入れられたレコードがそのビジネスキーを含む;
- nullableフィールドは隣接する値をシフトするのではなく、
nullのままになる; - ソースURLが各行と一緒に保存される。
ロボット排除プロトコルは、自動クライアントに遵守が求められるクローラーディレクティブを指定します。これは、認証や適用法の置換えにはなりません。
BeautifulSoupスクレーパーのトラブルシューティング
| 症状 | 考えられる原因 | チェック |
|---|---|---|
| ステータス200でゼロレコード | JavaScriptレンダリングされたコンテンツまたは誤ったセレクター | レスポンスHTMLと必要な要素を検査 |
| 文字化け | 不正確なレスポンスエンコーディング | ヘッダー、文書メタデータ、およびデコードされたボディを比較 |
| フィールドが誤ったカードとペアになる | グローバルフィールドセレクター | 各レコードコンテナの下でフィールドクエリのスコープを設定 |
| 重複行 | ページネーションループがURLを再訪する | 訪問したURLと安定したレコードキーを追跡 |
| パーサーの動作が異なる | 異なるパーサーバックエンド | 明示的に選択されたパーサーを固定 |
結論
BeautifulSoupウェブスクレイピングは、レスポンスにすでにデータが含まれている場合に信頼性があります。リクエストは静的取得を処理し、BeautifulSoupは解析を処理し、制約のあるページネーションと検証が結果を構造化された出力に変えます。
動的ページの場合、最初にパブリックな内部JSONソースを確認してください。必要なページ状態にJavaScriptが必要な場合、Universal Scraping APIを通じてレンダリングされたHTMLを返し、BeautifulSoupをパーサーとして維持します。
Python解析を維持し、レンダリングをクラウドに移動
Scrapelessの価格を比較し、Puppeteerダウンロードガイドでブラウザ境界を学び、Scrapelessアカウントを作成してください。実装の議論のためにDiscordやTelegramに参加しましょう。
FAQ
Q: BeautifulSoupはウェブスクレイピングに適していますか?
BeautifulSoupは、別のコンポーネントがすでに正しい文書を取得している場合に実用的なHTMLおよびXMLパーサーです。
Q: BeautifulSoupは動的なウェブサイトをスクレイピングできますか?
BeautifulSoupはJavaScriptを実行できませんが、内部API、管理されたレンダラー、またはブラウザの自動化ステップによって返されたHTMLを解析できます。
Q: BeautifulSoupのWebスクレイピングは合法ですか?
アクセスを許可された公開データのみをスクレイピングし、サイトの利用規約やロボットの指示を確認し、収集を最小限に抑え、該当する jurisdicion の法律について助言を求めてください。
Q: BeautifulSoupのスクレーパーはプロキシが必要ですか?
小さな許可された静的リクエストはプロキシを必要としないかもしれませんが、地理的または製品ワークロードには適切なプロキシと明示的なトラフィック制御が必要な場合があります。
Q: DOMが変更された場合、何が起こるべきですか?
現在のレスポンスを再検査し、属性やJSONキーなど持続可能なソースを特定し、セレクタを絞り込み、データを保存する前に必要なフィールドを検証してください。
Q: BeautifulSoupのスクレーパーはどのくらいの同時処理を使用するべきですか?
ホストごとに3人以上のワーカーを使用せずに開始し、サイトのポリシーとレスポンスの動作を観察し、ターゲットまたはユースケースが少ないトラフィックを必要とする場合は並行性を減少させてください。
Q: 例はAIエージェントなしで実行できますか?
はい。Pythonの例は直接実行されます;エージェントは同じ取得と解析のステップの周りのオーケストレーションに選択的に使用されます。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。




