1. 抖音主页视频数据采集的场景与字段拆解抖音用户主页的视频列表里点赞、收藏、分享这三个数字是很多人做内容分析时最想拿到的原始素材。它们不像播放量那样被隐藏也不像评论内容那样需要额外清洗属于公开可见、结构稳定的互动指标。把这三个字段按视频维度落库之后你可以做选题复盘、账号成长曲线、竞品对比甚至给投放团队做参考。这个场景的核心难点不在能不能拿到而在怎么稳定地拿、怎么不重复地拿、怎么把字段对齐。抖音主页是滚动加载的接口用max_cursor做游标分页has_more判断是否还有下一页sec_user_id是作者维度的标识藏在主页 URL 里请求头里真正必需的字段其实很少但少一个就可能返回空数据。很多人第一次抓的时候会以为count100就能一次拿 100 条实测下来并不是服务端会按自己的节奏返回必须靠游标一页页翻。字段层面点赞对应digg_count收藏对应collect_count分享对应share_count评论是comment_count发布时间是create_time视频唯一标识是aweme_id。这几个字段组合起来就能构成一张可去重、可增量、可关联的主表。下面我会按先跑通一次请求 → 再封装分页 → 再落库去重 → 最后排错的顺序把整条链路写清楚。TaoToken 在这里的角色是统一 Key 通道你不需要在代码里散落多个平台的鉴权逻辑而是把请求统一走一个 API 入口由它做配额管理和鉴权。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。这样做的直接好处是当你有多个采集任务并行时配额和调用记录集中在一处排查到底是谁把额度用完了会轻松很多。适合读这篇的人写过一点 Python、知道 requests 怎么发 POST、想搭一套可复用采集流程的开发者或者做数据分析、需要把互动字段定期入库的运营同学。不需要你懂逆向但需要你愿意按步骤把请求参数一个个试出来。2. TaoToken 统一 Key 通道的前置准备在写采集脚本之前先把鉴权通道理顺。抖音主页接口本身对请求头敏感而如果你同时还在跑别的数据任务把 Key 管理集中到 TaoToken 会省掉很多重复配置。它的定位是统一 Key/API 通道负责请求鉴权和配额管理你只需要在请求里带上自己的 Key剩下的额度统计、调用记录由通道侧处理。第一步是拿到 Key。打开 https://taotoken.net/api-keys 登录后创建一个新的 API Key复制出来保存好。这个 Key 只显示一次丢了就得重建。建议按任务命名比如douyin-profile-crawler这样后面在控制台看用量时能一眼对应上。第二步是确认调用入口。API 基址是 https://taotoken.net/api 所有请求都往这个域名下发。如果你用的是 OpenAI 兼容风格的客户端Base URL 填https://taotoken.net/api即可如果是自己写 requests就把完整路径拼在基址后面。模型对话相关的调试可以在 https://taotoken.net/models 里先验证 Key 是否生效确认通道通了再进采集环节。第三步是理解配额。采集任务的特点是短时间高频、长时间低频所以配额管理比单次调用更重要。你可以在 https://taotoken.net/console 里看到当前用量如果发现某个任务把额度吃得太快优先检查是不是分页逻辑写成了死循环——这是新手最常见的额度黑洞。这里给一个最小可用的配置片段把 Base URL、Key、Model ID 三件套写全后面所有请求都复用这套{ base_url: https://taotoken.net/api, api_key: sk-你的TaoTokenKey, model_id: gpt-4o-mini, timeout: 30, retry: 3 }注意Key 不要硬编码进提交到 Git 的脚本里用环境变量或本地配置文件读取。采集脚本经常要分享给同事硬编码等于把额度送人。如果你后续要做长期编码或 Agent 类任务可以了解 Coding Plan https://taotoken.net/coding-plan 如果只是验证模型连通性用模型对话页 https://taotoken.net/models 就够。采集本身不依赖模型推理但把 Key 通道统一之后你可以在同一个项目里既做采集又做文本清洗不用来回切换鉴权方式。前置准备做完你应该手里有一个可用的 Key、一个确认能通的 Base URL、以及一份本地配置。接下来进入真正的请求构造。3. 可复制的请求配置与字段映射抖音主页视频列表接口是 POST请求体里带sec_user_id、max_cursor、count等参数。sec_user_id从主页 URL 里取形如https://www.douyin.com/user/MS4wLjABAAAA...最后那段就是。max_cursor初始传 0之后每次用上一页返回的值。count建议传 20传太大服务端也不会全给反而增加解析负担。请求头里真正必需的字段很少但User-Agent、Referer、Cookie这三样不能少。Cookie 是最容易过期的一环所以后面我会用自动化方式定期刷新。先把一次完整请求写成可复制的 Python 片段import requests, json, os BASE https://taotoken.net/api KEY os.environ[TAOTOKEN_KEY] HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://www.douyin.com/, Content-Type: application/json, Authorization: fBearer {KEY} } def fetch_page(sec_user_id, max_cursor0, count20): url f{BASE}/douyin/aweme/post payload { sec_user_id: sec_user_id, max_cursor: max_cursor, count: count } resp requests.post(url, headersHEADERS, jsonpayload, timeout30) resp.raise_for_status() return resp.json()字段映射表是这篇的核心交付物之一照着填就能把原始 JSON 转成结构化行原始字段含义入库列名类型aweme_id视频唯一标识video_idVARCHARdesc视频文案titleTEXTcreate_time发布时间戳publish_tsBIGINTdigg_count点赞数like_countINTcollect_count收藏数collect_countINTshare_count分享数share_countINTcomment_count评论数comment_countINTstatistics.play_count播放数可能为0play_countINT解析时注意statistics是嵌套对象点赞收藏分享都在里面不要在外层找。写一个转换函数def parse_item(item): stat item.get(statistics, {}) return { video_id: item.get(aweme_id), title: item.get(desc, ), publish_ts: item.get(create_time), like_count: stat.get(digg_count, 0), collect_count: stat.get(collect_count, 0), share_count: stat.get(share_count, 0), comment_count: stat.get(comment_count, 0), play_count: stat.get(play_count, 0), }分页策略用一个 while 循环控制判断has_more和max_cursordef crawl_all(sec_user_id): cursor, rows 0, [] while True: data fetch_page(sec_user_id, cursor) items data.get(aweme_list, []) rows.extend(parse_item(i) for i in items) if not data.get(has_more): break cursor data.get(max_cursor) if not cursor: break return rows提示has_more为 1 表示还能翻为 0 表示到底。不要只靠len(items) 0判断有些页会返回空列表但has_more仍为 1直接 break 会漏数据。这套配置跑通之后你已经能拿到一个作者的全部视频互动数据。下一步是验证请求是否真的成功以及把数据落库去重。4. 验证请求与去重入库脚本验证分两层先验证单次请求返回结构对不对再验证整条分页链路有没有漏页。单次验证最简单的方式是打印aweme_list的长度和第一条的digg_count如果长度大于 0 且字段有值说明请求头、参数、鉴权都通了。如果返回空优先查sec_user_id是否复制完整、Cookie 是否过期。分页验证用一个计数动作记录每次请求的max_cursor和返回条数最后看总数是否和主页显示的视频数接近。差太多就说明中途断了。我试过在翻到第 5 页时因为 Cookie 失效返回空列表但has_more还是 1脚本继续翻结果后面全是空——所以每页都要检查返回条数连续两页为 0 就主动停。落库用 SQLite 最省事单文件、无需服务、支持INSERT OR IGNORE去重。建表语句CREATE TABLE IF NOT EXISTS douyin_video ( video_id TEXT PRIMARY KEY, author_id TEXT, title TEXT, publish_ts INTEGER, like_count INTEGER, collect_count INTEGER, share_count INTEGER, comment_count INTEGER, play_count INTEGER, crawled_at INTEGER );video_id设为主键配合INSERT OR IGNORE就能天然去重。入库函数import sqlite3, time def save_rows(rows, author_id, db_pathdouyin.db): conn sqlite3.connect(db_path) now int(time.time()) for r in rows: conn.execute( INSERT OR IGNORE INTO douyin_video (video_id, author_id, title, publish_ts, like_count, collect_count, share_count, comment_count, play_count, crawled_at) VALUES (?,?,?,?,?,?,?,?,?,?) , (r[video_id], author_id, r[title], r[publish_ts], r[like_count], r[collect_count], r[share_count], r[comment_count], r[play_count], now)) conn.commit() conn.close()增量更新用INSERT OR REPLACE也可以但会覆盖crawled_at如果你要保留首次采集时间就用INSERT OR IGNORE加一条单独的 UPDATE 语句更新互动数。实测下来互动数每天变一次用 REPLACE 更符合最新快照的需求。一次完整抓取验证动作选一个视频数在 50 以内的作者跑crawl_all打印len(rows)再查库SELECT COUNT(*) FROM douyin_video WHERE author_id?两个数字应该一致。然后重复跑一次库里的总数不变说明去重生效。这一步做完你的采集流程就算闭环了。Cookie 过期问题用自动化方式解决用浏览器自动化工具打开主页登录后把 Cookie 导出到本地文件脚本启动时读取。这样每次 Cookie 失效只需重新登录一次不用改代码。5. 常见报错排查401、local proxy failed、reading choices采集过程中最容易撞上的几类报错我按出现频率排一下每个都给定位思路。401 UnauthorizedTaoToken 侧返回 401通常是 Key 没带、带错、或者过期。检查Authorization头是不是Bearer sk-xxx格式中间有没有多余空格。如果 Key 是从环境变量读的打印一下长度确认没被截断。还有一种情况是 Key 有效但配额用尽控制台会显示用量去 https://taotoken.net/console 看一眼就知道。local proxy failed这个报错一般出现在本地网络层不是 Key 的问题。常见原因是请求超时设置太短、或者本地 DNS 解析异常。把timeout从 10 调到 30重试次数设 3基本能缓解。如果持续出现检查一下是不是同时开了多个采集进程把连接池占满了。reading choices 相关报错这类报错通常出现在解析响应时服务端返回的不是预期 JSON而是 HTML 或空体。原因多半是请求头缺了Referer或User-Agent被识别。补全请求头并且确认Content-Type是application/json。如果返回体里出现choices字段但结构不对说明你误把模型对话接口的响应当成了采集接口的响应检查 URL 路径有没有拼错。OAuth 相关报错如果你用的是需要 OAuth 流程的客户端报错通常指向 token 刷新失败。检查 refresh token 是否过期或者回调地址是否和注册时一致。采集脚本一般用 API Key 就够不需要走 OAuth遇到这类报错先确认自己是不是用错了鉴权方式。返回空列表但 has_more1这不是报错但比报错更坑。原因是 Cookie 失效或sec_user_id错误。加一个连续空页计数连续两页为空就停并打印当前 cursor 方便复查。字段缺失某些视频没有collect_count解析时用.get(collect_count, 0)兜底不要用[collect_count]直接取否则 KeyError 会中断整批。排查顺序建议先看 HTTP 状态码再看响应体前 200 字符最后看请求头。80% 的问题在前两步就能定位。6. 把采集流程接到统一通道上到这里你已经有了请求构造、分页、字段映射、去重入库、排错五块拼图。最后一步是把它们串成一个可重复运行的任务读配置 → 取 Cookie → 翻页采集 → 落库 → 打印统计。每次运行只改sec_user_id和作者名其余复用。统一 Key 通道的价值在批量场景下才明显。当你同时跑 5 个作者的采集每个作者的请求都走同一个 Base URL 和同一把 Key配额消耗、失败重试、调用记录都集中在一处。出问题时不用挨个脚本查直接看控制台用量曲线哪个时间段飙高就是哪个任务在跑。如果你要把采集结果接到下游做分析可以在落库后加一步导出 CSV或者直接查 SQLite。长期跑的话建议把 Coding Plan https://taotoken.net/coding-plan 纳入考虑把采集、清洗、报告生成放在同一个项目里Key 只配一次。接入文档在 https://taotoken.net/doc 里面有完整的接口说明和示例。API Key 管理在 https://taotoken.net/api-keys 控制台在 https://taotoken.net/console 。采集脚本本身不复杂复杂的是稳定性和去重把这两点做扎实后面换任何作者都只是改一个参数的事。