1. 从requests爬虫到IP被封:一个爬虫工程师的必经之路
做爬虫的朋友们应该都经历过这个阶段——刚开始用requests库写爬虫时,感觉整个世界都是你的。简单几行代码就能抓取数据,requests.get()就像一把万能钥匙。直到某天,你突然发现自己的IP被目标网站封禁了,返回的是那个令人绝望的429状态码(Too Many Requests)。这就像你拿着同一把钥匙反复开别人家的门,终于被保安发现并列入黑名单。
我刚开始做爬虫时也踩过这个坑。记得第一次遇到IP被封是在抓取一个电商网站的商品数据时,连续运行了几个小时的爬虫突然开始返回429错误。当时完全不明白发生了什么,还以为是自己的代码出了问题。后来才知道,这是网站的反爬机制在起作用——当你的请求频率超过网站设定的阈值时,就会触发防护。
2. 为什么requests爬虫容易被封IP?
2.1 请求特征过于明显
requests库发送的HTTP请求头部信息非常"干净",缺少普通浏览器访问时会携带的许多特征。比如:
- User-Agent通常是python-requests/版本号
- 缺少Accept-Encoding、Accept-Language等常见浏览器头
- 没有Referer信息
- 不会携带cookies
这种"过于规范"的请求特征,对于网站的反爬系统来说就像黑夜中的灯塔一样显眼。我曾经用Wireshark抓包对比过requests和真实浏览器的请求差异,结果令人震惊——requests的请求头只有不到10个字段,而Chrome浏览器则有20多个。
2.2 缺乏请求间隔控制
新手常犯的错误是直接用for循环连续发送请求,没有任何间隔:
python复制for page in range(1, 100):
url = f"https://example.com/products?page={page}"
response = requests.get(url)
# 处理数据...
这种高频请求模式会迅速触发网站的速率限制。根据我的经验,大多数商业网站对单个IP的请求频率限制在每分钟30-60次左右,学术类网站可能更严格。
2.3 没有处理动态反爬机制
现代网站的反爬手段越来越复杂,常见的有:
- Cloudflare的5秒盾(需要等待JS验证)
- 基于用户行为的检测(鼠标移动、点击模式等)
- 指纹识别(Canvas指纹、WebGL指纹等)
- WebRTC泄露真实IP(即使使用代理)
requests作为一个简单的HTTP库,无法处理这些动态的反爬机制。我曾经遇到过即使降低了请求频率,仍然被封锁的情况,后来发现是因为网站使用了Canvas指纹识别。
3. 突破IP封锁的实战方案
3.1 基础防护:伪装请求头
最简单的防护措施是完善请求头,至少应该包含:
python复制headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Referer': 'https://www.google.com/'
}
重要提示:User-Agent不要固定使用一个,最好准备一个列表随机切换。我维护了一个包含200多个常见浏览器User-Agent的文本文件,每次请求随机选择一个。
3.2 核心策略:使用代理IP池
当基础伪装仍然无法避免封锁时,就需要使用代理IP了。代理IP的类型和特点:
| 代理类型 | 匿名度 | 速度 | 稳定性 | 成本 | 适用场景 |
|---|---|---|---|---|---|
| 透明代理 | 低 | 快 | 高 | 低 | 简单数据采集 |
| 匿名代理 | 中 | 中 | 中 | 中 | 中等防护网站 |
| 高匿代理 | 高 | 慢 | 低 | 高 | 严格反爬网站 |
| 数据中心代理 | 高 | 快 | 高 | 高 | 商业级爬虫 |
| 住宅代理 | 最高 | 慢 | 低 | 最高 | 模拟真实用户 |
我个人的代理IP实施方案:
- 免费代理测试阶段(适合学习和小规模采集):
python复制import requests
from bs4 import BeautifulSoup
def get_free_proxies():
url = "https://free-proxy-list.net/"
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
proxies = []
for row in soup.select("table.table tbody tr"):
cells = row.find_all('td')
if cells[6].text == 'yes': # 只选择HTTPS代理
proxy = f"{cells[0].text}:{cells[1].text}"
proxies.append(proxy)
return proxies
- 商业代理服务集成(适合生产环境):
python复制import random
PROXY_POOL = [
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
# ...更多代理
]
def get_with_proxy(url):
proxy = {'http': random.choice(PROXY_POOL), 'https': random.choice(PROXY_POOL)}
try:
response = requests.get(url, headers=headers, proxies=proxy, timeout=10)
return response
except Exception as e:
print(f"代理 {proxy} 失败: {str(e)}")
return None
3.3 高级技巧:请求节奏控制
即使使用代理,也需要控制请求节奏。我常用的几种模式:
- 固定延迟(最简单):
python复制import time
for url in urls:
response = get_with_proxy(url)
time.sleep(random.uniform(1, 3)) # 随机等待1-3秒
- 自适应延迟(根据网站响应动态调整):
python复制def adaptive_delay(last_response_time):
base = max(1, last_response_time * 2) # 至少1秒,是上次响应时间的2倍
return base + random.uniform(0, 2) # 加上0-2秒随机值
- 工作日/周末不同策略(模拟人类行为):
python复制import datetime
def get_delay_strategy():
now = datetime.datetime.now()
if now.weekday() in [5, 6]: # 周末
return random.uniform(3, 8) # 周末访问间隔长
else:
if 9 <= now.hour < 18: # 工作时间
return random.uniform(1, 3)
else: # 晚上
return random.uniform(2, 5)
3.4 处理Cloudflare防护
对于使用Cloudflare的网站,简单的requests很难绕过。这时可以考虑:
- 使用cloudscraper库:
python复制import cloudscraper
scraper = cloudscraper.create_scraper()
response = scraper.get("https://protected-site.com")
- 或者使用undetected-chromedriver模拟真实浏览器:
python复制import undetected_chromedriver as uc
driver = uc.Chrome()
driver.get("https://protected-site.com")
html = driver.page_source
4. 我的爬虫架构演进之路
4.1 第一阶段:纯requests脚本
python复制# 新手时期的简单爬虫
import requests
urls = [f"https://example.com/page/{i}" for i in range(1, 101)]
for url in urls:
response = requests.get(url)
# 解析数据...
问题:IP很快被封,没有错误处理,无法恢复中断的任务。
4.2 第二阶段:加入基础防护
python复制# 加入User-Agent轮换和简单代理
import random
import time
USER_AGENTS = [...] # 多个User-Agent
PROXIES = [...] # 多个代理IP
def safe_get(url):
headers = {'User-Agent': random.choice(USER_AGENTS)}
proxy = {'http': random.choice(PROXIES)} if PROXIES else None
try:
response = requests.get(url, headers=headers, proxies=proxy, timeout=10)
time.sleep(random.uniform(1, 3))
return response
except Exception as e:
print(f"请求失败: {str(e)}")
return None
改进:延长了被封的时间,但仍然不够健壮。
4.3 第三阶段:完整爬虫框架
python复制# 使用Scrapy框架的完整实现
import scrapy
from scrapy.crawler import CrawlerProcess
class MySpider(scrapy.Spider):
name = 'my_spider'
custom_settings = {
'DOWNLOAD_DELAY': 2,
'CONCURRENT_REQUESTS_PER_DOMAIN': 2,
'RETRY_TIMES': 3,
'ROTATING_PROXY_LIST': [...], # 代理列表
'USER_AGENT_ROTATION': True,
}
def start_requests(self):
urls = [...] # 初始URL列表
for url in urls:
yield scrapy.Request(url, callback=self.parse)
def parse(self, response):
# 解析逻辑...
pass
process = CrawlerProcess()
process.crawl(MySpider)
process.start()
优势:内置重试机制、自动代理轮换、并发控制等企业级功能。
4.4 第四阶段:分布式爬虫系统
对于大规模数据采集,我最终转向了分布式架构:
- 使用Redis作为任务队列
- 多个爬虫节点从队列获取任务
- 集中管理代理IP池
- 分布式去重
- 监控和报警系统
python复制# 分布式爬虫工作节点示例
import redis
import requests
from multiprocessing import Process
r = redis.Redis(host='redis-server', port=6379)
def worker():
while True:
url = r.lpop('task_queue')
if url:
try:
proxy = get_proxy_from_pool() # 从代理池获取
response = requests.get(url, proxies=proxy)
process_data(response.text)
mark_success(proxy) # 标记代理可用
except Exception as e:
mark_failed(proxy) # 标记代理失败
r.rpush('task_queue', url) # 重新放回队列
# 启动多个工作进程
for _ in range(4):
Process(target=worker).start()
5. 爬虫工程师的防封checklist
根据我的经验,每次写爬虫前都应该检查以下事项:
-
请求头是否完整:
- 是否有随机的User-Agent
- 是否设置了Referer(可以模拟从Google跳转)
- 是否有合理的Accept-*头
-
请求频率是否合理:
- 单IP请求间隔是否大于2秒
- 是否模拟了人类的不规律访问模式
- 是否考虑了目标网站的高峰时段
-
代理管理是否完善:
- 是否有足够的代理IP备用
- 是否有代理质量监控机制
- 是否定期更换代理IP
-
错误处理是否健壮:
- 是否有429/403状态码的重试机制
- 是否有连接超时的处理
- 是否有任务中断后的恢复能力
-
是否触发高级反爬:
- 检查browserleaks.com看是否有WebRTC泄露
- 测试Canvas指纹是否唯一
- 验证是否有JavaScript挑战
我在实际项目中总结出一个规律:越是商业价值高的数据,网站的反爬措施就越严格。有时候,与其花大量时间破解反爬,不如考虑通过官方API获取数据,或者与网站合作。毕竟,爬虫只是获取数据的手段之一,而不是唯一途径。
