开场白
做过爬虫项目的人大概都清楚,真正让人头疼的不是“发不出请求”,而是“发出了请求,却总觉得数据不稳定”。要么页面结构一夜之间改了,要么代理池突然死了一半,要么调度跑着跑着开始疯狂触发验证码。
这篇文章我想换个角度,把爬虫开发看成“侦探破案”。案件是什么?——拿到目标数据。线索是什么?——页面规则、代理策略、调试记录。本文会拆解我的一些思路,并给出代码示例(含代理配置),最后画一份“技术关系图谱”,方便大家把全局串起来。
关键数据指标
做侦探要先锁定嫌疑人,爬虫也是一样。你得先想清楚,哪些指标最能反映任务的健康度。
- 成功率:HTTP 200 占比。结构改动、代理失效都能在这里反映出来。
- 响应时间分布:p50、p90、p99。能看出慢页面和瓶颈点。
- 防刷触发率:验证码、封禁页面的比例。这个直接和策略挂钩。
- 代理可用率:哪个 IP 好用,哪个 IP 在拖后腿。
- 数据质量:字段缺失率。很多时候页面请求都成功了,但字段提取错了。
- 资源消耗:带宽和 CPU,方便评估要不要限流。
这些指标就像是“现场勘探”的证据,越早收集越好。
规则设计:别一头撞南墙
设计解析规则其实特别像“问案子证人”。有些证人很靠谱(比如 API),有些证人爱胡说八道(比如临时 className)。
我的经验是分三层:
- 优先 API 或 meta 标签:这些接口或标签很少会乱改。
- 再用 DOM 路径:尽量选稳定的 id 或 class,不要指望绝对路径。
- 兜底正则 / 模糊匹配:就像口供不准时的补充。
另外别忘了:
- 用 显式等待 替代固定的
sleep(5)。等节点出来再走,才算靠谱。 - 给字段设置两套提取方法,互相校验。就像有两个证人,防止口供跑偏。
- 抽样人工检查。哪怕每天抽 0.1%,也能防止全量数据悄悄坏掉。
代理策略:穿好“伪装衣”
代理就是我们的“伪装衣”。穿得好,能在人群里消失;穿得差,一下就被盯出来。
关键点:
- 用支持用户名/密码认证的代理池(比如爬虫代理)。
- 定期给代理打分(延迟、失败率、匿名度),别一股脑乱用。
- 不同任务用不同池子:大文件任务用带宽型,敏感页面用高匿名型。
- 控制 IP 使用频率,别让同一个 IP 疯狂轰炸一个站点。
示例代码
示例一:requests + 代理
# requests_proxy_example.py
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# ====== 代理配置(参考:亿牛云示例www.16yun.cn) ======
PROXY_HOST = "proxy.16yun.cn"
PROXY_PORT = 3100
PROXY_USER = "16YUN"
PROXY_PASS = "16IP"
proxy_url = f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
proxies = {
"http": proxy_url,
"https": proxy_url
}
# 配置重试逻辑
session = requests.Session()
retries = Retry(total=3, backoff_factor=0.5,
status_forcelist=[429, 500, 502, 503, 504])
session.mount("http://", HTTPAdapter(max_retries=retries))
session.mount("https://", HTTPAdapter(max_retries=retries))
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36"
}
def fetch_json(url):
try:
r = session.get(url, headers=headers, proxies=proxies, timeout=15)
r.raise_for_status()
return r.json()
except Exception as e:
print("请求失败:", e)
return None
if __name__ == "__main__":
print(fetch_json("https://httpbin.org/get"))
示例二:Playwright + 代理
# playwright_proxy_example.py
from playwright.sync_api import sync_playwright
# ====== 代理配置(参考:亿牛云示例www.16yun.cn) ======
PROXY_HOST = "proxy.16yun.cn"
PROXY_PORT = 3100
PROXY_USER = "16YUN"
PROXY_PASS = "16IP"
proxy_server = f"http://{PROXY_HOST}:{PROXY_PORT}"
def fetch_page(url):
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={"server": proxy_server, "username": PROXY_USER, "password": PROXY_PASS},
headless=True
)
context = browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
)
page = context.new_page()
try:
page.goto(url, wait_until="domcontentloaded")
page.wait_for_selector("article, .post, #content", timeout=8000)
return page.content()
except Exception as e:
print("访问失败:", e)
return None
finally:
context.close()
browser.close()
if __name__ == "__main__":
html = fetch_page("https://example.com")
print("页面长度:", len(html) if html else "None")
演进思路:记录版本
- v1.0:单线程,硬编码 UA,无代理。
- v1.1:加重试,加日志,支持代理认证。
- v2.0:引入代理池,多进程或异步抓取,采样校验。
- v3.0:自适应调度(根据失败率动态调整),加监控报警。
(这里建议用 Mermaid 图或者画图软件,做一份演进关系图,团队讨论时很好用。)
技术关系图谱(简化版)
采集调度 → 抓取引擎 → (代理池 + 解析器) → 数据清洗 → 存储
↓
监控与QA
调试建议
- 最小化失败用例:把失败请求的 URL、headers、返回体前 1k 字保存下来。
- 先用 curl/httpie 复现:比代码调试快,能立刻看出是不是代理问题。
- 代理诊断:定期访问 httpbin,验证 IP、UA 是否生效。
- 降级机制:失败率高时降低并发,切换备用代理池。
- 回归测试:每天小规模跑一遍,检测解析是否跑偏。
小结
采集方案重点:
- 目标数据 ;
- 抓取规则 ;
- 代理池 ;
- 调试记录 。
只要你把指标盯紧、规则分层、代理养好、日志打全,哪怕目标网站天天变脸,你也能比较从容地应对。