第一章:当你的爬虫第一次“阵亡”
还记得那个阳光明媚的下午,你写了个自认为完美无缺的爬虫,信心满满地按下F5。前几分钟,数据哗啦啦地来,你感觉自己就是数据世界的神。然后...突然之间,世界安静了。连接超时、403错误、甚至收到一封来自网站管理员的“亲切问候”。
此时的你,是不是很像那个刚充了游戏会员就被封号的倒霉蛋?
别问我是怎么知道的,每个爬虫工程师的成长路上,都躺着无数个被禁的IP地址。但好消息是,被封IP就像程序员掉头发一样,虽然无法完全避免,但绝对可以延缓!
第二章:为什么网站要跟你过不去?
在开始我们的“反封禁大业”之前,先来个灵魂拷问:网站为什么这么小气?
想象一下,你开了一家网红奶茶店,突然来了个大哥,不仅每秒买一杯,还拿着尺子量你的柜台,用秒表计时的速度。你是不是也得考虑叫保安?
网站服务器也是同理。过多的突发请求会占用大量带宽,影响正常用户访问,严重的甚至会导致服务器宕机。所以,它们设置了一系列防御措施:
- 频率检测:看你是否像打了鸡血一样疯狂请求
- User-Agent检查:识别你是不是“正规军”
- 行为模式分析:你的操作是否太像机器人
- 验证码:经典的“你是人吗”测试
- IP封禁:终极大招——直接拉黑
理解了对方的防御策略,我们就能见招拆招了。
第三章:基础伪装术——给你的爬虫“整容”
3.1 User-Agent:别再顶着“Python”出门了
新手最常犯的错误就是直接用默认的User-Agent。这就好比在脑门上贴个纸条:“我是爬虫,快来封我!”
# 反面教材——自杀式写法
import requests
response = requests.get('http://example.com')
# 正面教材——低调行事
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'
}
response = requests.get('http://example.com', headers=headers)
更高级的做法是准备一个User-Agent列表,每次随机选择:
import random
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36',
]
headers = {'User-Agent': random.choice(user_agents)}
3.2 Referer设置:假装是从正经地方来的
Referer告诉服务器你从哪个页面跳转过来的。设置一个合理的Referer,就像进小区时对保安说“我是3号楼王阿姨的侄子”一样自然。
headers = {
'User-Agent': random.choice(user_agents),
'Referer': 'https://www.google.com/' # 假装是从谷歌搜索来的
}
第四章:控制节奏——爬虫不是百米冲刺
4.1 时间间隔:别把服务器当仇人
有些朋友写爬虫时,总觉得时间延迟是懦夫的行为。结果就是:你的IP成功进入了网站的“黑名单名人堂”。
import time
import random
# 基础版——固定间隔
time.sleep(1) # 休息1秒
# 进阶版——随机间隔,更像真人
time.sleep(random.uniform(0.5, 3)) # 随机休息0.5-3秒
# 高级版——根据页面大小动态调整
def smart_delay(response):
content_length = len(response.content)
# 内容越大,休息越久
delay = min(10, max(1, content_length / 1024)) # 至少1秒,最多10秒
time.sleep(delay)
4.2 请求速率限制:使用令牌桶算法
对于需要长时间运行的爬虫,可以使用令牌桶算法来控制整体速率:
import time
class RateLimiter:
def __init__(self, rate, period):
self.rate = rate # 允许的请求数
self.period = period # 时间周期(秒)
self.tokens = rate
self.last_refill = time.time()
def acquire(self):
now = time.time()
time_passed = now - self.last_refill
new_tokens = time_passed * (self.rate / self.period)
if new_tokens > 0:
self.tokens = min(self.rate, self.tokens + new_tokens)
self.last_refill = now
if self.tokens >= 1:
self.tokens -= 1
return True
else:
time_to_wait = (1 - self.tokens) * (self.period / self.rate)
time.sleep(time_to_wait)
self.tokens = 0
self.last_refill = time.time()
return True
第五章:IP代理池——你的“分身大军”
当基础伪装已经不够用时,是时候祭出大杀器——IP代理池了。
5.1 免费代理 vs 付费代理
免费代理就像路边试吃——不要钱,但可能吃不饱甚至拉肚子。适合练手,但不适合正经项目。
付费代理则是米其林餐厅——质量有保障,但得花钱。根据需求选择:
- 数据量小、频率低:轮拨代理(每次连接换IP)
- 数据量大、需要稳定:独享代理(一个IP只用给你)
- 需要高匿名:住宅代理(最像真实用户)
5.2 构建简单的代理池
class ProxyPool:
def __init__(self):
self.proxies = []
self.current_index = 0
def add_proxy(self, proxy):
self.proxies.append(proxy)
def get_proxy(self):
if not self.proxies:
return None
proxy = self.proxies[self.current_index]
self.current_index = (self.current_index + 1) % len(self.proxies)
return proxy
def remove_proxy(self, proxy):
if proxy in self.proxies:
self.proxies.remove(proxy)
if self.current_index >= len(self.proxies):
self.current_index = 0
# 使用示例
proxy_pool = ProxyPool()
proxy_pool.add_proxy({'http': 'http://proxy1:port'})
proxy_pool.add_proxy({'http': 'http://proxy2:port'})
proxy = proxy_pool.get_proxy()
response = requests.get('http://example.com', proxies=proxy)
5.3 代理质量检测
不是所有代理都能用,需要定期检测:
def check_proxy(proxy, timeout=5):
try:
response = requests.get('http://httpbin.org/ip',
proxies=proxy, timeout=timeout)
return response.status_code == 200
except:
return False
第六章:高级隐身技巧——做个“社交牛逼症”爬虫
6.1 会话保持:别每次都重新自我介绍
使用Session可以保持Cookies,让你的多次请求看起来是同一个用户在操作:
session = requests.Session()
# 第一次访问,获取cookies
session.get('http://example.com/login')
# 后续请求会自动携带cookies
session.get('http://example.com/dashboard')
6.2 处理JavaScript渲染的页面
有些网站大量使用JavaScript,简单的requests无法获取数据。这时候需要请出我们的“浏览器模拟器”:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--headless') # 无界面模式
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(options=options)
driver.get('http://example.com')
# 等待页面加载完成
driver.implicitly_wait(10)
# 获取渲染后的页面源码
html = driver.page_source
driver.quit()
6.3 模拟鼠标移动和滚动
真正的人类不会直来直去,我们的爬虫也不能:
from selenium.webdriver.common.action_chains import ActionChains
driver.get('http://example.com')
# 模拟鼠标移动
actions = ActionChains(driver)
actions.move_by_offset(100, 100).perform()
# 模拟滚动
driver.execute_script("window.scrollTo(0, document.body.scrollHeight/2);")
time.sleep(1)
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
第七章:应对突发状况——当封禁还是来了
即使做了万全准备,有时还是会碰到封禁。这时候需要有完善的异常处理:
7.1 识别封禁信号
def is_blocked(response):
blocked_indicators = [
response.status_code in [403, 429],
'access denied' in response.text.lower(),
'bot detected' in response.text.lower(),
'验证码' in response.text,
]
return any(blocked_indicators)
7.2 自动应对策略
def smart_request(url, session, proxy_pool, retry_count=3):
for attempt in range(retry_count):
try:
proxy = proxy_pool.get_proxy()
response = session.get(url, proxies=proxy, timeout=10)
if is_blocked(response):
print(f"IP可能被封锁,尝试更换代理...")
proxy_pool.remove_proxy(proxy)
continue
return response
except Exception as e:
print(f"请求失败: {e}")
if proxy in proxy_pool.proxies:
proxy_pool.remove_proxy(proxy)
print("所有重试次数已用尽")
return None
第八章:道德与法律——爬虫工程师的自我修养
在结束之前,我们必须严肃地讨论一下爬虫的伦理问题。
8.1 robots.txt:网站的“交通规则”
每个网站根目录下的robots.txt文件规定了哪些内容可以爬取,哪些禁止访问:
import requests
from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.set_url('https://example.com/robots.txt')
rp.read()
if rp.can_fetch('*', 'https://example.com/private-data'):
# 可以爬取
pass
else:
# 禁止爬取,尊重规则
print("这个页面不允许爬取,跳过")
8.2 遵循的基本原则
- 尊重网站资源:控制访问频率,不影响网站正常运行
- 遵守版权法律:不随意传播或商用获取的数据
- 保护用户隐私:不收集、不存储个人敏感信息
- 明确使用目的:仅用于学习和研究,避免商业竞争
第九章:实战演练——搭建一个健壮的爬虫系统
让我们把所有技巧整合到一个实际的例子中:
import requests
import time
import random
from typing import List, Optional
class RobustSpider:
def __init__(self, delay_range=(1, 3)):
self.session = requests.Session()
self.delay_range = delay_range
self.setup_headers()
def setup_headers(self):
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
})
def random_delay(self):
time.sleep(random.uniform(*self.delay_range))
def request_with_retry(self, url, max_retries=3):
for attempt in range(max_retries):
try:
self.random_delay()
response = self.session.get(url, timeout=10)
if response.status_code == 200:
return response
elif response.status_code == 429: # 请求过于频繁
wait_time = 2 ** attempt # 指数退避
print(f"触发频率限制,等待{wait_time}秒后重试")
time.sleep(wait_time)
else:
print(f"请求失败,状态码: {response.status_code}")
except Exception as e:
print(f"请求异常: {e}")
return None
# 使用示例
spider = RobustSpider(delay_range=(1, 5))
response = spider.request_with_retry('http://example.com/data')
if response:
# 处理成功的响应
data = response.text
else:
# 处理失败情况
print("数据获取失败")
结语:爬虫生存之道
写一个能跑的爬虫很简单,但写一个能长期稳定运行的爬虫却是一门艺术。记住,我们的目标不是成为最快的爬虫,而是成为活得最久的爬虫。
就像玩生存游戏一样,有时候需要勇往直前,有时候需要潜伏等待,有时候需要改头换面。掌握了这些技巧,你的爬虫就不再是那个刚出新手村就被秒的菜鸟,而是能够深入敌后、获取关键情报的特种兵。
最后送给大家一句爬虫界的至理名言:慢就是快,活着才能输出。 祝大家的爬虫都能长命百岁,数据拿到手软!
附:紧急情况处理清单 ✅
- IP被封?立即切换代理
- 遇到验证码?考虑使用验证码识别服务或手动处理
- 频率过高?增加延迟时间,使用指数退避算法
- User-Agent被识别?更新更真实的User-Agent列表
- 需要登录?维护会话状态,合理管理Cookies
希望这篇指南能让你在爬虫的道路上越走越远,越爬越稳!
Python爬虫防封指南

被折叠的 条评论
为什么被折叠?



