Python requests.session()登录纽约时报网站失效问题求助
问题核心原因
你的代码跑不通本质上是对现在主流网站的登录逻辑有误解:纽约时报根本没有用传统的静态表单提交做登录,整个流程是前端JS控制的多步交互,还带动态参数校验、反爬识别,你对着静态页面源码找form action、猜参数名,肯定登不进去。
至于错密码也返回200太正常了,现在几乎没有网站会用HTTP状态码标识登录失败,都是返回200状态,把错误信息放在响应体里靠前端弹窗提示,靠状态码判断登录成功从一开始就不对。
你踩的几个明确的坑:
- 找
<form action>的方法只适用于老旧网站,现在NYT登录页根本没有静态form标签,所有提交请求都是JS动态生成接口地址发出去的,提交目标根本不是登录页本身的URL,你POST到login_page相当于把数据发给了一个根本不处理登录逻辑的地址。 - 密码框name从
hiddenPassword变password,本身就是两步登录的前端表现:第一步只提交邮箱,后端校验邮箱存在、没有风控拦截,才会返回密码输入框的渲染逻辑,你一次性把邮箱密码全POST过去,流程根本没走到密码校验那一步,传什么参数名都没用。 - 所有登录请求都需要带动态生成的校验参数,最常见的就是CSRF令牌、会话流水、风控埋点参数,这些参数每次打开登录页都会变,你不带这些参数,后端直接把请求当非法流量拦截,根本不会校验你的账号密码对不对。
具体解决步骤
别盯着页面源码猜了,按下面的流程走就能通:
- 先抓真实的登录请求。打开浏览器的开发者工具(F12),切到「网络」面板,勾选「保留日志(Preserve log)」,手动完整走一遍登录流程:输入邮箱点下一步,输入密码点登录。这时候你能在网络面板里看到两个核心的POST请求,一个是提交邮箱做第一步校验的,一个是提交密码做最终登录的,这俩请求的URL才是你真正要请求的接口,不是浏览器地址栏里的登录页地址。
- 第一次用Session访问登录页的时候,不要急着POST账号密码,先从返回的页面源码里把所有动态参数解析出来:最常见的是放在meta标签里的CSRF token,或者写在页面script标签里的全局配置参数,这些值每次刷新页面都会变,必须实时拿,不能写死。
- 严格按照抓包看到的顺序复现请求,别跳步:
- 第一步:把解析到的动态参数和你的邮箱拼到请求体里,POST到你抓包拿到的邮箱校验接口。注意请求头必须带正常的浏览器
User-Agent,requests默认带的python-requests/版本号UA会被NYT的反爬直接拦,直接复制你浏览器里的UA就行,另外把Referer头设成登录页地址,更不容易被拦。 - 第一步请求成功后,从响应的Cookie或者返回的JSON数据里,拿到第二步需要的新校验参数,再把密码拼进去,POST到密码校验接口。这里密码字段的名字、请求体格式(是form表单还是JSON)完全照着抓包看到的写,不要自己猜是
password还是hiddenPassword,抓包里是什么就写什么。
- 第一步:把解析到的动态参数和你的邮箱拼到请求体里,POST到你抓包拿到的邮箱校验接口。注意请求头必须带正常的浏览器
- 两步请求都发完之后,先检查Session里的Cookie有没有带上登录态标识(一般字段名会带auth、sid、login这类关键词),确认有登录Cookie之后,再去请求收藏文章的页面,这时候拿到的才是登录后的内容。
参考代码框架
注意:代码里的接口地址、参数解析逻辑需要替换成你自己抓包拿到的真实值,不能直接硬抄运行
import requests from bs4 import BeautifulSoup # 以下两个接口地址替换成你自己抓包拿到的真实地址 EMAIL_CHECK_API = "抓包获取的邮箱校验接口URL" PASSWORD_LOGIN_API = "抓包获取的密码提交接口URL" LOGIN_PAGE_URL = "https://myaccount.nytimes.com/auth/login" SAVED_ARTICLES_URL = "https://www.nytimes.com/saved" # 请求头直接复制你浏览器里的内容,重点是User-Agent不能用默认值 REQUEST_HEADERS = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", "Referer": LOGIN_PAGE_URL } if __name__ == "__main__": with requests.Session() as session: session.headers.update(REQUEST_HEADERS) # 第一步:访问登录页,拿动态参数 login_page_resp = session.get(LOGIN_PAGE_URL) login_page_resp.raise_for_status() soup = BeautifulSoup(login_page_resp.text, "html.parser") # 这里根据你实际在页面里找到的CSRF参数位置调整,比如如果是meta标签存的就这么写 csrf_token = soup.select_one('meta[name="csrf-token"]')["content"] # 其他抓包里看到的必填隐藏参数,都按这个方式从页面里解析出来 # 第二步:提交邮箱做第一步校验 email_payload = { "email": "你的注册邮箱", "csrf_token": csrf_token, # 把其他抓包里看到的必填参数都补上 } # 注意看抓包里的请求是form格式(传参用data=)还是json格式(传参用json=),别写错 email_resp = session.post(EMAIL_CHECK_API, json=email_payload) email_resp.raise_for_status() # 从邮箱校验的返回里拿第二步需要的参数,根据实际返回格式调整解析逻辑 step2_auth_data = email_resp.json() # 第三步:提交密码完成登录 login_payload = { "password": "你的账号密码", "csrf_token": csrf_token, # 把第二步拿到的校验参数都补上,字段名和抓包完全一致 } login_resp = session.post(PASSWORD_LOGIN_API, json=login_payload) login_resp.raise_for_status() # 登录完成后访问收藏页 saved_articles_resp = session.get(SAVED_ARTICLES_URL) # 这里打印内容验证是否登录成功 print(saved_articles_resp.text)
实操提醒
- 所有接口地址、参数名、请求格式,一律以你自己抓包看到的内容为准,不要硬抄网上的旧代码,NYT前端经常迭代,字段和接口随时会变。
- 如果抓包看到密码是加密后传输的,说明前端JS对密码做了加密处理,你需要找到对应的加密逻辑,把密码按同样规则加密之后再传,传明文肯定过不了校验。
- 如果账号开了二次验证,就在密码登录成功之后,多一步二次验证的请求,逻辑完全一样,照着抓包的请求顺序复现参数就行。
- 调试的时候一步一步来,先把邮箱校验接口调通,确认返回成功了再调密码登录接口,哪步返回不对,就把你发的请求和浏览器的请求挨个对比:请求头差什么、参数差什么、Cookie差什么,补上就能过。
内容的提问来源于stack exchange,提问作者itsallkosher
相关产品推荐
相关产品推荐

