问题情景
混淆群内的小伙伴遇到这么个问题,Mailivery 这个网站登录后,明明提交的表单(邮箱和密码也正确)、请求头等等都没问题,为啥一直重定向到登录页面呢?唉,该出手时就出手啊,我也看看咋回事吧!

登录参数分析
显而易见,需要:邮箱(有邮箱校验)、密码
打开开发者工具,随意输入邮箱和密码登录,查看登录接口的请求和方法:

请求网址:https://app.mailivery.io/login 请求方法:POST 状态代码:302 Found
302?!重定向吗?在登录不成功后重定向到了登录网站,要求重新登录。目测前后端不分离项目(阿巴阿巴),如果登录成功,肯定会携带登录成功时设置的 Cookie 重定向到主页或者相关主面板。
查看一下登录接口提交头:
POST /login HTTP/1.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br, zstd Accept-Language: zh-CN,zh;q=0.9 Cache-Control: no-cache Connection: keep-alive Content-Length: 341 Content-Type: application/x-www-form-urlencoded Cookie: XSRF-TOKEN=省略; mailivery_session=省略; ...... DNT: 1 Host: app.mailivery.io Origin: https://app.mailivery.io Pragma: no-cache Referer: https://app.mailivery.io/login Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows"
没有啥加密参数,emm……,很简单,的确是看着很简单,注意 Content-Type 是 application/x-www-form-urlencoded。
再看看提交的参数:
_token=lCsu2Ruuw33uHlHkRlKwZG3C2tw7TQBjUoTo1yjz&paid_user_Khe2xqZLA4Tkq3py=&submitted_in_seconds=eyJpdiI6IjJ4OXdFK3ZPZklKUnNadXVtRTk0L3c9PSIsInZhbHVlIjoiNHg5NzJmUjM3UnlDOU1tanlnUHpWdz09IiwibWFjIjoiMDYxYmRkODU0YmY1ZjY0MDk4OWMzNmM5YWU5MjNmZDM4NTg5NzQ1MmM3MzBjNzQ3YjYxNTg0MjliYjFjYzM3OCIsInRhZyI6IiJ9&email=xxx%40foxmail.com&password=123
格式化看一下:
_token: lCsu2Ruuw33uHlHkRlKwZG3C2tw7TQBjUoTo1yjz paid_user_Khe2xqZLA4Tkq3py: submitted_in_seconds: eyJpdiI6IjJ4OXdFK3ZPZklKUnNadXVtRTk0L3c9PSIsInZhbHVlIjoiNHg5NzJmUjM3UnlDOU1tanlnUHpWdz09IiwibWFjIjoiMDYxYmRkODU0YmY1ZjY0MDk4OWMzNmM5YWU5MjNmZDM4NTg5NzQ1MmM3MzBjNzQ3YjYxNTg0MjliYjFjYzM3OCIsInRhZyI6IiJ9 email: xxx%40foxmail.com password: 123
表单提交嘛,多多少少也是带点验证参数,比如这里的:_token、paid_user_Khe2xqZLA4Tkq3py、submitted_in_seconds。
经过多次登录提交,发现 _token 与 submitted_in_seconds 的值是变动的,而 paid_user_Khe2xqZLA4Tkq3py 变动的是 paid_user_ 之后的部分。并且这些参数与窗口的 Cookie 相关,如果不一致,将发生 416 错误,提示页面过期。看来这就是个反爬点了,还记得我说的吗,这个网站是前后端不分离,那么这些参数肯定也隐藏于表单,在元素中搜索特色浓重的 submitted_in_seconds。

非常的好,可以看到我们这三个参数的来源了,捋一捋:
访问 login 网站(GET) 得到三个参数,此时响应头所设置的 Cookie 无登录效力 对 login 网站(POST) 携带三个参数,根据结果重定向: 如果登录失败,此时响应头所设置的 Cookie 也是无登录效力的,携带 Cookie 要求咱们继续登录 如果登录成功,此时响应头所设置的 Cookie 就是具备登录效力的,携带 Cookie 直接上主页
接下来在 Python 中实现一下,创建 login.py 与 dashboard.py
login.py 实现登录的接口:
import requests url = "https://app.mailivery.io/login" _token = '' paid_user = '' submitted_in_seconds = '' email = 'xxx%40163.com' password = 'xxxxxxx' payload=f'_token={_token}&{paid_user}=&submitted_in_seconds={submitted_in_seconds}&email={email}&password={password}' headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Host': 'app.mailivery.io', 'Origin': 'https://app.mailivery.io', 'Referer': 'https://app.mailivery.io/login', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-User': '?1', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', 'Cookie': 'XSRF-TOKEN=eyJpdiI6Ikk1WjFjMFRsdVhKdzBuMjBWamNVVEE9PSIsInZhbHVlIjoiblAzS0ZBOEUyK1lXWGV0ZXhuT1Y5MldLc290d2ZzL0E3dTQxT3BDMmNGR3d6aG0vamhUekozeVFCUVVVczRJSWxxQTM1ZGpvOU5KTm11bFp4NEsvWGlObUJ6V1A3WWc1WFJXcUlPYWYzYTgrSGNMZ2VtM0s1R0tGUlJ4Z0ZMSy8iLCJtYWMiOiI2NGUxYmNhMjEwMmE3ZDNmOTc4OTcxMWVlZGY3ODIyNDZhODBiYzUxZjVhMWE2YWZkMWVhOGM2YjA4MmQzYmY0IiwidGFnIjoiIn0%3D; mailivery_session=eyJpdiI6IjVwL1QwUHNhMTlTdGUyZ0ozUzY3aGc9PSIsInZhbHVlIjoiYlNuSXI2d2tKWjMrYjFpVmx1Ym5uTEVOUGhXZjFKRGhVV1VMeXRHQ1BpRWFsV0ZnYVFkNDd4Vm9wdXY1ZElqVWpVL2xhSytNdnBDYS9NNHRBWmNzRDF4ZjJtWFhPTHFJRFBLVnNYSmFPMW9HSkEweVVpQTZZVjhJU2k5WSswR0oiLCJtYWMiOiJkYzg0ZmY2ODEwZmEyMzczNzU5NGU4YzMwYjA2MDRlZTc0ZWJiNDc4ZDBhMDU4OTgyM2E3NDMzZDM3NmRmNTcxIiwidGFnIjoiIn0%3D', 'Content-Type': 'application/x-www-form-urlencoded', 'Connection': 'keep-alive' } response = requests.request("POST", url, headers=headers, data=payload, allow_redirects=False, proxies={ 'http': None, 'https': None }) print(response.headers) print(response.status_code)
注意,我说过,Cookie 和三个参数是有紧密联系的,上述 Cookie 是通过对登录页面 GET 时得到的,三个参数同理。
三个参数的值我已省略,特别特别要注意的是:请求时,allow_redirects这个值最好设置为 False,因为重定向底层和浏览器不同,如果为 True,你会发现你的 Cookie 一直都是失效的。因为产生了如下的重定向过程:
邮箱密码正确的情况: login -> dashboard -> login 邮箱密码错误的情况: login -> login 参数未校验的情况: login -> 页面过期
为什么最终又到 login 了???
我猜测可能是反爬点,动态加载未加载好,太快了,Cookie 或者三个参数在后端都还是未准备好的状况。
我们只需要取得跳转到 dashboard 前响应头中的 Set-Cookie 的值,也就是 XSRF-TOKEN 与 mailivery_session。
因此我们停止使用 requests 的重定向。
接下来看一下 dashboard.py 这个文件,主要测试一下取得 Cookie 是否有效:
import requests url = "https://app.mailivery.io/campaign/dashboard" payload={} xsrf_token = 'xxx' mailivery_session = 'xxx' headers = { 'Cookie': f'XSRF-TOKEN={xsrf_token}; mailivery_session={mailivery_session}', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', 'Accept': '*/*', 'Host': 'app.mailivery.io', 'Connection': 'keep-alive' } response = requests.request("GET", url, headers=headers, data=payload, proxies={ 'http': None, 'https': None }) print(response.text)
全套模拟
我们最终的效果是:自动获取三个参数和初始的 Cookie,然后根据邮箱和密码登录,再携带 Cookie 访问 Dashboard。
-
自动获取这个很简单,通过 lxml 中的 etree 解析,Cookie 通过 requests 响应头得到。
-
然后去登录,也很简单,携带好参数和上一级得到的 Cookie,如果登录密码这些没错,将得到有效的 Cookie。
-
最后携带上一级得到的有效 Cookie 进行登录。
如果说你觉得到此结束了,那很抱歉,你再进行上述的实现后,将发现 Cookie 无效!!!
略带一笔,自动获取的表单参数和初始 Cookie 如果有问题,请求时将响应 416。如果没问题,你在取到后立马进行登录,请求将响应 200。200 就一定是好事吗?我已经说过了,要重定向到 dashboard 页面,那么应该是 302 才对!所以我们在取参数到登录这个请求中进行延时,等待后端缓过神,比如 3 秒?4秒?都是可以的。或者你根据响应状态码增加重试机制,尝试到出现 302 的情况。
完整代码就不提供啦,自行琢磨吧~