豆瓣客电影解析 API 开发者文档

DEVELOPER API

API 开发者文档

通过 HTTP JSON 接口解析豆瓣电影/剧集,获取排版文本或结构化数据。本文档涵盖接入流程、鉴权、频控、状态码与完整字段说明。

Base URL https://www.doubanke.com/api

概述

本 API 提供两类能力:

  • parse:根据豆瓣 ID、链接或片名解析影片(片名可能返回候选列表)。
  • movie:根据豆瓣 ID 读取本站已缓存的影片数据(不触发豆瓣抓取)。

所有接口均返回 application/json; charset=utf-8。成功时顶层包含 ok: true;失败时 ok: false 并附带 error 与 HTTP 状态码。

项目说明
协议HTTPS 推荐(生产环境)
方法GET / POST
编码UTF-8
强制 Key未开启(建议仍使用 Key 以便统计与更高配额)

鉴权说明

注册并登录用户中心后,可获取以 dk_ 开头的 API Key。支持以下三种传递方式(任选其一):

方式示例
Query 参数 key?key=dk_xxxxxxxx
Query 参数 api_key?api_key=dk_xxxxxxxx
请求头 X-API-KeyX-API-Key: dk_xxxxxxxx
请求头 AuthorizationAuthorization: Bearer dk_xxxxxxxx

Key 可在用户中心随时启用 / 停用 / 更换。停用或账号被禁用后,接口返回 403

接口列表

解析影片 action=parse

GET https://www.doubanke.com/api?action=parse&key=YOUR_KEY&q=肖申克的救赎&format=json

根据豆瓣 ID、链接或片名解析。若片名匹配多部作品,返回 type: candidates 候选列表,需用 id + type 再次请求确认。

读取缓存 action=movie

GET https://www.doubanke.com/api?action=movie&key=YOUR_KEY&id=1292052&format=json

仅读取本站数据库中已存在的影片记录。若从未解析过该 ID,返回 500 及错误信息「缓存中没有这部影片」。

请求参数

参数位置必填说明
actionQuery / Bodyparse(默认)或 movie
key / api_keyQuery / Header建议API Key
q / queryQuery / Bodyparse 时豆瓣 ID、链接或片名
idQuery / Bodymovie 时 / 候选确认时豆瓣条目 ID
typeQuery / Body候选确认时传 movietv
formatQuery / Bodytext(默认)、jsonmarkdownmd 同义)
refreshBody / Query1 / true 强制刷新豆瓣数据与海报缓存

POST 请求可使用 Content-Type: application/json,字段名与 Query 相同,例如:

{
  "query": "1292052",
  "format": "json",
  "refresh": false
}

响应结构

成功 — 单条结果 type: result

{
  "ok": true,
  "type": "result",
  "douban_id": "1292052",
  "title": "肖申克的救赎",
  "original_title": "The Shawshank Redemption",
  "year": "1994",
  "rating": 9.7,
  "rating_count": 3245678,
  "poster": "https://你的域名/posters/1292052.jpg",
  "poster_local": "https://你的域名/posters/1292052.jpg",
  "poster_url": "https://img*.doubanio.com/...",
  "formatted": "◎译  名 ...\n◎片  名 ...",
  "cached": true,
  "data": { "...": "结构化影片对象,见字段说明" }
}

format=json 时,posterposter_localdata.poster 均返回本站海报的完整 URL(已保存到 posters/ 目录)。豆瓣原图仅保留在 poster_url 字段。若本地尚未保存海报,图片字段为空字符串。

format=markdown 时,额外返回 markdown 字段(与 formatted 相同),为可直接粘贴到 GitHub、Notion、Obsidian 等平台的 Markdown 卡片,含海报、表格与简介引用块。

成功 — 候选列表 type: candidates

{
  "ok": true,
  "type": "candidates",
  "query": "无间道",
  "candidates": [
    {
      "id": "1307919",
      "title": "无间道",
      "year": "2002",
      "sub_title": "Infernal Affairs",
      "type": "movie",
      "img": "https://你的域名/posters/1307919.jpg",
      "url": "https://movie.douban.com/subject/1307919/"
    }
  ]
}

确认候选后再次请求:action=parse&id=1307919&type=movie&q=无间道

失败

{
  "ok": false,
  "error": "错误描述",
  "code": "error_code"
}

HTTP 状态码

状态码含义常见 code
200请求成功(含业务成功与候选列表)
400请求参数错误missing_querymissing_idunknown_action
401未提供 API Key(管理员开启强制 Key 时)missing_api_key
403Key 无效、已停用或账号禁用invalid_api_keyapi_key_disabled
429超过频控限制rate_limit_minuterate_limit_dailyrate_limit_exceeded
500服务端或上游豆瓣错误server_error

频控策略

携带 API Key 的调用按用户等级限流;未带 Key 的匿名调用按 IP 限制。响应头会附带配额信息,便于客户端自行退避。

用户等级配额

等级分钟频率每日上限适合场景
免费用户 10 次 / 分 200 次 / 天 个人学习、接口联调、小规模脚本测试、偶尔查询影片信息。
初级用户 60 次 / 分 5,000 次 / 天 个人博客、小型网站、公众号工具、日调用量较低的应用集成。
中级用户 200 次 / 分 20,000 次 / 天 中小型平台、影视社区、内容站批量入库、多端应用与中频业务调用。
高级用户 不限 极高(按量抵扣) 大型企业、高并发业务、数据平台与定制化对接;配额极高,按量抵扣,详谈专属方案。

新注册用户默认为免费用户。升级等级请联系管理员或在用户中心查看说明。

匿名调用(未带 Key)

场景限制窗口
按 IP10 次60 秒

响应头

Header说明
X-RateLimit-Tier当前用户等级(free / basic / intermediate / advanced)
X-RateLimit-Limit每分钟允许的最大请求数
X-RateLimit-Remaining当前分钟内剩余可用次数
X-RateLimit-Limit-Day每日允许的最大请求数
X-RateLimit-Remaining-Day今日剩余可用次数
X-RateLimit-Window分钟窗口长度(固定 60 秒)
X-RateLimit-ResetUnix 时间戳,建议在此时间后重试
Retry-After触发 429 时返回,建议等待秒数

触发限流时响应示例:

{
  "ok": false,
  "error": "本分钟调用次数已达上限(10 次 / 分)",
  "code": "rate_limit_minute",
  "retry_after": 60
}

建议:优先使用 API Key 获取对应等级配额;对 429 实施指数退避;避免高频 refresh=1 强制刷新。

多语言调用示例

YOUR_KEY 替换为用户中心获取的 Key,将域名替换为实际站点。

# 解析片名
curl -G "https://www.doubanke.com/api" \
  --data-urlencode "action=parse" \
  --data-urlencode "key=YOUR_KEY" \
  --data-urlencode "q=肖申克的救赎" \
  --data-urlencode "format=json"

# POST JSON
curl -X POST "https://www.doubanke.com/api?action=parse" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_KEY" \
  -d '{"query":"1292052","format":"json"}'
const res = await fetch("https://www.doubanke.com/api?action=parse", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": "YOUR_KEY"
  },
  body: JSON.stringify({ query: "1292052", format: "json" })
});
const data = await res.json();
if (!data.ok) throw new Error(data.error);
console.log(data.title, data.data);
import requests

resp = requests.get("https://www.doubanke.com/api", params={
    "action": "parse",
    "key": "YOUR_KEY",
    "q": "1292052",
    "format": "json",
}, timeout=30)
resp.raise_for_status()
data = resp.json()
if not data.get("ok"):
    raise RuntimeError(data.get("error"))
print(data["title"])
$ch = curl_init("https://www.doubanke.com/api?action=parse");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Content-Type: application/json",
        "X-API-Key: YOUR_KEY",
    ],
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode([
        "query" => "1292052",
        "format" => "json",
    ], JSON_UNESCAPED_UNICODE),
]);
$body = curl_exec($ch);
$data = json_decode($body, true);
if (!$data["ok"]) {
    throw new RuntimeException($data["error"]);
}
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
)

func main() {
    u, _ := url.Parse("https://www.doubanke.com/api")
    q := u.Query()
    q.Set("action", "parse")
    q.Set("key", "YOUR_KEY")
    q.Set("q", "1292052")
    q.Set("format", "json")
    u.RawQuery = q.Encode()

    resp, err := http.Get(u.String())
  if err != nil { panic(err) }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}
HttpClient client = HttpClient.newHttpClient();
String url = "https://www.doubanke.com/api?action=parse&key=YOUR_KEY&q=1292052&format=json";
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(url))
    .GET()
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

返回字段详尽说明

顶层字段(type: result

字段类型说明
okboolean是否成功
typestringresult / candidates
douban_idstring豆瓣条目 ID
titlestring中文片名
original_titlestring原名
yearstring上映年份
ratingnumber|null豆瓣评分(0–10)
rating_countinteger评分人数
posterstring本站海报完整 URL(format=json 时)
poster_urlstring豆瓣原始海报 URL(仅供参考)
poster_localstringposter 相同,本站海报完整 URL
is_tvboolean是否为剧集
urlstring豆瓣条目页链接
formattedstring◎ 风格排版文本;format=markdown 时为 Markdown 卡片
markdownstringMarkdown 卡片(仅 format=markdown 时返回)
cachedboolean是否来自本站缓存(未实时抓取豆瓣)
updated_atstring|null缓存更新时间 Y-m-d H:i:s
data / movieobject结构化影片对象(内容相同)

data 对象字段

字段类型说明
douban_idstring豆瓣 ID
titlestring片名
original_titlestring原名
akastring[]又名列表
yearstring年份
countriesstring[]制片国家/地区
genresstring[]类型,如剧情、犯罪
languagesstring[]语言
pubdatesstring[]上映日期
durationsstring[]片长或单集时长
episodes_countinteger集数(剧集)
episodes_infostring集数说明
directorsobject[]导演,含 namelatin_name
writersobject[]编剧
actorsobject[]主演(通常最多 8 人)
ratingnumber|null评分
rating_countinteger评分人数
introstring剧情简介
posterstring本站海报完整 URL
poster_localstring本站海报完整 URL
poster_urlstring豆瓣原始海报 URL
urlstring豆瓣链接
imdbstringIMDb ID,如 tt0111161
tagsstring[]豆瓣标签
honorsstring[]获奖信息
is_tvboolean是否剧集
subtypestringmovie / tv
card_subtitlestring卡片副标题

候选对象 candidates[]

字段类型说明
idstring豆瓣 ID
titlestring标题
yearstring年份
sub_titlestring副标题/英文名
typestringmovietv
imgstring封面图;format=json 且本地已缓存时为本站完整 URL,否则为豆瓣地址
urlstring豆瓣链接

安全与防盗

本站对海报资源与 API 调用做了基础防护,可在后台 API 设置 → 安全与防盗 中调整。

海报防盗链

JSON 返回的 poster 字段指向本站资源网关(/serve_asset 或经伪静态映射的 /posters/文件名)。开启防盗链后:

  • 仅允许来自本站或「额外允许来源」的 Referer 访问;
  • 若配置了 asset_token,URL 会附带 exp + sig 签名,在有效期内可跨站引用;
  • 也可在 URL 上附加 token=你的密钥(与后台 asset_token 一致)供服务端拉取。

Nginx 环境请在伪静态中加入 posters 重写规则,参见项目 rewrite/baota_nginx.conf

API 来源校验

未携带 API Key 的浏览器请求会校验 Origin / Referer 是否属于允许站点,防止第三方网页滥用本站接口。携带有效 Key 的调用不受此限制。

响应安全头

全站默认输出 X-Content-Type-OptionsX-Frame-OptionsContent-Security-Policy 等头;HTTPS 下启用 HSTS。

错误处理建议

  1. 始终检查 HTTP 状态码与 JSON 中的 ok 字段。
  2. type: candidates 做二次选择,不要直接当作最终结果。
  3. 收到 429 时读取 retry_afterRetry-After 头后重试。
  4. 豆瓣风控(验证码)会返回 500,请降低频率或稍后再试。
  5. 生产环境请妥善保管 Key,不要写入前端公开代码。

常见问题

网页解析和 API 有什么区别?
能力相同。API 适合程序调用;携带 Key 的调用会计入用户中心统计并享有更高频控配额。
format=textjsonmarkdown 有何不同?
text 返回 ◎ 风格排版文本;json 侧重结构化 datamarkdown 返回含海报、表格、简介引用块的 Markdown 卡片,适合论坛/GitHub/笔记软件。
JSON 里的海报地址是什么格式?
format=json 时,poster / poster_local 返回本站完整地址(经资源网关,可能含签名参数)。可直接用于本站或已授权域名下的展示;外站引用需配置允许来源或签名密钥。
如何获取 API Key?
用户中心注册并登录,于控制台复制 Key。