
你写好的爬虫,三个月后还能用吗?
读完本文你将了解:自适应选择器如何对抗页面改版 | Scrapling 的三层架构设计 | 无感知绕过 Cloudflare 的技术细节 | 它和 Scrapy/Playwright 到底该怎么选
🎯 这个项目解决什么问题?
你有过这种经历吗——花了一下午写好爬虫,第二天网站改了个 class 名,全白费。或者更惨,Cloudflare 的五秒盾卡住一切请求,代理池都救不了。
Python 爬虫生态不缺工具:Scrapy 是全家桶但太重,BeautifulSoup 是解析器不是爬虫,Playwright 能绕反爬但写起来像在操作浏览器。缺的是一个「聪明」的中间层——既有轻量级 HTTP 请求的速度,又能在遇到 Cloudflare 时自动切到隐身浏览器模式;既能写 CSS 选择器提取数据,又能网站改版后自动找到那些元素。
Scrapling 干的就这事儿。58k star、日增 1486 star 不是没道理。
🔧 快速上手
pip install "scrapling[fetchers]"
scrapling install
安装完就能直接干活。先试个最简单的:
from scrapling.fetchers import Fetcher
page = Fetcher.get('https://quotes.toscrape.com/')
quotes = page.css('.quote .text::text').getall()
print(quotes)
看到没?API 几乎和 Scrapy 一模一样,但不需要创建 Project、写 Spider 类、配置 Pipeline——三行代码拿结果。
现在上个硬的。Cloudflare 拦截的网站:
from scrapling.fetchers import StealthyFetcher
StealthyFetcher.adaptive = True # 开启自适应模式
page = StealthyFetcher.fetch(
'https://nopecha.com/demo/cloudflare',
headless=True,
network_idle=True
)
data = page.css('#padded_content a').getall()
StealthyFetcher 会在后台启动隐身浏览器,伪造指纹、解决 Cloudflare Turnstile 挑战,拿到页面后自动关浏览器。对调用方来说,就是一个函数调用。
⚠️ 踩坑提示:StealthyFetcher 依赖 Playwright 的 Chromium,首次使用需要 scrapling install 下载浏览器。Docker 用户可以直接用 pyd4vinci/scrapling 镜像,浏览器已预装。
如果要爬整站,上 Spider:
from scrapling.spiders import Spider, Response
class QuotesSpider(Spider):
name = "quotes"
start_urls = ["https://quotes.toscrape.com/"]
concurrent_requests = 10
async def parse(self, response: Response):
for quote in response.css('.quote'):
yield {
"text": quote.css('.text::text').get(),
"author": quote.css('.author::text').get(),
}
# 自动翻页
next_page = response.css('.next a')
if next_page:
yield response.follow(next_page[0].attrib['href'])
result = QuotesSpider().start()
result.items.to_json("quotes.json") # 直接导出 JSON
支持 Ctrl+C 暂停,下次运行自动续爬——长任务不用从头再来。
⚙️ 技术原理
Scrapling 的核心卖点不是「又一个浏览器自动化工具」,而是三层引擎 + 自适应选择器的组合设计。
三层 Fetcher 架构
#mermaid-svg-3qIAMiXpkuQXoajQ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3qIAMiXpkuQXoajQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3qIAMiXpkuQXoajQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3qIAMiXpkuQXoajQ .error-icon{fill:#552222;}#mermaid-svg-3qIAMiXpkuQXoajQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3qIAMiXpkuQXoajQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3qIAMiXpkuQXoajQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3qIAMiXpkuQXoajQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3qIAMiXpkuQXoajQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3qIAMiXpkuQXoajQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3qIAMiXpkuQXoajQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3qIAMiXpkuQXoajQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3qIAMiXpkuQXoajQ .marker.cross{stroke:#333333;}#mermaid-svg-3qIAMiXpkuQXoajQ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3qIAMiXpkuQXoajQ p{margin:0;}#mermaid-svg-3qIAMiXpkuQXoajQ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-3qIAMiXpkuQXoajQ .cluster-label text{fill:#333;}#mermaid-svg-3qIAMiXpkuQXoajQ .cluster-label span{color:#333;}#mermaid-svg-3qIAMiXpkuQXoajQ .cluster-label span p{background-color:transparent;}#mermaid-svg-3qIAMiXpkuQXoajQ .label text,#mermaid-svg-3qIAMiXpkuQXoajQ span{fill:#333;color:#333;}#mermaid-svg-3qIAMiXpkuQXoajQ .node rect,#mermaid-svg-3qIAMiXpkuQXoajQ .node circle,#mermaid-svg-3qIAMiXpkuQXoajQ .node ellipse,#mermaid-svg-3qIAMiXpkuQXoajQ .node polygon,#mermaid-svg-3qIAMiXpkuQXoajQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3qIAMiXpkuQXoajQ .rough-node .label text,#mermaid-svg-3qIAMiXpkuQXoajQ .node .label text,#mermaid-svg-3qIAMiXpkuQXoajQ .image-shape .label,#mermaid-svg-3qIAMiXpkuQXoajQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-3qIAMiXpkuQXoajQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3qIAMiXpkuQXoajQ .rough-node .label,#mermaid-svg-3qIAMiXpkuQXoajQ .node .label,#mermaid-svg-3qIAMiXpkuQXoajQ .image-shape .label,#mermaid-svg-3qIAMiXpkuQXoajQ .icon-shape .label{text-align:center;}#mermaid-svg-3qIAMiXpkuQXoajQ .node.clickable{cursor:pointer;}#mermaid-svg-3qIAMiXpkuQXoajQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3qIAMiXpkuQXoajQ .arrowheadPath{fill:#333333;}#mermaid-svg-3qIAMiXpkuQXoajQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3qIAMiXpkuQXoajQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3qIAMiXpkuQXoajQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3qIAMiXpkuQXoajQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3qIAMiXpkuQXoajQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3qIAMiXpkuQXoajQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3qIAMiXpkuQXoajQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3qIAMiXpkuQXoajQ .cluster text{fill:#333;}#mermaid-svg-3qIAMiXpkuQXoajQ .cluster span{color:#333;}#mermaid-svg-3qIAMiXpkuQXoajQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3qIAMiXpkuQXoajQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3qIAMiXpkuQXoajQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-3qIAMiXpkuQXoajQ .icon-shape,#mermaid-svg-3qIAMiXpkuQXoajQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3qIAMiXpkuQXoajQ .icon-shape p,#mermaid-svg-3qIAMiXpkuQXoajQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3qIAMiXpkuQXoajQ .icon-shape .label rect,#mermaid-svg-3qIAMiXpkuQXoajQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3qIAMiXpkuQXoajQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3qIAMiXpkuQXoajQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3qIAMiXpkuQXoajQ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
纯 HTTP
动态页面
绕过防火墙
用户代码
需要哪种 Fetcher?
Fetcher(curl_cffi 驱动)
DynamicFetcher(Playwright Chrome)
StealthyFetcher(隐身浏览器 + 指纹伪造)
Adaptive Selector(自适应选择器层)
数据结果
- Fetcher:底层用 curl_cffi 模拟浏览器 TLS 指纹(Chrome/Firefox 都能伪装),纯 HTTP 请求,速度最快
- DynamicFetcher:包装 Playwright Chromium,处理 JS 渲染的动态页面
- StealthyFetcher:在 DynamicFetcher 基础上加隐身层——browserforge 伪造浏览器指纹 + apify-fingerprint-datapoints 注入反检测数据
三者共享同一个选择器接口,切换成本为零。你写 .css('.product'),不管底层是 curl 发的请求还是隐身浏览器渲染的页面,都能用。
自适应选择器:为什么网站改版了爬虫还能跑?
这是 Scrapling 最值得讲的设计。传统爬虫靠 CSS 选择器定位元素——.product-price、#main-content。网站一改 HTML 结构,选择器就失效。
Scrapling 的 auto_save=True 模式在首次选择时保存元素的指纹信息:标签名 + 文本内容 + 属性键值 + DOM 树上下文。下次网站改版后,用 adaptive=True 打开自适应模式,Scrapling 不再用死选择器去找,而是用元素相似度算法在页面上搜索匹配度最高的节点。
# 第一遍:正常选择 + 自动保存指纹
products = p.css('.product', auto_save=True)
# 网站改版后:自适应模式自动重定位
products = p.css('.product', adaptive=True)
这套机制的核心实现思路是:
跟竞品对比:Scrapy 的自适应方案是 AutoScraper,但它依赖手动标注训练样本;Scrapling 的 auto_save 是在你第一次正常抓取时就隐式标注了,零额外成本。
隐身引擎如何绕过 Cloudflare?
Cloudflare Turnstile 是当前最常见的反爬挑战。Scrapling 从三个层面绕过它:
┌─────────────────────────────────────┐
│ StealthyFetcher │
│ │
│ ┌───────────────────────────────┐ │
│ │ browserforge: 随机化浏览器指纹│ │
│ │ – WebGL 指纹 │ │
│ │ – Canvas 指纹 │ │
│ │ – 字体列表 │ │
│ │ – 屏幕分辨率 & 平台信息 │ │
│ └───────────────────────────────┘ │
│ ┌───────────────────────────────┐ │
│ │ apify-fingerprint-datapoints │ │
│ │ – 注入真实浏览器特征数据 │ │
│ └───────────────────────────────┘ │
│ ┌───────────────────────────────┐ │
│ │ solve_cloudflare=True │ │
│ │ – 自动等待 + 通过 Turnstile │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
和 Playwright Stealth 插件相比,Scrapling 的隐身层更「无感」——你不需要手动注入 stealth 脚本或配置额外参数,一行 StealthyFetcher.adaptive = True 就搞定。
🏗️ 架构分析
模块划分
Scrapling 的项目结构很清晰,核心模块都放在 scrapling/ 下:
#mermaid-svg-d8lzzEzimgxA62kA{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-d8lzzEzimgxA62kA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-d8lzzEzimgxA62kA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-d8lzzEzimgxA62kA .error-icon{fill:#552222;}#mermaid-svg-d8lzzEzimgxA62kA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-d8lzzEzimgxA62kA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-d8lzzEzimgxA62kA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-d8lzzEzimgxA62kA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-d8lzzEzimgxA62kA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-d8lzzEzimgxA62kA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-d8lzzEzimgxA62kA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-d8lzzEzimgxA62kA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-d8lzzEzimgxA62kA .marker.cross{stroke:#333333;}#mermaid-svg-d8lzzEzimgxA62kA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-d8lzzEzimgxA62kA p{margin:0;}#mermaid-svg-d8lzzEzimgxA62kA .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-d8lzzEzimgxA62kA .cluster-label text{fill:#333;}#mermaid-svg-d8lzzEzimgxA62kA .cluster-label span{color:#333;}#mermaid-svg-d8lzzEzimgxA62kA .cluster-label span p{background-color:transparent;}#mermaid-svg-d8lzzEzimgxA62kA .label text,#mermaid-svg-d8lzzEzimgxA62kA span{fill:#333;color:#333;}#mermaid-svg-d8lzzEzimgxA62kA .node rect,#mermaid-svg-d8lzzEzimgxA62kA .node circle,#mermaid-svg-d8lzzEzimgxA62kA .node ellipse,#mermaid-svg-d8lzzEzimgxA62kA .node polygon,#mermaid-svg-d8lzzEzimgxA62kA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-d8lzzEzimgxA62kA .rough-node .label text,#mermaid-svg-d8lzzEzimgxA62kA .node .label text,#mermaid-svg-d8lzzEzimgxA62kA .image-shape .label,#mermaid-svg-d8lzzEzimgxA62kA .icon-shape .label{text-anchor:middle;}#mermaid-svg-d8lzzEzimgxA62kA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-d8lzzEzimgxA62kA .rough-node .label,#mermaid-svg-d8lzzEzimgxA62kA .node .label,#mermaid-svg-d8lzzEzimgxA62kA .image-shape .label,#mermaid-svg-d8lzzEzimgxA62kA .icon-shape .label{text-align:center;}#mermaid-svg-d8lzzEzimgxA62kA .node.clickable{cursor:pointer;}#mermaid-svg-d8lzzEzimgxA62kA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-d8lzzEzimgxA62kA .arrowheadPath{fill:#333333;}#mermaid-svg-d8lzzEzimgxA62kA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-d8lzzEzimgxA62kA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-d8lzzEzimgxA62kA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d8lzzEzimgxA62kA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-d8lzzEzimgxA62kA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d8lzzEzimgxA62kA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-d8lzzEzimgxA62kA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-d8lzzEzimgxA62kA .cluster text{fill:#333;}#mermaid-svg-d8lzzEzimgxA62kA .cluster span{color:#333;}#mermaid-svg-d8lzzEzimgxA62kA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-d8lzzEzimgxA62kA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-d8lzzEzimgxA62kA rect.text{fill:none;stroke-width:0;}#mermaid-svg-d8lzzEzimgxA62kA .icon-shape,#mermaid-svg-d8lzzEzimgxA62kA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d8lzzEzimgxA62kA .icon-shape p,#mermaid-svg-d8lzzEzimgxA62kA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-d8lzzEzimgxA62kA .icon-shape .label rect,#mermaid-svg-d8lzzEzimgxA62kA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d8lzzEzimgxA62kA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-d8lzzEzimgxA62kA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-d8lzzEzimgxA62kA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
依赖
依赖
依赖
依赖
scrapling/fetchers/
请求获取层
scrapling/spiders/
整站爬取层
scrapling/core/
解析 + 自适应引擎
scrapling/cli.py
命令行 & 交互 Shell
fetchers/:三层请求器 + Session 管理 + ProxyRotator。Lazy import 设计避免初始化时加载浏览器驱动。
spiders/:Scrapy 风格的 Spider 框架——但用 asyncio 重写,支持并发爬取、断点恢复、多 Session 切换。Scheduler 负责请求队列调度,SessionManager 管理不同类型的连接池。
core/:解析器和自适应选择器。基于 lxml 的 Selector 类提供 CSS/XPath/text/regex 四种选择方式,自适应能力通过 auto_save 指纹存储实现。mixins.py 里的选择器生成算法能自动生成稳定的 CSS/XPath 选择器。
cli.py:提供 scrapling shell 交互式调试环境和 scrapling extract 无代码数据提取。
设计亮点
懒加载 + 渐进安装:核心包只有 lxml/cssselect/orjson 三个依赖,不到 10MB。fetcher 的 Playwright/curl_cffi 放在 [fetchers] extras 里,MCP 支持放 [ai] 里。用户按需装,不污染环境。
Fetcher-Session-Spider 三层解耦:Fetcher 可以独立用(快速单页抓取),也可以挂 Session(带 Cookie/代理管理的连续请求),还可以配合 Spider(全站并发爬取)。三个层级各自独立,组合使用。
可以改进的地方
✅ 优缺点 & 适用场景
三个优点
两个缺点
谁该立刻试试
- 正在维护多条爬虫、经常被网站改版折磨的工程师
- 需要抓 Cloudflare 保护站点的数据分析师
- 想从 Scrapy 迁移到更轻量方案但不想放弃 Spider 模式的人
谁该再等等
- 只需要抓几个静态页面的:requests + lxml 就够,别过度设计
- 必须用 Firefox 做隐身抓取的:目前只支持 Chromium
- 完全不会 Python 的:虽然有 CLI 模式,但进阶功能还是需要写代码
如果只能用一个词形容 Scrapling,那会是 「务实」。它没有发明新技术,但把自适应解析、隐身浏览器、Spider 框架这三个已有能力做到了最顺滑的组合。在这个反爬技术日新月异的时代,这种「帮你少写适配代码」的工具,才是最值钱的。


