
简介这是一个面向 Java 开发者的“人人网校内自动登录”示例程序解决旧版登录脚本失效、无法直接复用的问题。资源定位为简易版适合希望快速了解模拟登录流程、并在此基础上二次开发的读者。压缩包共 4 个文件整体约 4.31MB除核心的 Java 登录代码外还附带三个 zip 格式的依赖库包导入项目后即可直接调用相关接口省去单独查找依赖的麻烦。已有 42578 人学习下载说明该示例对研究校内网登录机制有较高参考价值。读者可以学习如何构造登录请求、处理 Cookie 与跳转逻辑也能借鉴作者对老代码的升级思路自行扩展登录后的个性化功能作为入门模拟登录和 HttpClient 实战的良好起点。 这个标题放在今天多少带点考古色彩。我自己第一次接触到校内网这个概念还是大学宿舍里用着 2M 宽带、对着 CRT 显示器刷网页的时候。但离谱的是直到现在偶尔还会有人私信问我怎么把早年存在人人网相册里的照片批量扒下来或者怎么帮家里长辈定时登录一下、防止账号进入休眠被回收。这时候你就发现自动登录这个需求压根没消失只是被挤到了小众角落里。这篇博文我就把人人网校内自动登录的完整实现思路拆开讲清楚。它适合三类人一是想找回早年照片、状态、留言数据的怀旧党二是想研究老牌网站登录加密流程、练手 Python 爬虫的初学者三是需要批量管理几个休眠账号的人。整个方案用 Python 实现核心就三件事破解密码加密逻辑、模拟登录请求、持久化 Cookie 会话。我会把抓包、RSA 加密、请求头伪装、验证码处理这些关键环节全部过一遍最后还会附上我实测中踩过的几个坑。1. 为什么老掉牙的人人网还要写自动登录1.1 还在用的人到底在登录什么先说结论现在愿意折腾人人网登录的人九成不是为了刷信息流。我接触到的典型场景有这么几类。第一类是数据打捞。人人网当年是很多 80、90 后上传照片、写状态、留板砖的主阵地。后来平台几经易手但只要你账号还在那些老照片、老留言、甚至多年前的站内信理论上都还在数据库里躺着。手动一张张翻开太慢脚本登录之后按相册 ID 遍历抓取是最常规的操作。第二类是低频保活。人人网现在的活跃度大家都懂但越是活跃度低的平台越可能对长期不登录的账号做回收或者限制。我帮一个朋友处理过账号被保护性冻结的问题解冻流程复杂得要命。所以定期挂个脚本自动登录一次是在给账号续命。第三类是技术怀旧。人人网的前身校内网是中国社交网络早期最具代表性的产品之一。它的登录加密机制虽然比不过银行 App但作为一个教学样本很合适有公钥加密、有参数签名、有频率限制难度适中。拿它练手比对着那些已经被爬烂了的电商网站有意思得多。1.2 自动登录这件事本质是模拟一次人肉操作你手动登录时做了四件事打开登录页、输入账号密码、识别验证码如果有、点击登录按钮。自动登录脚本做的事情一模一样只不过把每一步都换成了代码。但这里有个关键差异人手动登录时浏览器替我们处理了密码加密、Cookie 存储、请求头附加这些细节而脚本得自己来。以人人网为例你输入密码后浏览器并不是把明文密码直接发到服务器而是先通过一段 JavaScript 对密码做非对称加密再把密文塞进表单。如果你不管三七二十一直接 POST 明文密码过去服务器返回的只会是用户名或密码错误。所以我做这个项目的第一步不是写 Python而是把登录页面的请求流程和加密逻辑彻底扒明白。2. 登录链路拆解人人网到底怎么验证密码2.1 抓包看到的真实请求流程我建议你不要直接信任何现成的教程包括我这篇自己动手抓一次包印象会深得多。用 Chrome 打开人人网登录页按 F12 切到 Network 面板勾选 Preserve log然后手动登录一次。你会看到登录请求发往的地址类似https://passport.renren.com/PLogin.do请求方法是 POSTForm Data 里大致包含这几个关键字段字段名含义userName登录账号邮箱或手机号passwordRSA 加密后的密码密文rkey一串随机字符串每次登录都不同其它表单字段包括一些固定的隐藏参数仔细看这个 password 字段一大串十六进制或者 Base64 字符根本读不出原文。这说明密码在离开浏览器之前就已经被加密了。2.2 RSA 公钥加密密码不是明文传输的为了搞清楚加密逻辑我习惯直接切到 Sources 面板搜索password、encrypt、RSA这些关键词定位到加密用的 JS 函数。人人网走的是一套典型的RSA 非对称加密流程跟你网银转账用的原理一样服务器把公钥下发到前端前端用公钥把密码加密成密文后端拿私钥解。就算密文在传输过程中被截获没有私钥也还原不出明文。具体到人人网登录页加载时会请求一个接口获取 RSA 公钥里面包含两个核心参数{ modulus: C2E9D0A6...一段很长的十六进制字符串, exponent: 10001 }modulus 是 RSA 算法的模数相当于密钥的主体exponent 是公钥指数通常固定为 65537转成十六进制就是 10001。有了这两个参数任何语言都能做 RSA 加密。Python 里最常用的是rsa库两行就能搞定import rsa # 把 hex 字符串转成字节再用公钥加密 pub_key rsa.PublicKey(int(modulus, 16), int(exponent, 16)) encrypted rsa.encrypt(password.encode(utf-8), pub_key) # 最终提交时一般是转成十六进制字符串 cipher_text encrypted.hex()这个cipher_text就是你要提交的 password 字段。至于 rkey 这种随机字符串一般是前端 JS 生成的用来增加每次请求的随机性。你只需要在每次登录前先生成一个随机的 rkey或者干脆从登录页源码里提取一个现成的值两种方式我都试过都能过。2.3 验证码识别与绕过的取舍人人网在检测到异常登录行为时会触发验证码。验证码的类型一般是滑块或者点选式。我在测试中发现只要请求频率不高、请求头伪装得够像浏览器验证码并不会频繁出现所以我的策略是优先避免触发而不是硬着头皮做识别。具体做法放在第 4 节的踩坑记录里细说。这里先记住一个原则触发验证码往往不是因为你技术不行而是因为你的请求指纹太像机器人了。3. 核心代码实现从加密到会话保持环境准备Python 3.8安装requests、rsa两个库。如果你的环境里没有直接pip install requests rsa即可。import time import random import requests import rsa # 登录页 URL 和登录接口 URL 根据实际抓包结果调整 LOGIN_PAGE_URL https://www.renren.com/PLogin.do RSA_MODULUS_URL https://passport.renren.com/rkey # 示例地址以实际抓包为准 class RenRenLogin: def __init__(self, username: str, password: str): self.username username self.password password self.session requests.Session() # 模拟浏览器请求头这一步很重要后面细讲 self.session.headers.update({ User-Agent: (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36), Referer: https://www.renren.com/, Origin: https://www.renren.com, }) self.rkey self._generate_rkey() staticmethod def _generate_rkey() - str: 生成 rkey 随机字符串 letters abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 return .join(random.choices(letters, k16)) def _get_rsa_public_key(self): 获取 RSA 公钥modulus 和 exponent # 这里用 requests 请求公钥接口 resp self.session.get(RSA_MODULUS_URL, timeout10) data resp.json() return rsa.PublicKey(int(data[modulus], 16), int(data[exponent], 16)) def _encrypt_password(self) - str: RSA 加密密码 pub_key self._get_rsa_public_key() encrypted rsa.encrypt(self.password.encode(utf-8), pub_key) return encrypted.hex() def login(self): 执行登录并保存 Cookie password_cipher self._encrypt_password() form_data { userName: self.username, password: password_cipher, rkey: self.rkey, } resp self.session.post(LOGIN_PAGE_URL, dataform_data, timeout10) if resp.status_code 200: # 登录成功后把 session 的 Cookie 保存到本地文件 self._save_cookies() print(登录成功Cookie 已保存) else: print(登录失败HTTP 状态码:, resp.status_code) def _save_cookies(self): 将 Cookie 保存为本地文件方便下次直接复用 with open(cookies.txt, w, encodingutf-8) as f: for cookie in self.session.cookies: f.write(f{cookie.name}{cookie.value}\n) if __name__ __main__: login RenRenLogin(你的账号, 你的密码) login.login()3.1 为什么要用 Session 而不用裸 requests你可能注意到我用了requests.Session()而不是每次请求都重新requests.post。原因是 Session 会自动保存服务器返回的 Cookie并在后续请求中自动带上。这正好模拟了浏览器的会话机制。登录之后你再想抓相册、状态、留言只需要继续用同一个 session 发起请求服务器就会认为你是已登录用户。比如抓取当前登录用户的基本信息# 在 login() 之后调用 def fetch_profile(self): profile_url https://www.renren.com/profile.do # 示例地址 resp self.session.get(profile_url, timeout10) if resp.status_code 200 and 你的名字 in resp.text: print(会话有效登录状态正常) else: print(会话失效需要重新登录)3.2 Cookie 持久化的两种玩法上面的代码把 Cookie 存成了cookies.txt这只是最基础的做法。实际项目中我一般直接用requests自带的dict序列化下次启动时直接加载def load_cookies_from_file(self): 从文件加载 Cookie 并恢复到 session with open(cookies.txt, r, encodingutf-8) as f: for line in f: name, value line.strip().split(, 1) self.session.cookies.set(name, value)这样脚本启动时先尝试加载本地 Cookie只有发现会话失效才去走完整的 RSA 加密登录流程能省掉很多不必要的登录请求也降低了触发验证码的概率。4. 实测中踩过的坑以及完整的排查链路4.1 密码加密失败你加密的对象搞错了我最初踩的第一个坑是把整个表单里的密码做了 RSA 加密但提交后一直提示密码错误。排查了很久最后发现是加密时没有注意编码格式。人人网前端的加密函数是这么写的var encrypted encryptString(password, modulus, exponent);这个函数会将密码字符串按UTF-8编码后再做 PKCS#1 v1.5 填充加密。我在 Python 里直接用rsa.encrypt时如果忘了.encode(utf-8)传入的就是 str 而不是 bytes程序虽然不报错但加密出来的密文和后端预期的完全对不上。排查的过程是这样的我先用同一个账号密码在浏览器里登录一次抓包拿到浏览器生成的密文再用 Python 对同样的密码做加密输出我生成的密文发现两段密文完全不一样。但 RSA 本身有随机填充密文每次不同是正常的所以不能简单比对于是我把两段密文分别送到一个临时搭建的本地解密脚本里用私钥还原明文结果浏览器那段能还原出正确密码我的那段还原出来是一串乱码。问题就出在编码上。改过来之后再测试登录立即成功。提示遇到加密类接口第一件事是确认字符串编码。UTF-8 和 GBK 的加密结果完全不同差一个字节都解不开。4.2 请求头缺失服务器识别出你是爬虫第二个坑是登录请求虽然发出去了但服务器返回的是一段访问异常的提示页而不是登录成功。这种问题通常不是密码错而是请求指纹太干净了。浏览器发请求时会带上 User-Agent、Referer、Origin、Accept、Accept-Language 等一堆请求头而 Python requests 默认只带一个User-Agent: python-requests/x.x.x服务器一眼就能识别出来。排查链路是这样走的先对比浏览器和 Python 请求的 Headers把关键差异列出来补齐 User-Agent模拟 Windows Chrome 浏览器加上 Referer请求来源必须是人人网的登录页加上 Origin表明这是前端跨域请求。改完这三项问题解决。还有一个容易被忽视的点请求头里不要带Accept-Encoding: br但可以带gzip, deflate。有些库会自动帮你加上压缩头服务器如果返回 br 压缩格式requests 没有对应的解压能力轻则乱码重则报错。4.3 登录成功却被判定异常Cookie 与请求频率有一次我在脚本里加了循环登录测试第 5 次开始服务器开始弹出验证码第 8 次直接提示操作频繁请稍后再试。这个现象让我意识到登录接口本身是有频率限制的。后来我加了两个措施每次登录前随机 sleep 3~8 秒模拟人的操作间隔登录失败后立即停止而不是继续重试因为连续失败大概率是账号密码错误重试只会雪上加霜。如果你确实需要频繁登录比如多账号管理我建议用 IP 代理池轮换或者把脚本部署到多个执行节点上。这个问题无解老平台的反爬虽然不强但耐不住你高频怼它的登录接口。4.4 验证码怎么处理我的取舍遇到验证码我一般按以下优先级处理降低触发概率每次请求间隔久一点请求头伪装到位Cookie 复用到位手动介入脚本检测到页面出现验证码时把截图弹出来人工拖一下滑块再继续自动识别如果非要全自动可以对接打码平台但没必要为了人人网专门搞一套 OCR。我在实际项目里用的是方案 2。登录逻辑里判断响应内容是否包含验证码特征比如页面包含captcha关键词就调用一个manual_captcha()函数用 PIL 把验证码图片保存下来打印提示让用户手动扫码或拖拽再把人工处理后的结果回填进会话。5. 让脚本真正自动化部署与定时执行脚本写好了总不能每次手动跑一遍。我一般分两步走本地写定时任务云端挂常驻进程。5.1 本地定时任务Windows 和 LinuxWindows 上用任务计划程序创建一个基本任务触发器选每天操作选启动程序程序填 Python 解释器的路径参数填脚本路径。Linux 或 macOS 上更简单直接用 crontab# 每天凌晨 3 点执行一次登录脚本 0 3 * * * /usr/bin/python3 /path/to/renren_login.py /var/log/renren.log 21注意 Python 路径要用绝对路径避免 cron 环境里找不到解释器。日志重定向到文件方便排查。5.2 云函数免服务器方案如果你不想租服务器用云函数也可以。我之前试过把这段逻辑丢到阿里云函数计算或者腾讯云 SCF 上设置成定时触发器。云函数有一个坑函数执行环境是无状态的Cookie 没法直接存在本机。我的做法是把 Cookie 存到云存储的 KV 里每次函数启动时拉取登录成功后更新import os import json def load_cookies_from_env(): cookies_str os.environ.get(RENREN_COOKIES, {}) return json.loads(cookies_str) def save_cookies_to_env(cookies_dict): payload json.dumps(cookies_dict) # 写入环境变量或 KV 服务 os.environ[RENREN_COOKIES] payload这样函数实例即使被回收下一次启动也能从环境变量里恢复 Cookie。5.3 加一个登录成功后的通知自动化脚本有个刚需登录失败或成功要能通知到你。我一般用 Server酱 或者钉钉机器人登录成功不通知登录失败才推送一条告警标题写人人网自动登录脚本异常内容带上失败原因和当前时间。这样你不需要盯日志等通知就行。def send_notification(message: str): key 你的Server酱Key url fhttps://sctapi.ftqq.com/{key}.send requests.post(url, data{title: 人人网登录通知, desp: message})这套通知逻辑在任何人品项目里都能复用相比守着终端看输出体验提升不止一个档次。6. 写完这个脚本的一点心得最后说几个我反复摸索后觉得值得分享的体会。第一别直接照搬别人的代码先自己抓一次包。我上面给的接口地址和字段是基于我当时测试时的版本。人人网这种老平台接口说改就改。你真正动手时登录接口的路径、参数名、甚至是否还用 RSA都可能不一样。抓包的过程才是你真正学会逆向登录的过程。第二Cookie 复用的价值远超你的想象。很多初学者每次跑脚本都从用户名密码登录开始不仅慢还容易触发风控。你完全可以在手动登录一次后把浏览器 Cookie 导出给脚本用过期再走自动登录流程补一次。这样既稳又低频。第三自动登录只是起点别停在这里。登录成功后你能做的东西很多遍历相册下载原图、按年份导出状态、爬取留言板旧内容、批量给好友分组。我那次帮朋友找回老照片就是从自动登录开始最后写了一个完整的相册备份工具。技术不大但做出来的东西对当事人来说价值很高。第四也是最重要的一条底线这种脚本只能用于你自己的账号或者你明确获得授权的账号。做技术归做技术别拿去干不该干的事。这个项目看着小众但把加密、会话、指纹伪装、频率控制这一条链路跑通之后你对网站登录这件事的理解会通透很多。以后再碰到任何老系统的登录需求你大概率都能用同一套方法论快速拆解。这也是我为什么愿意写这篇长文的原因——技术会过时但拆解一个问题的方法论不会。本文还有配套的精品资源点击获取