mcpField
P
PyScrappy

PyScrappy

本地服务
github.com/mldsveda浏览器自动化2646 天前更新

关于 PyScrappy

PyScrappy 是一个面向 AI Agent 的 Python 网页抓取工具包,可将网站转换为结构化、适合 LLM 使用的数据。既可作为 Python 库使用,也可作为 MCP 服务运行。其特点包括自愈式选择器和对反爬虫的应对能力。

主要功能

  1. 01通用抓取,返回文本、链接、图片、表格与元数据
  2. 02输出 Markdown、JSON 与 DataFrame 格式
  3. 03自愈式自适应选择器,站点改版后可重新定位元素
  4. 04可选 Playwright 后端,用于渲染 JavaScript 页面
  5. 05基于站点地图的整站抓取与并发抓取
  6. 06内置 22 个 MCP 工具

适用场景

  1. 01为 AI Agent 提供网页内容,供模型总结或分析
  2. 02在站点结构变化后保持长期运行的抓取任务
  3. 03通过命令行将网页直接导出为 Markdown 文件

MCP 相关

PyScrappy 提供 pyscrappy-mcp 服务,将其抓取器以工具形式暴露给 Claude、Cursor 等 MCP 客户端。pyscrappy chat 命令还可让本地模型通过内置 Agent 直接调用这些工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"mldsveda-pyscrappy": {
"command": "uvx",
"args": [
"pyscrappy"
],
"env": {
"OMDB_API_KEY": "<OMDB_API_KEY>"
}
}
}
}
启动命令uvx pyscrappy
需要配置的环境变量
OMDB_API_KEY密钥Optional free OMDb API key, used only by the movie lookup tool (get one at https://www.omdbapi.com/apikey.aspx).

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypipyscrappy1.5.9

工具

24

收录的工具列表 · 更新于 2 周前

参数类型必填说明
urlstring是字符串,要抓取的页面 URL,需包含 scheme,例如 "https://example.com/products"。必填,无默认值。
max_pagesinteger否整数,按“next”样式的分页继续抓取,最多抓取指定页数,例如 3。默认值为 1(仅抓取给定 URL)。
render_jsboolean否布尔值,使用无头浏览器后端渲染 JavaScript,例如 True。默认值为 False;允许的值为 True 或 False,且 True 需要安装 pyscrappy[browser] 扩展包。
selectorsany否可选字典,将输出字段名映射到用于提取特定值的 CSS 选择器,并将这些值添加到每个 data 项中,例如 {"title": "h1", "price": ".amount"}。默认值为 None(仅返回标准文本/链接/图片/表格/元数据)。

详细介绍

PyScrappy

自适应 Python 网页抓取工具包(自修复、隐蔽)
+ 面向 AI Agent 的 MCP 服务

Python 3.9+ PyPI Latest Release License: MIT Downloads Glama quality Documentation MCP Toplist

PyScrappy 是一款面向 AI 的网页抓取工具包,可将网站转换为结构化、适用于大模型的数据。可将其用作 Python 库,或将其作为 MCP 服务提供给 AI Agent 使用。

📖 文档: pyscrappy.vercel.app

主要功能

  • 通用抓取器 — 输入任意 URL,即可获取结构化文本、链接、图片、表格和元数据
  • 适用于大模型的输出 — .to_markdown() 可将任意结果转换为简洁的 Markdown;还支持 .to_json() 和 .to_dataframe()
  • MCP 服务 — 将抓取器作为工具提供给 AI Agent 使用(Claude、Cursor、本地大模型等)
  • JS 渲染 — 可选的 Playwright 后端,适用于 JavaScript 内容较多的网站
  • 自定义选择器 — 传入 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 Agent

PyScrappy 附带一个 MCP 服务, 可将抓取器作为工具提供给 Agent 使用,因此 Agent(Claude、Cursor、OpenAI Agent、 本地大模型)可以从任意 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 通信的 Agent,无需宿主即可让本地模型 将抓取器作为工具调用:

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 Agent 使用 PyScrappy)

PyScrappy 提供可选的 Model Context Protocol 服务,因此 AI Agent(例如 Claude)可以将 PyScrappy 的抓取器作为工具调用,并 获取结构化的网页数据。

PyScrappy MCP 服务
pip install 'pyscrappy[mcp]'

MCP 扩展会安装独立的 fastmcp 包,并要求 Python 3.10 或更新版本。在 Python 3.9 上,核心抓取库仍可正常使用,但 MCP 服务不可用。

这会安装 pyscrappy-mcp 命令。它默认通过 stdio 为本地 MCP 客户端提供服务;远程部署可使用 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 不会继承你的 shell 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。

如需查看完整的实时列表,请让 Agent 调用 list_available_scrapers 工具,或在 shell 中运行:

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" }
    }
  }
}

注册完成后,像平常一样向 Agent 提出请求即可,例如:“使用 pyscrappy 获取 bbc.co.uk 的最新头条新闻和 AAPL 股票报价。”

内置抓取器

PyScrappy 内置 24 个抓取器,其中所有无需代理即可运行的抓取器也都作为 MCP 工具提供。

其中一些包括:

  • GenericScraper — 自动提取任意 URL 的内容(文本、链接、图片、表格、元数据)
  • 数据 / 研究 — WikipediaScraper、StockScraper(Yahoo Finance)、NewsScraper(RSS/Atom)、GitHubScraper、HackerNewsScraper,以及天气、加密货币、货币、词典、图片、LinkedIn 招聘和书籍搜索
  • 电子商务 — 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 Agent,且无需修改 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 Agent 可以通过 scrape_with MCP 工具调用它 — 无需修改核心代码。

一等 MCP 工具(可选)。 添加 mcp_tools 映射后,你的抓取器就会成为专用的、带类型的 MCP 工具,而不只是能通过通用的 scrape_with 调用 — 它的架构会根据方法签名生成,因此 Agent 能获得规范的命名参数:

@register_scraper("reddit")
class RedditScraper(BaseScraper):
    mcp_tools = {"search_reddit": "scrape"}   # tool name -> method

    def scrape(self, subreddit: str, sort: str = "hot") -> ScrapeResult:
        ...

有关完整且可复制的起始模板,请参阅插件模板;完整操作指南请参阅插件指南。

快速开始

抓取任意 URL → 整洁、适合大模型使用的 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])

更偏好原始字段?每个结果都是一个 ScrapeResult,其中 .data 是一个字典列表:

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() 会返回一个 SelectorList,其中包含 .get() / .getall() / .text()。find_similar() 会查找标签相同且类名有重叠的元素;找到一个卡片/行后,用它提取所有卡片/行会很方便。

自适应(自愈)选择器

硬编码的 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() 会将日志汇总为每个选择器一行(自愈次数、 最新/最低/平均置信度、上次自愈时间),并按自愈次数从高到低排序 ——这样漂移最多的选择器和最不稳定的重新定位结果(置信度最低) 会显示在顶部,供人工检查。

指纹会持久化到一个小型 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 提供两种绕过方式。

代理(或轮换列表)——适用于 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 指纹模拟——许多反机器人系统会根据普通 HTTP 客户端的 TLS/JA3 指纹将其拦截,不提供任何内容。设置 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 使用一个抓取器处理多个输入;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> 文件(递归一层)、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 前触发一次(缓存命中时不会触发)。
  • on_retry(url, attempt, delay, error) 会在每次退避等待前触发。
  • on_cache_hit(url) 会在请求由缓存提供时触发。

这三个回调都尽力执行:如果回调抛出异常,会以 debug 级别记录,且不会中断抓取。它们在同步和异步路径中都会触发。

依赖项

必需: httpx、beautifulsoup4、lxml

可选: playwright(JS 渲染)、pandas(DataFrame)、fastmcp(MCP 服务,Python 3.10+)

许可证

MIT

贡献

欢迎所有贡献。请参阅 Issues。

此软件包仅用于教育和研究目的。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。