Scrapling MCP Server 详解:把三级 Web 抓取能力接入 AI Agent 的完整实践
本文以 Scrapling 的 MCP Server 参考文档(agent-skill/Scrapling-Skill/references/mcp-server.md)为主线,完整覆盖其 10 个工具的参数与默认值、工具选择策略、内容缩减与提示注入防护,并结合核心实现文件 ScraplingMCPServer 的源码逐节展开底层机制。读完后你将能够:把 Scrapling 正确接入任意支持 MCP 的客户端,按站点保护等级选择合适工具,用持久化会话与截图工具完成多页抓取,并理解令牌鉴权、远程浏览器连接等生产部署细节。
总览:十个工具与统一返回结构
Scrapling MCP Server 通过 MCP(Model Context Protocol)协议对外暴露 10 个工具,其能力可归纳为五组:
| 能力层级 | 工具 | 适用场景 |
|---|---|---|
| 纯 HTTP 抓取 | get / bulk_get |
无/低反爬的静态页面 |
| 浏览器渲染抓取 | fetch / bulk_fetch |
需要 JS 渲染的 SPA/动态页面 |
| 隐身浏览器抓取 | stealthy_fetch / bulk_stealthy_fetch |
Cloudflare Turnstile/Interstitial 等强防护 |
| 截图 | screenshot |
需要模型"看到"页面像素 |
| 会话管理 | open_session / close_session / list_sessions |
跨多次请求复用同一浏览器实例 |
所有抓取类工具(get、bulk_get、fetch、bulk_fetch、stealthy_fetch、bulk_stealthy_fetch)都返回统一的 ResponseModel 结构,字段为 status(int)、content(字符串列表)、url(str)。在源码中,这一模型定义于 scrapling/core/ai.py:
class ResponseModel(BaseModel):
"""Request's response information structure."""
status: int = Field(description="The status code returned by the website.")
content: list[str] = Field(description="The content as Markdown/HTML or the text content of the page.")
url: str = Field(description="The URL given by the user that resulted in this response.")
而 screenshot 是唯一的例外:它不返回 JSON 结构,而是返回一组 MCP 内容块——一个 ImageContent(截图字节,模型可直接"看见"图像,而非 JSON 里的 base64 字符串)加一个 TextContent(重定向后的最终 URL)。这一行为在 screenshot 实现中可以直接确认:
image = Image(data=captured["bytes"], format=image_type).to_image_content()
return [image, TextContent(type="text", text=captured["url"])]
值得注意的设计是单 URL 工具内部委托给批量工具:get 的实现就是把 URL 包成长度为 1 的列表后调用 bulk_get(scrapling/core/ai.py),fetch/stealthy_fetch 同理。因此只需理解批量路径即可覆盖全部抓取行为。
HTTP 抓取工具:get 与 bulk_get
get —— 带浏览器指纹仿冒的 HTTP 请求
这是最快的抓取方式:纯 HTTP GET,但在 TLS 指纹、请求头层面仿冒真实浏览器(curl_cffi 风格),适合无/低反爬的静态页面。完整参数如下(摘自参考文档,与 get 源码签名逐一对应):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
str | 必填 | 目标 URL |
extraction_type |
"markdown" / "html" / "text" |
"markdown" |
输出格式 |
css_selector |
str 或 null | null | CSS 选择器,用于收窄内容(在 main_content_only 之后应用) |
main_content_only |
bool | true | 只保留 <body> 内容 |
impersonate |
str | "chrome" |
仿冒的浏览器指纹 |
proxy |
str 或 null | null | 代理 URL,如 "http://user:pass@host:port" |
proxy_auth |
dict 或 null | null | {"username": "...", "password": "..."} |
auth |
dict 或 null | null | HTTP Basic Auth,格式同 proxy_auth |
timeout |
number | 30 | 超时(秒) |
retries |
int | 3 | 失败重试次数 |
retry_delay |
int | 1 | 重试间隔(秒) |
stealthy_headers |
bool | true | 生成真实浏览器请求头并附加 Google referer |
http3 |
bool | false | 启用 HTTP/3(与 impersonate 组合时可能冲突) |
follow_redirects |
bool 或 "safe" |
"safe" |
跟随重定向;"safe" 会拒绝指向内网/私有 IP 的重定向(SSRF 防护) |
max_redirects |
int | 30 | 最大重定向次数(-1 为不限) |
headers |
dict 或 null | null | 自定义请求头 |
cookies |
dict 或 null | null | 请求 Cookie |
params |
dict 或 null | null | 查询字符串参数 |
verify |
bool | true | 是否校验 HTTPS 证书 |
其中两个参数的默认值得到源码双重印证:
follow_redirects="safe":在 get 的参数说明中写明,"safe"会跟随重定向但拒绝指向内网/私有 IP 的目标,是防 SSRF 的默认姿态;显式传True才完全放开。retries=3/retry_delay=1:默认每秒重试一次、共 3 次。测试文档中的示例提示词"如果失败,每秒重试最多 5 次"就是在覆盖这两个默认值。
bulk_get —— 并发批量 HTTP 请求
bulk_get 与 get 参数完全相同,唯一区别是 url 换成 urls(字符串列表),所有 URL 并行抓取,返回 ResponseModel 列表。源码中 bulk_get 的实现链路清晰可见:
normalized_proxy_auth = _normalize_credentials(proxy_auth)
normalized_auth = _normalize_credentials(auth)
async with FetcherSession() as session:
tasks = [
session.get(url, auth=normalized_auth, proxy=proxy, http3=http3, ..., impersonate=impersonate, ...)
for url in urls
]
responses = await gather(*tasks)
return [_translate_response(page, extraction_type, css_selector, main_content_only) for page in responses]
几个实现细节:
- 底层复用 Scrapling 的
FetcherSession(scrapling/fetchers/requests.py),并发靠asyncio.gather一把铺开; proxy_auth/auth会先经过_normalize_credentials校验——字典必须同时包含username和password,否则直接抛ValueError;- 响应统一经
_translate_response转为ResponseModel:调用Convertor._extract_content(定义于 scrapling/core/shell.py)做选择器截取与格式转换,并对每个 content 分片执行_CONTROL_CHARS_PATTERN.sub("", ...)清理控制字符。
浏览器抓取工具:fetch 与 stealthy_fetch
fetch —— Playwright 渲染动态内容
fetch 通过 Playwright 启动 Chromium 渲染 JavaScript,适合 SPA 和动态站点。参数表(摘自参考文档,与 fetch 源码签名一致):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
str | 必填 | 目标 URL |
extraction_type |
str | "markdown" |
"markdown" / "html" / "text" |
css_selector |
str 或 null | null | 提取前用选择器收窄内容 |
main_content_only |
bool | true | 只保留 <body> |
headless |
bool | true | true 隐藏运行,false 显示浏览器 |
proxy |
str / dict / null | null | 字符串 URL 或 {"server": "...", "username": "...", "password": "..."} |
timeout |
number | 30000 | 超时,单位毫秒 |
wait |
number | 0 | 页面加载完成后的额外等待(ms) |
wait_selector |
str 或 null | null | 等待某 CSS 选择器满足条件后再提取 |
wait_selector_state |
str | "attached" |
"attached" / "visible" / "hidden" / "detached" |
network_idle |
bool | false | 等待至 500ms 内无网络活动 |
disable_resources |
bool | false | 拦截字体、图片、媒体、样式表等请求以提速 |
google_search |
bool | true | 设置 Google referer 头 |
real_chrome |
bool | false | 使用本机已安装的 Chrome 而非内置 Chromium |
cdp_url |
str 或 null | null | 通过 CDP URL 连接已运行的浏览器 |
extra_headers |
dict 或 null | null | 附加请求头(google_search 设置的 referer 优先) |
useragent |
str 或 null | null | 自定义 UA;为 null 时自动生成 |
cookies |
list 或 null | null | Playwright 格式的 Cookie |
timezone_id |
str 或 null | null | 浏览器时区,如 "America/New_York" |
locale |
str 或 null | null | 浏览器语言环境,如 "en-GB" |
session_id |
str 或 null | null | 复用 open_session 创建的持久会话 |
另有 executable_path 参数(绝对路径,指向自定义 Chromium 兼容浏览器可执行文件),用于单次请求级别覆盖服务器级默认值,详见下文"自定义浏览器可执行文件"一节。
bulk_fetch 参数与 fetch 相同(含 session_id),url 换为 urls,每个 URL 开一个独立浏览器标签页并行抓取。源码 bulk_fetch 有两条路径:
- 带
session_id:调用_get_session(session_id, "dynamic")拿到既有AsyncDynamicSession,直接并发fetch。注意此时locale、useragent、cookies等浏览器级参数被忽略——它们在会话创建时已定,这正是服务器指令(见下文)提醒模型"用会话时浏览器级参数无效"的原因。 - 不带
session_id:临时创建AsyncDynamicSession,其中max_pages=_page_pool_size(urls)、block_ads=True。
页面池大小由 _page_pool_size 计算:
_MAX_POOL_PAGES = 50 # Upper bound of `PagesCount` in scrapling/engines/_browsers/_validators.py
def _page_pool_size(urls: Sequence[str]) -> int:
return min(max(len(urls), 1), _MAX_POOL_PAGES)
即批量请求无论多大,都通过最多 50 个并发页面的池子消化,且该值被刻意约束在引擎校验器(scrapling/engines/_browsers/_validators.py)允许的上限之内——测试 tests/ai/test_ai_mcp.py 中的 test_bulk_fetch_sizes_pool_within_validator_bounds 专门验证了这一点。
stealthy_fetch —— 反检测隐身抓取
stealthy_fetch 是应对 Cloudflare Turnstile/Interstitial 等强防护的抓取器,接受 fetch 的全部参数,另加以下隐身专用参数(与 stealthy_fetch 源码一致):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
solve_cloudflare |
bool | false | 自动求解 Cloudflare Turnstile/Interstitial 挑战 |
hide_canvas |
bool | false | 向 canvas 操作注入噪声,防指纹识别 |
block_webrtc |
bool | false | 强制 WebRTC 遵守代理设置,防真实 IP 泄漏 |
allow_webgl |
bool | true | 保持 WebGL 开启(禁用 WebGL 本身会被 WAF 检测到) |
additional_args |
dict 或 null | null | 额外传给 Playwright context 的参数,覆盖 Scrapling 默认值 |
session_id |
str 或 null | null | 复用 open_session 创建的 stealthy 会话 |
bulk_stealthy_fetch 为其并发版本,url 换为 urls。其源码 bulk_stealthy_fetch 与 bulk_fetch 结构对称:会话路径校验 entry = self._get_session(session_id, "stealthy");临时路径创建 AsyncStealthySession 并同样硬编码 block_ads=True(L887)。
持久会话管理:open_session / close_session / list_sessions
同一站点抓多页时,每次冷启动浏览器开销巨大。会话三件套让你启动一次浏览器、复用多次抓取。
open_session 参数表
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
session_type |
"dynamic" / "stealthy" |
必填 | 会话类型 |
session_id |
str 或 null | null | 自定义 ID;省略则生成 12 位随机十六进制 ID;若 ID 已占用则抛错 |
headless |
bool | true | 隐藏/显示运行 |
max_pages |
int | 5 | 并发标签页上限(1–50) |
proxy |
str / dict / null | null | 该会话全部请求的代理 |
timeout |
number | 30000 | 默认超时(ms) |
solve_cloudflare |
bool | false | (仅 stealthy)自动解 Cloudflare 挑战 |
hide_canvas |
bool | false | (仅 stealthy)canvas 指纹噪声 |
block_webrtc |
bool | false | (仅 stealthy)阻断 WebRTC 泄漏 |
allow_webgl |
bool | true | (仅 stealthy)保持 WebGL 开启 |
此外还接受全部浏览器会话参数:google_search、real_chrome、cdp_url、locale、timezone_id、useragent、extra_headers、cookies、disable_resources、network_idle、wait_selector、wait_selector_state、executable_path。
返回 SessionCreatedModel:session_id、session_type、created_at、is_alive、message(定义见此)。
源码层面有三个值得注意的约束(open_session 实现):
- ID 生成与冲突检测:
session_id = session_id or uuid4().hex[:12](L233),自定义 ID 撞车时抛出 "Session '...' already exists" 错误,便于提前发现命名冲突; - 会话类型强校验:dynamic 会话只能配
fetch/bulk_fetch,stealthy 会话只能配stealthy_fetch/bulk_stealthy_fetch。_get_session同时检查三件事——会话存在、仍存活(entry.session._is_alive)、类型匹配,测试 tests/ai/test_ai_mcp.py 中test_session_type_mismatch、test_open_session_duplicate_id_raises、test_fetch_with_closed_session分别覆盖了这三种失败路径; - 广告拦截不可关闭:创建会话时
block_ads=True被硬编码进common_kwargs(L247),任何会话都无法关闭广告拦截(原理见下文)。
close_session 与 list_sessions
close_session 接收必填的 session_id,关闭会话并释放浏览器资源,返回 SessionClosedModel(session_id + message)。用完必须关闭,否则浏览器进程会一直挂着——服务器指令明确要求模型在 open_session 之后"务必在完成后调用 close_session"(见下文服务器指令)。
list_sessions 无参数,返回 SessionInfo 列表,每项含 session_id、session_type、created_at、is_alive,用于排查"到底还开着哪些会话"。
screenshot:把页面作为图像交给模型
screenshot 在既有浏览器会话内导航到目标 URL 并截图,返回 ImageContent(截图字节)+ TextContent(重定向后 URL)两个内容块。必须先 open_session(dynamic 或 stealthy 均可),再把 session_id 传入。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
str | 必填 | 要导航并截图的 URL |
session_id |
str | 必填 | open_session 创建的会话 ID |
image_type |
"png" / "jpeg" |
"png" |
图像格式;"jpeg" 体积更小 |
full_page |
bool | false | true 则截取整页可滚动区域,默认只截视口 |
quality |
int 或 null | null | JPEG 质量 0–100;与 image_type="png" 同传会抛错 |
wait |
number | 0 | 加载后额外等待(ms) |
wait_selector |
str 或 null | null | 截图前等待的 CSS 选择器 |
wait_selector_state |
str | "attached" |
同 fetch 的取值 |
network_idle |
bool | false | 等待至 500ms 内无网络活动 |
timeout |
number | 30000 | 超时(ms) |
实现上有两个细节(screenshot 实现):
- 参数前置校验:
if quality is not None and image_type != "jpeg": raise ValueError("'quality' is only valid when 'image_type' is 'jpeg'.")(L344-L345); - 截图动作以
page_action回调形式注入session.fetch,复用会话内页面导航/等待逻辑,截完直接把字节转成Image内容块——测试test_screenshot_png_with_dynamic_session、test_screenshot_jpeg_with_quality、test_screenshot_with_stealthy_session验证了三种典型组合。
工具选择策略与服务器内置指令
参考文档给出的选择指南如下,从最低资源开销的 get 起步,逐级升级:
| 场景 | 推荐工具 |
|---|---|
| 静态页面,无反爬 | get |
| 多个静态页面 | bulk_get |
| JS 渲染 / SPA 页面 | fetch |
| 多个 JS 渲染页面 | bulk_fetch |
| Cloudflare 或强反爬 | stealthy_fetch(Turnstile 场景加 solve_cloudflare=true) |
| 多个受保护页面 | bulk_stealthy_fetch |
| 同一站点多页 | open_session + fetch/stealthy_fetch 并传 session_id |
| 需要页面截图 | open_session + screenshot 并传 session_id |
这条升级策略不只写在文档里,还被写进了服务器注册给客户端的全局指令中。在 _build_server 里,instructions 字段向模型下达了 10 条操作守则,包括:
- 使用过
open_session就必须在完成后close_session,丢了线索就用list_sessions查; - 用户未指定工具时先用
get,失败再升级;get/bulk_get只适合低/中防护等级; css_selector命中多个元素时全部返回;extraction_type控制返回格式(markdown/html/text);main_content_only默认开启,只返回<body>内内容;- 对同一站点的多次请求应开会话;
- 传了
session_id时浏览器级参数(headless、proxy、locale 等)会被忽略,因为它们在会话创建时已确定; - 多请求优先用 bulk 版本;
- 爬站时用
css_selector只取所需部分以省 token,例如先用a选择器把链接都提出来。
这些指令与测试、文档中的提示词经验("告诉 AI 用哪个工具")共同构成了一层服务端的行为约束:即使客户端模型不够自觉,服务器也会持续提醒它走正确的工具路径。
内容缩减:css_selector 与 main_content_only
MCP 场景下 token 成本直接取决于"多少网页内容进入模型上下文",Scrapling 在内容到达模型前提供了两级缩减:
css_selector:先用 Scrapling 引擎定位目标元素,再提取——这是 Scrapling MCP Server 区别于其他抓取服务器的关键能力。其他服务器普遍"整页抽出来交给 AI 自己找字段",无关内容大量消耗 token;而这里可以在服务端就裁掉噪声。即使不会写 CSS 选择器,也可以在提示词里让 AI 自行尝试选择器组合直到命中。main_content_only=true(默认):截取范围先收缩到<body>内部。源码中这一步发生在Convertor._extract_content:
if main_content_only:
page = cast(Selector, page.css("body").first) or page
page = cls._strip_noise_tags(page)
page = cls._sanitize_for_ai(page)
内容格式由 extraction_type 控制:"markdown"(默认,可读性最好)、"text"(最省)、"html"(需要保留结构时用)。选择器命中多个元素时,所有内容按顺序进入返回的 content 列表。
安全机制:提示注入防护与广告拦截
提示注入防护
当 main_content_only=true(默认)时,服务端会自动清洗抓取内容,剥离恶意网站可能用来向 AI 上下文注入指令的隐藏内容。清洗逻辑实现在 Convertor._sanitize_for_ai,配合一条预编译的 XPath _HIDDEN_XPATH 逐节移除:
- CSS 隐藏元素(
display:none、visibility:hidden、opacity:0、font-size:0、height:0、width:0); aria-hidden="true"元素;<template>标签;- HTML 注释(
keep_comments=False); - 零宽 Unicode 字符(
_ZWC_PATTERN)与控制字符(_CONTROL_CHARS_PATTERN)。
关键实现是"深拷贝后逐树移除":
clean_root = deepcopy(page._root)
for element in cast(list, _HIDDEN_XPATH(clean_root)):
element.drop_tree()
for element in clean_root.iter():
if element.text:
element.text = _CONTROL_CHARS_PATTERN.sub("", _ZWC_PATTERN.sub("", element.text))
if element.tail:
element.tail = _CONTROL_CHARS_PATTERN.sub("", _ZWC_PATTERN.sub("", element.tail))
return Selector(root=clean_root, url=page.url, keep_comments=False)
此外,HTTP 路径的 _translate_response 还会对每个 content 分片再做一次 _CONTROL_CHARS_PATTERN.sub("", ...)(scrapling/core/ai.py),测试 test_translate_response_strips_control_characters 专门验证了这层清理。建议:保持 main_content_only=true(默认)以获得最大防护。
广告拦截
所有浏览器工具(fetch、bulk_fetch、stealthy_fetch、bulk_stealthy_fetch)和持久会话(open_session)都会自动拦截约 3,500 个已知广告与追踪域名的请求,在 MCP 服务端始终开启,用于省 token 和加速页面加载,无需配置。源码中这是硬编码的 block_ads=True(如 bulk_fetch、bulk_stealthy_fetch、open_session),域名黑名单本身是 scrapling/engines/toolbelt/ad_domains.py 中的 AD_DOMAINS 常量——在该仓库环境中实际计量为 3526 个域名,与文档"约 3,500 个"的表述一致。
SSRF 防护
HTTP 工具的 follow_redirects 默认为 "safe",重定向目标若指向内网/私有 IP 会被直接拒绝(参数说明)。对"远程 AI 服务器抓任意 URL"这类暴露面而言,这条默认值比鉴权更先发挥作用。
部署:安装、传输与客户端接入
安装
# 安装带 MCP 支持([ai] 额外依赖)的 Scrapling
pip install "scrapling[ai]"
# 安装浏览器依赖(Chromium 等)
scrapling install
[ai] 额外依赖的定义见 pyproject.toml:mcp>=2.0.0、markdownify>=1.2.0 以及 scrapling[fetchers]。也可以直接用 Docker 镜像 pyd4vinci/scrapling。
启动服务器
# stdio 传输(绝大多数 MCP 客户端使用)
scrapling-mcp
# Streamable HTTP 传输
scrapling-mcp --http
scrapling-mcp --http --host 127.0.0.1 --port 8000
# Docker 方式
docker pull pyd4vinci/scrapling
docker run -i --rm pyd4vinci/scrapling mcp
scrapling-mcp 这个入口命令自 v0.4.13 起提供,直接映射到 scrapling mcp(pyproject.toml 入口定义:scrapling-mcp = "scrapling.cli:mcp");旧版本需用 scrapling mcp 形式。当前仓库 scrapling/__init__.py 中的版本号为 0.4.13,与文档描述一致。
CLI 侧的完整选项定义在 scrapling/cli.py:--http(是否走 streamable-http)、--host(默认 0.0.0.0)、--port(默认 8000)、--executable-path、--auth-token、--allowed-host(可重复)。最终都汇入 ScraplingMCPServer(executable_path=..., auth_token=...).serve(http, host, port, allowed_hosts=...)。
客户端配置示例
在 MCP 客户端中注册时,服务器名为 ScraplingServer。stdio 方式(以 Claude Desktop 类客户端为例):
{
"mcpServers": {
"ScraplingServer": {
"command": "scrapling-mcp"
}
}
}
建议用可执行文件绝对路径(Mac 上 which scrapling-mcp、Windows 上 where scrapling-mcp 查得)。Docker 方式:
{
"mcpServers": {
"ScraplingServer": {
"command": "docker",
"args": ["run", "-i", "--rm", "pyd4vinci/scrapling", "mcp"]
}
}
}
Streamable HTTP 方式则填服务器 URL,并配合后文的 Authorization 头。
自定义浏览器可执行文件与远程浏览器
自定义浏览器可执行文件
fetch、bulk_fetch、stealthy_fetch、bulk_stealthy_fetch 和 open_session 都支持用自定义的 Chromium 兼容浏览器可执行文件替代内置 Chromium,适合定制浏览器构建或轻量引擎。配置有三个层次,优先级从低到高:
- 环境变量
SCRAPLING_EXECUTABLE_PATH(常量定义于 scrapling/core/ai.py),启动前 export 即可; - 服务器启动参数,一次性对整个 MCP 服务器生效:
scrapling-mcp --executable-path "/path/to/chromium"
对应客户端配置:
{
"mcpServers": {
"ScraplingServer": {
"command": "/Users/<MyUsername>/.venv/bin/scrapling-mcp",
"args": ["--executable-path", "/path/to/chromium"]
}
}
}
- 单次工具调用直接传
executable_path参数,覆盖前两层。
解析逻辑非常直白——_resolve_executable_path:return executable_path or self._executable_path。测试 test_open_session_uses_environment_default、test_fetch_overrides_global_executable_path 验证了环境变量兜底与单次覆盖的完整链路。scrapling extract fetch / scrapling extract stealthy-fetch 等 CLI 命令也支持同样的 --executable-path 选项与环境变量兜底(见 scrapling/cli.py 中相关选项)。
连接远程浏览器
open_session 不必在本地启动浏览器:传入 cdp_url 即可通过 Chrome DevTools Protocol 连接一个已在运行的浏览器——同机、异机或托管浏览器服务商均可。dynamic 与 stealthy 两种会话都接受,拿到的 session_id 照常在 fetch 与 screenshot 工具中使用。
URL 支持两种形式:
- WebSocket 端点(
ws:///wss://),托管浏览器服务商通常给出这种; - 自行以
chrome --remote-debugging-port=9222启动的浏览器的 HTTP 端点,写作cdp_url="http://localhost:9222"(异机则换为主机地址)。
注意事项:
- 浏览器已经在跑,所以只适用于启动阶段的选项会被 CDP 会话忽略:
headless、real_chrome、executable_path(含服务器级默认); - 其余选项照常生效(
locale、useragent、proxy、cookies、timezone_id等),因为每个会话仍会在远程浏览器上创建自己的 context。
生产部署:HTTP 鉴权与 DNS 重绑定防护
stdio 传输只有启动它的进程能访问;而一旦切到 Streamable HTTP,任何能访问该端口的人都能调用全部工具,包括从运行服务器的机器抓取任意 URL。因此只要监听地址不是 localhost,就应当启用令牌鉴权:
scrapling-mcp --http --auth-token "$(openssl rand -hex 32)"
客户端在 Authorization 头中携带该令牌,缺失则返回 401:
{
"mcpServers": {
"ScraplingServer": {
"url": "https://your-server.example.com/mcp",
"headers": {
"Authorization": "Bearer <your-token>"
}
}
}
}
命令行传令牌会留在 shell 历史和进程列表里,更稳妥的是环境变量 SCRAPLING_MCP_AUTH_TOKEN:
export SCRAPLING_MCP_AUTH_TOKEN="<your-token>"
scrapling-mcp --http
监听公网地址时还应通过可重复的 --allowed-host 声明接受哪些主机名,这会同时开启 DNS 重绑定攻击防护(防止你浏览器访问的某个网站反过来向你的 MCP 端口发起请求):
scrapling-mcp --http --allowed-host 'your-server.example.com:8000'
源码印证了整套机制:
- 令牌校验器
_StaticTokenVerifier用hmac.compare_digest做常量时间比较,避免时序侧信道; _transport_security在提供allowed_hosts时构造TransportSecuritySettings,将每个 host 展开为http/https两种 origin 加入白名单;serve内置两条告警:--http但无令牌时警告"该端点对任何可达者敞开";配置了令牌却走 stdio 时警告"令牌仅对 streamable-http 生效,stdio 下被忽略"。
补充注意事项:
- 鉴权仅对 Streamable HTTP 传输生效,stdio 下被忽略(并有日志警告);
- 明文 HTTP 会让令牌裸奔,暴露公网前请置于终结 TLS 的反向代理之后;
- 这是单一共享密钥而非每客户端凭据,轮换令牌意味着重启服务器;
--http不带令牌仍可用于本地开发,但服务器会记录未鉴权警告。
实践要点速查
综合参考文档与实现细节,日常使用建议:
- 选对工具:
get(快、静态)→fetch(JS/动态)→stealthy_fetch(Cloudflare/反爬),能用低开销工具就不要升级;多 URL 一律用 bulk 版本。 - 省 token:
css_selector先行截取目标元素;extraction_type按场景选 markdown/text/html;多页同站用持久会话,避免反复冷启动浏览器。 - 动态内容处理:SPA 用
network_idle;盯住特定元素用wait_selector+wait_selector_state;慢站点调大timeout(注意浏览器系工具单位是毫秒,HTTP 系是秒)。 - 数据质量与安全:保持
main_content_only=true默认值以获得隐藏内容清洗;follow_redirects="safe"保留 SSRF 防护。 - 会话纪律:
open_session命名有意义(如"search"、"checkout")便于管理,撞名会立刻报错;结束后必须close_session,不确定时用list_sessions清点。 - 部署:本地 stdio 开箱即用;远程暴露必须配
--auth-token+--allowed-host,并置于 TLS 反向代理之后。
延伸阅读:关键源码与测试索引
| 主题 | 文件 |
|---|---|
| MCP 服务器核心实现(10 个工具、会话、鉴权、传输) | scrapling/core/ai.py |
scrapling mcp 命令与 CLI 选项 |
scrapling/cli.py |
| 内容转换、噪声剥离与 AI 内容清洗 | scrapling/core/shell.py |
| 广告/追踪域名黑名单(3526 个域) | scrapling/engines/toolbelt/ad_domains.py |
| 浏览器引擎参数校验(max_pages 上限 50) | scrapling/engines/_browsers/_validators.py |
| MCP 服务器测试(工具、会话、截图、可执行路径、页面池) | tests/ai/test_ai_mcp.py |
| 面向用户的 MCP 指南(安装、Claude 配置、示例提示词) | docs/ai/mcp-server.md |
| MCP 工具参考(本文主体) | agent-skill/Scrapling-Skill/references/mcp-server.md |
适用前提:以上参数默认值、命令与行为描述以当前仓库(版本 0.4.13)的实际代码为准;若你使用 Docker 镜像或旧版本发布包,请以对应版本的发行说明核对 scrapling-mcp 入口(0.4.13 之前需写 scrapling mcp)等差异。
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