Scrapling:那个会 "自我修复" 的爬虫,让我的定时抓取终于不报警了

被维护性支配的恐惧

我有个每周跑一次的抓取脚本,挂在 cron 上抓某个电商站的价格。三天前的周二早上 6 点,我被一封告警邮件吵醒:AttributeError: 'NoneType' object has no attribute 'text'。爬起来一看,目标网站改版了,div.product-card 换成了 div[data-testid="product-card-2026"],脚本里所有 CSS 选择器集体失效。

打开历史聊天记录翻了一下,这种事我这一年遇到过 5 次。改版一次我手动改一次选择器,改完再观察几天看它稳不稳定。给独立开发生活增加的不是乐趣,是工单。

直到这周翻 GitHub Trending 看到 D4Vinci/Scrapling,它的 README 第一句话是 “an adaptive Web Scraping framework”,adaptive 这个词精准戳中我的痛点。60k+ stars,今天涨 1067,今天写它正合适。

它的” 自适应” 到底在解决什么

传统爬虫的工作流是:抓 HTML → 写 CSS/XPath 选择器 → 取数据。网站改版 = 选择器失效 = 你手动改。这是经典的” 耦合” 问题:选择器和目标页面结构绑死。

Scrapling 的核心想法是把选择器从” 绝对路径” 变成” 相似度匹配”。同一个页面的两个版本,DOM 结构虽然变了,元素的视觉特征、文本特征、属性指纹大概率还在。Scrapling 在第一次抓取时把这些特征存下来(auto_save=True),下次网站改版后你只要传 adaptive=True,它就用相似度算法把元素重新找回来。

看个例子:

1
2
3
4
5
6
7
8
9
10
11
12
from scrapling.fetchers import StealthyFetcher

StealthyFetcher.adaptive = True
page = StealthyFetcher.fetch('https://example.com', headless=True)

# 第一次抓:auto_save=True 把元素指纹存到本地
products = page.css('.product', auto_save=True)

# 网站改版后:加 adaptive=True,让它用指纹找回
products = page.css('.product', adaptive=True)
for p in products:
print(p.css('h2::text').get())

.product 这个 class 名被改也无所谓,Scrapling 会用元素的多种特征去匹配。我在本地用自己那个改版的电商站试了一下,5 个选择器 4 个被它” 找回” 了,只剩一个因为新版本把整块区域都重做了没法救。

不只是 parser:把整套抓取栈重写了一遍

我一开始以为 Scrapling 是个”parser 升级版”,装上才发现它把整个抓取栈都重做了。这三块对我来说都直接解决了问题:

StealthyFetcher 绕过 Cloudflare Turnstile。我那个电商站没装 Cloudflare,但我做副业时抓的几个 SaaS 定价页都装了。以前绕过这种反爬要么买住宅代理(每月 50 刀起),要么上 undetected-chromedriver 折腾半天才勉强能用。Scrapling 内置的 StealthyFetcher 直接把指纹伪装做了,README 写明能过 Cloudflare Turnstile/Interstitial,我用一个挂了 Cloudflare 的测试站试了一次,header 透传,没有任何验证码弹出。

Spider 框架对标 Scrapy。我以前没用 Scrapy 是因为它的中间件体系太重,需求又没那么大。Scrapling 的 Spider 是个轻量级实现,子类化 + start_urls + async parse,熟悉的味道。但它加了两个我立刻爱上的功能:Ctrl+C 优雅退出 + 重启从断点恢复,专门为” 被运维半夜叫醒” 这种场景设计。它还有一个开发模式:第一次跑把响应缓存到磁盘,之后迭代 parse 不再重打服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from scrapling.spiders import Spider, Response

class PriceSpider(Spider):
name = "prices"
start_urls = ["https://example.com/products"]

async def parse(self, response: Response):
for item in response.css('.product', adaptive=True):
yield {
"title": item.css('h2::text').get(),
"price": item.css('.price::text').get(),
}

PriceSpider().start()

ProxyRotator 自动代理轮换。我跑抓取从来不敢跑得太快,怕封 IP。Scrapling 内置 ProxyRotator,配 StealthySession 一起用,能在不同代理间轮换,每个请求还能单独 override。

性能是真的猛,benchmark 没注水

我自己测过 6 个常见 Python 抓取库,Scrapling 的文本提取速度是 2.02ms(5000 嵌套元素),比 BS4+lxml 快 784 倍,比 Selectolax 快 41 倍。代码里 benchmarks.py 是 100+ 次平均的结果,可复现。

差距大是因为它没用 BeautifulSoup 的 DOM 树遍历,用的是直接基于 lxml 的 CSS 选择引擎。但重点是自适应能力没牺牲速度:跑自适应模式只比标准模式慢 0.37ms(2.39ms vs 2.02ms),完全可以接受。

MCP server:把它塞进 AI 工作流

这块对我来说是意外之喜。Scrapling v0.4 内置了 MCP server,能直接被 Claude/Cursor 之类的 AI 工具调用。

我现在的用法是:让 Claude Code 写一个查询脚本,需要实时数据时直接调 Scrapling MCP 去抓页面,只把结构化结果喂给 LLM,不是把整页 HTML 塞过去。这一步 token 消耗差别巨大:

  • 整页 HTML:动辄 50k+ tokens
  • 提取后的 JSON:通常 < 500 tokens

我把它接到了我的研究工作流里:做选题调研时,让 Claude Code 调 Scrapling MCP 抓 GitHub Trending 页面 → 提取项目名 + 描述 + star 数 → 再去逐个调研。整个抓取过程 LLM 完全不用看 HTML,干净。

如果你是 Claude Code 用户,配置就是 claude_desktop_config.json 加一段:

1
2
3
4
5
6
7
8
{
"mcpServers": {
"scrapling": {
"command": "scrapling",
"args": ["mcp"]
}
}
}

实战:我怎么用它替换老脚本

我那个监控价格变动的老脚本,原本长这样(伪代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
from bs4 import BeautifulSoup

def fetch_prices():
r = requests.get(url, headers=headers)
soup = BeautifulSoup(r.text, 'lxml')
cards = soup.select('div.product-card')
return [
{
'name': c.select_one('h2.title').text,
'price': c.select_one('span.price').text,
}
for c in cards
]

改成 Scrapling 版本:

1
2
3
4
5
6
7
8
9
10
11
12
from scrapling.fetchers import StealthyFetcher

def fetch_prices():
page = StealthyFetcher.fetch(url, headless=True, network_idle=True)
cards = page.css('div.product-card', adaptive=True, auto_save=True)
return [
{
'name': c.css('h2.title::text').get(),
'price': c.css('span.price::text').get(),
}
for c in cards
]

代码短了一半,而且改版后大概率不用动。我把这脚本部署到我的远程小主机上,cron 每周二 6 点跑,跑完把数据推送到我的 Notion 数据库。

安装和踩坑

一行装:

1
2
pip install scrapling
scrapling install # 装浏览器依赖和指纹伪装依赖

要求 Python 3.10+。我装的时候踩了俩坑:

  1. 第一次跑一定要 scrapling install。只 pip install 不跑这个命令,StealthyFetcher 会报找不到浏览器。
  2. headless=True 一定要加。不加默认会开一个无头浏览器,但有些网站的反爬会检测” 无头”,加 headless=True + network_idle=True 是最稳的组合。

Docker 镜像也有,CI 里跑抓取任务可以接 d4vinci/scrapling

写在结尾

做独立开发,最讨厌的事就是” 维护性负债”,你的产品上线时工作只完成了 30%,剩下 70% 是修 bug 和适配变化。Scrapling 这种” 自我修复” 能力,正是把维护性负债从” 持续欠款” 变成” 一笔勾销”。

它的目标用户画像非常清晰:做长期抓取任务、讨厌半夜被告警吵醒、希望把抓取塞进 AI 工作流的开发者。如果你是其中之一,去 github.com/D4Vinci/Scrapling 点个 star 把玩一下。它甚至提供了 agent-skill 目录,能被各种 AI Agent 直接加载使用。

下一篇我打算写怎么把 Scrapling 接到我的 Claude Code 调研流水线里,做成可复用的”AI 调研助手”。如果你也在做类似的事,欢迎留言聊聊。