【免费下载链接】douban-backup
命令行备份豆瓣账号的书影音游剧标记(想看/在看/看过),导出为与「豆伴」结构一致的 Excel。
豆瓣账号备份工具 douban-backup 是一个 Python 单文件脚本,能把豆瓣的书、影、音、游、剧标记(想看/在看/看过)抓下来,导出为与 Chrome 扩展「豆伴」结构一致的 Excel。整个工具的灵魂类就是DoubanClient——它只做了三件看似不起眼、实则决定备份成败的事:Cookie 认证、请求限速、Referer 改写。本文带你逐段读懂 douban_backup.py 中这段请求层的设计。
为什么需要单独一个请求层?
豆瓣的接口(m.douban.com/rexxar/api/v2/...)有三个特点:
- 需要登录态:几乎所有接口都要带
ck等 Cookie 才能返回数据; - 会校验来源:请求头里的
Referer不符合预期时容易异常; - 有限流:连续快速请求会触发 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 + dbcl2 | from_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 | 零配置登录态,快速失败 |
| 移动端 UA | L61-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。
相关推荐
douban-backup 源码深度解析(四):openpyxl 生成 14 Sheet Excel 与 CSV 降级方案
douban backup 源码深度解析(四):openpyxl 生成 14 Sheet Excel 与 CSV 降级方案 豆瓣备份工具 douban back
douban-backup 安全指南:Cookie 保管方法与豆瓣账号限流风险规避完整教程
douban backup 安全指南:Cookie 保管方法与豆瓣账号限流风险规避完整教程 douban backup 是一款命令行的豆瓣账号备份工具,能把你的
douban-backup 常见问题大全:Cookie 失效、429 限流等 10 个高频问题解决方案
douban backup 常见问题大全:Cookie 失效、429 限流等 10 个高频问题解决方案 使用 douban backup (豆瓣账号备份工具)备
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考