5.1 表单提交与模拟登录


文档摘要

5.1 表单提交与模拟登录 本节摘要:登录的本质是一段请求序列:取表单页、回填字段与隐藏令牌、提交、验证会话。框架的 FormRequest 能自动回填页面里已有的表单字段,配合作业舱携带凭证,即可完成绝大多数账号密码登录。本节给出完整的登录 Spider 模式与三条常见变体。 第 4 章你把 Cookie 会话的机制装进了中间件,本节是它的第一个实战应用:用一段请求序列换取登录态。登录流程写不好,多半不是代码问题,而是没把"序列"拆对——先看清楚浏览器在这件事上做了什么。 先拆解:浏览器登录到底发生了什么 打开开发者工具的网络面板,手动登录一次,观察请求序列。一个典型站点是这样的: 四步里有三处新手常踩的暗礁:csrftoken 是每次会话动态生成的,写死必失败;

5.1 表单提交与模拟登录

本节摘要:登录的本质是一段请求序列:取表单页、回填字段与隐藏令牌、提交、验证会话。框架的 FormRequest 能自动回填页面里已有的表单字段,配合作业舱携带凭证,即可完成绝大多数账号密码登录。本节给出完整的登录 Spider 模式与三条常见变体。

第 4 章你把 Cookie 会话的机制装进了中间件,本节是它的第一个实战应用:用一段请求序列换取登录态。登录流程写不好,多半不是代码问题,而是没把"序列"拆对——先看清楚浏览器在这件事上做了什么。

先拆解:浏览器登录到底发生了什么

打开开发者工具的网络面板,手动登录一次,观察请求序列。一个典型站点是这样的:

1. GET /login → 表单页,含隐藏字段 csrf_token 2. POST /login → 提交 用户名 密码 csrf_token 3. 302 → /dashboard → 重定向到登录后首页,Set-Cookie 下发会话 4. GET /dashboard → 带 Cookie 的后续请求,登录态生效

四步里有三处新手常踩的暗礁:csrf_token 是每次会话动态生成的,写死必失败;重定向是服务端指挥的,客户端要跟随;会话凭证全靠 Cookie 传递,Cookie 中间件的状态管理必须在线。框架对这三处都有现成解法。

主线解法:FormRequest 链

import scrapy from scrapy import FormRequest class MemberSpider(scrapy.Spider): name = "member" login_url = "https://members.example.com/login" start_urls = [login_url] def parse(self, response): # 第一步在表单页:from_response 自动回填页面已有字段(含隐藏令牌) yield FormRequest.from_response( response, formcss="form#login", # 多表单页面要指明哪张表单 formdata={"username": "demo", "password": "secret"}, # 只补业务字段,令牌自动带上 callback=self.after_login, ) def after_login(self, response): # 第三步的落点:验证登录是否成功,别急着拆数据 if b"welcome" not in response.body: self.logger.error("登录失败,停止旅程") return yield scrapy.Request("https://members.example.com/dashboard", callback=self.parse_dashboard) def parse_dashboard(self, response): # 第四步起,所有请求自动携带会话 Cookie for row in response.css("div.record"): yield {"name": row.css("h4::text").get(default="").strip()}

from_response 的价值在"自动回填":页面里已有的输入项(含隐藏的 csrf 令牌)原样带入,你只补充用户名密码两个字段。令牌轮换、字段改名这类站点侧的调整,绝大多数被它天然吸收——这比手工从 HTML 里抠令牌稳一个量级。

运行日志验证登录链路:

DEBUG: Crawled (200) members.example.com/login DEBUG: Crawled (302) members.example.com/login → members.example.com/dashboard DEBUG: Crawled (200) members.example.com/dashboard 'item_scraped_count': 48

302 出现且后续 200 正常拆出数据,会话链路即通。若 302 后又回到登录页,凭证或令牌有问题——回到网络面板对照浏览器多做了哪一步。

变体一:纯接口登录

站点若提供 JSON 登录接口,跳过表单页直取令牌更省一次请求。用 shell 先探接口的请求体格式,再在 Spider 里构造:

def start_requests(self): yield scrapy.Request(self.login_url, callback=self.get_token) def get_token(self, response): # 令牌可能藏在页面 meta 标签或某个接口返回里,按站点实际情况取 token = response.css("meta[name=csrf-token]::attr(content)").get() yield FormRequest( "https://members.example.com/api/login", formdata={"user": "demo", "pass": "secret", "csrf": token}, callback=self.after_login, )

变体二:凭证外置与验证码边界

账号密码写死在代码里是事故源。工程做法是凭证从环境变量或配置服务读入,Spider 只管流程。至于验证码,给出明确的工程边界:简单图形码可用打码服务接入(回调里把验证码请求交给服务、拿回结果填表单);短信码、滑块这类强人机校验,识别成本与合规风险陡增——先评估有没有不登录的替代数据源,或向站点申请正式接口。登录会话拿到后记得按 4.2 节的会话分桶管理,多账号并行时尤其如此。

图10 登录请求序列:四步链路与两个暗礁

图10 登录请求序列:四步链路与两个暗礁

变式三:会话过期的续签

长跑任务跑不过"会话有效期":登录成功后几个小时,会话被服务端作废,后续请求全被弹回登录页。工程解法是把"过期检测与续签"做成自动环节:在蜘蛛中间件里认出"被弹回登录页"的响应,触发重新登录,再把原票重发。

class SessionRenewMiddleware: def __init__(self): self.renewing = False def process_response(self, request, response, spider): # 识别信号:本来要详情页,回来的却是登录页 if "login" in response.url and "dashboard" in request.url: if self.renewing: return response # 续签中的连带弹回,放行避免死循环 self.renewing = True spider.logger.warning("会话过期,续签后重发: %s", request.url) from scrapy import FormRequest renew = FormRequest.from_response( response, formdata={"username": spider.settings["CRED_USER"], "password": spider.settings["CRED_PASS"]}, callback=lambda r: None, meta={"renew_for": request.url}, ) self.renewing = False return request.replace(dont_filter=True) # 原票重发 return response

判据要写得窄:只认"目标明确是受限页、回来的却是登录页"这一种形态,宽了会误伤正常跳转。续签流程与 5.1 主线解法完全同构——登录写对一次,续签只是换个触发时机。

实战清单:登录工程的验收动作

登录 Spider 上线前的四个必查项:其一,凭证不在代码与日志里出现(配置注入,日志脱敏);其二,连续运行超过会话有效期的模拟测试通过(续签真的会触发);其三,登录失败的告警通路可用(5.1 里 登录失败,停止旅程 的分支要能通知到人);其四,多账号场景检查会话分桶互不串号(4.2 节的桶键核对)。登录是采集工程里对状态最敏感的部分,验收宁严勿松。

本节要点回顾

  • 登录是请求序列:取表单、回填提交、跟随重定向、带证通行,四步缺一不可;
  • from_response 是主力:隐藏令牌自动回填,比手工抠字段稳一个量级;
  • 先验证再拆数据:登录回调第一件事是确认会话生效,失败即早退;
  • 验证码有工程边界:简单码走服务,强人机校验先找替代方案。

登录墙过了,还有一堵"渲染墙":页面在浏览器里看得到、响应体里没有。下一节判断与拆解动态内容。


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