動的ウェブサイトのスクレイピング
動的なWebサイトは、最初のページ読み込み後にJavaScriptを使用してコンテンツを読み込みます。このガイドでは、FourAのブラウザエンドポイントを使用してこれらのサイトからデータを収集する方法を説明します。
課題
JavaScriptを多用するWebサイトに標準のHTTPリクエストを送信すると、HTMLの骨組みのみが取得され、実際のコンテンツは取得できません。必要なデータ(製品リスト、価格、検索結果など)は、ブラウザでページがレンダリングされた後にJavaScriptによって読み込まれます。
これは、React、Vue、Angular、Next.jsなどの最新フレームワークでますます一般的になっています。
解決策: ブラウザリクエスト
FourAのブラウザエンドポイント(POST /api/browser/)は、次の処理を行うChromeブラウザインスタンスでURLを開きます。
- ページを読み込む
- すべてのJavaScriptを実行する
- コンテンツがレンダリングされるのを待つ
- 完全にレンダリングされたHTMLを返す
ステップ 1: 必要な要素を特定する
リクエストを送信する前に、ブラウザで対象のページにアクセスし、DevTools(F12)を使用してコンテンツが読み込まれたことを確認できるテキストまたは要素を見つけます。例:
- JSのレンダリング後に表示される製品名
- レンダリングされたHTML内の
product-gridなどのCSSクラス - データ読み込み時にのみ表示される"results"などのテキスト文字列
ステップ 2: ブラウザリクエストを送信する
curl -X POST https://eu.api.foura.ai/api/browser/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/products",
"timeout_ms": 15000,
"checkText": "product-grid"
}'
checkText オプションは、ページの読み込み完了後に「product-grid」が存在するか確認するよう FourA に指示します。存在しない場合、リクエストは checkText:product-grid not found エラーとして失敗し、失敗したリクエストには課金されません。checkText によって FourA のテキスト待機時間が長くなることはありません。
ステップ 3: HTML のパース
レスポンスの body フィールドには、完全にレンダリングされた HTML が含まれます。お好みのライブラリを使用してパースしてください。
Python (BeautifulSoup)
import requests
from bs4 import BeautifulSoup
resp = requests.post("https://eu.api.foura.ai/api/browser/", headers={
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}, json={
"url": "https://example.com/products",
"timeout_ms": 15000,
"checkText": "product-grid"
})
html = resp.json()["body"]
soup = BeautifulSoup(html, "html.parser")
for product in soup.select(".product-card"):
name = product.select_one(".product-name").text.strip()
price = product.select_one(".product-price").text.strip()
print(f"{name}: {price}")
Node.js (cheerio)
import * as cheerio from 'cheerio';
const resp = await fetch('https://eu.api.foura.ai/api/browser/', {
method: 'POST',
headers: { 'X-API-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://example.com/products',
timeout_ms: 15000,
checkText: 'product-grid'
})
});
const { body: html } = await resp.json();
const $ = cheerio.load(html);
$('.product-card').each((i, el) => {
console.log($(el).find('.product-name').text(), $(el).find('.product-price').text());
});
トラブルシューティング
コンテンツが空のままの場合
- ページが実際にJavaScriptレンダリングを使用しているか確認します(「ソースを表示」とDevToolsの表示を比較)。
timeout_msの値を増やします。一部のページは読み込みに時間がかかります。- ページに認証やcookieが必要か確認します(
cookiesパラメータを使用)。
検証ページ(CAPTCHA等)が表示される場合
- 単一/HTTPリクエストの場合は、proxyエンドポイント(
POST /api/proxy/)に切り替えて自動IPローテーションを利用します。 - ブラウザリクエストをproxy経由でルーティングするには、browserエンドポイントの
proxyパラメータを渡します。proxyエンドポイントは単一/HTTPリクエストのみをラップし、ブラウザリクエストには対応していません。
proxy パラメータには、以前のレスポンスで返された不透明なproxy IDを指定します。独自のproxyアドレスは指定できません。まず POST /api/proxy/(または POST /api/auto/)呼び出しを1回実行し、返された proxy フィールドを読み取って固定します。
# Step 1: let FourA find a working exit. The response carries "proxy": "A1B2C3".
curl -X POST "https://eu.api.foura.ai/api/proxy/" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"maxTries": 5, "request": {"method": "GET", "url": "https://example.com/products"}}'
# Step 2: render through that same exit.
curl -X POST "https://eu.api.foura.ai/api/browser/" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/products", "proxy": "A1B2C3", "timeout_ms": 20000}'
IDの代わりにアドレスを送信すると、400 Invalid proxy formatが返されます。完全なパターンはリクエスト間でのプロキシの再利用を参照してください。
次のステップ
- 適切なエンドポイントの選択: browserとsingleの使い分け
- 競合他社の価格を監視する: 価格追跡の完全なチュートリアル
- 保護されたサイト: 保護されたサイトの処理
- リクエスト間でのプロキシの再利用: ワークフロー全体で1つのイグジットを固定