Scrapling 交互式 Shell 实战:基于 IPython 的 Web Scraping REPL,从 get 快捷函数到 curl 命令转换
Scrapling 的交互式 Shell 是一个面向 Web 抓取任务的 IPython 增强版 REPL,进入即预置全部 Fetcher 类、请求快捷函数、自动页面追踪和 curl 命令转换工具,让“写脚本—运行—改选择器”的循环变成即输即得的探索式体验。本文基于文档 docs/cli/interactive-shell.md 展开,并结合 scrapling/core/shell.py、scrapling/core/_shell_signatures.py 等源码,讲清每个快捷命令背后的实现机制与可复制的实操流程。读完本文,你能够独立安装并启动 Shell、熟练使用 page/pages 页面管理、用 uncurl/curl2fetcher 把浏览器 DevTools 中的请求一键转为 Fetcher 请求,并理解其底层解析链路。
为什么使用交互式 Shell
文档把 Shell 定位成把抓取从“慢速脚本循环”变成“快速探索”的工具,官方列出的典型场景包括:
- 快速原型(Rapid prototyping):即时验证抓取策略;
- 数据探索(Data exploration):交互式地导航网站并抽取数据;
- 学习 Scrapling:在实时环境中试验各种特性;
- 调试爬虫:逐步检查请求并查看结果;
- 工作流转换(Converting workflows):把浏览器 DevTools 里的 curl 命令一行转换为 Fetcher 请求。
这些能力的共同基础是:Shell 把 Scrapling 最常用的一等对象(Fetcher、Selector、Response)和请求函数全部注入命名空间,用户不需要任何 import 语句。
前置知识
在开始之前,文档建议先阅读以下页面(链接均以仓库根目录为起点):
- Fetchers 基础,理解 Response 对象 以及如何选择 Fetcher;
- 元素查询,理解如何从 Selector/Response 对象中查找/提取元素;
- 主类,理解 Response 从 Selector 继承的属性与方法;
- 至少一页抓取器文档,用于实际发请求:HTTP 请求、动态网站 或 强保护动态网站。
安装与启动
安装依赖
Shell 属于 CLI 能力之一(参见 CLI 总览),需要先安装 shell 依赖组,再安装各 Fetcher 的浏览器等运行依赖:
pip install "scrapling[shell]"
scrapling install
从 pyproject.toml 可以看到 shell 额外依赖的具体内容:IPython>=8.37(源码注释说明这是最后一个支持 Python 3.10 的版本线)、markdownify>=1.2.0,以及传递依赖 scrapling[fetchers]。scrapling install 会下载 Chromium 浏览器、系统依赖与指纹处理相关依赖,详见 cli.py 中的 install 命令实现。
启动命令
# 启动交互式 Shell
scrapling shell
# 执行一段代码后退出(适合脚本化场景)
scrapling shell -c "get('https://quotes.toscrape.com'); print(len(page.css('.quote')))"
# 设置日志级别
scrapling shell --loglevel info
这三个入口对应 cli.py 中的 Click 命令定义:
-c/--code:在 Shell 中执行给定代码后退出。源码中通过InteractiveShellEmbed.run_cell(self.code, store_history=False)实现,执行异常会被捕获并记录日志而不是让进程崩溃;-L/--loglevel:取值为debug、info、warning、error、critical、fatal之一(case_sensitive=False),默认值为debug。源码 shell.py 用_known_logging_levels映射到标准 logging 级别,未知级别会回落到DEBUG并打一条 warning。
启动后终端会显示 Scrapling 的自定义 Banner。从 CustomShell.banner() 可以看到 Banner 分三部分:可用的 Scrapling 对象(Fetcher/AsyncFetcher/FetcherSession、DynamicFetcher/DynamicSession/AsyncDynamicSession、StealthyFetcher/StealthySession/AsyncStealthySession、Selector)、六个请求快捷函数、以及 page/response/pages/uncurl/curl2fetcher/view/help 这些实用命令。退出方式为 exit 或 Ctrl+D。
进入 Shell 后即可直接抓取,无需任何导入:
# 无需 import,一切就绪
get('https://news.ycombinator.com')
# 探索页面结构
page.css('a')[:5] # 查看前 5 个链接
# 精炼选择器
stories = page.css('.titleline>a')
len(stories) # 30
# 提取具体数据
for story in stories[:3]:
... title = story.text
... url = story['href']
... print(f"{title}: {url}")
# 尝试不同的写法
titles = page.css('.titleline>a::text') # 直接提取文本
urls = page.css('.titleline>a::attr(href)') # 直接提取属性
内置快捷函数与预置对象
六个请求快捷函数
Shell 提供了消除样板代码的快捷函数(对应关系见 get_namespace 与 banner):
| 快捷函数 | 等价于 | 说明 |
|---|---|---|
get(url, **kwargs) |
Fetcher.get |
HTTP GET 请求 |
post(url, **kwargs) |
Fetcher.post |
HTTP POST 请求 |
put(url, **kwargs) |
Fetcher.put |
HTTP PUT 请求 |
delete(url, **kwargs) |
Fetcher.delete |
HTTP DELETE 请求 |
fetch(url, **kwargs) |
DynamicFetcher.fetch |
基于浏览器的动态页面抓取 |
stealthy_fetch(url, **kwargs) |
StealthyFetcher.fetch |
隐身浏览器抓取(对抗强防护站点) |
常用类也被自动注入命名空间,包括 Fetcher、AsyncFetcher、FetcherSession、DynamicFetcher、DynamicSession、AsyncDynamicSession、StealthyFetcher、StealthySession、AsyncStealthySession 和 Selector,全部来自 scrapling.fetchers 的导出,无需 import 即可使用。
快捷函数的参数提示:**kwargs 的签名展开
一个容易忽略的细节是:get 等函数的签名中大量参数走 **kwargs(Unpack[TypedDict] 注解),在普通 IPython 里 Tab 补全只能看到 **kwargs,无法提示具体参数名。Shell 专门解决了这一点:
- scrapling/core/_shell_signatures.py 在模块级维护了三组参数字典:
_REQUESTS_PARAMS(params、cookies、auth、impersonate、http3、stealthy_headers、proxies、proxy、proxy_auth、timeout、headers、retries、retry_delay、follow_redirects、max_redirects、verify、cert、selector_config)、_FETCH_PARAMS(headless、disable_resources、network_idle、wait_selector、page_action、proxy、extra_headers、timeout、cdp_url、block_ads、retries、capture_xhr、dns_over_https等浏览器参数)、_STEALTHY_FETCH_PARAMS(在_FETCH_PARAMS基础上增加allow_webgl、hide_canvas、block_webrtc、solve_cloudflare等隐身参数),并以Signatures_map把函数名get/post/put/delete/fetch/stealthy_fetch映射到对应参数字典,其中post/put额外带有data与json参数; - scrapling/core/shell.py 的 _unpack_signature 在
CustomShell.create_wrapper中被调用,把**kwargs(Parameter.VAR_KEYWORD)替换为一个个KEYWORD_ONLY参数并写回包装函数的__signature__,于是 IPython 的补全和get?帮助就能像 IDE 一样展示impersonate、proxy、timeout等具体参数及其类型注解。
这就是文档中 page.c<TAB>、Fetcher.<TAB> 等补全体验完整的底层原因。
智能页面管理:page、response 与 pages
Shell 会自动追踪你的请求与页面,这是它区别于普通 Python REPL 的核心体验:
当前页面访问:page 和 response 两个名字会自动更新为最近一次抓取的页面:
get('https://quotes.toscrape.com')
# 'page' 和 'response' 都指向最后一次抓取的页面
page.url # 'https://quotes.toscrape.com'
response.status # 打印 200;与 page.status 等价
页面历史:pages 是一个 Selectors 对象,保留最近 5 个页面:
get('https://site1.com')
get('https://site2.com')
get('https://site3.com')
# 访问最近 5 个页面
len(pages) # 保存页面历史的 Selectors 对象 -> 3
pages[0].url # 历史中的第一个页面 -> 'https://site1.com'
pages[-1].url # 最新的页面 -> 'https://site3.com'
# 处理历史页面
for i, old_page in enumerate(pages):
... print(f"Page {i}: {old_page.url} - {old_page.status}")
源码侧的实现非常清晰:所有快捷函数都是 CustomShell.create_wrapper 生成的包装函数,包装函数在执行完真实请求后统一调用 update_page:
- 若返回结果是
Response或Selector实例,则self.page = result,并向self.pages追加;当len(self.pages) > 5时pop(0)丢弃最旧一项,这就是“最近 5 页”上限的来源; - 随后把
page、response、pages三个名字同步写回self.shell.user_ns(IPython 用户命名空间),保证你在 REPL 里看到的始终是最新值; - 若结果不是页面(例如
uncurl返回的Request对象),则原样返回且不更新页面状态。
Selectors 本体定义在 scrapling/parser.py,它是一个 List[Selector] 的子类,因此支持 len()、下标、切片和迭代。
附加实用命令
页面可视化:view()
在浏览器中直接查看抓下来的页面:
get('https://quotes.toscrape.com')
view(page) # 在默认浏览器中打开该页面的 HTML
对应实现 show_page_in_browser:它先校验输入必须是 Selector 实例,否则记录错误日志;然后写入一个 scrapling_view_ 前缀的临时 HTML 文件(编码取自 page.encoding),最后通过 webbrowser.open(f"file://{fname}") 调用系统默认浏览器打开。适合在调整 CSS 选择器时对照真实页面结构。
curl 命令集成:uncurl 与 curl2fetcher
Shell 提供了把浏览器 DevTools 中的 curl 命令转换为 Fetcher 请求的两个函数:uncurl(只转换)和 curl2fetcher(转换并直接执行)。使用流程:在 Chrome DevTools 的 Network 面板中选择请求,右键 “Copy as cURL”,粘贴进 Shell。
第一步:把 curl 命令转换为 Request 对象
curl_cmd = '''curl 'https://scrapling.requestcatcher.com/post' \
... -X POST \
... -H 'Content-Type: application/json' \
... -d '{"name": "test", "value": 123}' '''
request = uncurl(curl_cmd)
request.method # -> 'post'
request.url # -> 'https://scrapling.requestcatcher.com/post'
request.headers # -> {'Content-Type': 'application/json'}
第二步:一步转换并执行
# 转换 + 执行一步到位
curl2fetcher(curl_cmd)
page.status # -> 200
page.json()['json'] # -> {'name': 'test', 'value': 123}
底层实现:CurlParser 的解析细节
这两个函数背后是 scrapling/core/shell.py 的 CurlParser。它的设计目标是“优先处理从 DevTools 网络面板复制出来的 curl 命令”,要点如下:
- 基于 argparse 而非正则:
CurlParser.__init__构造一个禁用了 help 的NoExitArgumentParser(shell.py 中让解析错误抛出ValueError而不是直接sys.exit,保证 REPL 不会因一条坏命令而退出),注册了 DevTools 常见参数:url、-X/--request、-H/--header(可重复)、-A/--user-agent、-d/--data、--data-raw(浏览器 JSON body 常用)、--data-binary、--data-urlencode(可重复)、-G/--get、-b/--cookie、-x/--proxy、-U/--proxy-user、-k/--insecure、--compressed、-i/-s/-v等; - 解析主流程 parse():先剥离
curl前缀并把\\\n续行替换为空格,用shlex.split按 shell 语法切分 token,再用parse_known_args解析——出现未知参数会抛出AttributeError(测试用例test_invalid_curl_commands验证了这一点); - 方法推断规则:默认
get;-G强制 GET;否则取-X的值并转小写;若都没有但存在任意 data 参数(-d/--data-raw/--data-binary/--data-urlencode)则推断为post; - Header 与 Cookie:
-H列表交给 _ParseHeaders 拆分,其中键名为cookie的 header 会被 _CookieParser(基于http.cookies.SimpleCookie)解析为字典;-b的 cookie 字符串解析后按同名覆盖 header 中已有的 cookie; - 请求体优先级:
--data-binary(转为 bytes)>--data-raw(剥离行首$前缀)>-d>--data-urlencode(合并为&串再解析为字典);若最终 data 是字符串且能被 orjson 解析成 dict/list,则升级为json_data(即json=参数)并把data置空;-G场景下 data 会被移入 URLparams; - 代理:
-x缺省补http://前缀,-U user:pass会拼入代理 URL 的 netloc,最终生成{"http": proxy_url, "https": proxy_url}的标准字典格式; - 返回值:
uncurl返回一个 8 字段的Requestnamed tuple(method/url/params/data/json_data/headers/cookies/proxy/follow_redirects,定义见 shell.py),follow_redirects固定为"safe"——跟随重定向但拒绝指向内网/私有 IP 的重定向; - 执行链路 convert2fetcher:把
Request._asdict()展开,仅支持get/post/put/delete四种方法(_supported_methods);json_data键被重命名为json以匹配Fetcher的参数名;GET/DELETE 会丢弃data/json;最后getattr(Fetcher, method)(**request_args)发起真实请求,返回值经update_page写入page/pages,因此curl2fetcher之后可以直接page.status、page.json()继续操作。
这套解析逻辑有完整的测试覆盖,见 tests/cli/test_shell_functionality.py 的 TestCurlParser(基础 GET、headers、表单/JSON data、-H Cookie 与 -b 合并、-x/-U 代理、convert2fetcher 调用验证、非法命令抛错),以及 tests/core/test_shell_core.py 中对 _CookieParser、_ParseHeaders、Request 与日志级别映射的单元测试。
IPython 全部能力
Shell 继承自 IPython(InteractiveShellEmbed 嵌入模式,见 start()),因此所有 IPython 特性都可用:
# 魔术命令
%time page = get('https://example.com') # 计时
%history # 查看命令历史
%save filename.py 1-10 # 把第 1-10 条命令保存为文件
# 任意位置 Tab 补全
page.c<TAB> # 显示 css、cookies、headers 等
Fetcher.<TAB> # 显示 Fetcher 全部方法
# 对象检查
get? # 查看 get 的文档
实战示例
以下是文档提供的两个由 AI 辅助生成的示例场景,展示从探索到放大的完整工作流。
电商数据采集
# 从商品列表页开始
catalog = get('https://shop.example.com/products')
# 找到商品链接
product_links = catalog.css('.product-link::attr(href)')
print(f"Found {len(product_links)} products")
# 先抽样几个商品验证选择器
for link in product_links[:3]:
... product = get(f"https://shop.example.com{link}")
... name = product.css('.product-name::text').get('')
... price = product.css('.price::text').get('')
... print(f"{name}: {price}")
# 验证无误后,用 Session 提升批量抓取效率
from scrapling.fetchers import FetcherSession
with FetcherSession() as session:
... products = []
... for link in product_links:
... product = session.get(f"https://shop.example.com{link}")
... products.append({
... 'name': product.css('.product-name::text').get(''),
... 'price': product.css('.price::text').get(''),
... 'url': link
... })
这个示例体现了 Shell 的典型用法:先用 get 单发请求快速验证选择器,确认无误后再切换到 FetcherSession(命名空间中已预置,甚至不需要 import)做规模化抓取。
API 集成与测试
>>> # 交互式测试 API 端点
>>> response = get('https://jsonplaceholder.typicode.com/posts/1')
>>> response.json()
{'userId': 1, 'id': 1, 'title': 'sunt aut...', 'body': 'quia et...'}
>>> # 测试 POST 请求
>>> new_post = post('https://jsonplaceholder.typicode.com/posts',
... json={'title': 'Test Post', 'body': 'Test content', 'userId': 1})
>>> new_post.json()['id']
101
>>> # 测试更新请求
>>> updated = put(f'https://jsonplaceholder.typicode.com/posts/{new_post.json()["id"]}',
... json={'title': 'Updated Title'})
post/put 的 json= 参数与 Fetcher.post/Fetcher.put 完全一致(参见 Signatures_map 中 post/put 额外包含的 data、json 参数),因此交互中验证通过的请求可以原样搬进生产代码。
源码索引与延伸阅读
Shell 的实现集中在以下几个文件,便于按需深入:
- scrapling/cli.py:
scrapling shell的 Click 命令定义(-c、-L/--loglevel); - scrapling/core/shell.py:
CustomShell(命名空间、Banner、页面更新)、CurlParser(curl 解析与uncurl/curl2fetcher)、show_page_in_browser(view)、Convertor(供scrapling extract命令做 HTML→Markdown/文本转换,属于 CLI 总览 中的另一条能力线); - scrapling/core/_shell_signatures.py:六个快捷函数的参数签名表,支撑 IPython 的参数级补全;
- scrapling/core/utils/_shell.py:Header/Cookie 字符串解析工具;
- 测试:tests/cli/test_shell_functionality.py(
CurlParser、CustomShell、Convertor)、tests/core/test_shell_core.py(cookie/header 解析、Request、日志级别)。
与 Shell 相关的其他 CLI 文档可参考 CLI 总览 和 Extract 命令;抓取器选型见 choosing.md。Shell 的适用前提再次强调:需要安装 scrapling[shell] 依赖组并执行 scrapling install 完成浏览器依赖安装;-c 模式适合把 Shell 纳入脚本流程,--loglevel 默认 debug,生产调试时可调整为 info 及以上以降噪。
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
