前言
目标站点依靠请求头 UA 字段识别爬虫特征,固定单一 User-Agent 高频请求极易触发访问限制、验证码拦截、IP 封禁。UA 池通过海量浏览器标识随机轮换,搭配 Cookie、Referer、Accept 等请求头字段动态组装,破除单一请求指纹特征;结合移动端 / PC 端 UA 分类、权重随机分发、异常 UA 自动剔除,从请求报文层面伪装成真实客户端访问。本章实现可复用 UA 池模块,无缝对接前面单机爬虫、定时爬虫、分布式爬虫、代理 IP 池项目,配套完整封装代码与反指纹优化方案。
本文所需依赖官方文档超链接:
- Requests 官方文档
- Random 内置库
- Redis-Py 官方文档
一、UA 反爬原理与指纹风控逻辑
1.1 UA 风控判定规则
- 长期固定同一个 UA 高频请求,服务器标记为爬虫特征;
- UA 标识为爬虫引擎(python-requests/xxx)直接拦截;
- 请求头字段残缺(无 Accept、Accept-Language),报文指纹异常被拦截。
1.2 请求头指纹组成要素对照表
表格
| 字段 | 作用 | 反爬影响 |
|---|---|---|
| User-Agent | 客户端浏览器标识 | 核心风控字段,爬虫首要伪装点 |
| Referer | 来源页面地址 | 缺失易被判定直接恶意访问 |
| Accept | 可接收资源类型 | 缺失导致请求头残缺指纹异常 |
| Accept-Language | 系统语言标识 | 完善头部伪装真实浏览器 |
1.3 UA 池设计思路
- 分类存储 PC 端、移动端、平板三类 UA,按需定向取用;
- 内置权重机制,主流高版本浏览器 UA 分配更高随机概率;
- 支持 Redis 持久化 UA 池,动态新增、淘汰失效 UA;
- 封装统一获取请求头函数,一行代码获取完整 headers 字典。
二、环境依赖
bash
运行
pip install redis==5.0.8 requests==2.31.0三、基础本地 UA 静态池(轻量化小型爬虫)
3.1 UA 数据源分类封装
python
运行
import random # PC端Chrome、Edge、Firefox高版本UA PC_UA_LIST = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.2420.97", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", ] # 移动端安卓、IOS UA MOBILE_UA_LIST = [ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15", "Mozilla/5.0 (Linux; Android 14; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36", ] # 权重配置:PC权重8,移动端权重2 UA_WEIGHT_MAP = { "pc":8, "mobile":2 }3.2 随机取 UA + 自动组装完整请求头
python
运行
def get_random_ua(ua_type:str = "auto") -> str: """ ua_type: pc/mobile/auto auto按权重随机选择设备类型 """ if ua_type == "pc": return random.choice(PC_UA_LIST) elif ua_type == "mobile": return random.choice(MOBILE_UA_LIST) else: # 权重随机选型 total = UA_WEIGHT_MAP["pc"] + UA_WEIGHT_MAP["mobile"] rand_num = random.randint(1,total) if rand_num <= UA_WEIGHT_MAP["pc"]: return random.choice(PC_UA_LIST) else: return random.choice(MOBILE_UA_LIST) def get_random_headers(refer:str = None, ua_type:str="auto") -> dict: """生成随机完整请求头""" ua = get_random_ua(ua_type) headers = { "User-Agent":ua, "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8", "Accept-Encoding":"gzip, deflate, br", "Connection":"keep-alive", "Upgrade-Insecure-Requests":"1" } if refer: headers["Referer"] = refer return headers3.3 爬虫调用示例
python
运行
import requests def demo_crawl(url): headers = get_random_headers(refer=url) resp = requests.get(url,headers=headers,timeout=8) return resp.status_code if __name__ == "__main__": print(demo_crawl("https://httpbin.org/headers"))四、Redis 分布式 UA 池(适配分布式爬虫)
多节点爬虫共享 UA 资源,支持动态新增 UA、标记失效 UA,统一由 Redis 集合存储可用 UA。
python
运行
import redis # redis初始化 REDIS_CFG = {"host":"127.0.0.1","port":6379,"db":0,"decode_responses":True} rds = redis.Redis(**REDIS_CFG) PC_UA_KEY = "ua:pool:pc" MOBILE_UA_KEY = "ua:pool:mobile" # 初始化导入默认UA(仅首次运行执行) def init_ua_to_redis(): rds.sadd(PC_UA_KEY,*PC_UA_LIST) rds.sadd(MOBILE_UA_KEY,*MOBILE_UA_LIST) # 随机从Redis取UA def get_redis_ua(ua_type="auto"): if ua_type == "pc": return rds.srandmember(PC_UA_KEY) elif ua_type == "mobile": return rds.srandmember(MOBILE_UA_KEY) else: # 权重随机 if random.randint(1,10) <=8: return rds.srandmember(PC_UA_KEY) else: return rds.srandmember(MOBILE_UA_KEY) # 动态新增UA到池 def add_new_ua(ua_str:str,ua_type="pc"): if ua_type == "pc": rds.sadd(PC_UA_KEY,ua_str) else: rds.sadd(MOBILE_UA_KEY,ua_str) # 标记失效UA并移除 def remove_bad_ua(ua_str:str,ua_type="pc"): if ua_type == "pc": rds.srem(PC_UA_KEY,ua_str) else: rds.srem(MOBILE_UA_KEY,ua_str)五、高级反指纹优化:请求头随机微调
固定请求头字段顺序、固定参数内容会形成固定指纹,对部分字段值做小范围随机。
python
运行
def get_rand_finger_header(refer=None): ua = get_random_ua() # 随机Accept编码顺序 enc_list = ["gzip, deflate, br","gzip, deflate","br,gzip,deflate"] rand_enc = random.choice(enc_list) header = { "User-Agent":ua, "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language":random.choice(["zh-CN,zh;q=0.9","zh-CN,zh;q=0.8,en;q=0.7"]), "Accept-Encoding":rand_enc, "Connection":"keep-alive", "Upgrade-Insecure-Requests":"1" } if refer: header["Referer"] = refer return header六、搭配代理 IP 池联合使用
同时轮换 UA + 代理 IP,双重打散请求指纹,大幅降低封禁概率:
python
运行
# 对接代理池接口 PROXY_API = "http://127.0.0.1:5010/get_proxy" FAIL_PROXY_API = "http://127.0.0.1:5010/proxy_fail/" def crawl_proxy_ua(target_url): # 获取随机代理 proxy_res = requests.get(PROXY_API).json() if proxy_res["code"] !=200: return None proxy = proxy_res["proxy"] proxies = {"http":f"http://{proxy}","https":f"http://{proxy}"} # 随机请求头 headers = get_rand_finger_header(refer=target_url) try: res = requests.get(target_url,headers=headers,proxies=proxies,timeout=7) return res.text except Exception: requests.get(f"{FAIL_PROXY_API}{proxy}") return None七、定时扩充 UA 数据源(APScheduler 自动更新 UA 池)
python
运行
from apscheduler.schedulers.background import BackgroundScheduler def auto_add_ua(): """模拟定时从UA数据源接口拉取新UA入库Redis""" new_pc_ua = ["Mozilla/5.0 (Windows NT 11.0; Win64; x64) Chrome/125.0.0.0 Safari/537.36"] for ua in new_pc_ua: add_new_ua(ua,"pc") print("新增UA完成") def start_ua_update_task(): sch = BackgroundScheduler() sch.add_job(auto_add_ua,"interval",hours=6,id="ua_auto_update") sch.start() if __name__ == "__main__": start_ua_update_task()八、常见问题与优化表
表格
| 异常现象 | 优化方案 |
|---|---|
| 部分 UA 请求直接 403 | 调用 remove_bad_ua 剔除失效 UA,补充新版本浏览器 UA |
| 仍被站点识别爬虫 | 增加 Cookie 随机池、间隔随机 sleep (0.5~2) |
| 分布式多节点 UA 重复率高 | 统一接入 Redis UA 池,全局共用数据源 |
| 请求头固定特征 | 启用字段随机微调函数,打乱 Accept、Accept-Language 参数 |