
做用户研究或者内容监控的同学应该都碰到过这种需求拿到一条抖音热门视频想知道评论区里到底谁在发言、大家聊什么、评论内容整体是夸还是踩。要回答这些问题绕不开两样基础数据、评论正文和用户ID。这篇就是我从零开始捣鼓“抖音用户评论和ID采集方法”的完整记录方案不算高端但胜在能落地适合有一定Python基础的开发者也适合做数据分析、竞品调研的朋友参考。我试过纯手工复制也试过Selenium模拟点击最后稳定使用的还是“浏览器抓包 requests模拟接口请求”这条路。核心思路就一句话把抖音网页版自己发出的评论请求看懂然后用代码按同样的参数去请求再把JSON里的user_info和cid字段抽出来就行。下面直接开讲。1. 采集方案的选型与整体思路1.1 先搞清楚你要收集的是哪几个ID很多新手一开始就懵因为“ID”这个词在抖音场景里起码有四种含义。第一种是视频IDaweme_id每条视频的唯一编号通常是一串18位左右的数字。采集评论时它是请求接口的入参没有它连门都进不去。第二种是评论IDcid每条评论的唯一编号。做后续的去重、增量采集、回复关联全靠这个字段。第三种是用户IDuid账号在系统内部的唯一数字编号。注意它和用户可以自己改的“抖音号”不是一回事抖音号是自定义的字母数字组合而uid是平台分配的数据结构里藏在user_info字段下。第四种是sec_uid加密后的用户ID形似一串长字符串用于用户主页等场景的请求参数。明白了这几个概念采集目标就很清晰了通过视频ID拿到评论ID、用户ID和评论内容。至于其他字段比如点赞数、评论时间、昵称、粉丝信息都是附加收获。1.2 不同采集路径怎么选市面上流传的方案五花八门我按实际体验做了个对比方案优点缺点适合场景手动复制粘贴零门槛不用写代码效率极低量大了完全不可行临时看几条Selenium/Playwright模拟浏览器不碰接口签名看到什么抓什么运行慢、吃内存、容易被平台风控识别低频小批量、应急兜底requests模拟接口请求速度快、返回结构化、方便后续分析需要登录Cookie要处理翻页和频率控制批量数据分析、长期监控第三方采集器或付费接口开箱即用费用高、数据稳定性不透明没有开发人力或时间紧迫我最终选了requests模拟接口这条路。原因很简单代码量少、能拿到完整字段、方便做成定时任务做增量采集。Playwright方案则作为备用等接口校验变严、常规请求被挡住时让浏览器自己跑我直接拦截它发出的接口响应。2. 核心原理与前置准备2.1 先用F12把接口底层逻辑摸清楚很多朋友一听说“爬虫”就头大其实抖音网页版的接口逻辑并不神秘。打开抖音网页版随便进一个有评论的视频按F12打开开发者工具切到Network面板过滤条件选Fetch/XHR。然后往下滚动评论区一排请求就会刷出来其中名字里带着comment/list的那个就是我们最需要关注的东西。点击这个请求能看到完整的接口地址和参数。请求地址一般是https://www.douyin.com/aweme/v1/web/comment/list/参数里比较关键的有aweme_id视频ID、cursor游标、count每页数量、sort_type0按热度1按时间。后端返回的是JSON数据里面有comments数组数组里每一项就代表一条评论。接口这东西听着玄说白了就是前端向后端要数据的固定URL。你怎么传参数它就怎么回数据。我们要做的用代码复刻浏览器这个“要数据”的动作。唯一绕不开的是登录状态正常的网页端评论查看需要登录所以后期的请求里必须带上自己的Cookie。2.2 环境搭建和工具准备本方案需要的环境非常轻量我列一下自己一直在用的组合Python 3.8及以上版本requests库发起HTTP请求pandas清洗数据用可选但推荐一个能正常登录抖音网页版的账号浏览器开发者工具用来复制Cookie和查看请求参数备用方案Playwright 浏览器驱动安装命令就两行pip install requests pip install pandas如果要用Playwright兜底再加一行pip install playwright playwright install chromiumCookie的获取方式用浏览器打开抖音网页版并登录F12打开开发者工具切到Network随便点击一个接口请求在请求头里找到Cookie那一长串全部复制出来备用。需要注意Cookie是有有效期的尤其是登录后的sessionid过期之后请求会返回异常到时候重新复制一遍就行。3. 实操流程从一条抖音链接到一份评论CSV3.1 第一步从分享链接还原出视频ID实际场景中我们更多拿到的是分享口令类似“7.43 复制打开抖音看看【某某的作品】…… https://v.douyin.com/xxxxx/”。这种短链接需要先还原成真实地址再从真实地址里抽取视频ID。短链接本质是一个302跳转用requests跟随重定向即可import requests import re def resolve_short_url(short_url): headers { 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 } resp requests.get(short_url, allow_redirectsTrue, headersheaders, timeout10) return resp.url def extract_aweme_id(url): # 优先匹配 /video/ 或 /note/ 后面的数字 m re.search(r/(?:video|note)/(\d), url) if m: return m.group(1) # 某些情况下ID会出现在路径末尾 m re.search(r/(\d{15,})/, url) if m: return m.group(1) return None short_url https://v.douyin.com/xxxxx/ real_url resolve_short_url(short_url) aweme_id extract_aweme_id(real_url) print(real_url, aweme_id)实际跑下来短链接重定向之后常见两种格式www.douyin.com/video/一大串数字或者www.douyin.com/note/一大串数字。正则在写的时候最好两种都兼容别只认video否则容易漏。3.2 第二步构造评论请求并解析关键字段拿到视频ID之后就可以去请求评论接口了。下面是我一直在用的核心代码注释已经写得很详细直接拷贝改Cookie就能用import requests import json import time import random COOKIE 换成你在浏览器复制的完整Cookie HEADERS { 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://www.douyin.com/, Cookie: COOKIE, } COMMENT_API https://www.douyin.com/aweme/v1/web/comment/list/ def fetch_comments(aweme_id, max_pages10): cursor 0 all_comments [] for page in range(max_pages): params { aweme_id: aweme_id, cursor: cursor, count: 20, sort_type: 0, device_platform: webapp, aid: 6383, } try: resp requests.get(COMMENT_API, paramsparams, headersHEADERS, timeout10) resp.raise_for_status() data resp.json() except Exception as e: print(f第{page 1}页请求失败: {e}) break comments data.get(comments, []) if not comments: print(当前页没有评论数据停止翻页) break for c in comments: user_info c.get(user_info, {}) or {} all_comments.append({ comment_id: c.get(cid), video_id: aweme_id, user_id: user_info.get(uid), nickname: user_info.get(nickname), content: c.get(text), digg_count: c.get(digg_count), create_time: c.get(create_time), }) # has_more 为 0 或不存在说明没有下一页 if not data.get(has_more): break cursor data.get(cursor, cursor) # 随机延时避免请求过快 time.sleep(2 random.random() * 3) return all_comments代码逻辑不复杂但有几个地方值得展开说明都是我自己踩过之后才理解的。3.3 第三步游标翻页而不是页码翻页很多没接触过接口开发的朋友一看“翻页”就条件反射去找page1、page2这样的参数。抖音网页版评论接口不是这套逻辑它用的是游标cursor。cursor可以理解成一个书签。第一次请求时cursor0后端返回当前列表同时返回一个next cursor的值。下一次请求我们把这个值原样带上去后端就会从上次的位置继续往下给数据。返回的JSON里对应的是has_more和cursor这两个字段。为什么这样设计因为评论数据是实时增长的页码翻页很容易出现重复或遗漏而游标是增量式的位置信息被服务端记录稳定性更好。所以循环里判断是否继续翻页的唯一标准就是has_more是否为1而不是简单数页面。count参数我习惯设成20这是网页端比较常见的每页数量。设太大不一定有效后端可能直接截断。3.4 第四步把用户ID、评论ID和内容全部提取出来评论数据提取的功夫全在字段解析上。一段原始评论对象长这样我做了脱敏处理{ cid: 7353924598720315660, text: 这段视频我看了三遍质量确实高, digg_count: 1234, create_time: 1712908856, user_info: { uid: 1234567890123456789, sec_uid: MS4wLjABAAAAxxxxxx, nickname: 某位网友, avatar_thumb: {url_list: [...]} } }cid对应评论IDtext是评论正文user_info.uid是用户ID。如果你的目标只是评论正文和用户ID这些已经够用了。但实际场景往往更复杂一点。评论区会有“作者赞过”的置顶评论也会有大量带emoji的表情评论还会有回复评论的二级评论。常规接口返回的主要是一级评论也就是直接挂在视频底下的评论不包含那种“回复某人”的嵌套结构。如果你想连二级评论一起要需要额外解析reply_comment相关字段或者调用单独的回复列表接口这一块后续可以单独开一篇写。我的习惯是尽量先保存原始字段清洗是后面的事。字段宁多勿少等分析的时候再来挑。4. 数据落地字段清洗与文件存储4.1 CSV存储与去重细节评论采集完下一步就是落地。最省事的方案是转成DataFrame后存CSVimport pandas as pd df pd.DataFrame(all_comments) # 去掉重复评论避免网络异常导致的重复请求 df.drop_duplicates(subset[comment_id], inplaceTrue) # 时间戳转成可读时间 df[create_time] pd.to_datetime(df[create_time], units) # 处理评论内容里的换行符免得CSV错位 df[content] df[content].str.replace(r[\n\r\t], , regexTrue) # 编码务必用 utf-8-sig否则Excel打开是乱码 df.to_csv(douyin_comments.csv, indexFalse, encodingutf-8-sig)这里有一个很容易被忽视的坑CSV的编码。很多同学第一次存完用Excel打开全是乱码还以为是数据出问题了其实只是编码没选对。utf-8是给程序读的utf-8-sig是给Excel这类带BOM头的软件读的。论省心程度直接存成utf-8-sig最稳妥。如果你后续要做更复杂的分析比如情感分析、评论关键词提取建议同时存一份JSONL格式保留原始字段的完整结构尤其是user_info里那些潜在的扩展信息。4.2 数据质量与字段一致性检查请求返回的数据不一定总是干净的我在实际采集中最常遇到的几个脏数据问题第一user_info为空。有些被屏蔽或注销的账号接口可能返回空对象解析的时候直接用get然后兜底成空字典避免程序崩掉。第二content内容里夹杂大量表情符号。抖音评论里的emoji在JSON里可能是正常的Unicode不影响存储但如果你要做词频统计建议提前把特殊符号过滤掉。第三create_time是Unix时间戳。存进数据库或做分析之前一定要先转成可读时间不然看起来就是一行十位数字没法直接用。第四同一个用户在一条视频下可能发多条评论去重的时候要按comment_id去重不是按user_id。有些做用户分析的朋友会想当然按用户ID去重结果把真实评论数算低了。5. 常见问题与排查技巧实录5.1 评论列表返回空这是最常遇到的情况。第一反应先看aweme_id对不对可以用手机App打开视频查看分享链接里的ID是否和代码里一样。其次是Cookie问题没登录或者登录过期的Cookie后端会把你当成游客评论接口直接返回空内容。另外如果视频本身设置了“仅粉丝评论”或“作者关闭了评论区”接口也会返回空列表。这种情况纯属正常不用纠结。5.2 出现验证码或滑块当你请求频率一高抖音网页端可能会返回验证码页面或者直接弹滑块。遇到这种情况我个人经验是立刻停手不要继续用同一IP和Cookie死磕。等待10到30分钟把请求间隔调到5秒以上再继续。如果还是频繁触发可以换个网络环境或者换一个日常使用的账号。专门用来采集的小号平时多正常刷刷视频、看看评论账号权重高了触发验证的概率会明显降低。5.3 HTTP 429 Too Many Requests这个错误很多跑过采集的人都见过对应的提示大概是exceeded retry limit, last status: 429 too many requests429的意思是请求太频繁服务端在限流。这不是代码逻辑错误而是频率控制问题。解决办法也很直白降低单位时间内的请求数量每次请求之间加入随机延时不要用固定时间间隔因为固定间隔更容易被识别成机器行为。我自己的节奏是单视频采集时每页间隔2到5秒批量视频采集时每个视频之间间隔10秒以上。虽然慢但胜在稳定。数据采集是个体力活稳定比速度重要。我把常见问题整理成了一张速查表方便大家直接对照现象可能原因处理办法返回空comments视频ID错误、Cookie过期、未登录核对链接、重新复制Cookie返回verify或滑块请求频率过高、账号异常停止请求等待后再试降低频率状态码429请求太快被限流延长间隔加随机延时退避重试接口提示参数错误headers缺Referer或UA不对补上浏览器UA和RefererExcel打开CSV乱码编码不是utf-8-sig存文件时指定encodingutf-8-sig拿不到uid解析的字段名写错检查user_info下的uid字段5.4 接口签名校验变严怎么办严格来说抖音网页端接口有一套签名参数比如X-Bogus、a_bogus之类的。各平台的反爬策略一直在调整代码写完后过一阵子可能就失效了。遇到这种情况我一般不会死磕签名算法而是直接用Playwright降级方案。思路是这样的用Playwright打开浏览器登录抖音网页版访问目标视频页面然后拦截浏览器发出的评论接口响应直接解析拦截到的JSON。from playwright.sync_api import sync_playwright import json, time with sync_playwright() as p: browser p.chromium.launch(headlessTrue) context browser.new_context( user_agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ) page context.new_page() def on_response(response): if /aweme/v1/web/comment/list/ in response.url: data response.json() for c in data.get(comments, []): print(c.get(cid), c.get(user_info, {}).get(uid), c.get(text)) page.on(response, on_response) page.goto(https://www.douyin.com/video/视频ID, wait_untilnetworkidle) for _ in range(10): page.mouse.wheel(0, 2000) time.sleep(2)这个方案不直接拼接口签名、Cookie这些问题都被真实浏览器“内部消化”了。代价是慢、占资源但胜在稳定。在接口方案失效的间隙它能保证数据不断档。6. 采集频率控制和数据使用底线做抖音数据采集技术难度真的不是最高的真正的门槛在于“分寸”。先说我的个人经验。采集评论和ID本身是面向公开数据的技术操作但不代表可以不受限制地狂扫。我给自己定的几条规则很简单第一只采公开展示的数据。私信、隐藏作品、好友可见的内容、需要特定权限才能看的信息一律不碰。第二严格控制请求频率。无论是浏览网页还是调用接口单位时间内的请求量都保持在温和水平。原因很简单接口是公共服务资源高频请求既容易被限流也会给平台服务器带来不必要的压力。第三数据使用要规范。采集到的用户ID和评论内容用于个人学习、学术研究、竞品内容分析这类正当目的不用于骚扰用户、恶意营销、人肉搜索等场景。原始数据不要随意公开转售尤其是包含用户ID这种能关联到个人的信息。第四有商业化、规模化需求时优先考虑官方渠道。抖音开放平台提供了一部分官方数据能力与其和风控手段较劲不如走正规路径成本算下来可能还更低。这些不是我冠冕堂皇的说教而是踩过坑之后的真实感想。账号被限制、IP被临时封禁、数据采了一半断掉这些教训都在提醒我细水长流远比一锤子买卖靠谱。写在最后的几点体会整套流程跑下来你会发现核心就三步拿到视频ID、请求评论接口、解析字段。真正花时间的反而是心态调整和参数调优。我自己的建议是先拿一条视频跑通全流程确认每个字段都能拿到再扩展到批量采集先拿几十条数据验证格式再上全量任务。还有一个小技巧批量采集的时候把成功和失败的ID分开记录。失败的ID隔一段时间重试一次因为有些视频可能只是暂时下架或者网络抖动过会儿又能正常访问。这个小习惯能帮你省下很多重复请求的时间。另外采集脚本不要跑完就删尽量做成可复用的函数把Cookie、请求头、延时策略都参数化。抖音这边的接口政策有变动很正常把代码结构写好后面调整参数或者切换解析方式成本都会低很多。这篇分享基本把抖音用户评论和ID采集的主路径讲清楚了。后面我还会整理一份如何把采集结果做成可视化报表的流程包括评论情感分析、用户地域分布、高频词统计这些有意思的方向有需要的朋友可以先收藏关注。