Ollamaウェブスクレイピング:APIキーなしでローカルLLMスクレイパーを実行する
Senior Web Scraping Engineer
TL;DR:
- Ollamaはあなたのマシン上で抽出モデルを実行します。そのため、スクレイパーのLLMステップにはAPIキーやトークンあたりの請求がかかりません。
- Ollamaが行わないことはフェッチです。 ローカルモデルにはブラウザがなく、ページに対するネットワークアクセスもありません;それはあなたが渡したテキストを構造化します。
- そのギャップは1つのスクリプトに現れます。 JavaScriptレンダリングされたデモページに対する単純なGETリクエストは0の引用ブロックを返し、
js_renderを使用したScrapeless Universal Scraping API経由で同じURLは全10を返します — そして、そのレンダリングされたHTMLがモデルが読み取るものです。 - このガイドの抽出は本物です。 ライブ実行では、3つのレンダリングされた引用ブロックがローカルの
qwen2.5:0.5bモデルに渡され、各テキストと著者のクリーンなJSONが返されました — どこにもクラウドコールはありません。 - 小さなモデルはノイズが少ないことを求めます。 モデルがそれを見る前にHTMLをコンテンツ地域にトリミングします;焦点を絞ったスニペットの0.5Bモデルは速く正確であり、完全なページは収まらないでしょう。
- フェッチ側は無料で開始できます。 あなたのScrapeless APIキーをapp.scrapeless.comで作成してください。
スクレイパーのモデルをローカルで実行する理由
LLMスクレイパーはページのコンテンツを構造化されたレコードに変換し、そのモデルは他人のクラウド内に存在する必要はありません。Ollamaは、あなた自身のハードウェア上で小さなHTTP APIの背後でオープンモデルを実行します。そのため、抽出ステップではAPIキー、レート制限、トークンあたりのコストがありません — これは、多くのページを処理したり、第三者に送信したくないデータを扱うときに便利です。
ローカルモデルができないことはフェッチです。ブラウザがなく、セッションを保持せず、JavaScriptレンダリングされたページ上の単純なHTTPリクエストでは、コンテンツのないマークアップが返されます。したがって、Ollamaウェブスクレイパーは2つの層からなります:忠実にレンダリングされたHTMLを返すフェッチ層と、それをレコードに変換するローカルモデルとしての抽出層です。このガイドでは、最初の層としてScrapeless Universal Scraping APIを使用します。代わりに、ローカルモデルにライブウェブ検索を提供したい場合は、モデルが検索ツールを呼び出し、その結果に基づいて推論する — それは異なる作業であり、ローカルLLMウェブ検索ガイドで扱われています;この投稿は特定のページから構造化データを抽出することについてです。
前提条件
- Ollamaがインストールされており、実行中で、小さなツールフレンドリーモデルが取得されています:
ollama pull qwen2.5:0.5b。 - Python 3.9以降がインストールされており、
requestsが追加されています。 - ダッシュボードからのScrapeless APIキーが
SCRAPELESS_API_KEYとしてエクスポートされています。
インストール
小さなモデルを取得し、1つのPython依存関係をインストールします。Ollamaサーバーは実行中にlocalhost:11434でリッスンします。
bash
ollama pull qwen2.5:0.5b
pip install requests
ソースの中にではなく、環境にキーを保持します:
bash
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"
モデルが実際に読むことができるページを取得する
抽出品質はフェッチの忠実度に制限されるため、そこから始めます。JavaScriptレンダリングされたページでは、単純なHTTPクライアントが受け取るドキュメントは、リーダーが見るドキュメントではありません — コンテンツはスクリプトがDOMを構築した後にのみ到着します。このライフサイクルはHTMLスクリプティング仕様によって定義されています。1つのスクリプトが差をカウントします:
python
# fetch_rendered.py — 単純GET対サーバーサイドレンダリング、同じURL
import os
import requests
URL = "https://quotes.toscrape.com/js/"
MARKER = '<div class="quote">'
plain = requests.get(URL, timeout=60).text
print("plain GET chars:", len(plain), "| quote blocks:", 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, "js_render": True}},
timeout=120,
)
resp.raise_for_status()
rendered = resp.json().get("data", "")
print("rendered chars:", len(rendered), "| quote blocks:", rendered.count(MARKER))
実行では、単純なフェッチに対して0の引用ブロックが印刷され、レンダリングされたものに対して10が印刷されます:
text
plain GET chars: 5806 | quote blocks: 0
rendered chars: 8940 | quote blocks: 10
レンダリング、アンロック、プロキシルーティングはすべてサーバーサイドでその1つのPOSTで発生します — Universal Scraping APIがフェッチ層であり、レンダリングされたHTMLがローカルモデルが抽出する対象です。
ローカルモデルで抽出する
今、コンテンツをOllamaに渡してください。小さなモデルの精度を保つために必要な二つのことがあります:HTMLをコンテンツ領域にトリムして、モデルがページのchromeを読まないようにし、format: "json"を指定してJSON形式で出力を解析できるようにします。Ollamaの/api/generateエンドポイントは、モデル名とプロンプトを受け取り、完了を返します。これはOllama APIリファレンスに記載されています:
python
# extract_local.py — ローカルOllamaモデルで取得、トリム、抽出
import json
import os
import re
import requests
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": "https://quotes.toscrape.com/js/", "js_render": True}},
timeout=120,
)
resp.raise_for_status()
html = resp.json().get("data", "")
# 最初の三つの引用ブロックにトリム - 小さなローカルモデルはノイズが少ない方が良いです。
blocks = re.findall(r'<div class="quote">.*?</small>', html, re.S)[:3]
snippet = "\n".join(blocks)
completion = requests.post(
"http://localhost:11434/api/generate",
json={
"model": "qwen2.5:0.5b",
"prompt": 'このHTMLからすべての引用を正確な形でJSONに抽出してください '
'{"quotes":[{"text":"...","author":"..."}]}. JSONのみで返信してください。\n\nHTML:\n' + snippet,
"stream": False,
"format": "json",
"options": {"temperature": 0, "num_predict": 400},
},
timeout=600,
)
data = json.loads(completion.json()["response"])
records = data["quotes"]
print("records:", len(records))
print("first author:", records[0]["author"])
print("first text:", records[0]["text"])
ローカル実行は三つのレコードを返し、最初のものにはテキストと著者がそのまま残っていました:
text
records: 3
first author: アルバート・アインシュタイン
first text: 我々がそれを創り上げた世界は我々の思考のプロセスです。それを変えるには、我々の思考を変えなければなりません。
これが全体のスクレイパーで、モデルの半分はネットワークに触れませんでした:Scrapelessに取得とレンダリングのために一度POSTし、localhostに一度コールして抽出しました。呼び出しごとのブロック数は、ハードウェアが処理できるものにスケールしてください。CPU上の0.5Bモデルはクラウドエンドポイントより遅いので、小さなバッチで入力をコンパクトに保ちます。
無料プランでAPIキーを取得: app.scrapeless.com
高度なパターン
- モデルをハードウェアに合わせる。
qwen2.5:0.5bはCPU上でデモを実行するには十分速く、3Bまたは7Bモデルは、メモリと忍耐があれば混乱したページからより信頼性高く抽出します。リクエストの形状は変わりません — 変わるのはmodel文字列だけです。 - プロンプトの前にトリムする。 コンテンツコンテナを隔離することは、小さなモデルにとって最大の品質向上要因です。全体の文書ではなく、繰り返されるブロックを送信すると、トークンコストとエラー率の両方が低下します。
format: "json"とtemperature: 0を維持する。 JSONモードは出力を解析可能なJSONに制約し、ゼロ温度は抽出を安定させます。どちらもスクレイパーには必須です。- プロンプトではなくPythonでループする。 リクエストごとに一つのコンテンツ領域を扱うことで、レコードが混ざり合うことを防ぎ、特定のページに起因する不良抽出を特定できるようにします。
トラブルシューティング
Connection refusedがlocalhost:11434で発生。 Ollamaサーバーが起動していません。起動し(ollama serve)、モデルがollama listで引き出されていることを確認してください。- 抽出するブロックがゼロ。 フェッチは事前レンダリングされたHTMLを返しました。最初のスクリプトのように既知の要素を数えます。ほぼ空のフェッチはレンダリングの問題であり、
js_renderで修正可能であり、モデルの問題ではありません。 - モデルがJSONではなく散文を返す。
format: "json"を省略しました。それを持つと、Ollamaは出力を制約しますが、それを持たないと、あなたは解析の善意に依存することになります。 - 抽出が遅い、またはレコードをドロップする。 モデルが入力サイズに対して小さすぎます。呼び出しごとのブロック数を少なくするか、より大きなモデルに移行してください — 小さなローカルモデルは精度を速度と引き換えにし、短くてクリーンなプロンプトが両方を取り戻す方法です。
結論
Ollamaは、あなたが行う場所で動作する抽出レイヤーとしてその地位を確立しました:キーやトークンごとの請求なしに、あなた自身のハードウェア上でオープンモデルを使い、ページコンテンツをJSONに変換します。そのすべてが可能かどうかを決定するレイヤーはフェッチです — 最初のスクリプトの0対10のカウントがそれを明確にし、一度のサーバーレンダリングされたPOSTがギャップを埋めます。二つを接続すれば、レンダリングされたページが構造化されたレコードに変わり、モデルの部分は完全にlocalhostに留まります。
以下の英語のテキストを日本語に翻訳します:
無料のScrapelessアカウントを作成してAPIキーを取得し、開発者向けドキュメントはunlocker.webunlockerパラメータをカバーしています。定期的なジョブを計画する際は、Scrapelessの価格設定を確認してください。
FAQ
Q: Ollamaは自分でウェブサイトをスクレイピングできますか?
いいえ。Ollamaはローカルで言語モデルを実行します。任意のページに対するHTTPクライアントはなく、JavaScriptをレンダリングしません。渡されたテキストを構造化します。これに「フェッチレイヤー」を組み合わせます — ここではScrapeless Universal Scraping APIがサーバーサイドでページをレンダリングし、ローカルモデルが抽出を処理します。
Q: これはローカルLLMにウェブ検索を行わせることとどう違いますか?
方向性の違いです。ウェブ検索の設定はモデルが検索ツールを呼び出し、結果をもとに質問に答えることができますが、この設定はあなたが選んだ特定のページをフェッチし、モデルに構造化されたフィールドを抽出させます。1つは検索拡張型の回答、もう1つはスクレイピングのパイプラインです — 上記のローカルLLMウェブ検索ガイドが最初のものをカバーしています。
Q: 抽出にどのローカルモデルを使用すればよいですか?
あなたのスキーマを保持できる最小のモデルです。厳密なJSONプロンプトとtemperature: 0での抽出は推論が重くないため、上記の実行が示すように0.5Bモデルがトリミングされたスニペットで機能します。ページがかなり乱雑で小さなモデルがフィールドを落とすようになるまで、3B以上のモデルに移行する必要はありません。
Q: GPUは必要ですか?
いいえ。このガイドでの実行はCPU上で0.5Bモデルを使用しました。GPUを使用すると大きなモデルが実用的になり、全てが速くなりますが、CPU上の小さなモデルでもパイプラインの構築とテストには十分です。
Q: ローカルモデルでのスクレイピングは合法ですか?
ローカルでモデルを実行することは、収集ルールを変更しません。公開ページのみをフェッチし、サイトの利用規約およびロボット排除プロトコルで標準化されたロボット指令を尊重し、ボリュームを抑制し、あなたに適用される法律に基づいて個人データを扱ってください。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



