第 25 期 | 网络代理与爬虫 MCP (WebFetch / Playwright)

更新于 2026/4/5

副标题/大纲:越过本地磁盘:让 Claude Code 在外网上收集参考资料后注入代码项目。

🎯 学习目标

  • 理解网络代理在爬虫中的重要性、工作原理及其在 Claude Code 中的应用场景。
  • 掌握 Claude Code 中通过 WebFetch 工具进行轻量级、静态网页内容抓取的方法。
  • 学习如何利用 Playwright 工具进行复杂的动态网页交互、数据提取与自动化操作。
  • 了解 Claude Code 如何整合这些外部数据收集能力,实现从互联网获取信息并注入到代码项目中的高级工作流。

📖 核心概念讲解

25.1 网络代理与爬虫伦理:数据获取的基石与边界

在进行大规模网络数据抓取时,直接使用本地 IP 地址往往会遇到诸多限制,例如:

  • IP 封禁: 目标网站可能会识别并封禁来自同一 IP 地址的频繁访问。
  • 频率限制: 网站对单个 IP 的访问频率设有阈值,超出即会触发验证码或临时封禁。
  • 地理限制: 部分内容或服务仅对特定地区开放。

网络代理服务器(Proxy Server)正是为了解决这些问题而生。它充当客户端(我们的爬虫)和目标服务器之间的中间人,所有的请求都通过代理服务器转发。

+--------------------+      +-----------------------+      +--------------------+
|  Claude Code /     |      |                       |      |                    |
|  你的爬虫程序      |----->|   代理服务器 (Proxy)  |----->|   目标网站服务器   |
|  (Client)          |      |                       |      |    (Target Server) |
+--------------------+      +-----------------------+      +--------------------+
                           ^                               ^
                           |                               |
                           +-- 客户端看到的是代理IP --------+-- 目标服务器看到的是代理IP

代理的类型:

  • HTTP/HTTPS 代理: 最常见,用于网页浏览。
  • SOCKS 代理: 更底层,支持 TCP/UDP,可用于非 HTTP 协议的流量。
  • 透明代理: 目标网站知道你使用了代理且知道你的真实 IP。
  • 匿名代理: 目标网站知道你使用了代理,但不知道你的真实 IP。
  • 高匿名代理: 目标网站甚至不知道你使用了代理,认为你是直接访问。

爬虫伦理与法律边界: 尽管代理提供了便利,但进行网络爬取时必须严格遵守伦理和法律规定:

  1. robots.txt 协议: 任何负责任的爬虫都应首先检查目标网站的 robots.txt 文件,并遵守其中定义的抓取规则(哪些路径不允许抓取、哪些用户代理不允许抓取等)。
  2. 抓取频率: 模拟人类访问行为,避免在短时间内对服务器造成过大压力,否则可能被视为拒绝服务攻击。
  3. 数据用途: 明确收集数据的合法用途,不得用于侵犯隐私、版权或商业秘密。
  4. 登录与认证: 未经授权不得绕过登录机制,除非是抓取公开数据。
  5. 条款与条件: 遵守网站的服务条款。

Claude Code 在执行网络抓取任务时,会内置对 robots.txt 的初步检查,并提供配置选项来控制抓取频率和代理使用,以帮助用户在合规的前提下高效工作。

25.2 WebFetch:Claude Code 的轻量级网络抓取器

WebFetch 是 Claude Code 内部封装的一个轻量级、高效的网络请求工具,它旨在简化对静态网页内容的获取。你可以将其理解为 curl 或 Python requests 库的高级抽象,专门为 Claude 的任务执行环境优化。

核心特性:

  • 简洁的 API: 通过简单的 JSON 或 YAML 配置即可发起 HTTP/HTTPS 请求。
  • 支持代理: 可轻松集成代理服务器,实现匿名访问或 IP 轮换。
  • 请求头定制: 允许自定义 User-Agent、Referer 等 HTTP 请求头,模拟不同客户端行为。
  • 超时控制: 防止请求无限期等待。
  • 自动重试: 在网络不稳定的情况下,可配置自动重试机制。

典型应用场景:

  • 抓取博客文章、新闻页面等静态 HTML 内容。
  • 获取 API 接口返回的 JSON 或 XML 数据。
  • 检测网站的可访问性或特定内容的更新。

WebFetch 工具定义示例 (假设 Claude Code 的工具配置是 YAML 格式):

tools:
  - name: WebFetch
    description: "用于通过 HTTP/HTTPS 请求获取网页内容或API数据。"
    parameters:
      type: object
      properties:
        url:
          type: string
          description: "目标URL。"
        method:
          type: string
          enum: ["GET", "POST", "PUT", "DELETE"]
          default: "GET"
          description: "HTTP 请求方法。"
        headers:
          type: object
          description: "自定义HTTP请求头,键值对形式。"
        data:
          type: string
          description: "POST/PUT请求体数据,可以是JSON字符串或表单数据。"
        proxy:
          type: string
          description: "代理服务器地址,格式如 'http://user:pass@host:port' 或 'socks5://host:port'。"
        timeout:
          type: integer
          default: 30
          description: "请求超时时间(秒)。"
        follow_redirects:
          type: boolean
          default: true
          description: "是否跟随HTTP重定向。"
      required: ["url"]

25.3 Playwright:应对动态网页的瑞士军刀

当目标网页大量依赖 JavaScript 进行内容渲染(如单页应用 SPA)或需要复杂的用户交互(如点击按钮、填写表单、滚动页面加载更多内容)时,仅仅使用 WebFetch 这样的 HTTP 客户端是无法满足需求的。这时,我们就需要一个无头浏览器(Headless Browser)。

Playwright 是 Microsoft 开发的一个强大的无头浏览器自动化库,它支持 Chromium (Chrome)、Firefox 和 WebKit (Safari) 三大浏览器引擎,并提供了统一的 API。

核心特性:

  • 多浏览器支持: 在真实浏览器环境中运行测试或爬虫。
  • 无头模式: 可以在后台运行浏览器,无需显示图形界面,节省资源。
  • 丰富的交互 API:
    • 页面导航 (page.goto())
    • 元素选择 (page.locator(), page.$())
    • 点击 (element.click())
    • 输入文本 (element.fill())
    • 键盘/鼠标操作
    • 等待元素出现/网络请求完成 (page.waitForSelector(), page.waitForLoadState())
    • 截图 (page.screenshot())、生成 PDF (page.pdf())
  • 网络拦截: 可以拦截、修改或阻止网络请求,例如加载代理、注入脚本、阻止图片加载等。
  • 上下文隔离: 每个 BrowserContext 都是独立的,拥有自己的缓存、cookie 和本地存储,互不影响。

Playwright 在 Claude Code 中的集成: Claude Code 可以通过一个专门的 Playwright 工具或 browser_automation 工具来调用 Playwright 的能力。用户通过提供一个描述任务的自然语言指令,或者一个预定义的 Playwright 脚本,让 Claude 来执行。

Playwright 工具定义示例 (假设 Claude Code 的工具配置):

tools:
  - name: Playwright
    description: "用于在无头浏览器中执行复杂网页交互和动态内容抓取。"
    parameters:
      type: object
      properties:
        script:
          type: string
          description: "要执行的Playwright Python脚本代码字符串。"
        url:
          type: string
          description: "可选:初始导航URL,如果脚本未指定。"
        browser:
          type: string
          enum: ["chromium", "firefox", "webkit"]
          default: "chromium"
          description: "使用的浏览器类型。"
        headless:
          type: boolean
          default: true
          description: "是否在无头模式下运行浏览器。"
        proxy:
          type: string
          description: "代理服务器地址,格式如 'http://user:pass@host:port'。"
        args:
          type: array
          items:
            type: string
          description: "传递给浏览器启动的额外参数,例如 '--disable-gpu'。"
        context_options:
          type: object
          description: "Playwright BrowserContext 选项,如 user_agent, locale 等。"
      required: ["script"]

通过 Playwright,Claude Code 能够模拟真实用户行为,绕过 JavaScript 渲染障碍,从而获取到最接近用户浏览器所见的内容,极大地扩展了 Claude 的信息收集能力。


💻 模拟 Claude TUI 交互

场景 1:使用 WebFetch 快速抓取特定技术博客的最新文章标题

我需要获取 https://www.example-tech-blog.com/ 网站上关于 "AI" 标签的最新五篇文章标题和链接。

╭─ Claude ──────────────────────────────╮ │ 好的,我将使用 WebFetch 工具尝试抓取该博客的 "AI" 标签页,│ │ 然后解析 HTML 内容以提取标题和链接。如果该页面是动态渲染的,│ │ 我可能需要切换到 Playwright。请稍等... │ │ │ │ 工具调用: WebFetch │ │ json │ │ { │ │ "url": "https://www.example-tech-blog.com/tags/AI", │ │ "method": "GET", │ │ "headers": { │ │ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"│ │ } │ │ } │ │ │ │ │ │ 工具输出: WebFetch (部分 HTML 内容) │ │ html │ │ <!DOCTYPE html> │ │ <html lang="zh"> │ │ <head>...</head> │ │ <body> │ │ <div id="posts-list"> │ │ <article class="post-item"> │ │ <h2><a href="/article/ai-future-trends">AI 未来趋势分析</a></h2>│ │ <p>...</p> │ │ </article> │ │ <article class="post-item"> │ │ <h2><a href="/article/llm-development">大型语言模型开发实践</a></h2>│ │ <p>...</p> │ │ </article> │ │ <!-- 更多文章 --> │ │ </div> │ │ </body> │ │ </html> │ │ │ │ │ │ 已成功抓取并解析内容。以下是关于 "AI" 的最新五篇文章: │ │ │ │ 1. AI 未来趋势分析 │ │ 链接: https://www.example-tech-blog.com/article/ai-future-trends│ │ 2. 大型语言模型开发实践 │ │ 链接: https://www.example-tech-blog.com/article/llm-development│ │ 3. 深度学习在图像识别中的应用 │ │ 链接: https://www.example-tech-blog.com/article/deep-learning-image-recognition│ │ 4. 强化学习入门指南 │ │ 链接: https://www.example-tech-blog.com/article/reinforcement-learning-guide│ │ 5. AI 伦理与治理的挑战 │ │ 链接: https://www.example-tech-blog.com/article/ai-ethics-challenges│ │ │ │ 您希望我将这些信息注入到您的项目文档中,还是用于生成代码? │ ╰───────────────────────────────────────╯

场景 2:使用 Playwright 登录并抓取需要认证的用户仪表盘数据 (假设已获授权)

我需要从 https://my-dashboard.secure-app.com/ 上的个人仪表盘抓取我的项目列表。我的用户名是 myuser,密码是 mypassword。请使用代理 http://proxy.example.com:8080

╭─ Claude ──────────────────────────────╮ │ 好的,我将使用 Playwright 模拟登录并抓取您的仪表盘数据。 │ │ 请注意,在实际操作中,泄露密码是危险的。通常我会建议您使用 │ │ 环境变量或安全凭据管理系统来传递敏感信息。本次操作我会直接使用 │ │ 您提供的凭据和代理。 │ │ │ │ 工具调用: Playwright │ │ json │ │ { │ │ "browser": "chromium", │ │ "headless": true, │ │ "proxy": "http://proxy.example.com:8080", │ │ "script": """ │ │import asyncio │ │from playwright.async_api import Page, expect, BrowserContext, request as playwright_request, TimeoutError # 导入 TimeoutError │ │ │ │async def run(page: Page, context: BrowserContext): │ │ try: │ │ print("Navigating to login page...") │ │ await page.goto("https://my-dashboard.secure-app.com/login", wait_until="domcontentloaded")│ │ await page.wait_for_selector('input[name="username"]', timeout=10000) # 等待用户名输入框出现 │ │ │ │ print("Filling credentials...") │ │ await page.fill('input[name="username"]', "myuser") │ │ await page.fill('input[name="password"]', "mypassword")│ │ │ │ print("Clicking login button...") │ │ await page.click('button[type="submit"]') │ │ │ │ print("Waiting for dashboard to load...") │ │ await page.wait_for_url("https://my-dashboard.secure-app.com/dashboard", timeout=30000) # 等待跳转到仪表盘页面 │ │ await page.wait_for_load_state("networkidle") │ │ │ │ print("Extracting project list...") │ │ project_elements = await page.locator('.project-item').all()│ │ projects = [] │ │ for el in project_elements: │ │ title = await el.locator('.project-title').text_content()│ │ status = await el.locator('.project-status').text_content()│ │ projects.append({"title": title.strip(), "status": status.strip()})│ │ │ │ print(f"Extracted {len(projects)} projects.") │ │ return {"projects": projects} │ │ except TimeoutError: │ │ print("Playwright operation timed out.") │ │ return {"error": "Playwright operation timed out. Check selectors or network."}│ │ except Exception as e: │ │ print(f"An error occurred: {e}") │ │ return {"error": str(e)} │ │ │ │""" │ │ } │ │ │ │ │ │ 工具输出: Playwright (JSON 结果) │ │ json │ │ { │ │ "projects": [ │ │ {"title": "Website Redesign Project", "status": "进行中"},│ │ {"title": "Mobile App v2.0", "status": "已完成"}, │ │ {"title": "Backend API Optimization", "status": "待开始"} │ │ ] │ │ } │ │ │ │ │ │ 成功从您的仪表盘获取了项目列表: │ │ │ │ - Website Redesign Project: 进行中 │ │ - Mobile App v2.0: 已完成 │ │ - Backend API Optimization: 待开始 │ │ │ │ 您希望我根据这些项目信息做些什么?例如,生成项目状态报告,或者为 │ │ "进行中" 的项目创建 Jira 任务? │ ╰───────────────────────────────────────╯


💻 代码演示

以下是一些在 Claude Code 环境中,WebFetchPlaywright 工具可能被调用的实际配置或脚本示例。

1. WebFetch 调用示例 (获取 JSON API 数据,带代理和自定义 User-Agent)

假设你需要从一个公共 API 获取数据,并希望通过代理隐藏真实 IP,同时模拟浏览器请求。

{
  "tool_name": "WebFetch",
  "parameters": {
    "url": "https://api.public-data-source.com/v1/latest-news?category=technology",
    "method": "GET",
    "headers": {
      "Accept": "application/json",
      "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
      "X-Requested-With": "XMLHttpRequest"
    },
    "proxy": "http://user:[email protected]:8888",
    "timeout": 45
  }
}

2. Playwright 脚本示例 (模拟用户登录、搜索并截图)

这个 Python 脚本展示了如何使用 Playwright 启动一个浏览器,导航到登录页,输入凭据,执行搜索,并截取搜索结果页面的图片。Claude Code 将执行这个脚本并返回结果。

# playwright_script.py (此脚本将作为字符串传递给 Claude 的 Playwright 工具)
import asyncio
from playwright.async_api import Page, expect, BrowserContext, request as playwright_request, TimeoutError

async def run(page: Page, context: BrowserContext):
    """
    此函数将在Playwright的Page和BrowserContext中执行。
    """
    try:
        # 1. 导航到登录页面
        print("Navigating to login page...")
        await page.goto("https://www.my-internal-tool.com/login", wait_until="domcontentloaded")
        await page.wait_for_selector('input[name="username"]', timeout=15000)

        # 2. 填写登录凭据
        print("Filling credentials...")
        await page.fill('input[name="username"]', "my_secure_username")
        await page.fill('input[name="password"]', "my_secure_password_from_env") # 假设密码从环境变量或安全存储中获取

        # 3. 点击登录按钮
        print("Clicking login button...")
        await page.click('button[type="submit"]')

        # 4. 等待跳转到内部仪表盘或主页
        print("Waiting for dashboard to load...")
        await page.wait_for_url("https://www.my-internal-tool.com/dashboard", timeout=30000)
        await page.wait_for_load_state("networkidle") # 等待所有网络请求完成

        # 5. 执行搜索操作
        print("Performing search...")
        search_input = page.locator('input[placeholder="Search projects..."]')
        await search_input.fill("critical bug")
        await search_input.press("Enter") # 模拟按下回车键

        # 6. 等待搜索结果加载
        print("Waiting for search results...")
        await page.wait_for_selector('.search-results-list', timeout=20000)
        await page.wait_for_load_state("networkidle")

        # 7. 截图保存结果
        screenshot_path = "search_results.png"
        await page.screenshot(path=screenshot_path, full_page=True)
        print(f"Screenshot saved to {screenshot_path}")

        # 8. 提取搜索结果的标题和链接
        result_elements = await page.locator('.search-results-list .result-item h3 a').all()
        results = []
        for el in result_elements:
            title = await el.text_content()
            href = await el.get_attribute('href')
            results.append({"title": title.strip(), "link": href})

        return {"status": "success", "screenshot": screenshot_path, "search_results": results}

    except TimeoutError:
        print("Playwright operation timed out.")
        return {"status": "error", "message": "Playwright operation timed out. Check selectors or network issues."}
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
        return {"status": "error", "message": str(e)}

# 在 Claude Code 的 tool_name: Playwright 中,这个脚本会作为 "script" 参数的字符串值传入。
# Claude 会在后台执行这个脚本,并将 run 函数的返回值作为工具输出。

3. Claude Code 调用 Playwright 工具的 JSON 配置

当用户通过自然语言指令请求 Claude 执行复杂网页操作时,Claude 可能会生成如下的 Playwright 工具调用:

{
  "tool_name": "Playwright",
  "parameters": {
    "browser": "chromium",
    "headless": true,
    "proxy": "http://myproxy.com:8080",
    "script": "import asyncio\nfrom playwright.async_api import Page, expect, BrowserContext, request as playwright_request, TimeoutError\n\nasync def run(page: Page, context: BrowserContext):\n    try:\n        print(\"Navigating to login page...\")\n        await page.goto(\"https://www.my-internal-tool.com/login\", wait_until=\"domcontentloaded\")\n        await page.wait_for_selector('input[name=\"username\"]', timeout=15000)\n\n        print(\"Filling credentials...\")\n        await page.fill('input[name=\"username\"]', \"my_secure_username\")\n        await page.fill('input[name=\"password\"]', \"my_secure_password_from_env\")\n\n        print(\"Clicking login button...\")\n        await page.click('button[type=\"submit\"]')\n\n        print(\"Waiting for dashboard to load...\")\n        await page.wait_for_url(\"https://www.my-internal-tool.com/dashboard\", timeout=30000)\n        await page.wait_for_load_state(\"networkidle\")\n\n        print(\"Performing search...\")\n        search_input = page.locator('input[placeholder=\"Search projects...\"]')\n        await search_input.fill(\"critical bug\")\n        await search_input.press(\"Enter\")\n\n        print(\"Waiting for search results...\")\n        await page.wait_for_selector('.search-results-list', timeout=20000)\n        await page.wait_for_load_state(\"networkidle\")\n\n        screenshot_path = \"search_results.png\"\n        await page.screenshot(path=screenshot_path, full_page=True)\n        print(f\"Screenshot saved to {screenshot_path}\")\n\n        result_elements = await page.locator('.search-results-list .result-item h3 a').all()\n        results = []\n        for el in result_elements:\n            title = await el.text_content()\n            href = await el.get_attribute('href')\n            results.append({\"title\": title.strip(), \"link\": href})\n\n        return {\"status\": \"success\", \"screenshot\": screenshot_path, \"search_results\": results}\n\n    except TimeoutError:\n        print(\"Playwright operation timed out.\")\n        return {\"status\": \"error\", \"message\": \"Playwright operation timed out. Check selectors or network issues.\"}\n    except Exception as e:\n        print(f\"An unexpected error occurred: {e}\")\n        return {\"status\": \"error\", \"message\": str(e)}\n"
  }
}

🔧 涉及的 Tools / Commands

| 工具名称 | 用途 | 说明