news 2026/11/1 1:48:31

douban-backup 源码深度解析(一):DoubanClient 如何管好 Cookie、限速与 Referer 改写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
douban-backup 源码深度解析(一):DoubanClient 如何管好 Cookie、限速与 Referer 改写

【免费下载链接】douban-backup

命令行备份豆瓣账号的书影音游剧标记(想看/在看/看过),导出为与「豆伴」结构一致的 Excel。

项目地址:https://gitcode.com/gh_mirrors/dou/douban-backup
点击查看免费下载

豆瓣账号备份工具 douban-backup 是一个 Python 单文件脚本,能把豆瓣的书、影、音、游、剧标记(想看/在看/看过)抓下来,导出为与 Chrome 扩展「豆伴」结构一致的 Excel。整个工具的灵魂类就是DoubanClient——它只做了三件看似不起眼、实则决定备份成败的事:Cookie 认证、请求限速、Referer 改写。本文带你逐段读懂 douban_backup.py 中这段请求层的设计。

为什么需要单独一个请求层?

豆瓣的接口(m.douban.com/rexxar/api/v2/...)有三个特点:

  1. 需要登录态:几乎所有接口都要带ck等 Cookie 才能返回数据;
  2. 会校验来源:请求头里的Referer不符合预期时容易异常;
  3. 有限流:连续快速请求会触发 429 甚至封禁。

所以工具没有让上层代码直接requests.get(),而是把这些规则收敛到一个类里。项目注释也点明了设计来源:对齐原插件 background.js 的declarativeNetRequest(把 Referer 改成 m.douban.com)与 service.js 的requestInterval限速。

Cookie 认证:三种构造方式,一套会话

DoubanClient提供三个构造入口,对应 README.md 中「Cookie 来源(三选一)」:

方式入口适用场景
自动读浏览器from_browser()douban_backup.py#L123-L154本机已登录豆瓣,零配置
完整 cookie 串from_cookie_string()douban_backup.py#L156-L165从浏览器 F12 复制整串 Cookie
仅 ck + dbcl2from_ck_dbcl2()douban_backup.py#L167-L172只拿到两个关键 Cookie

几个值得注意的细节:

  • 自动读取的容错链:from_browser()会按chrome → edge → brave → opera → chromium → firefox → safari的顺序逐个尝试,任一浏览器里存在douban.com域名的 Cookie 就立即采用;全部失败时把每个浏览器的错误汇总后抛出,并提示改用--cookie。
  • 统一并入 Session:无论哪种来源,最终都是把 Cookie 写进同一个requests.Session的 cookie jar,后续请求自动携带。
  • 启动即校验:构造函数里调用_read_ck()(L175-L179)从 jar 中取出ck,取不到直接抛RuntimeError并提示「cookie 中缺少 ck」。这样在真正开始抓取前就能快速失败,而不是抓了几页才报错。
  • 伪装移动端:Session 创建后立即写入移动端 UA(MOBILE_UA,L61-L64),因为所有接口都是m.douban.com移动版端点,UA 与域名保持一致才「像真人在手机上操作」。

登录校验由checkin()(L218-L237)完成:请求https://m.douban.com/mine/页面,用 BeautifulSoup 解析#user元素拿到uid和用户名;解析不到就提示「未登录豆瓣,或登录已失效」。

请求限速:最小间隔节流,支持突发豁免

限速实现非常精炼,就是_throttle()(L181-L186):

def _throttle(self, continuous: bool = False) -> None: if continuous: return elapsed = time.time() - self._last_request if elapsed < self.request_interval: time.sleep(self.request_interval - elapsed)

设计要点:

  • 保间隔,而非等固定时长:记录上一次请求时刻_last_request,本次请求前若距上次不足request_interval秒(默认 1.0,可用--interval调大),就只 sleep「差值」。若上一次请求本身耗时 3 秒,则本次几乎不等待——既保证最小间隔,又不浪费时间。
  • continuous 豁免开关:request()接受continuous=True参数(L189-L195),表示允许连续发请求不做节流。当前主流程未使用它,但它为「500 降级逐条重抓」这类突发场景预留了通道。
  • 重试不吞错:request()内部对requests.RequestException最多重试retries次(默认 2),每次失败 sleep 1 秒后重试;重试耗尽则抛出最后一次异常(L199-L210)。
  • 超时统一 30 秒:避免网络异常时脚本无限挂起。

Referer 改写:一个默认值解决「来源校验」

request()的签名里有个容易被忽略的参数:

def request(self, method, url, referer: str = "https://m.douban.com/", ...)

它用headers.setdefault("Referer", referer)(L197-L198)给每个请求补上Referer头,默认是https://m.douban.com/。这对应了原 Chrome 扩展用declarativeNetRequest规则批量改写 Referer 的做法——扩展里要靠浏览器 API 拦截请求,Python 版则直接在每个请求头里写死,效果相同、实现更简单。

上层还会按场景传入更精确的 Referer,让请求来源与真实操作路径一致:

  • 查总数:https://m.douban.com/mine/(get_total,L441-L444)
  • 翻页抓取:https://m.douban.com/mine/{type},如https://m.douban.com/mine/movie(_crawl_segment,L461-L499)

这种「按业务路径伪造来源」的手法,是让接口稳定返回 200 的关键之一。

500 容错:限速之外的第二道保险

限速防的是「被打」,容错防的是「偶发故障」。InterestTask._crawl_segment发现接口返回 HTTP 500 时,会调用_crawl_sliced()(L502-L522):把原本一页 50 条的请求拆成 50 个count=1的单条请求逐个重试,仍然失败的单条直接跳过,不让一条坏数据毁掉整个备份。这同样对齐了原插件的行为。

小结

机制实现位置作用
多源 Cookie 构造 + 启动校验L123-L179零配置登录态,快速失败
移动端 UAL61-L64与 m.douban.com 端点保持一致
最小间隔节流 + 重试L181-L210避开限流,容忍网络抖动
Referer 默认值 + 场景化改写L189-L198通过来源校验
500 逐条重抓L502-L522单条失败不拖累整体

DoubanClient不到 150 行,却把「能不能稳定抓到数据」这件事兜住了。下一篇我们将深入Storage与InterestTask:SQLite 增量备份的 history 机制、_interest_equal的变更判定,以及 14 个 sheet 的 Excel 导出是如何做到与「豆伴」逐字一致的。

【免费下载链接】douban-backup

命令行备份豆瓣账号的书影音游剧标记(想看/在看/看过),导出为与「豆伴」结构一致的 Excel。

项目地址:https://gitcode.com/gh_mirrors/dou/douban-backup
点击查看免费下载
上一篇:Stoolap查询优化器解析:基于成本的计划与自适应执行如何提速你的查询
下一篇:多窗口与多根工作区:puppyone 桌面版窗口架构设计完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/1 1:47:43

DXVK 2.7.1:Direct3D 转 Vulkan 提速实操

DXVK 2.7.1&#xff1a;Direct3D 转 Vulkan 提速实操 【免费下载链接】dxvk Vulkan-based implementation of D3D8, 9, 10 and 11 for Linux / Wine 项目地址: https://gitcode.com/gh_mirrors/dx/dxvk Windows 游戏搬到 Linux&#xff0c;画面撕裂、帧率腰斩、进游戏就…

作者头像 李华
网站建设 2026/11/1 1:46:51

腾讯二面:@Bean 与 @Component 用在同一个类上,会怎么样?

一、面试场景还原&#xff1a;这道题到底在问什么先还原一个典型的腾讯二面现场。面试官已经问完 Spring IOC、循环依赖、Bean 生命周期这些常规问题&#xff0c;你回答得都比较顺。这时候他突然抛出一句&#xff1a;“你说 Component 是类级注解&#xff0c;Bean 是方法级注解…

作者头像 李华
网站建设 2026/11/1 1:46:50

Python ai-ensemble-suite 包实战案例与常见错误

1. 引言在机器学习工程实践中&#xff0c;单一模型的预测能力往往存在上限&#xff0c;而集成学习&#xff08;Ensemble Learning&#xff09;通过组合多个基学习器&#xff0c;能够显著提升模型的泛化能力、稳定性和鲁棒性。Python 生态中虽然已有 scikit-learn 的 VotingClas…

作者头像 李华