Scrapling:从单次请求到全站爬取的自适应 Web 抓取框架全面解析
Scrapling 是一个自适应 Web 抓取框架(adaptive Web Scraping framework),覆盖从“发一个 HTTP 请求解析几行数据”到“并行、多会话、可断点续爬的全站爬取”的完整场景。本篇基于仓库内的 俄语版项目主文档 展开,完整覆盖其核心特性、三类 Fetcher 与 Session 的用法、Spider 框架、CLI 与交互式 Shell、性能基准与安装配置,并结合仓库源码逐一印证关键 API 的真实实现位置与调用关系,帮助你在几行 Python 代码内搭建可长期运行的抓取系统。
项目定位:一个库解决三件事
从主文档的概述可以看出,Scrapling 的设计目标是“一件事不留妥协地做完”(one library, no compromises):
- 自适应解析器:其 parser 能学习网站结构的变化,当页面改版后自动“搬移”你之前保存过的元素选择位置——即 adaptive(自适应)机制;
- 抗反爬 Fetcher:内置的 Fetcher 系列开箱即用即可处理 Cloudflare Turnstile 等反机器人系统;
- Spider 框架:可规模化到并行、多会话的爬取,支持 Pause & Resume(暂停与恢复)与自动代理轮换。
仓库的模块结构也印证了这一分层:scrapling/engines(抓取引擎与浏览器底座)、scrapling/fetchers(三类 Fetcher 门面)、scrapling/spiders(爬虫框架)、scrapling/parser.py(解析器)、scrapling/core/storage.py(自适应存储)与 scrapling/cli.py(命令行与 Shell)。
核心特性总览
Spider 框架:Scrapy 风格的完整爬取框架
主文档列出的 Spider 能力,均可在 scrapling/spiders/ 目录中找到对应实现:
- Scrapy 风格 API:通过
start_urls、异步parse回调与Request/Response对象定义 Spider,分别对应 request.py 与 result.py; - 并行爬取:可配置并发限制、按域名的速率限制与加载延迟,相关实现位于 throttle.py;
- 多会话支持:同一个 Spider 内可同时挂载 HTTP 会话与 stealthy 无头浏览器会话,按请求的
sid(session ID)路由到不同会话; - Pause & Resume:基于 Checkpoint 的爬取持久化,Ctrl+C 软停止、再次启动时从断点继续,实现见 checkpoint.py;
- Streaming 模式:通过
async for item in spider.stream()边爬边消费结果,并附带实时统计; - 阻塞检测:自动识别被拦截的请求并按可定制逻辑重发;
- AutoThrottle:Spider 根据站点响应速度自动调整每个域名的延迟,一旦遇到限速/封锁则倍增延迟(或遵循
Retry-After),恢复后再提速; - robots.txt 合规:可选的
robots_txt_obey标志,遵循Disallow、Crawl-delay、Request-rate指令并按域名缓存,实现在 robotstxt.py; - 开发模式:首次运行把响应缓存到磁盘,后续运行直接回放,让你迭代
parse()逻辑而不重复请求目标服务器(见 cache.py); - 内置模板:
CrawlSpider(基于规则的链接跟随)、SitemapSpider(sitemap/robots 驱动)、XMLFeedSpider/CSVFeedSpider(XML/RSS 与 CSV feed 遍历)、ShopifySpider(通过 Shopify JSON API 抽取整个商品目录,每个 variant 一条记录)——这些模板分别位于 crawler.py、sitemap.py、feed.py 与 shopify.py; - LinkExtractor:独立的链接抽取原语,支持 allow/deny 模板、域名过滤、CSS/XPath 作用域限定、扩展名过滤与链接规范化(links.py);
- 结果导出:内置 JSON/JSONL/CSV/XML 导出器,
result.items.to_json()、to_jsonl()、to_csv()、to_xml()均可用,四个方法都定义在 result.py。
高级 Fetcher:三种抓取方式与 Session 体系
scrapling/fetchers/__init__.py 通过懒加载映射对外暴露了 10 个核心类(外加 ProxyRotator):
| 类别 | 类名 | 底层模块 |
|---|---|---|
| HTTP 请求 | Fetcher / AsyncFetcher / FetcherSession |
requests.py |
| 动态渲染 | DynamicFetcher / DynamicSession / AsyncDynamicSession |
chrome.py |
| 隐身浏览器 | StealthyFetcher / StealthySession / AsyncStealthySession |
stealth_chrome.py |
从 fetchers/init.py 的源码结构看,所有 Fetcher 类都采用 __getattr__ 惰性导入,这意味着 import scrapling.fetchers 本身不会触发 playwright/curl_cffi 等重依赖加载,只有在真正访问某个类时才会导入对应模块。
各 Fetcher 的能力边界(来自主文档,并有源码佐证):
- HTTP 请求(Fetcher):快速且“安静”的 HTTP 请求,可模拟浏览器的 TLS fingerprint、自定义请求头、使用 HTTP/3;
- 动态加载(DynamicFetcher):基于 Playwright 的完整浏览器自动化,支持 Chromium 与本机 Google Chrome;
- 反爬绕过(StealthyFetcher):指纹伪装与高级隐身,可自动化通过各类 Cloudflare Turnstile/Interstitial 挑战——
solve_cloudflare等参数在 stealth_chrome.py 中均有定义; - 会话管理:
FetcherSession、StealthySession、DynamicSession用于在多个请求间维持 cookie 与状态; - 代理轮换:内置
ProxyRotator支持循环或自定义策略,适用于所有会话类型,且每个请求都可单独覆盖代理(proxy_rotation.py); - 域名与广告屏蔽:屏蔽指定域名(含子域名)的请求,或启用内置广告拦截(约 3500 个已知广告/追踪域名,见 ad_domains.py);
- DNS 防泄漏:可选 DNS-over-HTTPS,将 DNS 请求经 Cloudflare DoH 路由,防止走代理时发生 DNS 泄漏;
- 远程浏览器:通过
cdp_url连接已运行的浏览器(同机、异机或托管浏览器服务);executable_path可把任意浏览器 Fetcher 指向自建 Chromium; - XHR 拦截:向
capture_xhr传入 URL 模板后,页面加载期间所有匹配的 XHR/fetch 响应会被收集为Response对象挂在response.captured_xhr上,无需手工逆向站点 API; - 全量 async 支持:所有 Fetcher 与专用 async 会话类都提供异步版本。
自适应抓取与 AI 集成
- 智能元素追踪:网站改版后基于相似度算法自动重新定位元素,
parser.py中adaptive相关逻辑出现 40 余处,是库的核心差异点之一; - 灵活选择:CSS、XPath、基于过滤条件的查找、文本搜索、正则搜索等;
- 相似元素搜索:自动发现与已定位元素相似的其它元素;
- MCP 服务器:内置面向 AI 的 MCP 服务器,用于 Web 抓取与数据抽取。它会在把内容交给 Claude/Cursor 等模型前先做目标内容提取,从而压缩 token 消耗;支持跨调用保持浏览器会话、页面截图与 CDP 远程浏览器控制。源码入口是 core/ai.py,文档见 ai/mcp-server.md;
- Agent Skill:仓库自带可安装的 Agent Skill,教会代码智能体按最新 API 编写 Scrapling 代码,而不是靠猜。
架构与工程化
主文档宣称的“高性能与工程可靠性”在仓库中有对应支撑:依赖声明(pyproject.toml)中解析层只依赖 lxml、cssselect、orjson 等轻量库,JSON 序列化走 orjson;全量 type hints,代码库在每次变更时经 PyRight 与 MyPy 检查(pyproject.toml 中同时配置了 [tool.mypy] 与 [tool.pyright]);测试分布于 tests/ 下的 parser、fetchers、spiders、cli 等子目录。
快速上手
基础用法:三类 Fetcher 与 Session
带 Session 的 HTTP 请求(模拟 Chrome 的 TLS 指纹):
from scrapling.fetchers import Fetcher, FetcherSession
with FetcherSession(impersonate='chrome') as session: # 使用最新版 Chrome 的 TLS 指纹
page = session.get('https://quotes.toscrape.com/', stealthy_headers=True)
quotes = page.css('.quote .text::text').getall()
# 或者使用一次性请求
page = Fetcher.get('https://quotes.toscrape.com/')
quotes = page.css('.quote .text::text').getall()
高级隐身模式(自动处理 Cloudflare):
from scrapling.fetchers import StealthyFetcher, StealthySession
with StealthySession(headless=True, solve_cloudflare=True) as session: # 浏览器保持打开直到结束
page = session.fetch('https://nopecha.com/demo/cloudflare', google_search=False)
data = page.css('#padded_content a').getall()
# 或者一次性请求风格 - 为该请求打开浏览器,完成后自动关闭
page = StealthyFetcher.fetch('https://nopecha.com/demo/cloudflare')
data = page.css('#padded_content a').getall()
完整浏览器自动化:
from scrapling.fetchers import DynamicFetcher, DynamicSession
with DynamicSession(headless=True, disable_resources=False, network_idle=True) as session:
page = session.fetch('https://quotes.toscrape.com/', load_dom=False)
data = page.xpath('//span[@class="text"]/text()').getall() # 也可以用 XPath
page = DynamicFetcher.fetch('https://quotes.toscrape.com/')
data = page.css('.quote .text::text').getall()
三种方式的能力取舍(静态 vs 动态 vs 隐身)在 choosing.md 中有专门讨论,分别对应 static.md、dynamic.md、stealthy.md。
自适应抓取的“两步走”
主文档首页示例展示了 adaptive 机制的核心用法:
from scrapling.fetchers import Fetcher, AsyncFetcher, StealthyFetcher, DynamicFetcher
StealthyFetcher.adaptive = True
p = StealthyFetcher.fetch('https://example.com', headless=True, network_idle=True) # 隐身加载网站
products = p.css('.product', auto_save=True) # 抓取数据并自动保存选择状态,页面改版后依然有效
products = p.css('.product', adaptive=True) # 之后若站点结构变化,传 adaptive=True 自动重新定位
先 auto_save=True 保存“指纹”,之后网站改版时用 adaptive=True 恢复。这套机制的存储实现位于 storage.py,原理文档见 adaptive.md。
Spider:从单个页面到完整爬虫
带翻页的并行爬虫:
from scrapling.spiders import Spider, Request, 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()
print(f"Extracted {len(result.items)} quotes")
result.items.to_json("quotes.json")
response.follow() 在 engines/toolbelt/custom.py 中实现,用于把相对链接解析为新的 Request 并入队。
一个 Spider 内混合多种会话(按 sid 路由请求):
from scrapling.spiders import Spider, Request, Response
from scrapling.fetchers import FetcherSession, AsyncStealthySession
class MultiSessionSpider(Spider):
name = "multi"
start_urls = ["https://example.com/"]
def configure_sessions(self, manager):
manager.add("fast", FetcherSession(impersonate="chrome"))
manager.add("stealth", AsyncStealthySession(headless=True), lazy=True)
async def parse(self, response: Response):
for link in response.css('a::attr(href)').getall():
# 受保护的页面路由到 stealth 会话
if "protected" in link:
yield Request(link, sid="stealth")
else:
yield Request(link, sid="fast", callback=self.parse) # 显式 callback
Pause & Resume:以 crawldir 启动即可启用 Checkpoint:
QuotesSpider(crawldir="./crawl_data").start()
Ctrl+C 软停止时进度自动保存;下次用相同 crawldir 再启动,Spider 会从断点继续(实现见 checkpoint.py)。
模板化爬取:例如完整导出一个 Shopify 商店的商品目录(每个 variant 一条记录):
from scrapling.spiders import ShopifySpider
class MyStore(ShopifySpider):
target_website = "example.com"
result = MyStore().start()
Spider 的架构细节、请求/响应生命周期与模板用法分别见 spiders/architecture.md、spiders/requests-responses.md 与 spiders/generic-templates.md。
高级解析与 DOM 导航
from scrapling.fetchers import Fetcher
page = Fetcher.get('https://quotes.toscrape.com/')
# 多种选择方式
quotes = page.css('.quote') # CSS 选择器
quotes = page.xpath('//div[@class="quote"]') # XPath
quotes = page.find_all('div', {'class': 'quote'}) # BeautifulSoup 风格
# 以下写法等价:
quotes = page.find_all('div', class_='quote')
quotes = page.find_all(['div'], class_='quote')
quotes = page.find_all(class_='quote')
# 按文本内容查找
quotes = page.find_by_text('quote', tag='div')
# 高级导航
quote_text = page.css('.quote')[0].css('.text::text').get()
quote_text = page.css('.quote').css('.text::text').getall() # 选择器链式调用
first_quote = page.css('.quote')[0]
author = first_quote.next_sibling.css('.author::text')
parent_container = first_quote.parent
# 元素关联与相似性
similar_elements = first_quote.find_similar()
below_elements = first_quote.below_elements()
不加载网站时也可以直接解析本地 HTML:
from scrapling.parser import Selector
page = Selector("<html>...</html>")
# API 完全相同
选择方法与父/兄弟/子导航的完整 API 参考见 selection.md 与 main_classes.md。
Async 会话示例
import asyncio
from scrapling.fetchers import FetcherSession, AsyncStealthySession, AsyncDynamicSession
async with FetcherSession(http3=True) as session: # 上下文感知,sync/async 两种模式都可用
page1 = session.get('https://quotes.toscrape.com/')
page2 = session.get('https://quotes.toscrape.com/', impersonate='firefox135')
# 使用 async 会话
async with AsyncStealthySession(max_pages=2) as session:
tasks = []
urls = ['https://example.com/page1', 'https://example.com/page2']
for url in urls:
task = session.fetch(url)
tasks.append(task)
print(session.get_pool_stats()) # 可选:浏览器标签页池状态(busy/free/error)
results = await asyncio.gather(*tasks)
print(session.get_pool_stats())
浏览器会话的标签页池统计 get_pool_stats() 在 engines/_browsers/_base.py 中定义,用于观察并发浏览时各标签页的占用情况。
CLI 与交互式 Shell
Scrapling 提供完整的命令行入口,入口函数注册在 pyproject.toml 的 [project.scripts](scrapling 与 scrapling-mcp 两个可执行命令,均指向 cli.py)。
交互式 Web Scraping Shell(基于 IPython,内置 curl 转 Scrapling 请求、结果浏览器预览等工具):
scrapling shell
从 cli.py 的源码看,shell 命令支持 -c/--code 直接执行一段代码并退出,以及 -L/--loglevel 控制日志级别(默认 debug);Shell 本体实现在 core/shell.py。
免代码直接抽取页面到文件(按扩展名决定输出格式:.txt 为纯文本、.md 为 Markdown 表示、.html 为 HTML 内容,默认抽取 body 内容):
scrapling extract get 'https://example.com' content.md
scrapling extract get 'https://example.com' content.txt --css-selector '#fromSkipToProducts' --impersonate 'chrome'
scrapling extract fetch 'https://example.com' content.md --css-selector '#fromSkipToProducts' --no-headless
scrapling extract stealthy-fetch 'https://nopecha.com/demo/cloudflare' captchas.html --css-selector '#padded_content a' --solve-cloudflare
命令参数在源码中可查证:extract 是一个 Click 命令组,HTTP 类命令(get/post/put/delete)共享 _common_http_options 装饰器(--impersonate 支持逗号分隔的随机选择、--stealthy-headers 默认开启、--timeout 默认 30 秒、支持 --params/--cookies/--headers/--proxy 等);浏览器类命令(fetch/stealthy-fetch)共享 _common_browser_options(--headless 默认开启、--wait 毫秒级附加等待、--network-idle、--disable-resources、--block-ads、--dns-over-https、--executable-path 等)。CLI 的完整说明见 cli/overview.md 与 cli/extract-commands.md。
性能基准
主文档给出了两类官方基准(均为 100+ 次运行取平均,方法学见 benchmarks.py):
文本提取速度(5000 个嵌套元素):
| # | 库 | 耗时(ms) | 相对 Scrapling |
|---|---|---|---|
| 1 | Scrapling | 1.99 | 1.0x |
| 2 | Parsel/Scrapy | 2.06 | 1.035 |
| 3 | Raw Lxml | 2.56 | 1.286 |
| 4 | PyQuery | 23.98 | ~12x |
| 5 | Selectolax | 197.02 | ~99x |
| 6 | MechanicalSoup | 1545.15 | ~776.5x |
| 7 | BS4 + Lxml | 1562.1 | ~785.0x |
| 8 | BS4 + html5lib | 3412.73 | ~1714.9x |
元素相似性与文本搜索:
| 库 | 耗时(ms) | 相对 Scrapling |
|---|---|---|
| Scrapling | 2.3 | 1.0x |
| AutoScraper | 12.58 | 5.47x |
如需复现,可直接运行仓库根目录的 benchmarks.py(测试脚本与依赖见 tests/requirements.txt)。
安装与配置
Scrapling 要求 Python ≥ 3.10(pyproject.toml 中 requires-python = ">=3.10"):
pip install scrapling
重要:基础安装只包含解析引擎及其依赖(lxml、cssselect、orjson 等),不含任何 Fetcher 或 CLI 依赖——此时从 scrapling.fetchers 或 scrapling.spiders 导入会抛 ModuleNotFoundError。要使用 Fetcher/Spider,需先安装 extras 并初始化浏览器:
pip install "scrapling[fetchers]"
scrapling install # 常规安装
scrapling install --force # 强制重装
从 cli.py 源码看,scrapling install 会执行 playwright install chromium 与 playwright install-deps chromium,并更新 tld 的公共后缀数据,成功后写入 .scrapling_dependencies_installed 标记文件以避免重复安装。也可以从代码内触发:
from scrapling.cli import install
install([], standalone_mode=False) # 常规安装
install(["--force"], standalone_mode=False) # 强制重装
可选 extras:
pip install "scrapling[ai]" # MCP 服务器能力(依赖 mcp、markdownify,并隐含 fetchers)
pip install "scrapling[shell]" # Shell 与 extract 命令(依赖 IPython、markdownify,并隐含 fetchers)
pip install "scrapling[all]" # 全部
无论装哪个 extra,都记得随后运行 scrapling install 安装浏览器依赖。
Docker:官方镜像含全部 extras 与浏览器,从 DockerHub 或 GitHub 容器镜像仓库拉取:
docker pull pyd4vinci/scrapling
# 或
docker pull ghcr.io/d4vinci/scrapling:latest
镜像由 Dockerfile 构建并随主分支自动发布。
合规与免责声明
主文档明确:该库仅供教育与研究目的,使用者须自行遵守当地及国际数据抓取与隐私法律,作者与贡献者不对滥用负责;应始终尊重网站服务条款与 robots.txt(框架也为此提供了 robots_txt_obey 选项)。
许可、引用与致谢
- 许可证:BSD-3-Clause(见 LICENSE);
- 引用(若用于研究):
@misc{scrapling,
author = {Karim Shoair},
title = {Scrapling},
year = {2024},
note = {An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!}
}
- 致谢:项目包含改编自 Parsel(BSD 许可)的代码,用于 translator 子模块;
- 社区参与:参与开发前请先阅读 CONTRIBUTING.md。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00

