ラマウェブスクレイピング:なぜレンダリングがデータを決定するのか
Senior Cybersecurity Analyst
TL;DR:
- 「抽出できない」と「きれいに抽出する」の間のギャップは、1回のレンダー呼び出しで、この記事ではそれを正確に測定します。 JavaScript専用の製品カタログに対する通常のGETリクエストは、1つの未レンダリングのテンプレートプレースホルダを返しますが、
js_renderを使ったScrapeless Universal Scraping API経由の同じURLは、13の実際の製品スパンを返し、Llamaはその中から12の本物の製品を正しく抽出しました。 - 現在の安価な tier はLlama 4であり、Llama 3ではありません。
meta-llama/llama-4-scoutは、現在の世代の最も安価なLlamaで、ライブのOpenRouterカタログにあります — これは、既存のチュートリアルでまだ使用されているLlama 3.1とは異なる、新しいファミリーです。 - モデルは、自動的に壊れたテンプレート行を静かにフィルタリングしました。 レンダリングされたページには実際には13の
product-nameスパンが含まれており、そのうちの1つは未ハイドレートの${product.name}プレースホルダで、Llamaの抽出は本物の12製品を正確に返し、偽物を指示されることなくスキップしました。 - これはクラウドパスであり、ローカルパスではありません。 このサイトの別のガイド、LLaMA 3を用いたウェブスクレイピングでは、Ollamaを使用してローカルでLlamaを実行する方法を説明しています; こちらは、ホスティングされたAPIを介してLlama 4を実行します。
- まだネイティブのLlama APIキーはありませんか?同じリクエストがOpenRouterを介して実行されます。 このガイドでは
meta-llama/llama-4-scoutでライブ実行され、キャプチャした出力が表示されます。 - フェッチ側では無料で始められます。 app.scrapeless.comでScrapeless APIキーを作成してください。
Llamaはウェブサイトをスクレイピングできますか?
ホスティングされたLlamaエンドポイントはテキストを読み取り、フィールドを返します; ページを取得したり、JavaScriptを実行したり、セッションを保持したりはしません。これは、モデルがOllamaでローカルに実行される場合でも、クラウドAPIを介して実行される場合でも同じです — モデルの重みは、HTTPの動作には何も変わりません。ローカルパスとクラウドパスの違いは、推論が行われる場所だけです。
このサイトにすでにあるウェブスクレイピングにおけるLLaMA 3では、リモートのルート: llama3.1:8bをOllama経由で製品ページに対してSeleniumを使ってカバーしています。このガイドでは、API経由でアクセスされるホスティングされたLlama 4モデルについて説明し、ローカルモデルのダウンロードやGPU要件なしで、フェッチレイヤーが重要である理由を証明するために特に選ばれた異なるデモターゲットを使用します。より広いカテゴリの質問については、LLMスクレイパーエクスプレイナーが一般的なLLM-as-parserツールについて説明します。
インストール
openaiはOpenRouterパスをカバーし(LlamaはそこにOpenAI互換の表面の背後で提供されています)、requestsはフェッチレイヤーをカバーします:
bash
pip install "llama-api-client==0.6.0" "openai==2.48.0" requests
設定
bash
export LLAMA_API_KEY="your_llama_api_key"
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"
Llamaが実際に読み取れるページを取得する
デモターゲットは、正確にこのギャップを示すために特別に作られた公共のレンダリング実践ページです: クライアント側のJavaScriptがそれをポピュレートするまで、その製品グリッドは空であり、生のHTMLには未実行のテンプレートリテラル文字列が含まれています。1つのスクリプトが差を示し、抽出する価値のあるバージョンを保存します:
python
# fetch_rendered.py — プレーンGET vs サーバーサイドレンダリング、同じURL
import os
import requests
URL = "https://www.scrapingcourse.com/javascript-rendering"
MARKER = 'class="product-name"'
plain = requests.get(URL, timeout=60).text
print(f"プレーンGET: {len(plain):,}文字 | product-nameスパン: {plain.count(MARKER)}")
resp = requests.post(
"https://api.scrapeless.com/api/v2/unlocker/request",
headers={
"Content-Type": "application/json",
"x-api-token": os.environ["SCRAPELESS_API_KEY"],
},
json={
"actor": "unlocker.webunlocker",
"input": {"url": URL, "method": "GET", "js_render": True},
},
timeout=120,
)
resp.raise_for_status()
rendered = resp.json().get("data", "")
print(f"レンダリングされた: {len(rendered):,}文字 | product-nameスパン: {rendered.count(MARKER)}")
with open("page.html", "w", encoding="utf-8") as f:
f.write(rendered)
実行はプレーンフェッチのために product-name spans: 1 と出力されます — その単一のヒットは未実行の <span class="product-name">${product.name}</span> テンプレートであり、実際のデータではありません — そして、レンダリングされたもので product-name spans: 13 となります。2つの間のギャップは、正確に文書ライフサイクルが定義するものであり、DOMに対してスクリプトが実行された後にのみ存在するコンテンツを示しています。レンダリングとプロキシルーティングは、単一のPOSTでサーバー側で発生します — ユニバーサルスクレイピングAPI がフェッチレイヤーであり、page.html はモデルが抽出できるものになっています。
基本実装:抽出器としてのLlama
Metaの公式Llama APIは、こちらで提供されるSDKに従って、構造化された出力を response_format={"type": "json_schema", "json_schema": {...}} を介して受け取ります — このエンドポイントにはベアの json_object モードはなく、スキーマに導かれた出力のみです。現在のOpenRouterカタログでは、meta-llama/llama-4-scout が最も安価なLlama 4世代モデルとしてリストされています;Metaの自社のネイティブAPIでは、同等のモデルIDは Llama-4-Scout-17B-16E-Instruct-FP8 というより完全な形式を持ちます。
注:このブロックはクレジット付きの
LLAMA_API_KEYを必要とします — このガイドが前提としない唯一の前提条件です。次のセクションでは、キャプチャされた出力を伴ってOpenRouterを通じて同一の抽出を実行します。
python
# extract_llama.py — ネイティブLlama API抽出(LLAMA_API_KEYが必要)
import json
import os
from llama_api_client import LlamaAPIClient
client = LlamaAPIClient(api_key=os.environ["LLAMA_API_KEY"])
page_html = open("page.html", encoding="utf-8").read()
response = client.chat.completions.create(
model="Llama-4-Scout-17B-16E-Instruct-FP8",
max_completion_tokens=2000,
response_format={
"type": "json_schema",
"json_schema": {
"name": "Products",
"schema": {
"type": "object",
"properties": {
"products": {
"type": "array",
"items": {
"type": "object",
"properties": {"name": {"type": "string"}, "price_usd": {"type": "number"}},
"required": ["name", "price_usd"],
},
}
},
"required": ["products"],
},
},
},
messages=[
{"role": "system", "content": "このページから実際の製品をすべて抽出してください。未レンダリングのテンプレートプレースホルダーは無視してください。"},
{"role": "user", "content": page_html},
],
)
data = json.loads(response.completion_message.content.text)
print(f"抽出した製品数:{len(data['products'])}個")
レスポンスの形状に注意してください:Metaのネイティブクライアントは、response.completion_message.content.text の下でメッセージを返し、OpenAI互換クライアントが使用する choices[0].message.content パスではありません。
ネイティブキーがない?OpenRouterを通して実行
OpenRouterは、他のモデルと同様のOpenAI互換のチャット補完の表面を背景にLlamaを提供します。これは、このガイドが実行した実際のバージョンであり、フェッチと抽出を1つの自己完結型スクリプトで行っています:
python
# extract_openrouter.py — 同じ抽出、OpenRouter経由で実行
import json
import os
import requests
from openai import OpenAI
URL = "https://www.scrapingcourse.com/javascript-rendering"
resp = requests.post(
"https://api.scrapeless.com/api/v2/unlocker/request",
headers={
"Content-Type": "application/json",
"x-api-token": os.environ["SCRAPELESS_API_KEY"],
},
json={"actor": "unlocker.webunlocker", "input": {"url": URL, "method": "GET", "js_render": True}},
timeout=120,
)
resp.raise_for_status()
page_html = resp.json().get("data", "")
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"])
completion = client.chat.completions.create(
model="meta-llama/llama-4-scout",
temperature=0,
max_tokens=2000,
response_format={"type": "json_object"},
messages=[
{
"role": "system",
"content": 'すべての製品を抽出してください。返信はJSONのみで:{"products":[{"name":str,"price_usd":number}]}の形でお願いします。',
},
{"role": "user", "content": page_html},
],
)
data = json.loads(completion.choices[0].message.content)
print(f"レンダリングされたページから抽出した製品数:{len(data['products'])}個")
for row in data["products"]:
print(json.dumps(row, ensure_ascii=False))
ライブ実行でちょうど12個の実際の製品が抽出され、プレースホルダーは一切含まれていませんでした:
text
レンダリングされたページから抽出した製品数:12個
{"name": "チャズ・カンガルーロゴフーディ", "price_usd": 52}
{"name": "テトン プルオーバー フーディ", "price_usd": 70}
両者は同じフェッチ・その後抽出するアーキテクチャで動作します。Ollamaを利用したローカルではトークンごとのコストがかからないですが、GPU対応のホストとモデルのダウンロードが必要です。このガイドのクラウドパスはローカルハードウェアが必要なくなりますが、トークンごとの料金が発生します。補足記事Web Scraping with LLaMA 3では、ローカルルートについて詳しく説明しています。
Q: なぜモデルはレンダリングされたページの製品の一つをスキップしたのですか?
それは本物の製品ではなかったからです。このガイドのライブ実行でレンダリングされたHTMLには13個のproduct-nameスパンが含まれていましたが、そのうちの1つはページのクライアントサイドフレームワークから残された未ハイドレーションの${product.name}テンプレートプレースホルダーであり、モデルは正しく抽出された12からそれを除外しました。
Q: Llamaを使ったスクレイピングは合法ですか?
抽出レイヤーは、クラウドおよびローカルパスの両方における収集ルールを変更しません。公共のページのみをフェッチし、サイトの利用規約およびロボット排除プロトコルによって標準化されたロボット指令を尊重し、ボリュームを制限し、適用法律に基づいて個人データを取り扱います。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



