公开TikTok视频的评论是洞察受众反应的窗口:他们使用的语言、表达的情感,以及反复出现的主题。研究人员、分析师和内容团队从汇总信号中读取这些信息来把握趋势,而非追踪评论者个人。本指南将向您展示如何以实际有效的方式,针对JavaScript渲染的页面,使用Python抓取公开TikTok评论。
首先说明范围:这里的一切都限于公开视频上的公开评论。目标是汇总分析,包括评论文本、点赞数和回复数,可用于情感分析和主题汇总。这里不涉及构建单个评论者的用户画像。用户名和评论内容属于个人数据,因此整个流程都会谨慎对待,文末的合法性部分在您将其指向真实场景之前值得认真阅读。如果您希望先阅读更广泛的指南,请参阅我们的TikTok抓取指南。
您将构建什么
一个小型Python脚本,它接受一个公开的TikTok视频URL,通过带有JavaScript令牌的Crawling API获取完整渲染的页面,滚动加载更多评论,并解析少量公开的、主要是汇总性的字段:
- 评论文本 每条公开评论的可见内容。
- 点赞数 评论显示的汇总点赞数,而非点赞者的身份。
- 回复数 评论获得的汇总回复数。
- 视频元数据 评论所属的公开视频URL,用于归属标注。
请注意,分析输出中有意缺失了哪些内容:没有评论者资料、没有粉丝数据、没有任何将用户名与真实身份关联的尝试。这些都是个人数据,采集它们不在本文范围之内。我们会从页面读取用户名,因为标记中包含它,但隐私部分解释了为什么您不应将其与身份绑定并存储或传播。
为什么普通请求在TikTok上会失败
用普通的HTTP客户端请求一个公开的TikTok视频URL,您会得到一个技术上成功但实际上几乎为空的响应。TikTok在客户端渲染内容:真正的标记(包括评论)只有在页面的JavaScript在浏览器中运行并从内部接口获取数据后才会出现。单次静态请求永远不会执行该JavaScript,因此您想要的评论根本不在响应体中。
此外,TikTok会随着滚动异步延迟加载评论,并迅速标记爬虫形态的流量。数据中心IP范围、缺失的浏览器行为以及重复的模式在有意义的内容加载之前就会被限速或触发验证。因此,一个有效的评论抓取器在同一请求中需要具备两点:一个真实的浏览器(负责渲染和滚动页面),以及一个平台视为普通访问者的IP地址。您可以自己用无头浏览器和轮换住宅代理池来搭建,但维护这套栈的健康运行才是大部分工作量所在。Crawling API将两者折叠进一次调用:您发送一个带有JavaScript令牌的URL,它在受信任的住宅IP后渲染和滚动,并返回可供解析的完整HTML。底层机制请参阅JavaScript网站爬取指南。
Crawlbase提供两种令牌类型。普通令牌获取静态HTML,而JavaScript(JS)令牌会先在真实浏览器中渲染页面。TikTok是重度客户端渲染的,因此这里需要使用JS令牌。普通令牌返回的是与普通请求相同的接近空白的外壳,无法从中解析出任何评论。
前提条件
首先需要准备几样东西,每样都不费多少时间。
Python、HTML和CSS基础。 您应该能够运行脚本、用pip安装包,并能读懂CSS选择器,以便在TikTok标记发生变化时调整评论选择器。
Python 3.8或更高版本。 用python --version确认版本。如果没有,请从python.org安装,并确保pip已在PATH中。
Crawlbase账号和JS令牌。 注册后打开仪表盘,从账号文档页面复制您的JavaScript(JS)令牌。免费层包含1,000次请求,足够跟随操作。请像对待密码一样对待令牌:它用于验证您的请求,所以不要将其放入版本控制。
搭建项目
创建一个隔离的虚拟环境,然后安装爬虫所需的库。
python --version python -m venv tiktok_env source tiktok_env/bin/activate pip install crawlbase beautifulsoup4 pandas
在Windows上,用tiktok_env\Scripts\activate代替source命令激活环境。三个依赖各司其职:crawlbase是Crawling API的官方客户端,beautifulsoup4通过选择器从返回的HTML中解析字段,pandas则帮助您将结果汇总以便后续聚合分析。
第1步:获取渲染后的视频页面
首先获取完整页面。导入CrawlingAPI,用您的JS令牌初始化,然后请求公开视频URL。针对客户端渲染目标,有两个选项至关重要:ajax_wait指示API等待异步内容加载完毕,page_wait则固定等待若干毫秒,以确保延迟渲染的评论在捕获前出现。在解析前检查状态,让失败可见而非静默。
from crawlbase import CrawlingAPI crawling_api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) options = { "ajax_wait": "true", "page_wait": 10000, "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", } def fetch_html(url): try: response = crawling_api.get(url, options) if response["headers"]["pc_status"] == "200": return response["body"].decode("utf-8") print(f"Failed to fetch. Crawlbase status: {response['headers']['pc_status']}") return None except Exception as e: print(f"An error occurred: {str(e)}") return None if __name__ == "__main__": video_url = "https://www.tiktok.com/@nasa/video/7255327059302419738" html = fetch_html(video_url) print(html[:500] if html else "No HTML returned")
Crawling API从响应头中读取pc_status,该值反映上游获取结果,独立于代理传输的原始HTTP状态码。对于TikTok,10秒的page_wait是合理的起点;如果评论返回为空,可以适当增加。示例使用了一个公开组织账号,正是因为它是公开且非个人的。运行脚本后,您应该看到真实渲染的标记,这在您编写任何选择器之前就能确认渲染正常工作。
TikTok需要在受信任IP背后渲染并滚动的页面,一次调用完成。Crawling API接受JS令牌,在真实浏览器中运行页面,滚动加载延迟评论,并在服务端轮换住宅IP,让您无需自己维护无头浏览器集群和代理池。先在免费层指向一个公开视频试试。
第2步:将评论解析为结构化数据
手握渲染后的HTML,将其加载到BeautifulSoup并提取公开字段。TikTok使用稳定的data-e2e属性标记其组件,这比追踪深层嵌套、频繁改名的CSS类名耐久得多。评论列表位于评论容器内;每个评论项包含文本、点赞数和回复数。我们还会从页面头部读取视频作者用户名,用于归属上下文。
from bs4 import BeautifulSoup def text_or_none(node): return node.text.strip() if node else None def scrape_video_info(soup): username = soup.select_one("span[data-e2e='browse-username']") return {"Video Author": text_or_none(username)} def scrape_comments_listing(soup): return soup.select( "div[data-e2e='search-comment-container'] > " "div[class*='CommentListContainer'] > " "div[class*='DivCommentItemContainer']" ) def parse_comment(comment): text = comment.select_one( "div[class*='DivCommentContentContainer'] " "p[data-e2e='comment-level-1'] > span" ) likes = comment.select_one("div[class*='DivLikeContainer'] span") replies = comment.select_one("div[class*='DivReplyContainer']") return { "Comment Text": text_or_none(text), "Like Count": text_or_none(likes), "Reply Count": text_or_none(replies), }
每个辅助函数都防范节点缺失的情况,当元素被改名或不存在时返回None而不是抛出异常。评论列表选择器反映了TikTok的嵌套结构:一个评论容器,然后是列表容器,然后是各个评论项。我们从每个评论项中提取评论文本、点赞数和回复数。后两者是汇总数字,正是您进行主题和情感分析所需要的非个人信号。
TikTok会在无通知的情况下修改其标记和混淆的类名,这就是为什么这段代码依赖稳定的data-e2e属性和部分class*=匹配,而非脆弱的精确类名。当某个字段返回None时,在浏览器开发者工具中重新检查实时页面并更新选择器。定期维护选择器对于任何生产级爬虫来说都是正常操作。
第3步:通过滚动处理评论分页
TikTok使用无限滚动动态加载更多评论,因此单次渲染只能捕获第一批。Crawling API提供了一个scroll参数,告知无头浏览器在返回之前滚动页面并加载更多内容。默认滚动间隔为10秒;scroll_interval参数允许您延长它,以便加载更多评论批次。将这些选项加入分页请求中。
def fetch_html_with_scroll(url): scroll_options = { "ajax_wait": "true", "user_agent": options["user_agent"], "scroll": "true", "scroll_interval": 20000, } try: response = crawling_api.get(url, scroll_options) if response["headers"]["pc_status"] == "200": return response["body"].decode("utf-8") print(f"Failed to fetch. Crawlbase status: {response['headers']['pc_status']}") return None except Exception as e: print(f"An error occurred: {str(e)}") return None
20秒的scroll_interval给延迟加载的评论留出了渲染时间。更长的间隔可以加载更多评论,但每次请求的等待时间也更长,因此请根据您实际需要的批次数量进行调整。保持较低的请求量:代表性样本通常足以进行汇总分析,您很少需要某个视频上的全部评论。
第4步:组装完整爬虫
现在将获取、滚动和解析整合为一个可运行的脚本。它带着滚动渲染视频页面,读取公开视频作者作为上下文,将每条已加载评论解析为文本、点赞数和回复数,并输出干净的JSON供分析使用。
import json from crawlbase import CrawlingAPI from bs4 import BeautifulSoup crawling_api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) options = { "ajax_wait": "true", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", } def fetch_html_with_scroll(url): scroll_options = {**options, "scroll": "true", "scroll_interval": 20000} try: response = crawling_api.get(url, scroll_options) if response["headers"]["pc_status"] == "200": return response["body"].decode("utf-8") print(f"Failed to fetch. Crawlbase status: {response['headers']['pc_status']}") return None except Exception as e: print(f"An error occurred: {str(e)}") return None def text_or_none(node): return node.text.strip() if node else None def scrape_comments_listing(soup): return soup.select( "div[data-e2e='search-comment-container'] > " "div[class*='CommentListContainer'] > " "div[class*='DivCommentItemContainer']" ) def parse_comment(comment): text = comment.select_one( "div[class*='DivCommentContentContainer'] " "p[data-e2e='comment-level-1'] > span" ) likes = comment.select_one("div[class*='DivLikeContainer'] span") replies = comment.select_one("div[class*='DivReplyContainer']") return { "Comment Text": text_or_none(text), "Like Count": text_or_none(likes), "Reply Count": text_or_none(replies), } def main(): video_url = "https://www.tiktok.com/@nasa/video/7255327059302419738" html = fetch_html_with_scroll(video_url) if not html: return soup = BeautifulSoup(html, "html.parser") comments = [parse_comment(c) for c in scrape_comments_listing(soup)] output = {"Video URL": video_url, "Comments": comments} print(json.dumps(output, indent=2, ensure_ascii=False)) if __name__ == "__main__": main()
该脚本以视频URL而非个人作为输出的键,这是聚合工作的正确默认做法。每条评论记录只包含文本和两个计数。如果您希望持久化结果,可以写入CSV或数据库,但请先阅读隐私部分:评论文本和用户名是个人数据,您保留它们多久以及如何使用,是法律问题,而不仅仅是技术问题。
输出示例
运行完整脚本后,您将得到一份干净的公开评论字段记录,可直接用于情感或主题分析。
{ "Video URL": "https://www.tiktok.com/@nasa/video/7255327059302419738", "Comments": [ { "Comment Text": "this is incredible", "Like Count": "1243", "Reply Count": "18" }, { "Comment Text": "how was this filmed?", "Like Count": "87", "Reply Count": "4" } ] }
从这里开始,汇总才是关键。将评论文本分组以揭示常见主题,对语料库运行情感分析以衡量总体反应,并按点赞数和回复数加权,找出哪些情感获得了共鸣。这样就能了解受众如何反应,而无需为任何单个评论者建立档案。如果您计划将其输入模型,我们关于如何为AI和机器学习构建和清洗网络爬取数据的指南涵盖了训练前的文本规范化和去标识化。
保持不被封锁
即使由Crawling API处理渲染,TikTok仍会监控爬虫形态的流量。以下几个习惯能让抓取运行保持健康,它们适用于任何防御严密的目标。
- 控制请求节奏。 滚动渲染比静态获取耗时更长,因此不要在紧密循环中连续发送请求。适当间隔,避免激进的并行化。
- 依赖轮换。 住宅IP池将请求分散到众多真实用户地址上,使单个地址不会触发速率限制。Crawling API为您处理这一切;如果您自己搭建,这是最需要做对的部分。
- 关注状态码。 运行开始返回挑战或错误时,说明当前速率或IP级别已不够用。退缩而不是强行推进。
- 保持低请求量。 代表性评论样本通常足够用于汇总分析。您很少需要某个热门视频上的全部评论。
更广泛的操作手册请参阅如何在不被封锁的情况下抓取网站。如果您希望通过自己的流量路由轮换池而非使用托管API,Smart AI Proxy以直接代理端点的形式提供与之相同的住宅轮换能力。
抓取TikTok评论合法吗?
这是您在编写生产代码之前必须阅读的部分。抓取本身并不违法,公开视频上的公开评论无需登录即可被任何人看到。但TikTok的服务条款限制自动化采集,而且评论是个人数据:它们是可识别人员写下的内容,通常与用户名绑定。因此这里的合法性更多取决于您采集什么、出于什么目的以及之后如何处理,而非数据是否公开。请阅读TikTok的服务条款和robots.txt,将两者都视为您操作的边界。
如果您处理欧盟或英国居民的数据,GDPR适用;加利福尼亚州居民适用CCPA。两者都将用户名和用户写下的评论视为个人数据,即使是公开的。实际上,这意味着您需要合法依据才能处理这些数据,应当最小化保留内容,并且必须响应删除和反对请求。这类工作最安全的立场是聚合分析:提炼情感、主题和计数,然后丢弃或去标识化原始评论和用户名。不要构建个人评论者的档案,不要将某人的评论与其身份挂钩后重新发布,也不要存储与您对其所做推断意见相关联的用户名。本指南中的脚本读取用户名是因为页面提供了它,但您不应将其与身份绑定存储。
严格限于公开侧,永远不要越过这条线。不要抓取私人账户、需要登录才能访问的内容、私信或粉丝才能看到的任何内容。不要绕过身份验证或速率限制,也不要重新分发受版权保护的视频或媒体。对于任何真实的、持续的或商业性的使用,正确的工具是官方TikTok API,包括符合条件的Research API。这是官方认可的路径,为您提供了明确的条款和结构,并让您在TikTok规则框架内运营。本文是一篇技术演练,范围限定于公开评论的汇总分析,并非对大规模个人数据采集的背书。
核心要点
- TikTok是客户端渲染的,且防御严密。 普通请求返回的是接近空白的外壳,没有评论,因此您必须在解析之前渲染并滚动页面。
-
渲染、滚动和受信任IP应在一次调用中完成。 使用JS令牌的Crawling API一次完成这三项;
ajax_wait、page_wait和scroll_interval控制等待和加载时间。 -
解析稳定的信号。 TikTok的
data-e2e属性和部分class*=匹配比脆弱的混淆类名更耐久。 - 聚合,而非画像。 提取评论文本、点赞数和回复数用于情感和主题分析;永远不要为个人评论者建立档案或存储与身份绑定的用户名。
- 遵守规则,优先使用官方API。 TikTok的服务条款限制抓取,GDPR和CCPA将评论视为个人数据,官方TikTok API是任何正式用途的合规路径。
常见问题
为什么普通请求从TikTok获取不到评论?
因为TikTok使用JavaScript在客户端渲染内容,并随着滚动延迟加载评论。初始HTML只是一个外壳,只有页面脚本在浏览器中运行后才会填充,因此原始HTTP请求返回的是接近空白的响应体。要获取真实的公开评论,您必须先渲染并滚动页面,这正是Crawling API的JS令牌和scroll参数为您处理的工作。
TikTok需要普通令牌还是JS令牌?
JS令牌。普通令牌获取静态HTML,在TikTok上这与普通请求返回的空白外壳相同。JS令牌在将HTML交还之前先在真实浏览器中渲染页面,因此BeautifulSoup解析时评论元素是存在的。
如何加载超过第一批的评论?
向Crawling API传入scroll: "true",让无头浏览器滚动页面并触发TikTok的无限加载。scroll_interval参数以毫秒为单位,控制滚动之间等待的时间;更长的间隔可以加载更多评论批次,但每次请求的等待时间也更长。根据您实际需要多少评论来调整,并保持较低的请求量。
哪些TikTok评论数据适合采集?
仅限公开视频上的公开评论,理想情况下只进行汇总:将评论文本汇总为主题和情感,加上点赞数和回复数作为数字指标。私人账户、需要登录的内容、私信以及任何为个人评论者建立画像的尝试都超出限制。用户名和评论文本是个人数据,因此应最小化保留内容,并在可能的情况下进行去标识化。
应该使用官方TikTok API而非抓取吗?
对于任何真实的、持续的或商业性的使用,是的。官方TikTok API(包括符合条件的Research API)是官方认可的路径:它提供明确的条款、有保障的数据结构,并让您在TikTok规则框架内运营。在没有API访问权限的情况下,抓取少量公开评论适合轻量级的汇总研究,只要您遵守条款、robots.txt、速率限制和隐私法律。
如何避免在抓取TikTok评论时被封锁?
保持低频的每IP请求速率,将滚动渲染分散开而不是紧密循环,将请求量控制在代表性样本范围内,并通过轮换住宅IP路由,使单个地址不会触发速率限制。Crawling API为您管理轮换和受信任的IP池。监控pc_status值,一旦开始遇到挑战就立即退缩。更深入的说明请参阅我们整理的最佳TikTok数据采集工具汇总。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
