☰
requests模拟登录豆瓣:session会话保持实战教程
2026/10/17 6:03:42 网站建设 项目流程

有手就行的系列写到第三篇,前面两篇把requests发请求、解析页面讲得差不多了。这一篇我们玩点带感的:用requests模拟登录豆瓣网,并且用session对象把登录状态一路保持到底。说白了就是让服务器认为“你是同一个带着登录凭证的人”,而不是每发一次请求就变成一个陌生访客。很多刚学爬虫的人卡在这一步,拿到URL直接requests.get,发现回来的永远是登录页,问题就出在会话状态没管好。这篇内容适合已经能跑通最简单的requests请求、但一碰到“需要登录才能看的数据”就不知道怎么处理的读者。把session的原理和实战跑完,后面再去碰cookies、代理、验证码这些东西,你会顺很多。

1. 为什么需要模拟登录:搞懂HTTP的“身份”是怎么认的

1.1 登录前后到底差了什么

每次requests.get就是一个独立的HTTP请求。HTTP协议本身是无状态的,服务器不记得你上一秒是谁。那为什么你在浏览器里登录一次,刷新页面还保持登录状态?因为浏览器帮你把“身份凭证”放在Cookie里带过去了。

类比一下:食堂刷卡。匿名访客站在窗口前,阿姨问“你谁啊”,你没卡只能灰溜溜走;刷了饭卡,阿姨看到余额、看到你的订餐记录,该给什么给什么。模拟登录做的事,就是替你把那张饭卡(Cookie)拿到手,并且每一次请求都主动出示。

所以“模拟登录”不是简单地把用户名密码POST过去就完事,而是要把服务器返回的身份凭证接住、存好、每次带上。requests.Session存在的意义,就是把这三件事全部自动化。它内部维护了一个CookieJar,服务器通过Set-Cookie返回的所有键值对,都会被自动保存下来,并在后续请求里自动写进Cookie头。你写代码时几乎感觉不到它的存在,但它一直在替你干活。

1.2 session对象到底在干什么

Session对象说白了就是一个“带记忆的requests客户端”。你用session.post登录,它会把响应里的Set-Cookie自动存进自己的CookieJar;再用session.get请求其他页面时,自动把存好的Cookie加到请求头里。整个过程你不需要手动去解析Set-Cookie、拼Cookie头。

我最早写爬虫时,傻乎乎手动维护Cookie。每次登录后从响应头里抠出cookie字符串,写进下一个请求的headers,“cookie=xxx; yyy=zzz”。后来换了一台电脑登录,Cookie格式一变,代码直接报废。用session之后,这类问题基本消失。Session还顺带帮你维护TCP连接池,频繁请求时比每次新建连接快不少。直白点说,它把你的请求做成了连续剧,而不是一集一个独立故事。

这里补充一个很多人会误会的点:session对象不等于“登录状态本身”。它只是负责保存和携带Cookie的容器。真正让服务器认出你的,还是Cookie里那些会话标识。所以判断登录是否成功,不要看session对象有没有创建,而要看它携带的Cookie是否有效、是否被服务器接受。

1.3 为什么用requests而不是selenium

有人问:既然要模拟登录,为什么不直接用selenium把浏览器打开,输入账号密码,等着它登录完再抓数据?能用,但它不是第一选择。requests是纯HTTP请求,速度快、资源占用低,适合做批量数据采集;selenium要驱动一个完整浏览器,几乎等于你手动操作一台电脑,慢且容易暴露。

我的做法是:优先requests。只有遇到前端JS渲染、滑块验证码、需要浏览器指纹这种难搞的场景,才考虑上selenium。即便上了selenium,也要设计好“用浏览器登录、把Cookie导出给requests用”的配合方案,这样后续抓数据依然很快。这也是下面要讲session的另一个原因:它不只是处理登录,还决定着你后续抓数据的效率。

2. 登录前的准备工作:先学会“看”一次真实的登录请求

2.1 用浏览器开发者工具抓住登录请求

写代码前,先做一件事:打开目标网站,按F12进开发者工具,切到Network标签,勾选Preserve log(保存日志),不然页面跳转后之前的请求会被清掉。然后在网页上正常点登录。输错密码也没关系,只要能看到请求就行。

在Network列表里找到那个“发送登录表单”的请求。它会是一个POST请求,URL可能是/accounts/login、/login等。点击这个请求,看Headers里的请求URL、请求方法,往下翻能看到Form Data或者Payload,里面就是真正提交过去的用户名、密码、记住状态等字段。

每个网站的字段名都不一样。有的叫name,有的叫username,有的叫email。千万别靠猜,要以开发者工具里看到的Form Data为准。我见过太多人抄某篇博客的代码,发现登录不了,其实只是字段名对不上。这一步抓包,是整个模拟登录最容易被忽略、也最关键的一步。抓到一次之后,建议顺手把请求URL、请求方法、字段结构截个图留下来,方便写代码时对照。

2.2 请求头里那些“小细节”

登录请求的Headers里,有几个字段值得留意。User-Agent是你的客户端身份,很多网站会校验,如果直接使用requests默认的python-urllib,很容易被识别出来。Referer表示你从哪个页面发起的请求,有些网站会用它做来源校验。Content-Type决定了后端怎么解析你提交的数据,表单登录一般是application/x-www-form-urlencoded,也有网站用application/json。

实际操作中,我一般先把浏览器请求头完整复制下来,放到代码里,只保留User-Agent、Referer、Content-Type、Accept这些常规字段。不要无脑全抄,像Accept-Encoding如果设置了gzip,requests会自动解压,问题不大,但加上也没坏处。Cookie头不要手写,session会帮你处理。这里有个很容易踩的坑:如果你手动在headers里写死了一个Cookie,而session自己又在维护一份,两边的值可能打架,导致登录状态错乱。所以原则是:Cookie交给session,不要自己拼。

2.3 验证码和密码加密:别慌,先分清楚

现在很多网站登录不只是“用户名+密码”两个字段。常见的有三种情况:一是密码先做MD5/SHA等加密再上传;二是登录前生成一个随机token,提交时要带上;三是出现图形验证码或滑块。这些都会让直接抓包模拟变复杂。

遇到这种情况,第一个建议是回到“为什么要模拟登录”这个问题上:如果只是做技术学习,完全可以找一台测试环境,或者用目标站点公开的API接口;如果确实要处理线上登录,必须确认你是否被允许这么做,并且考虑验证码的人工介入方案。我写这篇教程不会教绕过验证码,因为那既不安全,也不符合一个技术学习者该有的边界感。后面代码里,我们按最简单的“明文表单”来讲,重点始终是session会话保持这件事。

3. 代码实现:requests模拟登录豆瓣网并保持会话

3.1 先把环境准备好

需要Python 3.6以上,安装requests库。终端执行pip install requests,装好就行。requests的核心就一句话:一个能代替浏览器发请求的库。想设置请求头,用headers参数;想提交表单,用data参数;想保持登录,用requests.Session()。

代码里我会把登录地址、表单字段都写成变量。注意,网站接口随时可能调整,如果实测返回结果不对,优先回到第2章,重新抓包看当前接口和字段,而不是硬套代码。很多初学者在两三年前的文章下面留言“代码跑不通”,其实不是代码本身有错,而是网站页面改版了,字段名变了,甚至登录地址都换了。

3.2 完整代码:从创建session到登录成功

下面是一段可以直接跑的示例代码。这里用的是模拟场景,字段名以你自己抓包的为准:

import requests # 1. 创建一个session对象,所有请求都用它发 session = requests.Session() # 2. 设置通用请求头,模拟真实浏览器 session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Referer": "https://accounts.douban.com/login", }) # 3. 先GET一次登录页,拿到初始Cookie和一些隐藏字段 login_page_url = "https://accounts.douban.com/login" session.get(login_page_url) # 4. 构造登录表单 login_url = "https://accounts.douban.com/login" form_data = { "username": "你的账号", "password": "你的密码", "remember": "0", # 有些网站会有csrf_token之类的隐藏字段,需要从登录页面提取 } # 5. 用session发起登录请求 resp = session.post(login_url, data=form_data) print("登录请求状态码:", resp.status_code) print("当前Cookie:", session.cookies.get_dict())

登录请求发出去后,你可以打印session.cookies.get_dict()看看拿没拿到Cookie。这是一张很实用的“体检单”:如果返回的Cookie字典是空的,说明登录大概率没成功,别急着往下写。

3.3 这段代码的每一步都在干嘛

先GET一次登录页,这步经常被省略,但我不建议省。很多网站的登录流程是:你访问登录页时,服务器先给你种一个sessionid,同时把一些隐藏字段写进表单;这个前置请求帮你把“初始身份”拿到手,紧接着的POST才能被正常接受。没有初始Cookie,后面POST可能就是302跳回登录页。

用session.post而不是requests.post,这是整个会话保持的核心。如果你用requests.post,Cookie只会停留在那次响应里,下一次requests.get又是全新的请求,登录态直接断掉。session对象会把所有请求串联起来,第一次POST拿到的Cookie,会自动带进之后每一次请求。这也是标题里“session会话保持”五个字的实体所在。

确认登录是否成功,多数情况下看状态码没用,200可能只是登录页加载成功。我的习惯是请求一个“只有登录后才能看到”的页面,比如个人主页,然后看返回内容里是否包含个人昵称、收藏列表等标志性文本。也可以用resp.url判断有没有被重定向回登录页。如果resp.url停在accounts/login且返回内容里带着登录框,那基本可以断定登录没成。

3.4 登录之后:会话保持地访问个人页面

登录好了,下一步就是带着身份去访问真实目标。用同一个session对象,直接get你要的数据页面,它会自动携带登录凭据。示例代码如下:

profile_url = "https://www.douban.com/people/your_id/" resp2 = session.get(profile_url) print("最终URL:", resp2.url) if "你的昵称" in resp2.text: print("登录态有效,可以开始抓取数据") else: print("可能需要重新检查登录流程")

如果上述判断通过,说明从登录到会话保持这条链路是通的。你可以把这个session对象用在循环里,批量请求多个页面,全程都是登录后的身份。

注意一点:循环里也要每次带上适当的间隔,别一口气狂刷,服务端流量异常很容易触发风控。我自己写爬虫循环时,习惯在每两次请求之间加一个随机延时,比如time.sleep(random.uniform(1, 3)),既不显得像机器,也不会给服务器造成太大压力。

4. 常见问题与排查技巧实录

4.1 状态码200,但返回的仍是登录页

这是Session相关新手最容易踩的坑,现象是:requests.post登录后,再requests.get个人页,返回内容跟没登录一样。排查顺序如下。

第一步,检查你是不是一直在用同一个session对象。有人图省事,写了session.post,后面又改成requests.get,这就等于登录的是一只鸟,飞走的却是另一只鸟。所有请求都要通过session。第二步,检查GET登录页那步是否真的执行了,有些网站的登录地址本身就是302,需要先GET出初始化参数。第三步,把返回的HTML下载成文件,搜索location.href或“登录”关键词,确认是不是被重定向。

第四步,看请求头。有些网站会校验Referer,如果直接从post请求开始,没有来源页面,也可能被拒。把浏览器的完整请求头对照一下,缺什么补什么。这套流程走下来,八成问题都能定位。

4.2 拿着Cookie却还是被识别成爬虫

有些时候登录是成功的,Cookie也拿到了,但对方还是拒绝给你数据。常见原因有三:请求头缺失、频率过高、行为特征异常。请求头缺失比较好解决,把User-Agent、Referer这些补上;频率过高就加随机延时,比如time.sleep(random.uniform(1, 3))。行为特征这块,尽量模拟真实访问路径:先访问首页,再看列表页,再点详情,而不是一上来就狂刷几十个接口。

我在自己项目里踩过类似的坑。用session请求某个数据页,一开始加个1秒延时,连续跑500个页面都没问题;后来我为了测并发,同时起了5个进程,跑了不到100个页面就被临时限制了。那之后我养成了习惯:无论采集多急,都要给自己留一点“人味”的节奏,更重要的是,要先确认目标网站是否允许这样访问。

4.3 验证码、滑块、加密参数怎么办

这个问题无法绕开。如果目标网站登录遇到强验证码,requests静态模拟确实不好办。我的处理建议分三层。第一层,查文档,看目标网站有没有官方开放接口,很多平台有开发者API,正规、稳定又合规。第二层,如果是学习项目,找可以合法使用的测试环境,别把线上账号拿来反复试。第三层,如果就是要做数据采集,请先确认网站的服务条款,有些站点明确禁止自动化登录,这种情况下就换目标。

这里必须说清楚:本文的代码是会话保持的技术演示,不是一份“绕过验证码攻略”。验证码本身是网站的安全措施,用任何方式试图绕过都需要承担对应风险,这是我劝你三思而后行的地方。技术学习有技术学习的路,生产采集有生产采集的规矩,把这两件事分开,路才能走远。

4.4 常见问题速查表

现象可能原因解决办法
登录后Cookie为空账号密码错误、接口字段错误重新抓包核对Form Data字段名
状态码200但内容不对被重定向回登录页检查是否用session发所有请求
登录成功但下一页又失效服务器做了会话锁定检查UA是否稳定、是否补Referer
Cookie被拒绝频率过高或行为异常加随机延时、降低并发、先访问页面预热
需要验证码网站安全机制确认条款后,选择人工配合或放弃自动化

5. 扩展:session会话保持还能帮你做什么

5.1 带登录态地批量抓取

会话保持的最大价值,在于把“登录”这件事只做一次,然后反复使用。比如抓收藏列表、关注列表这种私密页面,每页都能复用同一个session。只需要在循环里改页码,加上延时,就能稳定地拿到多页数据。

这里也顺便说一句工程上的小经验:不要把全部逻辑写在一个文件里。把登录封装成一个函数,返回session对象;把数据抓取封装成另一个函数,接收session作为参数。这样你以后抓其他站,只需替换登录部分,抓取逻辑基本不用动。我自己的一个练手项目就是这么组织的,登录函数单独维护,采集函数纯粹处理页面解析,出了问题也好定位。

5.2 多账号切换与会话隔离

如果要管理多个账号,就创建多个session对象,每个账号对应一个session,互不干扰。有人图省事,在同一个session里反复改Cookie,结果两个账号的数据串了,出了很多奇怪的bug。多个session实际上就是多个独立的CookieJar,天然隔离,简单直接。

多账号并发时更要注意请求频率。不要把这几个session同时疯狂请求,从一个账号跳到另一个账号也要有点间隔。目标服务器的风控很多时候不看单账号频率,而看整个出口的全部请求特征。如果你在做一个需要多账号并行的事情,先想清楚自己的请求总量是否合理,再做技术设计,这比任何技巧都重要。

5.3 别把技术学成了麻烦

这是我最想强调的一点。模拟登录技术本身没有好坏,用在学习、测试、验证自己项目上,它是很好的基本功;但如果拿来批量刷接口、爬隐私数据、绕过风控,那就是另一回事了。

我看过不少教程,教人各种“破解”“绕过”,看得人热血沸腾,实则风险很大。真正的技术提升,是更精确地理解协议、理解会话、理解服务端设计逻辑,然后在合规的范围内做自己的事。遇到需要登录的站点,先看有没有开放API,再看服务条款,最后才考虑爬虫方案。这个习惯,值得每个写爬虫的人养成。

6. 收尾:一点个人经验

Session这个东西,说穿了并不神秘,它就是个帮你管理Cookie、维持会话状态的HTTP客户端。我刚开始学的时候,一直没搞明白为什么登录成功还要用同一个session,总觉得requests会自动记住我。后来自己抓包看请求头,才意识到:每一次真实请求里,服务器认的都是你带的Cookie,而不是你的IP或者代码。

踩过几次坑之后,我现在的标准流程是:先抓包,看请求方法和字段;再GET一次登录页拿初始参数;然后session.post登录;最后用一个私密页面验证登录态。这几步走顺了,几乎所有“需要登录才能拿数据”的站点,你都能很快复现出基本流程。

这套思路才是真正能带走的东西。等你自己亲手把一个登录态抓通,再回头看这篇文章,会觉得很轻松。就这样,下一篇见。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询