第 3 课
把 Scrapling 交给 AI 智能体:MCP stdio 与 HTTP
按官方文档把 scrapling-mcp 配进 Claude Desktop、Claude Code、Cursor,用 Python MCP SDK 2.x 写客户端走 stdio 列出 16 个工具,按顺序调用 make_request、browser_open、browser_fetch、browser_actions、brows
课程目录第 3 / 3 课
学习位置仅保存在当前浏览器,有效期 180 天。
图文教材
本课对照的官方页面:MCP 服务器指南、MCP 工具参考。命令与输出在 Linux x86_64 云主机上实测(Scrapling 0.5,随
scrapling[ai]安装的 Python MCP SDK 2.3.0),输出为节选。
前两课都是你自己写代码抓取。这一课把 Scrapling 作为 MCP 服务器交给 AI 应用(Claude Desktop、Claude Code、Cursor 等),让智能体自己决定用 HTTP 请求还是浏览器、什么时候点按钮和填表。测试机上没有桌面 AI 应用,所以第 2~4 步用一个 Python 脚本扮演 AI 客户端,把智能体会做的调用逐个跑一遍。
第 1 步:把 scrapling-mcp 配进 AI 应用(未实测)
本步按官方文档整理,测试机没有这些桌面应用。
上一课装的 scrapling[all] 已经包含 MCP 依赖;只想用 MCP 的话,装 scrapling[ai] 再运行 scrapling install 即可。scrapling-mcp 是 0.4.13 起提供的快捷命令,等同于 scrapling mcp。
先查到它的完整路径(Windows 用 where scrapling-mcp):
which scrapling-mcp
对照结果(测试机,实测):
/home/box/scrapling-lab/.venv/bin/scrapling-mcp
Claude Desktop:菜单 → Settings → Developer → Edit Config,写入下面的配置,把路径换成你自己的,然后完全退出并重启 Claude Desktop:
{
"mcpServers": {
"ScraplingServer": {
"command": "/home/box/scrapling-lab/.venv/bin/scrapling-mcp"
}
}
}
配置文件位置:
| 系统 | 文件 |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
Claude Code 用一条命令添加:
claude mcp add ScraplingServer \
"/home/box/scrapling-lab/.venv/bin/scrapling-mcp"
官方说明同样的配置也适用于 Cursor、Windsurf 等支持 MCP 的应用。用 Docker 的话,command 写 docker,args 写 ["run", "-i", "--rm", "pyd4vinci/scrapling", "mcp"]。
上面 which 的结果里,路径 /home/box/scrapling-lab 是本课的练习目录,你的会不一样。
第 2 步:用 Python 脚本连上服务器,列出 16 个工具
scrapling-mcp 默认走 stdio:AI 应用把它当子进程启动,通过标准输入输出通信。下面的脚本做同样的事。新建 list_tools.py:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
server = StdioServerParameters(command="scrapling-mcp")
async def main():
async with stdio_client(server) as (read, write):
async with ClientSession(read, write) as s:
info = await s.initialize()
print(info.server_info.name, info.server_info.version)
tools = (await s.list_tools()).tools
print(len(tools), "tools")
for t in tools:
print("-", t.name)
asyncio.run(main())
python list_tools.py
对照结果(工具名合并成多行显示):
Scrapling 0.5
16 tools
- browser_open - browser_save_state - browser_load_state
- open_request_session - close_session - list_sessions
- make_request - browser_fetch_once - browser_fetch
- session_make_request - browser_extract - browser_actions
- browser_evaluate - browser_network_requests
- browser_network_request - browser_screenshot
实际输出是每行一个工具名,共 16 行。注意 MCP SDK 2.x 的属性名是下划线写法(server_info、input_schema);网上很多 1.x 示例写成 serverInfo、inputSchema,在 2.3.0 上会报 AttributeError。
按官方文档,16 个工具分成这几组:
| 用途 | 工具 |
|---|---|
| 一次性抓取 | make_request(HTTP)、browser_fetch_once(浏览器) |
| 会话管理 | browser_open、open_request_session、close_session、list_sessions |
| 通过会话抓取 | browser_fetch、session_make_request |
| 读当前页面 | browser_extract、browser_screenshot、browser_evaluate |
| 页面操作 | browser_actions(点击、填表、按键、等待) |
| 网络记录 | browser_network_requests、browser_network_request |
| 登录状态 | browser_save_state、browser_load_state |
一次性工具抓完就关闭自己的浏览器或连接;会话工具保留 cookie 和设置,适合连续多步操作。官方还说明,浏览器类工具会自动屏蔽约 3500 个广告和跟踪域名,并清理隐藏元素、零宽字符、HTML 注释等可能藏提示词注入的内容。
第 3 步:跑一遍智能体的调用:抓列表、开浏览器、登录
下面的脚本按顺序调用 6 个工具:先用 make_request 只取作者名;再开一个浏览器会话,打开练习站登录页,用 browser_actions 填用户名密码并点登录,用 browser_extract 读页头确认已登录,最后关闭会话。练习站的登录页接受任意用户名和密码。新建 agent_flow.py:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
server = StdioServerParameters(command="scrapling-mcp")
def show(title, result, limit=300):
text = "".join(c.text for c in result.content if c.type == "text")
print(f"== {title} (isError={result.is_error})")
print(text[:limit])
async def main():
async with stdio_client(server) as (read, write):
async with ClientSession(read, write) as s:
await s.initialize()
# 1. 一次性 HTTP 请求,只取作者名
r = await s.call_tool("make_request", {
"url": "https://quotes.toscrape.com/",
"css_selector": ".quote .author",
"extraction_type": "text",
})
show("make_request", r)
# 2. 打开浏览器会话,登录后读取页面
r = await s.call_tool("browser_open", {})
show("browser_open", r)
sid = r.structured_content["session_id"]
r = await s.call_tool("browser_fetch", {
"session_id": sid,
"url": "https://quotes.toscrape.com/login",
"extraction_type": "markdown",
})
show("browser_fetch", r, 200)
r = await s.call_tool("browser_actions", {
"session_id": sid,
"actions": [
{"type": "textbox", "target": "#username",
"value": "kun"},
{"type": "textbox", "target": "#password",
"value": "demo"},
{"type": "click", "target": "input[type=submit]"},
{"type": "wait_load", "state": "load"},
],
})
show("browser_actions", r)
r = await s.call_tool("browser_extract", {
"session_id": sid,
"extraction_type": "text",
"target": ".header-box",
})
show("browser_extract", r)
r = await s.call_tool("close_session", {"session_id": sid})
show("close_session", r)
asyncio.run(main())
python agent_flow.py
对照结果(已去掉 INFO 日志行,作者列表与 Markdown 有删节,测试机全程约 4.5 秒):
== make_request (isError=False)
{
"status": 200,
"content": [
"Albert Einstein",
"J.K. Rowling",
...
"Steve Martin",
""
],
"url": "https://quotes.toscrape.com
== browser_open (isError=False)
{
"session_id": "38d40da85588",
"session_type": "stealthy"
}
== browser_fetch (isError=False)
{
"status": 200,
"content": [
"[Quotes to Scrape](/)\n=====...\n\n[Login](/login)\n\nUsername ...
== browser_actions (isError=False)
Actions completed.
== browser_extract (isError=False)
Quotes to Scrape
Logout
== close_session (isError=False)
{
"session_id": "38d40da85588",
"message": "Session '38d40da85588' closed successfully."
}
解读:
make_request按css_selector只返回 10 位作者名,列表末尾多一个空字符串"",是实测原样输出,处理时过滤掉即可。browser_open只返回session_id,后面的会话工具都靠它找到同一个浏览器标签页。session_id每次随机生成。browser_actions一次调用里连做了填两个输入框、点击、等待加载四件事,省去智能体来回好几轮。动作类型有textbox、click、wait_load、wait_element、press_key、combobox(下拉框)、checkbox等,完整字段见工具参考。- 登录后页头从
Login变成了Logout,说明 cookie 保存在会话里。 - 会话用完一定要
close_session,否则浏览器会一直开着。
第 4 步:改用 HTTP 模式,并加上访问令牌
AI 应用和 Scrapling 不在同一台机器时,用 Streamable HTTP 模式。默认监听 127.0.0.1:8000,地址是 http://127.0.0.1:8000/mcp。0.5 版要求 HTTP 模式必须设令牌,直接 --http 会拒绝启动:
scrapling-mcp --http
对照结果(节选,退出码 2,长句已折行):
Error: Refusing to serve the MCP server over HTTP without
authentication because anyone who can reach 127.0.0.1:8000 would be
able to use every tool, including fetching arbitrary URLs from this
machine. Pass `--auth-token` (or set the SCRAPLING_MCP_AUTH_TOKEN
environment variable) to require a bearer token, or `--no-auth` to
serve it unauthenticated anyway.
官方建议用环境变量传令牌,避免留在命令历史和进程列表里。在一个终端里启动服务:
export SCRAPLING_MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
echo "$SCRAPLING_MCP_AUTH_TOKEN"
scrapling-mcp --http
对照结果(节选):
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
开第二个终端,先确认不带令牌的请求会被拒绝:
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
http://127.0.0.1:8000/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
对照结果:
401
再用带令牌的 Python 客户端连接。在第二个终端里也 export 同一个令牌(把第一个终端 echo 出来的值粘进去),然后新建 http_client.py:
import asyncio
import os
from mcp import ClientSession
from mcp.client.streamable_http import (
create_mcp_http_client,
streamable_http_client,
)
URL = "http://127.0.0.1:8000/mcp"
TOKEN = os.environ["SCRAPLING_MCP_AUTH_TOKEN"]
async def main():
headers = {"Authorization": f"Bearer {TOKEN}"}
async with create_mcp_http_client(headers=headers) as http:
transport = streamable_http_client(URL, http_client=http)
async with transport as (read, write, *_):
async with ClientSession(read, write) as s:
await s.initialize()
tools = (await s.list_tools()).tools
print(len(tools), "tools over HTTP")
asyncio.run(main())
export SCRAPLING_MCP_AUTH_TOKEN="第一个终端里显示的令牌"
python http_client.py
对照结果:
16 tools over HTTP
令牌写错时,测试机上客户端报 MCPError: Server returned an error response。
AI 应用里按官方格式填 url 和请求头(未实测):
{
"mcpServers": {
"ScraplingServer": {
"url": "https://your-server.example.com/mcp",
"headers": {
"Authorization": "Bearer <your-token>"
}
}
}
}
要让别的机器访问,加 --host '0.0.0.0';对公网开放时,官方建议放在提供 HTTPS 的反向代理后面,并用 --allowed-host '你的域名:8000' 防 DNS 重绑定攻击。不要把 --no-auth 和 --host '0.0.0.0' 一起用,那等于任何人都能借你的机器抓任意网址。
第 5 步:给智能体下指令的写法(未实测)
本步的提示词来自官方文档示例,测试机没有在 Claude 等 AI 应用里实际对话。
官方的经验是:直接告诉 AI 用哪个工具,结果更稳定,也更省钱。比如同样是抓正文,第二种写法更好:
Scrape the main content from https://example.com and convert it
to markdown format.
Use regular requests to scrape the main content from
https://example.com and convert it to markdown format.
连续访问多个页面时,让它复用会话并在结束时关闭:
Open a stealthy browser session, then use it to scrape the main
details from the first 5 product pages on https://shop.example.com.
Close the session when you're done.
知道选择器时直接写进提示词,可以先缩小内容再交给模型,减少 token。中文提问也可以,比如「用普通 HTTP 请求,按 CSS 选择器 .quote .author 取出 https://quotes.toscrape.com/ 的作者名」。
使用边界:只抓取你有权访问的公开页面,遵守网站条款和 robots.txt;官方文档里绕过 Cloudflare 等防护的能力,不代表可以无视对方网站的规则。
小结
- 装
scrapling[ai],在 AI 应用里把command指向scrapling-mcp的完整路径,就能让智能体调用 16 个抓取工具。 - 一次性抓取用
make_request或browser_fetch_once;多步操作先browser_open,再用browser_fetch、browser_actions、browser_extract,最后close_session。 - 用 Python MCP SDK 2.x 写客户端时,属性名是
server_info、input_schema、structured_content这类下划线写法。 - HTTP 模式必须设令牌,不带令牌返回 401;对外开放要加 HTTPS 和
--allowed-host。 - 给 AI 下指令时点明工具、选择器和「用完关闭会话」,结果更稳定。
