手把手教你用Python+Selenium+Requests搞定有声小说下载,附完整防封禁策略
·
Python+Selenium+Requests实战:构建高稳定性有声小说下载系统
最近在技术社区看到不少关于音频资源抓取的讨论,许多开发者反馈现有的爬虫方案要么容易被封禁,要么效率低下。作为一个长期研究反爬策略的开发者,我想分享一套经过实战检验的解决方案。这套系统不仅适用于有声小说平台,经过简单调整也能用于其他音频、视频资源的抓取。
1. 为什么你的爬虫总被封?
每次运行爬虫脚本就像在玩猫鼠游戏——网站管理员不断升级防御措施,而我们则要找到更隐蔽的访问方式。理解封禁机制是设计反制策略的第一步。
常见封禁触发点:
- User-Agent检测:使用默认或单一UA会被轻易识别
- IP频率限制:单位时间内来自同一IP的请求过多
- 行为模式识别:固定时间间隔的请求、完整页面加载等非人类操作特征
- 验证码挑战:当系统检测到可疑行为时触发
# 典型的高风险爬虫特征示例
headers = {
'User-Agent': 'python-requests/2.26.0' # 使用库默认UA
}
for page in range(1, 101):
response = requests.get(f'https://example.com/page/{page}', headers=headers)
time.sleep(1) # 固定间隔
表格:常见防御手段及对应特征
| 防御类型 | 检测指标 | 典型应对方案 |
|---|---|---|
| UA检测 | 请求头中的User-Agent | 动态UA轮换 |
| IP限制 | 单个IP请求频率 | 代理IP池 |
| 行为分析 | 点击模式、停留时间 | 随机延迟+鼠标移动 |
| 验证码 | 特定响应状态码 | 自动识别或人工干预 |
2. 动态身份伪装系统
要让爬虫看起来像真实用户,我们需要构建完整的身份伪装层。这不仅仅是换个User-Agent那么简单,而是创建一套动态身份系统。
2.1 智能UA轮换方案
fake_useragent库虽然方便,但在高频率请求时会出现性能问题。我们可以自己维护一个UA池:
USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...',
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_4 like Mac OS X) AppleWebKit/605.1.15...',
'Mozilla/5.0 (Linux; Android 12; SM-S901U) AppleWebKit/537.36...'
]
def get_random_ua():
return random.choice(USER_AGENTS)
进阶技巧:根据目标网站的主流用户群体调整UA分布。例如,有声小说网站可能移动端访问量更大,应该增加移动端UA的权重。
2.2 请求头全面伪装
完整的请求头应该包含这些关键字段:
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Referer': 'https://www.google.com/', # 模拟从搜索引擎进入
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1'
}
提示:定期更新你的请求头模板,可以使用浏览器开发者工具抓取最新版本
3. 代理IP系统的工程化实现
单一代理很容易失效,我们需要构建一个具备自愈能力的代理池系统。
3.1 代理源管理与验证
class ProxyPool:
def __init__(self):
self.proxies = []
self.last_update = 0
def refresh(self):
# 从多个免费代理源获取IP
sources = [
'https://www.sslproxies.org/',
'https://free-proxy-list.net/',
'https://www.us-proxy.org/'
]
new_proxies = []
for url in sources:
try:
response = requests.get(url, timeout=10)
# 解析HTML获取代理IP和端口
# ...解析逻辑省略...
new_proxies.extend(found_proxies)
except Exception as e:
print(f"Failed to fetch from {url}: {str(e)}")
# 验证代理可用性
self.proxies = self._validate_proxies(new_proxies)
self.last_update = time.time()
def _validate_proxies(self, proxies):
valid_proxies = []
with ThreadPoolExecutor(max_workers=20) as executor:
futures = {
executor.submit(
self._check_proxy,
proxy
): proxy for proxy in proxies[:100] # 测试前100个
}
for future in as_completed(futures):
if future.result():
valid_proxies.append(futures[future])
return valid_proxies
def _check_proxy(self, proxy):
try:
response = requests.get(
'https://httpbin.org/ip',
proxies={'http': proxy, 'https': proxy},
timeout=5
)
return response.status_code == 200
except:
return False
def get_proxy(self):
if not self.proxies or time.time() - self.last_update > 3600:
self.refresh()
return random.choice(self.proxies) if self.proxies else None
3.2 代理在Selenium中的集成
from selenium.webdriver import Proxy
from selenium.webdriver.common.proxy import ProxyType
def get_webdriver_with_proxy(proxy_str):
proxy = Proxy({
'proxyType': ProxyType.MANUAL,
'httpProxy': proxy_str,
'sslProxy': proxy_str
})
options = EdgeOptions()
options.use_chromium = True
options.add_argument('--headless')
options.add_argument('--disable-gpu')
capabilities = webdriver.DesiredCapabilities.EDGE
proxy.add_to_capabilities(capabilities)
driver = webdriver.Edge(
executable_path='msedgedriver.exe',
options=options,
capabilities=capabilities
)
return driver
4. 人类行为模拟的高级策略
简单的随机延迟已经不足以应对现代反爬系统,我们需要更精细的行为模拟。
4.1 智能请求间隔系统
class RequestThrottler:
def __init__(self):
self.last_request_time = 0
self.base_delay = 3 # 基础延迟秒数
self.random_factor = 0.5 # 随机波动范围
def wait(self):
current_time = time.time()
elapsed = current_time - self.last_request_time
if elapsed < self.base_delay:
# 计算需要等待的时间
delay = self.base_delay - elapsed
# 添加随机性
delay *= 1 + (random.random() * 2 - 1) * self.random_factor
time.sleep(max(0, delay))
self.last_request_time = time.time()
4.2 页面交互模拟
def human_like_interaction(driver, element):
"""模拟人类点击行为"""
# 随机移动轨迹
action = webdriver.ActionChains(driver)
# 移动到元素附近
action.move_to_element_with_offset(element, random.randint(-10, 10), random.randint(-10, 10))
action.pause(random.uniform(0.1, 0.3))
# 可能的小范围移动
for _ in range(random.randint(1, 3)):
action.move_by_offset(
random.randint(-5, 5),
random.randint(-5, 5)
)
action.pause(random.uniform(0.05, 0.15))
# 点击
action.click()
action.perform()
5. 完整系统架构与异常处理
将上述组件整合为一个健壮的下载系统,需要考虑各种边界情况和异常处理。
5.1 状态监控与自适应调整
class DownloadMonitor:
def __init__(self):
self.error_count = 0
self.success_count = 0
self.last_error_time = 0
def record_error(self):
self.error_count += 1
self.last_error_time = time.time()
# 错误率过高时触发防御机制
if self.error_count > 3 and self.error_count / (self.success_count + 1) > 0.5:
self._activate_cool_down()
def _activate_cool_down(self):
cool_down = min(3600, 60 * (2 ** self.error_count)) # 指数退避
print(f"触发冷却机制,暂停{cool_down}秒")
time.sleep(cool_down)
def record_success(self):
self.success_count += 1
if self.success_count > 10:
# 重置错误计数
self.error_count = max(0, self.error_count - 5)
self.success_count = 0
5.2 断点续传与日志系统
def load_progress(book_id):
try:
with open(f'progress_{book_id}.json', 'r') as f:
return json.load(f)
except FileNotFoundError:
return {'downloaded': [], 'last_page': 0}
def save_progress(book_id, progress):
with open(f'progress_{book_id}.json', 'w') as f:
json.dump(progress, f)
def setup_logger():
logger = logging.getLogger('audio_downloader')
logger.setLevel(logging.INFO)
# 文件handler
file_handler = logging.FileHandler('download.log')
file_handler.setFormatter(logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s'
))
# 控制台handler
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(
'%(levelname)s: %(message)s'
))
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger
在实际项目中,这套系统成功实现了对多个音频平台的高效稳定抓取。最关键的体会是:反爬策略不是一劳永逸的,需要持续观察和调整。建议每天检查日志,分析失败原因,不断优化各个模块的参数和策略。
更多推荐


所有评论(0)