PyScrappy について
PyScrappy は、Web サイトを LLM で扱いやすい構造化データに変換する Python 製のスクレイピングツールキットです。ライブラリとして利用することも、AI エージェント向けの MCP サーバーとして実行することもできます。マークアップが変わった際に要素を再特定する適応型セレクターを備えています。
主な機能
- 01テキスト、リンク、画像、表、メタデータの汎用抓出
- 02Markdown、JSON、DataFrame 形式での出力
- 03ページ構造変更に対応する自己修復型セレクター
- 04JavaScript 描画ページ向けの Playwright バックエンド
- 05サイトマップを用いたクロールと並行処理
- 06MCP サーバーで公開される 22 のツール
活用シーン
- 01AI エージェントへのウェブコンテンツ提供と要約
- 02サイトのリニューアル後も継続稼働する抓取処理
- 03コマンドラインでのウェブページの Markdown 書き出し
MCP 対応
PyScrappy は pyscrappy-mcp サーバーを提供し、抓取機能を Claude や Cursor などの MCP クライアントにツールとして公開します。pyscrappy chat コマンドでは、ローカルモデルが内蔵エージェント経由で同じツールを呼び出せます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
24掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| url | string | はい | 文字列。スクレイピングするページの URL。"https://example.com/products" のように scheme を含めます。必須。デフォルト値なし。 |
| max_pages | integer | いいえ | 整数。「next」形式のページネーションをたどる最大ページ数。例:3。デフォルトは 1(指定された URL のみをスクレイピング)。 |
| render_js | boolean | いいえ | ブール値。ヘッドレスブラウザーバックエンドで JavaScript をレンダリングします。例:True。デフォルトは False。指定できる値は True または False です。True を使用するには pyscrappy[browser] エクストラのインストールが必要です。 |
| selectors | any | いいえ | 省略可能な辞書。出力フィールド名を CSS セレクターに対応付け、特定の値を各 data 項目に抽出します。例:{"title": "h1", "price": ".amount"}。デフォルトは None(標準のテキスト/リンク/画像/表/メタデータのみを返します)。 |
詳細
適応型 Python ウェブスクレイピングツールキット(自己修復、ステルス)
+ AI エージェント向け MCP サーバー
PyScrappy は、ウェブサイトを構造化された LLM 対応データに変換する、AI ネイティブなウェブスクレイピングツールキットです。Python ライブラリとして使うことも、AI エージェント向けの MCP サーバーとして公開することもできます。
📖 ドキュメント: pyscrappy.vercel.app
主な機能
- 汎用スクレーパー — 任意の URL を渡すと、構造化されたテキスト、リンク、画像、テーブル、メタデータを取得できます
- LLM 対応出力 —
.to_markdown()で任意の結果を整った Markdown に変換できます。.to_json()と.to_dataframe()も利用できます - MCP サーバー — スクレーパーを AI エージェント(Claude、Cursor、ローカル LLM など)向けのツールとして公開できます
- JS レンダリング — JavaScript を多用するサイト向けに、オプションで Playwright バックエンドを利用できます
- カスタムセレクター — CSS セレクターを渡して、必要な情報だけを抽出できます
- 連鎖可能な
Selector— CSS/XPath、find_all、find_by_text、find_similarを使って HTML を直接操作できます(Scrapy/BeautifulSoup 形式) - 適応型(自己修復)セレクター — 要素を記憶し、サイトのマークアップが変更された場合も類似性に基づいて見つけ直すため、スクレーパーが気付かないうちに動かなくなることを防ぎます
- 並行スクレイピング —
scrape_many/scrape_allで複数のスクレイピングを並行して実行できます - サイトマップのクロール —
sitemap.xmlからサイト全体を列挙してスクレイピングできます(インデックスと gzip に対応) - プロキシとスクレイピング API のサポート — プロキシまたは ScraperAPI/ScrapeOps 経由でアクセスし、ブロックされたサイトにも対応できます
- TLS フィンガープリントの偽装 —
impersonate="chrome"を使うと、通常のクライアントをブロックするボット対策フィルターを回避できます(オプションのcurl_cffiバックエンド) - コマンドライン抽出 —
pyscrappy extract <url> out.mdでコードを書かずに URL をスクレイピングし、ファイルに直接保存できます - 再試行とレート制限 — 指数バックオフとドメインごとのレート制限を標準で備えています
- 型安全 — 完全な型ヒントと
py.typedマーカーを備えています - 20 以上の組み込みスクレーパー — Wikipedia、IMDB、株式、ニュース、GitHub、Amazon/IKEA、YouTube などに対応しています。その他
インストール
pip install pyscrappy
オプションの追加機能:
# Browser support (for JS-rendered pages)
pip install 'pyscrappy[browser]'
playwright install chromium
# DataFrame support
pip install 'pyscrappy[dataframe]'
# MCP server (use PyScrappy's scrapers as AI-agent tools)
pip install 'pyscrappy[mcp]'
# Stealth (TLS-fingerprint impersonation to bypass anti-bot filters)
pip install 'pyscrappy[stealth]'
# Parquet / Excel export (ScrapeResult.to_parquet() / .to_excel())
pip install 'pyscrappy[parquet]'
pip install 'pyscrappy[excel]'
# Everything
pip install 'pyscrappy[all]'
AI エージェント向け
PyScrappy には MCP サーバーが付属しており、 スクレーパーをツールとして公開します。これにより、エージェント(Claude、Cursor、OpenAI エージェント、 ローカル LLM)は任意の URL から構造化されたウェブデータを取得し、モデルに直接渡せます。
AI agent ──MCP tool call──▶ PyScrappy ──fetch + extract──▶ Any website
▲ │
└────────────── clean Markdown / JSON ◀───────────────────────┘
pip install 'pyscrappy[mcp]'
claude mcp add pyscrappy pyscrappy-mcp
あとは、*「pyscrappy を使って bbc.com の最新見出しを要約して」*と頼むだけです。 詳しいセットアップ方法とツール一覧については、MCP サーバーをご覧ください。
ローカルモデル(Ollama)、MCP ホスト不要
Ollama は単独では MCP と通信できないため、通常は間にホスト(Goose、Cline など)を実行する必要があります。 PyScrappy では、Ollama と直接通信する組み込みエージェントを使うことで、この手順を省き、ローカルモデルがスクレーパーをツールとして呼び出せます。
pip install 'pyscrappy[mcp]' # needs Python 3.10+
pyscrappy chat --model qwen2.5 "what's the current AAPL quote?"
MCP サーバーと同じ 22 個のツールを公開します。必要なのは、ツール呼び出しに対応したモデル(Llama 3.1、Qwen 2.5、Mistral など)だけです。適切なツールをどれだけうまく選ぶかは、モデルによって異なります。--host でリモートの Ollama を指定し、-v を渡すと各ツールの呼び出しを確認できます。
MCP サーバー(AI エージェントから PyScrappy を使用)
PyScrappy には、オプションの Model Context Protocol サーバーが付属しています。これにより、AI エージェント(Claude など)が PyScrappy のスクレイパーをツールとして呼び出し、 構造化されたウェブデータを取得できます。
pip install 'pyscrappy[mcp]'
MCP の追加機能をインストールすると、スタンドアロンの fastmcp パッケージがインストールされ、Python 3.10 以降が必要になります。Python 3.9 でもコアのスクレイピングライブラリは動作しますが、MCP サーバーは利用できません。
これにより、pyscrappy-mcp コマンドがインストールされます。ローカルの MCP クライアント向けにはデフォルトで stdio を使用し、リモート環境へのデプロイでは Streamable HTTP と従来の SSE を利用できます。
pyscrappy-mcp # stdio (default)
pyscrappy-mcp --http # Streamable HTTP
pyscrappy-mcp --sse # legacy SSE
python -m pyscrappy.mcp を使って stdio サーバーを実行することもできます。
Claude Code への登録
claude mcp add pyscrappy pyscrappy-mcp
Claude Desktop への登録
claude_desktop_config.json に追加し、アプリを再起動してください。
{
"mcpServers": {
"pyscrappy": {
"command": "pyscrappy-mcp"
}
}
}
ヒント: Claude Desktop はシェルの
PATHを引き継ぎません。pyscrappy-mcpが見つからない場合は、コマンドの絶対パス(例:which pyscrappy-mcpの出力)を 使用してください。
利用可能なツール
サーバーは 20 個以上のツールを公開しています。よく使われるものは、scrape_url(任意の
URL → テキスト、リンク、画像、テーブル、メタデータ)、scrape_wikipedia、
scrape_stock、scrape_news、search_github です。さらに、画像/YouTube/LinkedIn/Hacker News/書籍の検索、天気、暗号資産、
通貨、辞書、Amazon/Newegg/IKEA/SoundCloud、IMDB、Zomato/Uber Eats など、多数のツールがあります。
最新の全リストを確認するには、エージェントに list_available_scrapers
ツールを呼び出すよう依頼するか、シェルから次を実行してください。
python -c "from pyscrappy import list_scrapers; print(', '.join(sorted(list_scrapers())))"
lookup_movie ツールには無料の OMDb API
キーが必要です。MCP クライアントの設定を通じてサーバーに渡してください。たとえば Claude Desktop の場合は次のとおりです。
{
"mcpServers": {
"pyscrappy": {
"command": "pyscrappy-mcp",
"env": { "OMDB_API_KEY": "your-key" }
}
}
}
登録したら、「pyscrappy を使って bbc.co.uk の最新の見出しと AAPL の株価を取得して」のように、自然な言葉でエージェントに依頼してください。
組み込みスクレイパー
PyScrappy には 24 個の組み込みスクレイパーが付属しており、プロキシなしで動作するものはすべて MCP ツールとしても公開されています。
その一部を紹介します。
GenericScraper— 自動抽出(テキスト、リンク、画像、テーブル、メタデータ)を使って任意の URL をスクレイピングします- データ/リサーチ —
WikipediaScraper、StockScraper(Yahoo Finance)、NewsScraper(RSS/Atom)、GitHubScraper、HackerNewsScraperのほか、天気、暗号資産、通貨、辞書、画像、LinkedIn の求人、書籍の検索 - E コマース —
AmazonScraper、NeweggScraper、IKEAScraper - ソーシャル/メディア/フード —
YouTubeScraper、SoundCloud、Zomato、Uber Eats(Instagram/Twitter/Spotify も付属していますが、ブロックされており、プロキシが必要です)
…ほかにも多数あります。最新の全リストを確認するには、次を実行してください。
python -c "from pyscrappy import list_scrapers; print(', '.join(sorted(list_scrapers())))"
IMDBScraper(lookup_movie)だけは例外で、キーが必要です。無料の
OMDb OMDB_API_KEY を使用してください(渡し方は上記の
MCP 設定を参照してください)。
プラグイン
PyScrappy は拡張可能です。独自のスクレーパーを追加でき、サードパーティはスタンドアロンの pyscrappy-<name> パッケージとして提供できます。登録したスクレーパーは、組み込みのスクレーパーと同じ場所で利用でき、MCP サーバーや pyscrappy chat エージェントでも、PyScrappy のコアを変更することなく使用できます。
独自のコードで使用する場合 — デコレーターで登録します。
from pyscrappy import BaseScraper, register_scraper, get_scraper
from pyscrappy.core.models import ScrapeResult, ScrapeMetadata
@register_scraper("reddit")
class RedditScraper(BaseScraper):
def scrape(self, subreddit: str, **kwargs) -> ScrapeResult:
data = self.fetch_and_parse(f"https://old.reddit.com/r/{subreddit}/.json")
# ... build a list of dicts ...
return ScrapeResult(data=[...], metadata=ScrapeMetadata(scraper="reddit"))
get_scraper("reddit")().scrape(subreddit="python")
配布可能なパッケージとして使用する場合 — pyproject.toml でエントリーポイントを指定すると、パッケージのインストール後に PyScrappy が検出します。
[project.entry-points."pyscrappy.scrapers"]
reddit = "pyscrappy_reddit:RedditScraper"
pip install pyscrappy-reddit の実行後、スクレーパーは list_scrapers() に表示され、AI エージェントは scrape_with MCP ツールを使って呼び出せます。コアの変更は不要です。
専用 MCP ツール(任意)。 mcp_tools マッピングを追加すると、スクレーパーは汎用の scrape_with からしか利用できない状態ではなく、専用の型付き MCP ツールになります。スキーマはメソッドのシグネチャから生成されるため、エージェントは適切な名前付き引数を利用できます。
@register_scraper("reddit")
class RedditScraper(BaseScraper):
mcp_tools = {"search_reddit": "scrape"} # tool name -> method
def scrape(self, subreddit: str, sort: str = "hot") -> ScrapeResult:
...
そのままコピーして使える完全なひな形についてはプラグインテンプレートを、詳しい手順についてはプラグインガイドをご覧ください。
クイックスタート
任意の URL をスクレイピング → LLM 向けのクリーンな Markdown
from pyscrappy import scrape
result = scrape("https://en.wikipedia.org/wiki/Web_scraping")
print(result.to_markdown()) # feed straight to an LLM
# ...or result.to_json() / result.to_dataframe()
# Write to a file — format inferred from the extension:
result.save("out.json") # .json .csv .md .ndjson .yaml .parquet .xlsx
# (.parquet needs pyscrappy[parquet]; .xlsx needs pyscrappy[excel])
生のフィールドが必要ですか? 結果はすべて、.data(辞書のリスト)を持つ ScrapeResult です。
print(result.data[0]["metadata"]["title"])
print(result.data[0]["text"]["word_count"])
カスタム CSS セレクター
from pyscrappy import GenericScraper
with GenericScraper() as gs:
result = gs.scrape(
url="https://news.ycombinator.com",
selectors={"title": ".titleline a", "score": ".score"},
)
for item in result.data:
print(item["title"], item.get("score", ""))
Selector で HTML をナビゲートする
構造化された辞書を取得するのではなく、マークアップを直接たどりたい場合(Scrapy/BeautifulSoup スタイル)は、Selector を使います。
from pyscrappy import Selector
page = Selector(html) # or navigate any HTML string
page.css(".title::text").getall() # CSS with ::text / ::attr(name)
page.xpath("//a/@href").getall() # XPath (elements, text(), @attr)
page.find_all("h2", class_="title") # BeautifulSoup-style search
page.find_by_text("Add to cart", tag="button") # search by text content
first = page.css(".product")[0]
first.css(".price::text").get() # chainable
first.find_similar() # sibling elements shaped like this one
css()/xpath() は、.get()/.getall()/.text() を備えた SelectorList を返します。find_similar() は同じタグと共通するクラスを持つ要素を特定するため、1 つ見つけた後にすべてのカードや行を取得するのに便利です。
アダプティブ(自己修復)セレクター
ハードコードされた CSS セレクターは、サイトのマークアップが変更された日から、気づかないうちに機能しなくなります。 アダプティブセレクターなら、最初に要素のフィンガープリントを保存しておき、後でセレクターが何も見つけられなくなった場合に、空の結果を返すのではなく、構造とテキストの類似性を使って要素を再特定します。
from pyscrappy import Selector
# First run: match normally and remember this element under an id.
page = Selector(html_v1, url="https://shop.example.com")
price = page.css(".price", auto_save=True, adaptive_id="price").get()
# Later, after a redesign renamed ".price" — heal instead of breaking.
# `expect` is an optional contract: the relocated element must satisfy it,
# so a good structural score can't smuggle in the wrong field.
page = Selector(html_v2, url="https://shop.example.com")
result = page.css(
".price",
adaptive=True,
adaptive_id="price",
expect=lambda s: s.text().startswith("$"),
)
print(result.get(), "→ confidence:", result.adaptive_confidence)
再特定の判断方法と、単純な類似度マッチングより優れている点は次のとおりです。
- 単純な平均ではなく、重み付けされたシグナル。 安定した
id/data-*フックは兄弟要素のタグ一覧よりはるかに重視されるため、弱いシグナルが強いシグナルを覆すことはありません。 - アンカーを基準に判定。 最も近い安定した祖先(id や
data-*を持つコンテナー)と深さを記憶するため、絶対位置が変わるレイアウトの入れ替えにも対応できます。 - 変動性を考慮したテキスト。 価格、日付、件数は重みが下げられるため、スクレイピングのたびに最も変わりやすいフィールドでも、修復の信頼性が保たれます。
- 信頼度をスコア化。
SelectorList.adaptive_confidence(0~100)で再特定の確信度を確認でき、threshold=で受け入れに必要な最低値を設定できます。 - 契約による検証(オプトイン)。
expect=<callable>を渡すと、修復後の要素が不変条件(例:「テキストが価格のように見える」)を満たすよう要求できます。しきい値を超えていても契約を満たさない修復は拒否されるため、構造上の類似性だけでフィールドの意味が変わることはありません。
修復はセレクターが解決する対象を変えるため、受け入れられた修復はすべて記録されます。ストアには、信頼度、次点候補との差、修復前後のフィンガープリントを含む追記専用の監査ログ(フィンガープリントストアの隣にある adaptive.heal.ndjson)が保存され、store.heal_log() から読み取れます。そのため、ドリフトが気づかないうちに吸収されるのではなく、観測可能な状態に保たれます。概要をすばやく確認するには、store.heal_report() でログをセレクターごとに 1 行に集約できます(修復回数、最新/最低/平均の信頼度、最後に修復された日時)。修復回数の多い順に並ぶため、ドリフトが最も大きいセレクターや、信頼度が最も低く不確かな再特定が上位に表示され、人が確認できます。
フィンガープリントは小さな JSON ストア(デフォルトでは ~/.pyscrappy/adaptive.json、または $PYSCRAPPY_HOME)に永続化され、サイトごとに名前空間が分かれているため、異なるサイトで同じ adaptive_id を使っても競合しません。アダプティブ機能は完全にオプトインです。adaptive=True を指定しなければ、壊れたセレクターはこれまでどおり空の結果を返します。
サイト固有のスクレイパー
組み込みスクレイパーはすべて同じパターンに従います。インスタンスを作成し、scrape(...) を呼び出して、result.data(または .to_dataframe() / .to_markdown())を読み取ります。
from pyscrappy import WikipediaScraper
with WikipediaScraper() as ws:
result = ws.scrape(query="Python (programming language)", mode="summary")
print(result.data[0]["text"])
スクレイパーごとに引数が異なります(Wikipedia、株式、IMDB、ニュース、YouTube、Amazon/Newegg/IKEA、Uber Eats など。全リストを参照してください)。スクレイパーごとの引数と使用例については、ドキュメントを参照してください。
コマンドラインから
コードを書かずに、URL を直接ファイルにスクレイピングできます。出力形式はファイル拡張子から推定されます。
pyscrappy extract https://example.com out.md # clean Markdown
pyscrappy extract https://example.com out.json # structured JSON
pyscrappy extract https://example.com out.txt # extracted page text
pyscrappy extract https://example.com out.html # raw fetched HTML
# Narrow to elements matching a CSS selector, or render JS first:
pyscrappy extract https://example.com items.txt --css-selector ".product"
pyscrappy extract https://example.com page.md --render-js
設定
from pyscrappy import ScraperConfig, GenericScraper
config = ScraperConfig(
timeout=20.0, # request timeout in seconds
max_retries=3, # retry failed requests
retry_jitter=True, # spread exponential retries to avoid lockstep traffic
rate_limit=2.0, # seconds between requests per domain
proxy="http://...", # proxy URL, or a list to rotate through
scraper_api=None, # route via a scraping-API service (see below)
headless=True, # browser runs headless
render_js="auto", # auto-detect if JS rendering is needed
cache_ttl=0, # response cache TTL in seconds (0 = disabled)
cache_dir=None, # also persist the cache to disk (survives restarts)
cache_dir_max_size=512, # max live entries kept on disk before oldest are pruned
impersonate=None, # e.g. "chrome" to spoof a browser's TLS fingerprint (see below)
)
with GenericScraper(config) as gs:
result = gs.scrape(url="https://example.com")
プロキシとブロックされたサイト
一部のサイト(例:eBay、Instagram、Twitter/X、Spotify)は、自動化された直接リクエストをブロックします。PyScrappy では、これらを回避するために 2 つの方法を利用できます。
プロキシ(またはローテーションするリスト)は、HTTP とブラウザーの両方のバックエンドに適用されます。
from pyscrappy import ScraperConfig, AmazonScraper
# Single proxy
config = ScraperConfig(proxy="http://user:pass@host:port")
# Rotating list (one picked per request)
config = ScraperConfig(proxy=["http://p1:8080", "http://p2:8080"])
スクレイピング API サービス(ScraperAPI、ScrapeOps、ScrapingBee)は、リクエストをサービス経由でルーティングします。サービスがプロキシとボット対策チャレンジを処理します。
config = ScraperConfig(scraper_api={
"provider": "scraperapi", # or "scrapeops", "scrapingbee"
"api_key": "YOUR_KEY",
"render_js": True, # optional
})
# Now any scraper works through the service, unchanged:
with AmazonScraper(config) as scraper:
result = scraper.scrape(query="laptop")
上記で「プロキシが必要」と記載されたスクレイパーを使うには、この方法が確実です。
TLS フィンガープリントの偽装 — 多くのボット対策システムは、コンテンツを配信する前に、TLS/JA3 フィンガープリントを使って通常の HTTP クライアントをブロックします。impersonate を設定して実際のブラウザーのフィンガープリントを模倣すれば、ヘッドレスブラウザーを使わずにこの種のブロックを回避できます。
from pyscrappy import ScraperConfig, GenericScraper
# needs the optional extra: pip install 'pyscrappy[stealth]'
config = ScraperConfig(impersonate="chrome") # or "chrome124", "safari", "firefox"
with GenericScraper(config) as gs:
result = gs.scrape("https://example.com")
偽装機能は同期と非同期の両方の処理経路で動作します(非同期では curl_cffi の AsyncSession を使用します)。そのため、ステルス性と高スループットの非同期スクレイピングを組み合わせられます。通常の再試行、レート制限、キャッシュ、robots の処理も引き続き適用されます。
import asyncio
from pyscrappy import scrape_async, ScraperConfig
async def main():
cfg = ScraperConfig(impersonate="chrome")
return await scrape_async("https://example.com", config=cfg)
asyncio.run(main())
並行スクレイピング
スクレイピングは I/O バウンドの処理なので、複数のスクレイピングを同時に実行すると、ネットワーク待ちを並列化できます。scrape_many は複数の入力に対して 1 つのスクレイパーを実行し、scrape_all は複数のスクレイパーを組み合わせて実行します。どちらも入力順を保持します。
from pyscrappy import scrape_many, scrape_all, AmazonScraper, WikipediaScraper, NewsScraper
# One scraper, many queries, concurrently:
results = scrape_many(AmazonScraper, [{"query": "laptop"}, {"query": "phone"}])
# Different scrapers at once:
results = scrape_all([
lambda: WikipediaScraper().scrape(query="Python"),
lambda: NewsScraper().scrape(feed_url="https://rss.nytimes.com/services/xml/rss/nyt/World.xml"),
])
サイトマップのクロール
ページネーションでは次ページへのリンクをたどりますが、サイトマップにはサイト全体の URL が直接列挙されています。GenericScraper は /sitemap.xml を読み込み(robots.txt の Sitemap: ディレクティブ、または標準的なパスから検出)、<sitemapindex> をたどって子サイトマップを取得し、列挙されたすべてのページをスクレイピングできます。
from pyscrappy import GenericScraper
with GenericScraper() as gs:
# Just enumerate the URLs:
urls = gs.sitemap_urls("https://example.com") # -> list[str]
# Or fetch + extract each, concurrently, into one result:
result = gs.scrape_sitemap("https://example.com", max_urls=100)
print(len(result.data), "pages scraped")
<urlset> の末端要素と <sitemapindex> ファイル(1 レベル再帰)、gzip 圧縮されたサイトマップ(.xml.gz)に対応し、URL の重複を除去します。取得には通常のレート制限、キャッシュ、プロキシ、ステルス機能が適用され、複数サイトへのリクエストには scrape_all が再利用されます。max_urls でクロールの上限を設定します(サイトマップには数万件の URL が列挙されることがあるため、scrape_sitemap では必須です)。
レスポンスキャッシュ
cache_ttl に正の秒数を設定すると、成功した GET レスポンスがキャッシュされます。TTL 内に同じ URL(およびクエリパラメーター)へのリクエストが繰り返された場合、ネットワークアクセスとレート制限をスキップしてキャッシュから応答します。キャッシュはデフォルトで無効です(cache_ttl=0)。
from pyscrappy import WikipediaScraper
from pyscrappy import ScraperConfig
config = ScraperConfig(cache_ttl=300) # cache for 5 minutes
with WikipediaScraper(config) as ws:
ws.scrape(query="Python") # fetched over the network
ws.scrape(query="Python") # served from cache
キャッシュはメモリ上にあり、同じプロセス内のスクレイパーインスタンス間で共有されます(そのため、MCP サーバーを介した繰り返しの呼び出しも高速化されます)。プロセスの終了時にクリアされます。手動で空にするには HttpClient.clear_cache() を呼び出します。
キャッシュのサイズにはLRU による上限があります。保持されるエントリーは最大 cache_max_size 件(デフォルトは 512)で、上限に達すると最も長く使われていないエントリーが削除されます。そのため、MCP サーバーなどの長時間稼働するプロセスで多数の異なる URL を取得しても、再起動までキャッシュが増え続けることはありません。必要に応じて上限を増減できます。
config = ScraperConfig(cache_ttl=300, cache_max_size=2000)
永続キャッシュ(ディスク上)。 cache_dir を設定すると、レスポンスもディスクに保存されるため、プロセスの再起動後や別の実行時にもキャッシュを利用できます。再取得せずにスクレイピングや CLI ジョブを再実行する場合に便利です。速度を高めるため、引き続きメモリキャッシュを先に参照し、ディスクからのキャッシュヒットはメモリにも戻されます。
config = ScraperConfig(cache_ttl=3600, cache_dir="~/.cache/pyscrappy")
ディスク上のキャッシュにも上限があります。書き込みのたびに期限切れのエントリーが削除され、cache_dir_max_size(デフォルトは 512)を超えた場合は古いエントリーが削除されるため、cache_dir に異なる URL ごとのファイルが際限なく増えることはありません。
clear_cache() はメモリキャッシュを空にします。ディスク上のキャッシュは意図的に保持されるため、クリアするには cache_dir を削除してください。
可観測性フック
長時間のクロールでは、ログを有効にせずリクエストの進行状況をリアルタイムで監視するために(プログレスバー、メトリクスなど)、軽量なコールバックを渡せます。
config = ScraperConfig(
on_request=lambda url: print("GET", url), # before a network fetch
on_retry=lambda url, attempt, delay, err: print("retry", attempt, url),
on_cache_hit=lambda url: print("cached", url), # served from cache
)
on_request(url)は、URL の取得前に 1 回呼び出されます(キャッシュヒット時は呼び出されません)。on_retry(url, attempt, delay, error)は、バックオフ待機の前に毎回呼び出されます。on_cache_hit(url)は、リクエストがキャッシュから応答されたときに呼び出されます。
3 つともベストエフォートで動作します。コールバックが例外を発生させても、デバッグレベルでログに記録されるだけで、スクレイピングが中断されることはありません。同期処理と非同期処理の両方で呼び出されます。
依存関係
必須: httpx、beautifulsoup4、lxml
任意: playwright(JS レンダリング)、pandas(DataFrame)、fastmcp(MCP サーバー、Python 3.10 以降)
ライセンス
貢献
すべての貢献を歓迎します。Issues をご覧ください。
このパッケージは教育および研究目的で提供されています。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。