首页
/ Scrapling 交互式 Shell 实战:基于 IPython 的 Web Scraping REPL,从 get 快捷函数到 curl 命令转换

Scrapling 交互式 Shell 实战:基于 IPython 的 Web Scraping REPL,从 get 快捷函数到 curl 命令转换

2026-09-04 15:20:29作者:柏廷章Berta

Scrapling 的交互式 Shell 是一个面向 Web 抓取任务的 IPython 增强版 REPL,进入即预置全部 Fetcher 类、请求快捷函数、自动页面追踪和 curl 命令转换工具,让“写脚本—运行—改选择器”的循环变成即输即得的探索式体验。本文基于文档 docs/cli/interactive-shell.md 展开,并结合 scrapling/core/shell.pyscrapling/core/_shell_signatures.py 等源码,讲清每个快捷命令背后的实现机制与可复制的实操流程。读完本文,你能够独立安装并启动 Shell、熟练使用 page/pages 页面管理、用 uncurl/curl2fetcher 把浏览器 DevTools 中的请求一键转为 Fetcher 请求,并理解其底层解析链路。

从 Chrome DevTools 复制请求为 curl 命令的截图,用于 Scrapling Shell 的 uncurl/curl2fetcher 命令

为什么使用交互式 Shell

文档把 Shell 定位成把抓取从“慢速脚本循环”变成“快速探索”的工具,官方列出的典型场景包括:

  • 快速原型(Rapid prototyping):即时验证抓取策略;
  • 数据探索(Data exploration):交互式地导航网站并抽取数据;
  • 学习 Scrapling:在实时环境中试验各种特性;
  • 调试爬虫:逐步检查请求并查看结果;
  • 工作流转换(Converting workflows):把浏览器 DevTools 里的 curl 命令一行转换为 Fetcher 请求。

这些能力的共同基础是:Shell 把 Scrapling 最常用的一等对象(Fetcher、Selector、Response)和请求函数全部注入命名空间,用户不需要任何 import 语句。

前置知识

在开始之前,文档建议先阅读以下页面(链接均以仓库根目录为起点):

  1. Fetchers 基础,理解 Response 对象 以及如何选择 Fetcher;
  2. 元素查询,理解如何从 Selector/Response 对象中查找/提取元素;
  3. 主类,理解 ResponseSelector 继承的属性与方法;
  4. 至少一页抓取器文档,用于实际发请求: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:取值为 debuginfowarningerrorcriticalfatal 之一(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_namespacebanner):

快捷函数 等价于 说明
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 隐身浏览器抓取(对抗强防护站点)

常用类也被自动注入命名空间,包括 FetcherAsyncFetcherFetcherSessionDynamicFetcherDynamicSessionAsyncDynamicSessionStealthyFetcherStealthySessionAsyncStealthySessionSelector,全部来自 scrapling.fetchers 的导出,无需 import 即可使用。

快捷函数的参数提示:**kwargs 的签名展开

一个容易忽略的细节是:get 等函数的签名中大量参数走 **kwargsUnpack[TypedDict] 注解),在普通 IPython 里 Tab 补全只能看到 **kwargs,无法提示具体参数名。Shell 专门解决了这一点:

  • scrapling/core/_shell_signatures.py 在模块级维护了三组参数字典:_REQUESTS_PARAMSparamscookiesauthimpersonatehttp3stealthy_headersproxiesproxyproxy_authtimeoutheadersretriesretry_delayfollow_redirectsmax_redirectsverifycertselector_config)、_FETCH_PARAMSheadlessdisable_resourcesnetwork_idlewait_selectorpage_actionproxyextra_headerstimeoutcdp_urlblock_adsretriescapture_xhrdns_over_https 等浏览器参数)、_STEALTHY_FETCH_PARAMS(在 _FETCH_PARAMS 基础上增加 allow_webglhide_canvasblock_webrtcsolve_cloudflare 等隐身参数),并以 Signatures_map 把函数名 get/post/put/delete/fetch/stealthy_fetch 映射到对应参数字典,其中 post/put 额外带有 datajson 参数;
  • scrapling/core/shell.py 的 _unpack_signatureCustomShell.create_wrapper 中被调用,把 **kwargsParameter.VAR_KEYWORD)替换为一个个 KEYWORD_ONLY 参数并写回包装函数的 __signature__,于是 IPython 的补全和 get? 帮助就能像 IDE 一样展示 impersonateproxytimeout 等具体参数及其类型注解。

这就是文档中 page.c<TAB>Fetcher.<TAB> 等补全体验完整的底层原因。

智能页面管理:pageresponsepages

Shell 会自动追踪你的请求与页面,这是它区别于普通 Python REPL 的核心体验:

当前页面访问pageresponse 两个名字会自动更新为最近一次抓取的页面:

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

  • 若返回结果是 ResponseSelector 实例,则 self.page = result,并向 self.pages 追加;当 len(self.pages) > 5pop(0) 丢弃最旧一项,这就是“最近 5 页”上限的来源;
  • 随后把 pageresponsepages 三个名字同步写回 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 命令集成:uncurlcurl2fetcher

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 的 NoExitArgumentParsershell.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 会被移入 URL params
  • 代理-x 缺省补 http:// 前缀,-U user:pass 会拼入代理 URL 的 netloc,最终生成 {"http": proxy_url, "https": proxy_url} 的标准字典格式;
  • 返回值uncurl 返回一个 8 字段的 Request named 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.statuspage.json() 继续操作。

这套解析逻辑有完整的测试覆盖,见 tests/cli/test_shell_functionality.pyTestCurlParser(基础 GET、headers、表单/JSON data、-H Cookie-b 合并、-x/-U 代理、convert2fetcher 调用验证、非法命令抛错),以及 tests/core/test_shell_core.py 中对 _CookieParser_ParseHeadersRequest 与日志级别映射的单元测试。

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/putjson= 参数与 Fetcher.post/Fetcher.put 完全一致(参见 Signatures_mappost/put 额外包含的 datajson 参数),因此交互中验证通过的请求可以原样搬进生产代码。

源码索引与延伸阅读

Shell 的实现集中在以下几个文件,便于按需深入:

与 Shell 相关的其他 CLI 文档可参考 CLI 总览Extract 命令;抓取器选型见 choosing.md。Shell 的适用前提再次强调:需要安装 scrapling[shell] 依赖组并执行 scrapling install 完成浏览器依赖安装;-c 模式适合把 Shell 纳入脚本流程,--loglevel 默认 debug,生产调试时可调整为 info 及以上以降噪。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384