从入门到精通的10个必学秘笈
目录导读
- 爬虫脚本的核心原则与反爬进化
- 异步IO与协程加速
- 智能请求头与指纹生成
- 动态渲染页面处理方案
- 分布式代理池实战构建
- 分布式爬虫调度与去重
- 数据清洗的脚本过滤器
- 容错与重试机制的优雅实现
- 爬虫健康监控与通知
- 布隆过滤器与增量爬取
- 脚本化部署与无服务器化
- Q&A:常见爬虫脚本问题解答
爬虫脚本的核心原则与反爬进化
在当今数据驱动的商业环境中,爬虫脚本已不再是简单的requests.get()加BeautifulSoup组合,主流平台(如淘宝、微博、知乎)的反爬手段已进化到浏览器指纹识别、hCaptcha、动态token验证等层面。实用爬虫脚本的核心在于“伪装成真实用户”——不仅要在请求层面模仿,更要在行为层面实现人机差异倍速。

问答:为什么有些爬虫脚本一开始能跑,过几分钟就被封?
答:绝大多数情况是因为缺乏“请求间隔随机化”和“IP池轮换”,静态间隔会被模式识别系统轻易检测,而单一IP高频请求在反爬日志中无所遁形,建议在脚本中采用指数退避+随机抖动(exponential backoff with jitter)的访问策略。
技巧一:异步IO与协程加速
关键库:aiohttp + asyncio + uvloop
同步爬虫在处理大量请求时,I/O等待占用了90%以上的时间,通过协程,你可以用一个线程同时发起成百上千个请求。
import asyncio
import aiohttp
async def fetch(session, url):
async with session.get(url) as resp:
return await resp.text()
async def main():
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, f'https://api.example.com/page/{i}') for i in range(100)]
results = await asyncio.gather(*tasks)
return results
进阶技巧:结合asyncio.Semaphore控制并发数,避免对目标造成洪水攻击。
避坑:aiohttp默认不处理Cookies自动管理,需手动启用cookie_jar。
技巧二:智能请求头与指纹生成
反爬识别请求的第一道防线是标头一致性,现代爬虫脚本需要动态生成“类浏览器”的User-Agent、Accept-Language、Sec-Fetch-*等标头。
实用方案:
- 使用
fake_useragent库随机轮换UA。 - 使用
tls_client或curl_cffi模拟浏览器的TLS握手指纹。curl_cffi的requests模式支持自动保持Chrome或Safari指纹。
from curl_cffi import requests
response = requests.get('https://httpbin.org/headers', impersonate='chrome110')
问答:只换UA够用吗?
答:完全不够,现代反爬还会检查Accept-Encoding、Connection、Sec-CH-UA等20+个标头的组合逻辑,而TLS指纹更是关键,建议直接用curl_cffi或playwright的stealth模式。
技巧三:动态渲染页面处理方案
大量前端经过React/Vue/Angular渲染,数据通过XHR或WebSocket加载,静态请求无法获取。三个主流方案:
- Selenium + undetected-chromedriver:模拟真实浏览器,但资源占用高。
- Playwright(推荐):支持自动等待、框架隔离、网络拦截,结合
playwright-stealth插件可绕过基础指纹检测。 - Pyppeteer:轻量级,适合容器化部署。
核心脚本技巧:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
viewport={'width': 1920, 'height': 1080}
)
page = context.new_page()
page.goto('https://example.com')
# 等待动态内容加载
page.wait_for_selector('.data-container')
html = page.content()
踩坑提醒:许多网站通过navigator.webdriver属性检测浏览器自动化,Playwright需注入脚本删除该属性。
技巧四:分布式代理池实战构建
IP封禁后如何存活?→ 维护一个自动测试、计分、轮换的代理池。
脚本核心逻辑:
- 采集免费代理(如proxylist+、geonode)或付费API。
- 使用异步HTTPS校验(带超时3秒)。
- 按成功率、响应速度、匿名等级打分。
- 每隔30秒爬虫从代理池池中取一个分数最高的代理。
伪代码:
import redis
r = redis.Redis()
# 每2分钟测试所有代理
def test_proxy(proxy):
try:
resp = requests.get('http://httpbin.org/ip', proxies={'http': proxy}, timeout=3)
score = resp.elapsed.total_seconds()
r.zadd('proxy_pool', {proxy: score})
except:
r.zrem('proxy_pool', proxy)
技巧五:分布式爬虫调度与去重
面对百万级URL,单机无法胜任。Scrapy-Redis是业界标准方案:
- 使用Redis作为调度队列和去重集合(基于指纹)。
- 多个爬虫Worker实例从Redis拉取任务,互持锁防止重复。
脚本改进:
# scrapy中配置 SCHEDULER = "scrapy_redis.scheduler.Scheduler" DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 去重使用sha1指纹,比URL哈希更准确
问答:去重只靠URL哈希行吗?
答:不行,相同内容可能通过不同URL展示(参数变化、锚点),更稳妥的做法是内容指纹去重:下载页面后提取正文MD5,或使用simhash判断相似度。
技巧六:数据清洗的脚本过滤器
原始数据脏乱差,清洗脚本需在爬虫流程中内嵌。
实用技巧:
- 结构归一化:将时间格式统一为ISO 8601。
- 错误标注:对特殊空值标记
__NA__而非删除。 - 编码修复:自动检测chardet库识别GBK/Shift_JIS编码。
- 正则提取模式:使用
re.compile预编译,提高速度。 - 数据验证钩子:如检查邮件格式、邮编范围、价格数值。
import re
def clean_price(raw):
num = re.sub(r'[^\d\.]', '', raw) # 去除¥, $等符号
return float(num) if num else None
技巧七:容错与重试机制的优雅实现
网络波动是常态,利用tenacity库实现指数退避重试,避免代码臃肿。
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=2, min=1, max=10))
def fetch_with_retry(url):
return requests.get(url, timeout=5)
高级技巧:
- 分层重试:网络错误(0级)重试3次;状态码403(1级)换代理重试;503(2级)休息30秒。
- 断路器模式:连续失败超过5次,暂停该任务队列60秒。
技巧八:爬虫健康监控与通知
长时间运行的任务必须配备“心跳检测”。脚本集成方案:
- 使用
loguru结构化日志记录每个任务耗时、错误码、数据量。 - 通过
pushbullet或Telegram Bot发送告警(如连续错误>10次)。 - 将监控数据推送到
Prometheus + Grafana(推荐prometheus_client库)。
from prometheus_client import Counter, Histogram, start_http_server
requests_total = Counter('http_requests_total', 'Total requests', ['status'])
# 启动metrics端点
start_http_server(8000)
技巧九:布隆过滤器与增量爬取
全量爬取成本极高,增量爬取依赖高效的已访问记录数据结构。布隆过滤器比set节省80%以上内存。
pybloom_live库实现:
from pybloom_live import BloomFilter
bf = BloomFilter(capacity=1000000, error_rate=0.001) # 百万容量,0.1%误报
bf.add('https://example.com/page/123')
if 'https://example.com/page/123' in bf:
print('已爬取')
技巧:配合Redis实现分布式布隆(django-bloom或pyreBloom)。
技巧十:脚本化部署与无服务器化
最后一步:将爬虫脚本打包为可重复执行、弹性伸缩的服务。
- Docker化:多阶段构建,基础镜像用
python:3.11-slim,体积≤150MB。 - Kubernetes CronJob:每天定时触发增量爬取。
- Serverless:AWS Lambda爬短周期任务,使用
Layer层存放依赖,冷启动时间控制在300ms内。 - 触发托管:使用GitHub Actions或Airflow编排依赖任务。
Q&A:常见爬虫脚本问题解答
Q1:爬虫脚本被人举报了怎么办?
A:首先停止对目标域的请求,检查robots.txt是否存在爬取限制,如果是公开数据且未造成性能影响,可联系对方开放API或申请许可。
Q2:脚本在Crontab中无法执行Playwright?
A:大部分因为缺少系统依赖,安装playwright install-deps和xvfb-run来模拟显示环境。
Q3:如何保护爬虫脚本不被反编译?
A:使用pyarmor或Nuitka将脚本编译为.c文件,同时将敏感配置加密存储到环境变量或KMS(如AWS Secrets Manager)。
Q4:大规模爬虫时CPU利用率忽高忽低?
A:合理配置协程并发数,结合asyncio.get_event_loop().set_default_executor(ThreadPoolExecutor(10))实现CPU密集操作与I/O并行。
Q5:爬虫脚本如何平衡ROI?
A:评估数据价值,如果目标网站有API,优先用API;如果API昂贵,再编写脚本,同时计算爬取成本(IP、代理、计算资源)与收益之比。
本文基于实际爬虫工程经验整理,结合requests-html、Scrapy、Playwright等工具链,并参考了HackerNews及Stack Overflow上的社区实践,如想深入探讨特定反爬对抗技法,欢迎在评论区留言。