一个网络爬取 API 替你完成收集网页中最费力的部分:它挑选代理、在页面需要时运行浏览器、对失败请求进行重试,并把你要的 HTML 或数据返回给你。你发送一个带有 URL 的请求,剩下的事情都由该服务在你和目标网站之间处理。
本教程将带你从第一个请求开始,逐步构建一个小型而可靠的爬虫,以 Shifter 的Web Scraping API为例。这些步骤和模式适用于大多数同类服务。如果你想先了解请求背后发生了什么,我们关于网络爬取 API 是如何工作的的说明文章涵盖了这方面内容。
第一步:获取 API 密钥
每个请求都通过 API 密钥进行身份验证。在 Shifter 的控制面板中,打开Web Scraping API,然后进入API Keys,复制一个有效的密钥。
不要把密钥写在代码里。把它存放在环境变量中,这样它就不会出现在代码仓库或截图里:
export SHIFTER_SCRAPER_KEY="your-key-here"
第二步:发出你的第一个请求
该 API 是一个单一的端点,https://scrape.shifter.io/v1。把你的密钥和想要抓取的页面作为查询参数传递:
curl "https://scrape.shifter.io/v1?api_key=$SHIFTER_SCRAPER_KEY&url=https://example.com"
响应就是该页面的 HTML,通过该 API 的代理获取。一个基本请求所需要的就是这些。
第三步:在页面需要时开启 JavaScript 渲染
许多现代网站发送的几乎是空页面,内容是在浏览器中通过 JavaScript 构建出来的。如果你拿到的 HTML 里缺少你在浏览器中能看到的数据,加上 render_js=1。此时该 API 会在无头浏览器中加载页面,并返回渲染后的结果。在 Shifter 上,一个渲染请求的费用与普通请求相同。
有两个相关设置对慢速页面有帮助:
wait_for_css会等到某个特定元素出现后才返回,这样你就不会在数据加载完成之前拿到页面。timeout为较慢的网站提供更多时间。
渲染比普通抓取要慢,所以只在确实需要的页面上使用它。我们关于抓取大量使用 JavaScript 的网站的指南解释了如何判断。
第四步:获取 JSON 而不是 HTML
自己解析 HTML 是大多数爬虫出问题的地方。你可以改用 extract_rules 来描述你想要的字段:这是一个 JSON 对象,把每个字段名映射到一个 CSS 选择器以及要从中返回什么内容。
output | 返回内容 |
|---|---|
text | 该元素的文本 |
html | 该元素的内部 HTML |
@href、@src 等 | 某个属性的值 |
举例来说,{"title": {"selector": "h1", "output": "text"}} 对 example.com 返回 {"title": "Example Domain"}。许多页面本身就已经以结构化 JSON 形式嵌入了数据;我们关于提取 JSON-LD 而不是解析 HTML的指南展示了如何使用它。
第五步:正确处理错误
每个爬虫都会遇到失败情况:网站响应慢、页面无法渲染、同时发出的请求太多。该 API 通过状态码告诉你出了什么问题,正确的应对方式取决于你拿到的是哪个状态码:
| 状态码 | 含义 | 该怎么做 |
|---|---|---|
200 | 成功 | 使用该响应 |
400、401、403 | 你的请求、密钥或套餐功能有问题 | 修复它;重试没有用 |
408 | 目标网站超时 | 重试,也许提高 timeout |
422 | 页面渲染失败 | 重试;检查该网站在普通浏览器中是否正常工作 |
429 | 超出你套餐允许的并发请求数 | 等待后重试 |
500 | 内部错误 | 稍作延迟后重试 |
在 Shifter 上,失败的请求不收费;你只为每个成功的响应支付一个额度。该 API 在返回结果前还会在内部对部分失败进行重试,这些内部重试算作一次调用。
整合在一起
下面是一个小型 Python 辅助函数,它封装了上述全部五个步骤:它从环境变量中读取密钥,在你需要时添加渲染和提取功能,对值得重试的错误以逐渐增长的间隔进行重试,并对需要修复的错误立即停止。
import json
import os
import time
import requests
API = "https://scrape.shifter.io/v1"
RETRY = {408, 422, 429, 500} # worth another try after a pause
STOP = {400, 401, 403} # fix the request; retrying will not help
def scrape(url, render_js=False, extract_rules=None, attempts=3, session=None, wait=2.0):
"""Fetch one page through the Web Scraping API; returns text, or parsed JSON with extract_rules."""
session = session or requests.Session()
params = {"api_key": os.environ["SHIFTER_SCRAPER_KEY"], "url": url}
if render_js:
params["render_js"] = 1
if extract_rules:
params["extract_rules"] = json.dumps(extract_rules)
for attempt in range(1, attempts + 1):
response = session.get(API, params=params, timeout=120)
if response.status_code == 200:
return response.json() if extract_rules else response.text
if response.status_code in STOP or attempt == attempts or response.status_code not in RETRY:
raise RuntimeError(f"{url}: HTTP {response.status_code} {response.text[:200]}")
time.sleep(wait * attempt) # back off a little more each time
使用方法:
from scrape_api import scrape
html = scrape("https://example.com")
data = scrape("https://example.com", extract_rules={"title": {"selector": "h1", "output": "text"}})
print(data["title"])
我们针对上述每种状态码的模拟响应测试了该辅助函数的请求构建、重试和停止条件。
如果你对重试逻辑还不熟悉,我们关于网络爬虫的重试与退避策略的指南解释了为什么每次重试的间隔要逐渐增长。
第六步:扩大规模
当单个请求可以正常工作后,随着规模扩大,有三件事变得重要:
- 并发。 使用线程池或异步客户端同时发送多个请求。每个套餐都有并发限制(在 Shifter 上,根据套餐不同为 20 到 500 个并发请求);保持在限制以下,并把
429当作减速的信号。 - 地理位置与代理类型。 使用
country参数来获取某个页面在特定国家呈现的样子。在 Growth 及以上套餐中,premium_proxy会通过住宅 IP 路由请求,适用于会屏蔽数据中心流量的网站。 - 会话。 对于多步骤流程,
session_id可以在多次请求之间保持相同的 IP 和 cookie。
当数据开始流入时,把它存放在可以查询的地方。我们关于把网络爬取 API 数据导入 SQL的指南介绍了一条从响应到数据表的清晰流程。
新手常见错误
- 把 API 密钥硬编码在代码中。 使用环境变量或密钥管理工具。
- 对所有内容都开启渲染。 先尝试普通请求;只对确实需要的页面开启渲染。
- 对所有错误都重试。
401永远不会成功。只重试那些之后可能成功的错误。 - 手动解析 HTML。 在可能的情况下使用提取规则或页面内嵌的 JSON。
- 忽视并发限制。 过多的并行请求只会带来
429,而不是速度。
常见问题
什么是网络爬取 API?
这是一种服务,它通过自身的代理和浏览器替你抓取网页,处理重试和封锁问题,并针对单个请求返回 HTML 或提取出的数据。
使用网络爬取 API 需要会写代码吗?
需要一点。单个请求可以用 curl 在命令行中完成,但大多数实际项目会使用一段简短的 Python 或其他语言脚本来发送请求、处理错误并存储结果。
我应该什么时候启用 JavaScript 渲染?
当未经渲染返回的 HTML 中缺少你在浏览器中能看到的内容时。许多网站用 JavaScript 构建页面,渲染会返回浏览器所呈现的那个页面版本。
失败的请求会扣除额度吗?
在 Shifter 上不会。你只为每个成功的响应支付一个额度,失败的请求,包括来自目标网站的错误,都是免费的。
结论
使用网络爬取 API 归结为几个习惯:把密钥保存在环境变量中,先从普通请求开始,只在需要时才加上渲染,用提取规则获取 JSON 而不是自己解析 HTML,只重试那些之后可能成功的错误,并遵守你套餐的并发限制。上面的辅助函数用大约二十行代码涵盖了所有这些要点。套餐和限制详情在Web Scraping API 定价页面上。