跳至内容
免费开始
返回博客

如何使用 WebScrapingAPI:抓取 API 和 Web MCP 指南

Robert Munceanu最后更新于 7 min read
如何使用 WebScrapingAPI:抓取 API 和 Web MCP 指南
简而言之:要使用 WebScrapingAPI 的 Scraper API,请向 https://api.webscrapingapi.com/v2 发送 GET 请求,附上您的 api_key 和经过 URL 编码的 url,然后从响应中读取页面。当您已经知道需要哪些 URL 时使用它;搜索结果请选择 SERP API,需要点击和等待时选择 Browser API,连接 AI 智能体则使用 Web MCP。本指南还介绍渲染、错误和费用。

您有一份 URL 列表,需要可靠地获取这些 URL 对应的页面,且无需自行运行代理或无头浏览器。这正是 WebScrapingAPI 的 Scraper API 所承担的任务。对这一主题还不太了解?不妨先从了解什么是网页抓取开始。 读完本指南后,您将能够通过环境变量设置密钥来发送首个请求,仅在目标页面需要时添加渲染、定位和数据提取功能,处理所有已记录的错误,并追踪每次调用的成本。您还将了解在何种情况下,Browser API、SERP API 或 Web MCP 更适合您的需求。

快速入门

  • 端点: https://api.webscrapingapi.com/v2
  • 两个必填参数: api_key 以及一个 URL 编码的 url
  • 免费试用:5,000 次请求
  • 无附加费用:JavaScript 渲染和地理定位不额外消耗积分

什么是 WebScrapingAPI 的 Scraper API,何时应该使用它?

Scraper API 是一个 REST 端点,它接收目标 URL,并以 HTML、JSON 或选定字段的形式返回页面内容。WebScrapingAPI 负责运行代理、轮询、访问处理、重试以及可选的 JavaScript 渲染。当您已知需要哪些 URL,且希望获取页面内容而无需自行运维抓取基础设施时,请使用该 API。

您的应用程序提供目标 URL 以及任何请求上下文(例如国家/地区或输出格式)。WebScrapingAPI 负责处理该请求背后的访问层:代理路由和轮转、访问处理策略、针对目标的内部重试,以及在您请求渲染时调用的浏览器。 返回结果可以是页面正文、包含元数据的 JSON 封装、JSON DOM,或是您所选定的字段。当请求无法完成时,您会收到明确且有文档记录的错误信息,而非静默返回错误内容。

该层之外的所有内容均由您负责:数据源、用途、解析、验证和存储。该 API 仅免除了代理和浏览器相关的工作,而非数据处理流程本身。

所有套餐(包括试用版)均包含 JavaScript 渲染、所有地理区域的国家/地区级定向,以及带访问处理功能的代理轮换。套餐之间的差异在于请求量、并发数和支持服务,而非功能本身。

还在犹豫托管式 API 是否适合您吗?请阅读关于使用爬取 API 替代自有技术栈的权衡分析。如果您需要搜索结果、页面点击和等待操作,或者需要结构化记录而非整页内容,请直接跳转至五种 Web Access API 的选择指南。

如何发送您的第一个 Scraper API 请求?

创建一个账户,从仪表盘的“Web Scraping API”选项卡中复制您的 API 密钥,并将其存储在环境变量中。然后向 https://api.webscrapingapi.com/v2 发送一个包含 api_key 和 URL 编码的 URL 的 GET 请求。 请让您的 HTTP 库负责参数编码,切勿在浏览器代码中直接调用该 API。

  1. 注册。创建一个 WebScrapingAPI 账户并完成验证。试用版提供 5,000 次请求配额。
  2. 复制您的密钥。在仪表盘中,打开左侧面板的“Web Scraping API”选项卡,并复制您的 API 密钥。
  3. 将其作为密钥存储。如下所示,将该密钥设置为执行调用的服务器上的环境变量。
  4. 发送请求。请使用以下示例之一。
export WSA_API_KEY=...   # paste your key here, never into code

密钥应保存在服务器端的密钥中,切勿将其置于浏览器代码、移动应用、公共代码库、日志、屏幕截图或共享笔记本中。任何掌握该密钥的人都能消耗您套餐中的请求配额。

curl

curl --get "https://api.webscrapingapi.com/v2" \
  --data-urlencode "api_key=$WSA_API_KEY" \
  --data-urlencode "url=https://example.com" \
  --data-urlencode "render_js=1" \
  --data-urlencode "country=us"

--get 配合 --data-urlencode 会指示 curl 将每个参数放入查询字符串中,并自动进行百分比编码。

Python(Python 3 搭配 pip install requests)

本指南后续内容将以此小型客户端为基础。它从 WSA_API_KEY,让 Requests 通过 params= (参见 Requests 快速入门),并且仅对值得重试的错误进行重试。

"""Minimal Scraper API client: env-var key, safe retries, optional JSON envelope.

Usage:
  export WSA_API_KEY=...            # never hardcode the key
  python wsa_scrape.py https://example.com
  python wsa_scrape.py https://example.com --render --country us --json
  python wsa_scrape.py https://example.com --dry-run   # print the request, no call
"""
import argparse
import json
import os
import sys
import time

import requests

ENDPOINT = "https://api.webscrapingapi.com/v2"
RETRYABLE = {429, 500, 503}          # back off and retry
FINAL = {400, 401, 403, 406}         # fix the request or the account; do not retry


def build_params(url, render=False, country=None, json_response=False, extract_rules=None):
    params = {"url": url}
    if render:
        params["render_js"] = "1"
    if country:
        params["country"] = country.lower()
    if json_response:
        params["json_response"] = "1"
    if extract_rules:
        # The API expects a JSON-stringified object; requests URL-encodes it.
        params["extract_rules"] = json.dumps(extract_rules, separators=(",", ":"))
    return params


def scrape(url, api_key, max_attempts=4, **opts):
    params = {"api_key": api_key, **build_params(url, **opts)}
    for attempt in range(1, max_attempts + 1):
        resp = requests.get(ENDPOINT, params=params, timeout=130)
        usage = {k: v for k, v in resp.headers.items() if k.lower().startswith("wsa-")}
        if resp.ok:
            return resp, usage
        if resp.status_code in FINAL:
            raise SystemExit(f"{resp.status_code}: {resp.text[:300]}")
        if resp.status_code == 422 and not opts.get("render"):
            # Scrape accepted but not completed (e.g. a challenge page). One rendered retry.
            opts["render"] = True
            params = {"api_key": api_key, **build_params(url, **opts)}
            continue
        if resp.status_code in RETRYABLE or resp.status_code == 422:
            time.sleep(min(2 ** attempt, 30))
            continue
        raise SystemExit(f"Unexpected {resp.status_code}: {resp.text[:300]}")
    raise SystemExit(f"Gave up after {max_attempts} attempts")


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("url")
    ap.add_argument("--render", action="store_true")
    ap.add_argument("--country")
    ap.add_argument("--json", dest="json_response", action="store_true")
    ap.add_argument("--dry-run", action="store_true")
    a = ap.parse_args()
    opts = dict(render=a.render, country=a.country, json_response=a.json_response)

    if a.dry_run:
        prepared = requests.Request("GET", ENDPOINT,
                                    params={"api_key": "REDACTED", **build_params(a.url, **opts)}).prepare()
        print(prepared.url)
        return

    api_key = os.environ.get("WSA_API_KEY")
    if not api_key:
        sys.exit("Set WSA_API_KEY first (dashboard -> Web Scraping API tab).")

    resp, usage = scrape(a.url, api_key, **opts)
    print("status:", resp.status_code)
    print("usage headers:", usage)
    if a.json_response:
        env = resp.json()
        print("initial-status-code:", env.get("initial-status-code"), "cost:", env.get("cost"), "type:", env.get("type"))
        print(env.get("body", "")[:300])
    else:
        print(resp.text[:300])


if __name__ == "__main__":
    main()

使用 --dry-run 标志会打印出精确的请求内容(但不会实际发送),并会将键值进行遮蔽。以下是一个包含空格和 ?,以及一个 &:

python wsa_scrape.py 'https://example.com/a b?x=1&y=2' --render --country US --json --dry-run
https://api.webscrapingapi.com/v2?api_key=REDACTED&url=https%3A%2F%2Fexample.com%2Fa+b%3Fx%3D1%26y%3D2&render_js=1&country=us&json_response=1

Node.js(Node 18 或更高版本,内置 fetch 和 URLSearchParams)

// Build a Scraper API request URL in Node 18+ without sending it.
// URLSearchParams handles encoding of the target URL and the extract_rules JSON.
const ENDPOINT = "https://api.webscrapingapi.com/v2";

export function buildScrapeUrl(apiKey, target, opts = {}) {
  const params = new URLSearchParams({ api_key: apiKey, url: target });
  if (opts.renderJs) params.set("render_js", "1");
  if (opts.country) params.set("country", opts.country);
  if (opts.extractRules) params.set("extract_rules", JSON.stringify(opts.extractRules));
  return `${ENDPOINT}?${params}`;
}

const target = "https://example.com/search?q=web scraping&page=2";
const url = buildScrapeUrl("REDACTED", target, {
  renderJs: true,
  country: "us",
  extractRules: { title: { selector: "h1", output: "text" } },
});
console.log(url);

// Round-trip check: the API sees exactly the original target and rules.
const parsed = new URL(url).searchParams;
console.log("url param decodes to original:", parsed.get("url") === target);
console.log("extract_rules parses:", JSON.parse(parsed.get("extract_rules")).title.selector);

// Live call (server-side only): uncomment with WSA_API_KEY set.
// const res = await fetch(buildScrapeUrl(process.env.WSA_API_KEY, "https://example.com"));
// console.log(res.status, res.headers.get("wsa-call-credit-usage"), (await res.text()).slice(0, 200));

输出:

https://api.webscrapingapi.com/v2?api_key=REDACTED&url=https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dweb+scraping%26page%3D2&render_js=1&country=us&extract_rules=%7B%22title%22%3A%7B%22selector%22%3A%22h1%22%2C%22output%22%3A%22text%22%7D%7D
url param decodes to original: true
extract_rules parses: h1

为何编码很重要。目标 URL 被封装在另一个 URL 的查询字符串中。若将 https://example.com/search?q=web scraping&page=2 原始内容, & 会过早地结束 url 参数过早结束,而 page=2 导致该字符作为独立参数传入API。虽然抓取仍能“运行”,但抓取的却是错误的页面。RFC 3986中定义的百分比编码会将 ? 为 %3F ,并将转换为 & 变为 %26 ,从而使整个目标保持完整。让库来处理吧:Node 中的 URLSearchParams, params= Requests 中的 URLSearchParams,或 --data-urlencode curl 中的。上述往返验证证明了 API 接收到的正是你最初发送的字符串。

更倾向于使用客户端库?WebScrapingAPI 维护着针对 Node.js、Python、Java、PHP 和 Rust 的已发布 SDK,此外还提供了 Scrapy 集成。若需进行超出 API 调用范围的解析和爬取,请参阅这些“API 调用之外的 Python 爬取模式”。

文档:发送请求。

你实际上需要哪些 Scraper API 参数?

仅 api_key 和 url 是必填项。对于客户端渲染的页面,请添加 render_js;指定位置时,请添加 country、city、state 或 asn;若需结构化输出,请添加 extract_rules 或 json_response;针对加载缓慢的页面,请添加 async;若要执行 POST、PUT 或 PATCH 请求,请在 method 参数中包含请求体。 timeout 的默认值和上限均为 120,000 毫秒。

表 1:Scraper API 参数

参数

类型

作用

何时使用

api_key (必填)

字符串

对请求进行身份验证

始终使用;从环境变量中读取

url (必填)

字符串

目标页面;对其进行 URL 编码

始终(除非使用 snapshot_id)

render_js

布尔值

在浏览器中渲染页面

纯HTML中缺少数据

country

字符串

两个字母的代理国家

内容因国家而异

city / state

字符串

更具体的位置; state 适用于 us/au 城市

城市级内容;需要 country

asn

字符串

网络(自治系统编号)

网络特定内容

extract_rules

JSON 字符串

以 JSON 格式返回所选字段

您需要的是字段,而不是页面

json_response

布尔值

将输出包装为包含标头、状态、成本和元数据的形式

存储元数据的管道

json_dom

布尔值

返回类似 DOM 的 JSON 树

很少使用;建议使用提取规则

method / body

字符串

GET、POST、PUT、PATCH 及有效载荷(测试版)

目标期望接收表单或 JSON 请求

timeout

int (毫秒)

在 N 毫秒后中止;默认值且最大值为 120000

建议采用“快速失败”策略

async

布尔值

将抓取任务加入队列并返回一个 response_id

目标速度慢、批次较大时

snapshot_id

字符串

获取队列中的结果(而非 url)

异步任务的第二步

布尔参数接受 1 或 true 来开启某项功能,并 0 或 false 来关闭该功能,因此 render_js=1 and render_js=true 表示相同的意思。 snapshot_id 替换 url 而非将其加入:仅在收集异步任务的结果时才发送它。

POST、PUT 和 PATCH 都发送到同一个 /v2 端点,且 body 有效载荷发送到同一个端点,文档中将其标记为测试版。请先将其指向无害的回显目标(例如 https://httpbin.org/post ,再将其指向真实表单。如果你宁愿快速失败,也不愿等待长达两分钟的慢速目标,请将 timeout 为更低的毫秒数。

针对每个新目标,先使用两个必填参数进行测试,观察返回结果,然后逐个添加选项。每个选项都应通过修复你在原始响应中发现的问题来证明其必要性。

文档:API 参数。

何时应启用 JavaScript 渲染?

仅当所需数据因页面通过 JavaScript 生成而缺失在纯 HTML 中时,才应启用 render_js=1。请先不进行渲染地获取数据,并检查目标字段是否存在。在 Scraper API 中,渲染不会消耗额外配额,但速度比纯 HTTP 获取慢,因此请针对每个目标酌情使用。

测试只需一次请求。选择一个在普通浏览器中可见的值(例如价格),在不进行渲染的情况下获取页面,并在响应正文中搜索该值。如果存在,则跳过渲染;如果缺失,说明页面在加载后通过 JavaScript 生成该内容,因此请使用 render_js=1.

请针对每个目标单独决定,而非按项目统一设置,并将该设置存储在配置文件中每个目标的旁边。某个网站的列表页面可能返回完整的 HTML,而其产品页面则通过脚本加载价格。

渲染还与错误处理相关。文档中列举了在 render_js 被禁用后返回的“挑战页面”是导致 422 响应的原因之一。这就是为什么本指南中的重试策略在收到 422 响应后,会进行恰好一次渲染重试,然后才放弃。您还可以将渲染与 json_dom=1 结合使用,以便将渲染后的页面作为 JSON 树获取。

不过,渲染功能存在局限。如果数据只有在等待特定元素出现、点击按钮、滚动页面、在字段中输入内容或截图之后才会显示,那么这已经超出了单次 Scraper API 请求的处理范围。这正是 Browser API 的用武之地。

如何使用地理定位抓取特定位置的内容?

添加一个由两个字母组成的国家代码,例如 us、de 或 gb。可通过城市(小写,不带空格)、美国或澳大利亚城市的州名,或 asn 来选择网络,进一步缩小范围。地理定位不消耗额外积分。 城市和 ASN 目标的可用性可能因套餐和地理位置而异,因此必要时请回退到国家级别。

价格、库存、语言和搜索结果通常会随访问者的位置而变化。 country 参数会将您的请求路由至该国的代理服务器,并 city, state,并 asn 进一步缩小范围。 city 始终需要 country,且 state 用于消除 us 或 au.

在信任某条路由之前,请通过抓取IP回显服务来确认其真实性。此请求查询柏林出口,并返回目标节点所看到的IP地址:

curl --get "https://api.webscrapingapi.com/v2" \
  --data-urlencode "api_key=$WSA_API_KEY" \
  --data-urlencode "url=https://httpbin.org/ip" \
  --data-urlencode "country=de" \
  --data-urlencode "city=berlin"

所有套餐均支持针对所有地理区域的国家级定位。更精细的定位则不确定性较高:具体可用性可能取决于当前套餐、地理区域和路由。因此,请将城市和ASN视为请求,而非保证。如果其中一项不可用,请尝试国家级定位,并在试用期间确认您的工作负载所需的具体位置。

Google 是唯一一个不应使用 country。文档中明确指出,不应将 Google 自身的 gl 参数与 Scraper API 的 country 。对于搜索结果,请使用 SERP API,该 API 将位置和语言作为搜索本身的一部分。

如何通过提取规则仅返回所需的字段?

将 extract_rules 作为 JSON 字符串化对象传递,该对象将输出名称映射到 CSS 或 XPath 选择器。每条规则可返回文本、HTML、属性、表格或嵌套字段,且每条匹配结果均包含所有内容,并经过清理以去除空格。随后,API 会返回 JSON 数据,其中每条规则对应一个属性,而非返回完整页面。

最简形式是将名称映射到 CSS 选择器,并返回第一个匹配项的文本:

{"title": "h1"}

规则对象形式增加了更多选项。当您需要属性、HTML、所有匹配项或表格解析时,请使用此形式:

{
  "title": {"selector": "h1", "output": "text"},
  "links": {"selector": "a", "output": "@href", "all": 1}
}

可选参数包括:

  • selector: 一个 CSS 选择器或 XPath 表达式。以 / 的CSS选择器将被视为XPath,除非您设置 selector_type 为 css 或 xpath.
  • output: text (默认值), html, @attr 对于诸如 @href, table_json, table_array,或嵌套规则对象。
  • all: 1 返回所有匹配项, 0 仅返回第一个。
  • clean: 1 从文本中去除首尾空格和换行符。

嵌套规则用于处理重复出现的卡片、行或搜索结果块。您使用 all: 1,然后定义要从每个匹配项内部提取的字段。响应会将每个值封装在列表中,因此请预期 "title": ["..."] ,而非裸字符串。

在消耗积分之前,请先在保存的页面副本上预览您的选择器。 先保存目标页面的 HTML 一次,然后在本地反复调整规则,直到得到预期结果。下面的脚本是使用 Beautiful Soup(而非 API 自身的引擎)检查 CSS 选择器的本地近似实现,但它支持此处展示的规则选项。使用 pip install beautifulsoup4.

该测试环境(fixture.html):

<html><body>
  <h1>  Example Store  </h1>
  <div class="result"><h2>Blue Mug</h2><a href="/p/blue-mug">view</a><span class="price">$12.00</span></div>
  <div class="result"><h2>Red Mug</h2><a href="/p/red-mug">view</a><span class="price">$14.50</span></div>
  <table id="specs"><tr><th>Size</th><th>Material</th></tr><tr><td>350 ml</td><td>Ceramic</td></tr></table>
</body></html>

预览脚本(preview_rules.py):

"""Preview extract_rules selectors locally on saved HTML before spending credits.

This is a local approximation for checking CSS selectors, not the API's own engine.
It supports the rule options used here: selector, output (text, html, @attr, nested), all, clean.
"""
import json
import sys
from bs4 import BeautifulSoup

RULES = {
    "title": {"selector": "h1", "output": "text", "clean": 1},
    "products": {
        "selector": ".result",
        "all": 1,
        "output": {
            "name": {"selector": "h2", "output": "text"},
            "url": {"selector": "a", "output": "@href"},
            "price": {"selector": ".price", "output": "text"},
        },
    },
}


def apply_rule(node, rule):
    if isinstance(rule, str):
        rule = {"selector": rule}
    matches = node.select(rule["selector"])
    if not rule.get("all"):
        matches = matches[:1]
    out = rule.get("output", "text")
    values = []
    for m in matches:
        if isinstance(out, dict):
            values.append({k: first(apply_rule(m, r)) for k, r in out.items()})
        elif out == "html":
            values.append(str(m))
        elif out.startswith("@"):
            values.append(m.get(out[1:]))
        else:
            text = m.get_text()
            values.append(text.strip() if rule.get("clean") else text)
    return values


def first(v):
    return v[0] if v else None


html = open(sys.argv[1] if len(sys.argv) > 1 else "fixture.html").read()
soup = BeautifulSoup(html, "html.parser")
print(json.dumps({k: apply_rule(soup, r) for k, r in RULES.items()}, indent=2))
print("extract_rules param:", json.dumps(RULES, separators=(",", ":"))[:80], "...")

输出:

{
  "title": [
    "Example Store"
  ],
  "products": [
    {
      "name": "Blue Mug",
      "url": "/p/blue-mug",
      "price": "$12.00"
    },
    {
      "name": "Red Mug",
      "url": "/p/red-mug",
      "price": "$14.50"
    }
  ]
}
extract_rules param: {"title":{"selector":"h1","output":"text","clean":1},"products":{"selector":".re ...

一旦预览效果正确,请将相同的 RULES 字典传递给 build_params(..., extract_rules=RULES) Python 客户端,它会为你自动进行序列化和编码。手动构建查询字符串是出错最频繁的地方。如果该值不是字符串化的对象,API 会因验证错误而拒绝该请求:

{"status": "Failure", "status_code": 400, "error": "Key `extract_rules` must be a stringified object."}

规则、模式以及在网站变更时保持选择器最新,这些都由您负责。

文档:提取规则。

完整的 JSON 响应包含哪些内容?何时应使用它?

添加 json_response=1 可将结果封装为 JSON 格式,其中包含目标的头部信息、初始状态码、正文、html、json 或 b64 格式的数据、信用点消耗量,以及提取的微数据和 JSON-LD 元数据。当您的处理流程需要存储元数据或获取目标的真实状态码时,请使用此选项。 否则,请解析原始正文。

默认情况下,Scraper API 会直接返回页面正文,这正是大多数 HTML 解析器所需要的。JSON 封装格式适用于需要了解每次抓取更多信息的管道:目标实际返回的状态码、成本以及页面携带的结构化数据。

该封装包含六个字段: headers (目标的响应头), cost (请求消耗的积分), initial-status-code (目标返回的首个状态码), body (页面内容), type (html, json,或 b64),以及 metadata (提取的微数据和 JSON-LD)。

该解析器在存储任何内容之前会检查目标的状态,解码二进制正文,并读取成本。输入是一个反映文档中字段集的示例文件,而非实时输出:

"""Parse a json_response=1 envelope. sample_envelope.json mirrors the documented field set;
it is a SAMPLE, not live output."""
import base64
import json
import sys
from bs4 import BeautifulSoup

env = json.load(open(sys.argv[1] if len(sys.argv) > 1 else "sample_envelope.json"))

status = env["initial-status-code"]
if status >= 400:
    raise SystemExit(f"Target answered {status}; inspect before storing")

body = env["body"]
if env.get("type") == "b64":               # binary targets arrive base64-encoded
    body = base64.b64decode(body)

print("cost (credits):", env["cost"])
print("content-type:", env["headers"].get("content-type"))
print("json-ld blocks:", len(env["metadata"]["json-ld"]))
if env.get("type") == "html":
    print("title:", BeautifulSoup(body, "html.parser").title.get_text())

对示例文件的输出:

cost (credits): 1
content-type: text/html; charset=UTF-8
json-ld blocks: 0
title: Example Domain

此外还有 json_dom=1,该功能会将页面作为类似 DOM 的 JSON 树返回。若需获取特定字段,提取规则通常比遍历整个树更简单。

您无需使用信封(envelope)即可追踪成本。每个响应都携带自定义标头,其中包含一个 WSA- 前缀:

  • WSA-Scraped-Resolved-Url:重定向后的最终 URL
  • WSA-Scraped-Headers:目标页面的响应头
  • WSA-Client-Cookies: 页面加载后由 JavaScript 设置的 Cookie
  • WSA-Call-Credit-Usage: 本次请求消耗的积分
  • WSA-Current-Credit-Usage: 本次请求后累计消耗的积分

每次调用时都要记录这两个积分标头。上面的 Python 客户端已经将所有 WSA- 头部信息都已收集到其 usage 字典中。经过一周的运行,这些日志将告诉你哪些目标消耗最多,以及你需要选择哪种套餐。关于同一任务的存储和验证部分,请参阅《构建你的第一个可靠数据管道》。

如何异步运行耗时较长或速度较慢的抓取任务?

使用 async=true 发送请求。API 会将抓取任务放入队列,并立即返回一个 response_id。随后,调用相同的 /v2 端点,将 api_key 和 snapshot_id 设置为该 ID,以获取已完成的页面。应采用延迟递增的方式进行轮询,而非保持连接打开,并将 ID 与任务一同存储。

同步请求会保持连接打开,直到页面返回,最长可达 120 秒。对于少数几个 URL 来说这没问题,但对于速度较慢的目标、大型批次,或者无法为每个请求等待两分钟的 worker 来说,这会很棘手。异步处理将调用拆分为提交和收集两个阶段:

"""Queue a long scrape with async=true, then poll by snapshot_id.

Needs WSA_API_KEY. Without it, prints the two request shapes (key redacted)."""
import os
import time
import requests

ENDPOINT = "https://api.webscrapingapi.com/v2"
key = os.environ.get("WSA_API_KEY")
target = "https://example.com/"

if not key:
    for p in ({"api_key": "REDACTED", "url": target, "async": "true"},
              {"api_key": "REDACTED", "snapshot_id": "<response_id>"}):
        print(requests.Request("GET", ENDPOINT, params=p).prepare().url)
    raise SystemExit("dry run: set WSA_API_KEY to run live")

job = requests.get(ENDPOINT, params={"api_key": key, "url": target, "async": "true"}, timeout=30).json()
snapshot_id = job["response_id"]
for delay in (2, 4, 8, 16, 30, 30):
    time.sleep(delay)
    r = requests.get(ENDPOINT, params={"api_key": key, "snapshot_id": snapshot_id}, timeout=60)
    if r.ok and r.text.lstrip().startswith("<"):
        print(r.text[:200])
        break
    print("not ready:", r.status_code, r.text[:120])

如果没有密钥,脚本会打印出它将发送的两个请求结构:

https://api.webscrapingapi.com/v2?api_key=REDACTED&url=https%3A%2F%2Fexample.com%2F&async=true
https://api.webscrapingapi.com/v2?api_key=REDACTED&snapshot_id=%3Cresponse_id%3E
dry run: set WSA_API_KEY to run live

轮询循环的设计刻意采取了保守策略。它在每次尝试之间会进行退避,并将任何非完成的 HTML 页面都视为“尚未就绪”。在生产环境中,请在 response_id ,这样即使工作进程重启也不会丢失已完成的结果。请及时收集快照,不要假设它们会无限期等待。

您的代码应如何处理 Scraper API 错误和速率限制?

将 400、401、403 和 406 视为需要修正请求或账户的问题,而非重试的理由。 遇到 429、500 和 503 状态码时,应暂缓并重试。422 状态码表示 API 接受了请求但无法完成抓取(例如,挑战页面返回了 HTTP 200 状态码),因此应先尝试一次渲染重试,再考虑放弃。

表 2:状态码及相应处理措施

状态码

含义

操作

400

请求或参数无效,或无法抓取该 URL

修正请求;不要盲目重试

401

缺少或无效的密钥,或信用额度已用尽

检查环境变量并规划使用方案

403

该套餐不包含所请求的功能

请检查套餐功能

406

不支持的响应格式

修正输出选项

422

已接受但未完成(例如,以 HTTP 200 状态码返回的验证页面)

使用 render_js=1,然后记录

429

请求过多,或并行请求过多

进行退避;将并发数限制在计划范围内

500 / 503

服务器错误或正在维护

请稍后重试

Python 客户端中的 scrape() Python 客户端中的该函数实现了此表。最终错误(400、401、403、406)会立即停止并返回响应文本,因为再次发送相同的请求将导致同样的失败。 可重试的错误(429、500、503)将采用指数退避策略进行等待,上限为 30 秒,最多尝试四次。未渲染请求返回 422 错误时,将触发一次带渲染的重试。 若 422 错误持续存在,则会像其他可重试错误一样进行退避,直到尝试次数用尽。

401 错误需要仔细检查,因为它涵盖了两种不同的问题。缺少密钥会返回此错误:

{"error":"Access denied. No key `api_key` provided."}

相同的代码也意味着您的账户信用额度已用尽,因此在轮换密钥前请检查套餐使用情况。

对于 429 错误,解决方法通常在于并发控制,而非速度优化。 各套餐允许的并发请求数在 5 到 500 之间,因此请根据套餐规格调整工作线程池的大小,而不是任其增长直到 API 返回错误。MDN 的 429 状态码参考文档详细介绍了该状态码本身。如果目标在多次重试后仍持续失败,请先尝试解决这些阻塞问题,再考虑更换工具。

文档:响应与错误。

Scraper API 请求的费用是多少?

套餐按每月请求量划分:入门版 19 美元(7,000 次)、基础版 49 美元(20,000 次)、标准版 99 美元(45,000 次), 高级版 149 美元(100,000 次)、尊享版 249 美元(180,000 次)以及专业版 599 美元(450,000 次)。试用版包含 5,000 次请求。无论哪种套餐,JavaScript 渲染和地理定位均不额外消耗积分。

表 3:Scraper API 套餐(价格截至 2026 年 10 月)

套餐

每月价格

请求量

并发请求数

≈ 每1,000次请求

试用

0 美元

5,000

5

—

入门版

19美元

7,000

20

2.71美元

基础版

49美元

20,000

20

2.45美元

标准

99美元

45,000

50

2.20美元

高级

149美元

100,000

100

1.49美元

高级版

249美元

180,000

200

1.38美元

专业版

599美元

450,000

500

1.33

从入门版升级到专业版后,每1,000次请求的实际成本大约降低了一半,因此在选择套餐时,请求量比功能更重要。并发量同样重要:如果您的处理管道需要同时处理100个请求,那么无论月请求量多少,这都会成为一个下限。

要了解实际支出情况,请参考 API 报告的每次调用成本:即 cost JSON 封装中的字段或 WSA-Call-Credit-Usage 标头中的“每调用成本”。将您实际的目标组合数据输入试用版进行测试,记录相关数值,并换算成月度成本。这比任何估算都更能准确判断哪种套餐更适合您。

您应该使用 Scraper API、Browser API、SERP API、Crawl API 还是 Data API?

当您拥有 URL 且需要获取页面内容时,请使用 Scraper API;当页面需要等待、点击、输入或截图时,请使用 Browser API;使用 SERP API 获取搜索结果,使用 Crawl API 获取有限的相关页面集合,使用 Data API 获取来自受支持来源的结构化记录。

WebScrapingAPI 提供五种 Web 访问 API。它们之间的功能重叠程度比名称所暗示的要小,因为每种 API 的输入源各不相同。

表 4:哪种 Web 访问 API 适合当前任务

API

您发送

您将获得

何时选择

定价模式

抓取API

URL + 选项

页面正文、JSON 或字段

您已知 URL

按月计费的请求套餐

浏览器 API

URL + 等待和操作

渲染后的 HTML、JSON、字段、截图

页面需要交互或处于特定状态

报价

搜索结果页面(SERP)API

查询 + 搜索引擎及市场背景

结构化搜索结果

您从一次搜索开始

月度套餐(Google 起价 28 美元)

抓取 API

来源范围

有限的页面集合

一个答案涵盖相关页面

引用

数据 API

针对受支持源的目标请求

结构化记录

116个受支持数据源中有一个符合条件

亚马逊套餐起价59美元;其他来源需另行报价

请按顺序回答以下问题,并在首次回答“是”时停止:

  1. 您是从搜索查询开始的吗?请使用 SERP API。它会按搜索引擎、位置和语言返回结构化结果,因此您无需自行解析结果页面。
  2. 您的数据源是否属于 Data API 支持的数据源之一,且您需要的是记录而非页面?请使用 Data API。它涵盖了 116 个受支持的公开数据源,且 WebScrapingAPI 负责维护相应的解析器。
  3. 您需要一组预定义的相关页面,而不是一次处理一个 URL 吗?请使用 Crawl API 进行有限范围的多页收集。
  4. 页面是否需要等待、点击、输入、导航步骤或截图?请使用 Browser API,该 API 会在 REST 请求背后运行一个托管浏览器。
  5. 否则,请使用 Scraper API, render_js=1 仅当纯 HTML 中缺少您的数据时才使用。

这些 API 还可以很好地组合使用。一种常见的工作流程是:先使用 SERP API 发现搜索结果 URL,再由您的应用程序筛选出相关结果,最后通过 Scraper API 或 Browser API 抓取这些页面。Browser API 和 Crawl API 的定价需另行报价。 截至 2026 年 10 月,Scraper API、SERP API 以及 Data API 的 Amazon 源均已发布月度套餐。

如需查看完整的对比表(包括各 API 的具体功能以及哪些功能由您的应用程序处理),请对比所有五种 Web Access API。

Web MCP 如何将 AI 代理连接到 Scraper API?

Web MCP 是一个托管在 https://mcp.webscrapingapi.com/mcp 上的模型上下文协议(MCP)服务器。您的代理通过专属的 MCP 令牌进行连接,并通过 Scraper API 或 Browser API 获取 web_read 和 web_read_more,同时通过 SERP API 获取 web_search。 它使用您现有的套餐和配额,且令牌不会增加额外配额。

Web MCP 并非需要单独购买的产品。它是构建在您账户现有 API 访问权限之上的连接层,无需安装任何软件包,也无需运行服务器。

您需要什么。您需要一个具备 SERP API、Web Scraping API 或 Browser API 访问权限的 WebScrapingAPI 账户。您还需要一个支持 Streamable HTTP 传输且带有自定义 Authorization: Bearer 标头,该令牌方案定义于 RFC 6750 中。仅支持 OAuth 登录的客户端无法连接。

设置。作为账户所有者,请在仪表盘中打开 MCP,复制服务器 URL,然后选择“创建令牌”。该令牌仅显示一次,因此请立即将其保存到客户端的密钥存储或密码管理器中。每个账户最多可拥有十个活跃令牌,若令牌丢失,应立即撤销并重新生成。 您的产品 API 密钥并非 MCP 令牌,也不应出现在连接设置中。接受 mcpServers 对象的客户端,其配置应如下所示:

{
  "mcpServers": {
    "webscrapingapi": {
      "url": "https://mcp.webscrapingapi.com/mcp",
      "headers": { "Authorization": "Bearer ${WSA_MCP_TOKEN}" }
    }
  }
}

请将占位符替换为您在“仪表盘 → MCP”中获取的 MCP 令牌。部分客户端从环境变量中读取该值,而其他客户端则设有令牌字段,具体插值语法因客户端而异。请将客户端的工具超时时间设置为 60 秒以上。

表 5:Web MCP 工具

工具

是否

需求

用法

web_search

Google自然搜索结果(1–10,默认5)

SERP API

SERP API 配额

web_read

一个以 Markdown 格式呈现的公开页面

抓取 API 或浏览器 API

Scraper API 配额(默认)或 Browser API 带宽

web_read_more

同一抓取任务的下一块数据

与 web_read

不发起新请求,不产生额外费用

您的客户端仅能发现您账户支持的工具。若无 SERP API 访问权限,代理仍可读取您提供的 URL,但无法进行搜索。

路由与计费。当您的账户启用了 Scraper API 时,读取操作将使用该 API;否则将使用 Browser API,高级调用者可通过 backend 参数选择其中一种。Scraper API 的读取操作消耗信用额度,默认采用静态配额。当代理设置 render_js: true时,即视为一次新的计量请求。Browser API 的读取操作始终会渲染页面并消耗带宽。请求失败或配额耗尽时,系统绝不会切换至其他产品。

限制。每个工具结果的大小上限为 16 KiB,因此长页面会分块返回,您可通过 web_read_more。HTML 大小超过 10 MiB 的页面将返回错误,且数据捕获最长持续一小时。上游超时时间为 55 秒。每个令牌允许四次并发工具调用和四个活跃会话,会话在空闲 15 分钟、一小时或达到 1,000 次请求后结束。

如需了解代理如何使用实时网页数据的整体情况,请参阅这些代理式网页访问模式。

文档:《Web MCP 入门指南》和《使用与错误》。

Scraper API 有哪些限制?在什么情况下它不适合使用?

Scraper API 每次请求仅抓取一个页面,因此无法为您执行点击、输入、滚动或登录等操作。 请求超时时间为120秒,并发数受套餐限制,且城市或ASN覆盖范围各不相同。它无法使页面符合合法采集条件。当这些限制成为瓶颈时,请选择其他API或工具。

  • 不支持交互操作。每次请求仅返回一个页面。等待选择器、点击、输入、滚动和截图等功能属于 Browser API 的范畴。
  • 不支持搜索。若需获取搜索引擎结果,请使用 SERP API 而非抓取结果页面。
  • 每次仅处理一个 URL。如果任务涉及一组定义好的相关页面,请使用 Crawl API 来管理该范围。
  • 120 秒超时。针对响应缓慢的目标和大型批次,应通过异步请求处理。
  • 并发数取决于套餐。从试用版的 5 个并行请求到 Pro 版的 500 个。请相应调整您的工作进程数量。
  • 地域覆盖范围因计划而异。国家层面的覆盖范围较广,但城市和 ASN 的可用性取决于计划、地理位置和路由。
  • 具有挑战性的页面仍可能失败。API 会为您处理访问问题,但某些目标页面仍可能返回 422 错误。该 API 不保证能破解验证码,因此请做好应对最难目标页面偶尔失败的准备。

此外,还有一条任何 API 都无法免除的责任界限。您需自行定义哪些来源符合条件以及数据用途。请仅将本服务用于您有权抓取的公开页面,仔细阅读每个网站的条款,遵守个人数据规则,并尊重机器人政策。

若想了解能让爬虫在数月内保持良好运行状态的习惯,请参阅这些附带可运行代码的生命周期最佳实践。正在考虑 WebScrapingAPI 以外的其他选项?我们的《其他供应商爬取 API 对比指南》涵盖了更广泛的领域。

常见问题

如何使用爬虫 API?

向 API 端点发送 HTTP 请求,其中包含您的 API 密钥和目标 URL,然后从响应中读取页面内容。 使用 WebScrapingAPI 时,需向 https://api.webscrapingapi.com/v2 发送一个包含 api_key 和 URL 编码的 URL 的 GET 请求。仅当目标页面需要时,才添加渲染、位置或提取参数。

如何在 Python 中使用网页抓取 API?

安装 Requests 模块,将 API 密钥设置为环境变量,并在 params 参数中传入 api_key 和 URL,以便 Requests 进行 URL 编码。检查状态码,对 429 和 5xx 响应采用退避策略重试,并使用您的 HTML 解析器解析正文,或通过 extract_rules 向 API 请求 JSON 数据。

WebScrapingAPI 爬虫 API 提供免费试用吗?

有。Scraper API 试用版包含 5,000 次请求,起步完全免费,支持 5 个并发请求。试用版已包含 JavaScript 渲染和国家/地区级定位功能,因此您可以在选择付费套餐前测试关键目标。付费套餐起价为每月 19 美元,包含 7,000 次请求。

JavaScript 渲染是否需要额外消耗积分?

不会。在 Scraper API 上,无论哪种套餐,render_js=1 和地理定位均不会消耗额外积分。不过,渲染速度比普通 fetch 慢,因此请仅对初始 HTML 中缺少数据的页面启用此功能。 请检查 WSA-Call-Credit-Usage 标头,以确认每次调用消耗了多少积分。

什么是网页数据提取 API?

这是一种为您抓取网页,并以可用格式返回其内容或选定字段的服务。服务商负责运行代理、重试机制和浏览器,您只需提供 URL 和相关选项。WebScrapingAPI 的 Scraper API 可返回 HTML、JSON 封装数据,或通过 CSS 或 XPath 提取规则选定的字段。

可以从任何网站抓取数据吗?

从技术上讲,许多公开页面都可以抓取,但并非所有页面都应被抓取。部分内容需要登录才能访问,某些页面需要浏览器交互,且网站条款或个人数据法律可能会限制数据收集。对于允许收集的公开页面,请使用 Scraper API;当页面需要交互时,请使用 Browser API。

如何从没有 API 的网站获取数据?

您可以自行抓取页面并提取字段,或者让爬取 API 代为抓取。使用 Scraper API 时,只需将页面 URL 与 extract_rules(将字段名称映射到 CSS 选择器)一并发送,即可获得 JSON 格式返回结果。建议先在页面的保存副本上预览选择器,以避免浪费请求。

WebScrapingAPI 能破解 CAPTCHA 验证码吗?

Scraper API 负责代理路由、轮换和访问策略,但不保证能破解 CAPTCHA。如果目标返回带有 HTTP 200 状态码的验证页面,API 会将此次抓取归类为 422 失败,而非向您传递错误内容。请尝试一次渲染重试,然后检查目标页面。

将 AI 代理与 Web MCP 连接需要什么?

您需要一个具备 Scraper API、Browser API 或 SERP API 访问权限的 WebScrapingAPI 账户、从仪表盘获取的专用 MCP 令牌,以及一个支持带 bearer 头部的 Streamable HTTP 的 MCP 客户端。 将客户端指向 https://mcp.webscrapingapi.com/mcp。仅支持 OAuth 登录的客户端无法连接。

Web MCP 会消耗我的 Scraper API 配额吗?

是的,当它通过 Scraper API 读取网页时会消耗配额。每次 web_read 操作都算作一次常规计费请求,而带渲染的重试则算作另一次请求。web_read_more 操作会继续进行相同的抓取,且不产生额外费用。搜索操作会消耗您的 SERP API 配额,而创建 MCP 令牌不会增加配额。

使用 5,000 次免费请求开始抓取

Scraper API 会在单次请求背后处理代理、轮换、重试和 JavaScript 渲染。渲染和地理定位不消耗额外配额,失败的请求也不会计费。当试用期帮助您明确需求后,请选择合适的套餐。

正在搭建 AI 代理?添加 SERP API,让 Web MCP 既能搜索又能读取内容。其试用版包含 100 次 Google 搜索。

关于作者

Robert Munceanu, 全栈开发工程师 @ WebScrapingAPI

Robert Munceanu

全栈开发工程师

罗伯特·蒙塞阿努(Robert Munceanu)是 WebScrapingAPI 的全栈开发工程师,他在产品各领域均有贡献,并协助构建了支持该平台的可靠工具和功能。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。