
1. 为什么放弃 Selenium一套更轻的登录 Cookie 方案微信视频号的登录 Cookie 获取很多人第一反应就是 Selenium 自动化浏览器。我在很早之前也这么干过拉起一个 Chrome 实例模拟扫码、等待跳转、从浏览器里把 Cookie 挖出来。这套流程能跑通但用着用着问题就来了。首先是环境太重。Selenium 需要浏览器驱动驱动版本还得跟浏览器版本对齐。Chrome 一升级driver 没跟上代码立刻罢工。其次是资源占用太高每次登录都要启动一个完整浏览器跑在服务器上内存直接告急。更糟的是在无图形界面的 Linux 服务器上跑 Selenium光装虚拟显示环境就得折腾半天。但最让我放弃的还不是这些而是稳定性。微信视频号网页端对自动化浏览器是有风控逻辑的Selenium 启动的浏览器有比较明显的自动化指纹被识别之后就会出现验证码、登录失败、甚至账号短期受限的情况。为了拿个 Cookie 把账号搭进去实在不划算。后来我换了思路既然核心需求只是“拿到登录后的 Cookie”为什么不直接用 Requests 模拟登录流程扫码的核心动作其实只需要一个会话浏览器能做的Requests 用更轻量的方式也能做到。现在这套方案我已经用了挺长时间稳定性和可维护性都远好于之前的浏览器方案。这篇文章就把整个过程完整写出来包括代码、步骤以及我当时踩过的坑。如果你也在为微信视频号 Cookie 获取发愁这篇应该能帮你省下不少时间。2. 方案思路与整体设计2.1 核心需求拆分微信视频号网页端的登录态本质上就是浏览器里那几十个 Cookie 字段。这些字段里面比较关键的包括sessionid、ssuin、uin等它们标记了当前用户是谁、登录状态是否有效。即使都是微信生态的产品视频号和其他网页端产品在请求接口时对 Cookie 的校验逻辑也不一样。这就导致了从别处拿到的 Cookie 未必适用于视频号的请求调用。所以最稳妥的方式就是让视频号网页端自己给我们发一套完整的登录 Cookie。操作起来可以分为三步启动浏览器打开视频号网页端登录页面。在这个过程中自动捕获浏览器网络请求里 Set-Cookie 的完整数据。将 Cookie 整理导出然后让 Requests 接管后续所有接口调用。关键在于浏览器在整个过程中只承担“扫码登录”这一件事。登录完成后拿到 Cookie浏览器就可以关掉了之后全部交给 Requests 处理。这种方式规避了 Selenium 的全部问题又保留了扫码登录的安全性。2.2 为什么用 Requests 而不是 SeleniumSelenium 的定位是自动化测试框架它做的是“模拟用户在浏览器里的行为”。而 Requests 是纯 HTTP 客户端做的是“模拟浏览器发出的网络请求”。这两种工具的定位从一开始就决定了它们适合的场景完全不同。用 Selenium 拿 Cookie本质是绕远路。它把浏览器整个加载出来渲染页面、执行 JS、加载图片浪费大量资源在做和登录这件事无关的操作上。用 Requests 的话我们只关心 HTTP 层面的东西不需要渲染不需要 JS 引擎直接走网络请求拿结果。另外我从使用成本和后期维护的角度对比过差距确实明显对比维度Selenium 方案Requests 方案环境依赖浏览器 对应版本驱动仅 Python 环境服务器部署需要虚拟显示等额外配置直接运行无特殊要求内存占用单进程 300MB 起30MB 以内登录成功率受自动化指纹影响可能触发验证无浏览器指纹稳定性高后续接口调用仍需从浏览器提取请求数据直接复用会话请求即可2.3 整体技术路线这套方案的技术路线可以概括为浏览器扫码登录并捕获 Cookie通过本地 HTTP 服务中转Requests 接管会话后调用接口。具体流程如下打开登录页面地址https://channels.weixin.qq.com/login。用户用微信扫码并确认登录。本地脚本捕获浏览器登录成功后产生的 Cookie。将 Cookie 整理为 Requests 可直接使用的格式。写入本地文件持久化之后请求直接加载复用。我把整个过程拆成两个模块。第一个模块负责“拿 Cookie”第二个模块负责“用 Cookie”。这两个模块解耦之后代码维护起来非常顺畅。后面如果视频号的接口有变动改一个模块就行不至于全盘推翻。3. 环境准备与前置工作3.1 安装依赖先说 Python 环境。推荐使用 Python 3.8 以上版本。我开发的时候用的是 3.10运行的一切正常。如果版本太老有些依赖库不一定兼容。安装依赖直接用 pippip install requests pyperclip其实核心只需要requests。pyperclip是辅助用的用于读取剪贴板里的 Cookie 数据。如果你是在服务器上操作没有剪贴板环境也可以跳过这个依赖手动粘贴文本即可。注意不要用系统自带的 Python 3.6 或更低版本跑这个脚本ssl 模块和 http 模块的行为差异会导致一些 HTTPS 请求异常。3.2 理解 Cookie 与 Session 的关系我见过很多新手在这里犯迷糊明明有了 Cookie为什么请求还是返回未登录这就要搞清楚 Cookie 和 Session 的协作关系了。Session 是服务端维持用户状态的一种机制它会在服务端记录“这个用户已登录”的信息。Cookie 是客户端保存的一段凭证每次请求时自动携带给服务端。服务端通过 Cookie 里的凭证找到对应的 Session才能确认当前请求的用户身份。放在微信视频号的场景里登录成功后服务端生成了 Session同时把 Session 标识写入了 Cookie。我们的 Requests 在后续请求中必须把完整 Cookie 带过去服务端才能对上号。有一个很容易被忽略的点部分 Cookie 字段设置了HttpOnly属性这类 Cookie 无法通过 JavaScript 读取但可以通过浏览器开发者工具里的 Network 面板完整查看。这也是为什么直接从浏览器控制台输入document.cookie拿不全数据的原因。3.3 准备浏览器调试环境我推荐的浏览器是 Microsoft Edge 或者 Chrome。两者都是 Chromium 内核操作方式完全一致。下面以 Chrome 为例说明操作步骤。打开 Chrome按F12进入开发者工具切到Network网络面板。默认情况下Network 面板只会记录打开面板之后发生的请求所以要先打开面板再访问登录页面。勾选Preserve log保留日志这个选项非常重要。因为页面登录成功后通常会发生跳转不勾选的话之前的请求记录会被清空你就看不到登录响应里的 Set-Cookie 信息了。另外建议在 Network 面板的筛选框里输入login或cgi-bin快速过滤出和登录相关的请求。4. 实操过程与核心代码实现4.1 第一步扫码登录并捕获请求在 Chrome 中打开https://channels.weixin.qq.com/login页面会显示一个二维码。用手机微信扫码在手机上确认登录。这时候观察 Network 面板会看到一系列请求。找到包含Set-Cookie响应头的那个请求点击它在右侧面板选择Headers下拉到Response Headers区域就能看到所有的 Cookie 字段。截图、复制、整理这一步在实际操作中比较繁琐。一次登录产生的 Cookie 可能有三十多个字段手动整理容易遗漏。我建议先用一个简单的方式做验证直接复制该请求的Request Headers里的完整 Cookie 字符串后续在脚本中直接使用。这里有一个取舍问题从 Network 面板直接复制的 Cookie 字符串时效性很好但格式比较乱。脚本里需要稍作处理才能转换成 dict。我在代码里写了一个通用解析函数可以自动完成这个转换。4.2 第二步构建 Requests 会话这部分是核心模块。我直接贴出我当时用的完整代码大家可以直接复制使用。import requests import json import time from http.cookies import SimpleCookie def parse_cookie_string(cookie_str): 将浏览器复制的 Cookie 字符串解析为字典 cookie_dict {} for item in cookie_str.split(;): item item.strip() if not item: continue if in item: key, value item.split(, 1) cookie_dict[key.strip()] value.strip() return cookie_dict def create_session_with_cookies(cookie_str): 基于 Cookie 创建可用的 Requests 会话 session requests.Session() # 设置常见的请求头 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://channels.weixin.qq.com/, Accept: application/json, text/plain, */*, Accept-Language: zh-CN,zh;q0.9, }) # 解析并设置 Cookie cookies parse_cookie_string(cookie_str) session.cookies.update(cookies) return session def save_cookies_to_file(session, filenamecookies.json): 将会话中的 Cookie 保存到文件 cookies session.cookies.get_dict() with open(filename, w, encodingutf-8) as f: json.dump(cookies, f, ensure_asciiFalse, indent2) print(fCookie 已保存至 {filename}共 {len(cookies)} 个字段) def load_cookies_from_file(filenamecookies.json): 从文件加载 Cookie 并创建会话 with open(filename, r, encodingutf-8) as f: cookies json.load(f) session requests.Session() 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://channels.weixin.qq.com/, }) session.cookies.update(cookies) return session # 示例用法 if __name__ __main__: # 第一步从浏览器复制 Cookie 字符串粘贴到这里 cookie_str # 粘贴你的 Cookie # 第二步创建会话 session create_session_with_cookies(cookie_str) # 第三步验证登录状态请求一个需要登录的接口 verify_url https://channels.weixin.qq.com/cgi-bin/...# 请替换为实际接口 resp session.get(verify_url, timeout10) print(状态码:, resp.status_code) print(响应内容:, resp.text[:500]) # 验证通过后保存 Cookie之后就可以直接加载使用 # save_cookies_to_file(session)注意上面代码中的接口地址是占位符因为视频号的接口经常调整且具体接口字段涉及业务逻辑不建议直接照搬。你需要根据自己实际要调用的功能自行在 Network 面板中找到对应的请求地址。4.3 第三步保存并复用 Cookie登录验证通过后Cookie 建议立即保存到本地文件中。原因很简单微信视频号的会话不会永久有效有时几小时有时几天过期时间不确定。把 Cookie 持久化后下次使用前先加载文件判断是否有效失效再重新扫码这是最合理的节奏。def is_session_valid(session): 快速判断 Cookie 是否仍然有效 try: # 访问一个需要登录态的接口根据返回判断 test_url https://channels.weixin.qq.com/cgi-bin/...# 占位 resp session.get(test_url, timeout10) if resp.status_code 200: # 这里需要结合具体接口的返回结构判断 return True return False except Exception: return False # 实际使用中循环判断 cookie_file cookies.json try: session load_cookies_from_file(cookie_file) if is_session_valid(session): print(本地 Cookie 有效直接复用) else: print(Cookie 已过期请重新扫码获取) except FileNotFoundError: print(未找到 Cookie 文件请先完成首次登录)这里不要用状态码 200 作为唯一判断依据还需要检查返回的 JSON 里有没有包含业务错误码。有些接口即使未登录也会返回 200但里面会把登录失效的信息放在业务字段里。4.4 第四步关于 429 限流的处理使用过程中有个高频问题请求频率稍微一高返回就变成429 Too Many Requests或者附带exceeded retry limit, last status: 429 too many requests的错误信息。不少人在这一步误以为是自己 Cookie 配错了折腾半天才发现根本不是。微信视频号的接口是有频率限制的。同一个 Cookie 在短时间内请求次数过多就会触发限流。我猜测服务端会根据 IP、Cookie、请求特征等多个维度做综合判断。规避的核心思路是降低请求频率以及保持请求特征的一致性。import time import random def safe_request(session, url, max_retries3): 带限流保护的请求封装 for attempt in range(max_retries): try: resp session.get(url, timeout10) # 处理限流 if resp.status_code 429: wait_time 30 * (attempt 1) print(f触发限流等待 {wait_time} 秒后重试) time.sleep(wait_time) continue # 处理其他错误 resp.raise_for_status() return resp except requests.exceptions.ConnectionError as e: print(f连接异常: {e}) time.sleep(5) except requests.exceptions.Timeout as e: print(f请求超时: {e}) time.sleep(5) print(多次重试后仍然失败) return None实际测试下来两次请求之间至少间隔 3 到 5 秒比较稳妥。如果是批量拉取数据建议每次请求后加一个随机延时模拟真实用户的操作节奏。5. 常见问题与排查技巧实录5.1 Cookie 中文乱码与编码问题微信视频号的 Cookie 里有几个字段的值是 URL 编码后的中文内容比如nickname字段。直接打印到控制台时会显示成%E6%9D%8E%E5%9B%9B这样的格式容易被误认为乱码。其实这是正常的 URL 编码。Requests 在请求时不需要手动解码直接原样携带发送即可。如果你需要读取明文内容解码一下就行from urllib.parse import unquote encoded_value %E6%9D%8E%E5%9B%9B decoded_value unquote(encoded_value) print(decoded_value) # 输出李四5.2 Cookie 中的 HttpOnly 字段丢失前面提到过HttpOnly 属性的 Cookie 无法通过document.cookie读取。如果你发现从控制台读到的 Cookie 比 Network 面板里看到的少了一截大概率就是漏掉了 HttpOnly 字段。解决方案就一个去 Network 面板里复制不要用控制台读取。这个坑我早期踩过好几次写脚本时以为 Cookie 已经拿全了结果接口一直返回未登录排查了半天才发现少了好几个关键字段。5.3 接口返回 429 的处理策略429 错误的处理分为两个层级第一层是代码层。用上面提供的 safe_request 函数做重试加上延时。注意重试次数不要过多三次左右足够过多的重试反而会加重限流状态。第二层是策略层。如果频繁触发 429说明你的请求频率确实太高了。这时候不要硬扛直接把请求间隔调大或者增加代理 IP 做流量分散。但要注意视频号对异常 IP 同样有风控策略换 IP 不能解决本质问题。5.4 登录二维码过期与刷新如果从打开登录页到扫码确认的时间太长二维码会过期。过期后页面会提示刷新二维码。这个不影响脚本运行重新打开一个二维码即可。在实际项目中我的做法是把登录页面的 Cookie 获取做成一个独立的命令行工具扫码成功后自动退出避免长时间保持浏览器打开。这样每次使用时重新执行一次扫码也就十几秒的事体验比一直挂着浏览器好很多。5.5 常见问题速查表问题现象可能原因解决方案接口返回未登录Cookie 缺失或过期重新扫码获取完整 Cookie接口返回 429请求频率过高触限加延时降低请求频率控制台无法看到 CookieHttpOnly 属性限制改用 Network 面板查看中文显示为 % 编码URL 编码正常现象使用 unquote 解码查看请求一直超时IP 被封或网络异常检查网络环境更换出口 IPCode 运行报 Cookie 格式错误手动整理时丢字段直接从浏览器复制原始字符串6. 扩展基于 Cookie 的更多玩法拿到 Cookie 之后能做的不只是登录验证。以视频号为例有几种典型的使用场景。6.1 批量获取视频信息视频号创作者后台提供了数据接口可以查看每个视频的播放量、点赞数、评论数等。通过 Requests 携带 Cookie 请求这些接口就能把数据拉下来存到 Excel 或数据库里做分析。这个方向特别适合做内容运营的人。每天定时跑一次脚本自动汇总前一天的数据省去手动打开后台逐个查看的麻烦。6.2 视频号视频下载通过视频号网页端的接口可以拿到视频的原始地址。然后直接用 Requests 下载不需要浏览器参与。这里需要说明只下载自己账号有权限访问的视频尊重平台的规则和版权。我的代码里就只处理了“自己创作内容”的场景不涉及任何违规操作。下载的核心逻辑是先请求视频信息接口拿到 play_url再流式写入本地文件def download_video(session, video_url, save_path): 下载视频文件 resp session.get(video_url, streamTrue, timeout30) total_size int(resp.headers.get(Content-Length, 0)) downloaded 0 with open(save_path, wb) as f: for chunk in resp.iter_content(chunk_size1024 * 256): f.write(chunk) downloaded len(chunk) print(f下载完成: {save_path})6.3 定时监控与通知把 Cookie 保存好后可以写一个定时任务每隔几分钟请求一次接口检测是否有新评论或新播放。发现变化后通过企业微信机器人或邮件发通知。这个需求本质上就是给脚本加一个定时调度用系统自带的 cron 就行不用额外安装复杂的调度框架。7. 关于这套方案的一些心得体会从我个人的使用感受来说用 Requests 替代 Selenium 获取微信视频号 Cookie是我在这个项目上做过的最正确的决定之一。整套方案的代码量大幅缩减部署环境从“必须装浏览器”变成了“只要有 Python 就能跑”线上运维省心太多了。有几个小经验想额外分享。第一Cookie 存储格式选 JSON 而不是文本。JSON 便于读写和转换。我自己最开始是存在纯文本里用split(;)去切后来改成 JSON 才发现之前绕了远路。第二定时任务跑批时程序里要显式设置请求超时。不加超时的话网络异常可能会导致脚本卡住十几个小时不退出。第三保存 Cookie 的文件权限要设置好。因为里面包含了账号的完整登录凭证如果服务器上还有别人能访问存在安全隐患。建议把文件权限设为 600。第四不要试图用同一套 Cookie 同时跑太多并发请求。视频号对并发控制比较严格并发太高很容易触发封禁。宁愿跑慢一点也不要冒这个险。这套方案的完整代码已经贴在前面了核心逻辑加密也就两百多行。如果你正打算用 Selenium 去做视频号登录我真心建议你先试试 Requests 这套轻量方案大概率能让你的维护成本降低一个量级。