4.2 身份与会话:UA、代理与 Cookies


文档摘要

4.2 身份与会话:UA、代理与 Cookies 本节摘要:目标站识别"访客是谁"的三个抓手是 User-Agent、来源 IP 与 Cookie。本节在中间件里实现 UA 轮换与代理接入,讲清 Cookies 的开关决策与会话保持方法——这是去程关卡最常用的三件装备,也是 5.1 节模拟登录的直接前置。 关卡里能做什么?上一节你掌握了返回值约定,本节把最常见的三件装备装上去:改身份(UA)、换线路(代理)、带通行证(Cookie)。三件事共享同一个模式:在 processrequest 里改写请求。 装备一:UA 轮换 固定 UA 是最容易被识别的特征——同一标识符以机器节奏高频出现,真实浏览器用户不会这样。

4.2 身份与会话:UA、代理与 Cookies

本节摘要:目标站识别"访客是谁"的三个抓手是 User-Agent、来源 IP 与 Cookie。本节在中间件里实现 UA 轮换与代理接入,讲清 Cookies 的开关决策与会话保持方法——这是去程关卡最常用的三件装备,也是 5.1 节模拟登录的直接前置。

关卡里能做什么?上一节你掌握了返回值约定,本节把最常见的三件装备装上去:改身份(UA)、换线路(代理)、带通行证(Cookie)。三件事共享同一个模式:在 process_request 里改写请求。

装备一:UA 轮换

固定 UA 是最容易被识别的特征——同一标识符以机器节奏高频出现,真实浏览器用户不会这样。轮换的实现在去程关卡里两行搞定:

import random class RotateUA: UA_POOL = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 Safari/17.4", "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0", ] def process_request(self, request, spider): ua = random.choice(self.UA_POOL) request.headers["User-Agent"] = ua return None @classmethod def from_crawler(cls, crawler): return cls()

注意装配数字要排在默认 UA 中间件之后(543 起步),否则改了也会被覆盖——4.1 节讲过的顺序问题,这里是它最高频的案发现场。UA 池要维护真实存在的标识串,生造的 UA 反而是显眼的异常信号。

装备二:代理接入

代理的接入点是 Request 的 meta 行李舱——框架保留的 proxy 键:

class RotateProxy: PROXIES = [ "http://10.0.4.11:7890", "http://10.0.4.12:7890", "http://10.0.4.13:7890", ] def process_request(self, request, spider): # 每张票随机挑一条线路;认证代理写全 鉴权信息加主机端口 request.meta["proxy"] = random.choice(self.PROXIES) return None

代理的工程难点不在接入而在质检:失效代理会让请求集体超时,且症状与"目标站挂了"一模一样。务必把代理健康检查做成独立逻辑——回程关卡里发现连续超时就换线路,是常见的自愈写法。代理成本不低,能用礼貌节奏(4.3 节)解决的场景,别急着上代理。

装备三:Cookies 的开关与会话

Cookie 的默认行为是启用并全站共享。要不要动它,按需求分三类:

需求 配置 说明
纯公开页面,无登录态 COOKIES_ENABLED = False 省去会话状态,减少被关联追踪的面
需要保持登录 默认开启 框架自动回存响应里的 Cookie
多账号隔离 自定义 Cookie 中间件 按规则给不同请求分配不同会话

多账号隔离是最容易写错的一类:默认的 Cookie 中间件是全局单会话,两个账号的通行证会互相覆盖。正确做法是关闭内置件、自写按账号分桶的会话管理——桶的键通常来自 Spider 在 meta 里标注的账号标识:

class SessionBuckets: def __init__(self): self.buckets = {} # 账号标识 到 会话串 的映射 def process_request(self, request, spider): account = request.meta.get("account", "default") cookie = self.buckets.get(account) if cookie: request.headers["Cookie"] = cookie return None def process_response(self, request, response, spider): new_cookie = response.headers.get("Set-Cookie") if new_cookie: account = request.meta.get("account", "default") self.buckets[account] = new_cookie.decode() return response

⚠️ 常见坑:手动设 Cookie 头时务必先关内置 Cookie 中间件(COOKIES_ENABLED = False),否则两套会话机制互相覆盖,症状是"登录成功后下一个请求又被踢回游客态"。

三件装备的协同与边界

三件装备的启用顺序有讲究:先问"能不能用礼貌节奏解决"(4.3 节),再考虑 UA 轮换,最后才上代理与多会话——装备越多,状态管理越复杂,故障面越大。把"反爬对抗"理解为概率游戏:目标站按特征打分,你的目标是让每张票的分数低,而不是每件装备都拉满。

图8 反爬特征对抗矩阵:识别维度与化解层

图8 反爬特征对抗矩阵:识别维度与化解层

实战案例:代理池的质检与自愈

背景:购入二十条代理线路,直接轮换使用后失败率反而升高。操作:把失败与线路关联统计——在回程关卡记录每条线路的连续失败数,超阈值临时摘除,冷却十分钟后再放回。

import time class ProxyHealth: def __init__(self): self.fail = {} # 线路 到 连续失败数 self.cooldown = {} # 线路 到 摘除时刻 def pick(self, pool): now = time.time() usable = [p for p in pool if now - self.cooldown.get(p, 0) > 600] return usable or pool # 全部冷却时兜底放行 def report(self, proxy, ok): if ok: self.fail[proxy] = 0 return self.fail[proxy] = self.fail.get(proxy, 0) + 1 if self.fail[proxy] >= 3: self.cooldown[proxy] = time.time() # 连败三条,摘除十分钟

结果:失败率从两成回落到百分之三以内;统计还顺带暴露了两条彻底坏死、应退租的线路。解读:代理是消耗品不是固定资产,质检逻辑让它"带病下岗、痊愈复岗",比人工盯梢便宜得多。变式:与 4.3 节的节流联动——摘除线路时顺手把延迟参数调大一档,给余下线路减负,恢复后再松开。

本节要点回顾

  • 三件装备一个模式:都在 process_request 里改写请求,回程负责回收凭证;
  • UA 轮换重顺序:装配数字排在默认件后,池子里只放真实存在的标识串;
  • 代理重在质检:失效线路的症状像站点故障,要有换线自愈逻辑;
  • Cookie 三分法:无登录就关、要登录用默认、多账号自己分桶且先关内置件。

身份与线路齐了,还差节奏。下一节把静态限速升级成自动节流——爬速学会看目标站的脸色。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U