关键要点:
- TikTok在页面中传输其数据,然后通过XHR补充其余部分。 用户个人资料和首个帖子存储在一个
#__UNIVERSAL_DATA_FOR_REHYDRATION__JSON 大块中;评论和更深层的帖子作为滚动触发的XHR通过网络接收。 - 区域是获取干净渲染的一部分。 同一个用户个人资料在一个出口超时后可以立即从另一个出口渲染------固定
proxyCountry并给会话足够的TTL,以便补充的XHR加载。 - 你是从网络中提取,而不是通过猜测选择器。 在页面滚动时监听
xhr/fetch响应,并解析TikTok已经返回的JSON------无需脆弱的DOM抓取视频网格。 - TikTok的指纹识别相当严格,因此浏览器必须是真实的。 使用Scrapeless Scraping Browser------反检测的Chromium与住宅出口结合------才可以顺利进行数据补充。
- 一个会话,五个表面。 用户资料、帖子、评论、搜索和频道都简化为在单个云会话中的相同渲染然后提取模式。
- 免费开始。 新的Scrapeless账户包含免费的Scraping Browser运行时------在 app.scrapeless.com 上注册。
引言:TikTok存储其数据的地方
TikTok以两个阶段渲染其网页。初始HTML承载一个大JSON岛------一个 <script id="__UNIVERSAL_DATA_FOR_REHYDRATION__"> 大块------其中包含了用户资料、其统计信息以及首个帖子。此后的所有内容(在滚动时更多帖子、评论线程、搜索页面)在页面引导后通过XHR获取。因此,抓取TikTok需要两种技术:读取补充JSON以获取已有内容,捕获XHR响应以加载按需内容。
这两个阶段都依赖于TikTok认为是真实的浏览器在驱动。该平台进行强力指纹识别,一般的HTTP请求或默认的无头浏览器将得到一个没有可用数据的外壳。渲染根本无法完成。
本指南使用Scrapeless Scraping Browser------一个反检测的云浏览器,将自开发的Chromium与住宅代理配对------通过CDP连接到Puppeteer。下面的用户资料提取是在实时捕获的;帖子和评论的模式使用相同的渲染然后读取网络的方法。始终使用公共数据。
你可以用它做什么
- 提取创作者资料 --- 生物、认证、关注者/点赞/视频数量 --- 从补充JSON中获取。
- 收集创作者的帖子,包括标题、视频元数据、作者和互动统计数据。
- 抓取评论线程,包括文本、点赞数、回复数和作者用户名。
- 进行关键词搜索并捕获结果列表。
- 浏览频道/话题标签,采用相同的滚动和捕获循环。
为什么选择Scrapeless Scraping Browser
Scrapeless Scraping Browser是一个可定制的反检测云浏览器,专为网络爬虫和AI代理而设计。特别针对TikTok,它提供:
- 自开发的Chromium --- 真实的引擎,因此补充JSON能够填充,XHR能够发送。
- 反检测指纹识别 --- 会话看起来像是一个正常的浏览器,因此TikTok发送真实的数据,而不是空壳。
- 覆盖195多个国家的住宅代理 --- 按国家固定出口,以便页面得以干净且一致地渲染。
- 可配置的会话TTL --- 保持会话足够长的时间,以便在终止之前让滚动触发的XHR能加载。
- 标准的Puppeteer连接 --- 使用SDK创建会话,然后通过CDP使用
puppeteer.connect();你的提取就是普通的Puppeteer。
前提条件
- Node.js 18或更高版本
- 一个Scrapeless账户和API密钥 --- 在官网注册,联系客服申领免费额度。
- 基本的Puppeteer和JSON使用经验
安装
bashCopy
npm install @scrapeless-ai/sdk puppeteer-core
bashCopy
export SCRAPELESS_API_KEY="你的_api_token_在这里"
第一步 --- 创建会话并连接Puppeteer
SDK 创建云会话(你需要固定代理国家和TTL),并返回一个WebSocket端点;Puppeteer通过CDP连接到此端点:
javascriptCopy
import { Scrapeless } from '@scrapeless-ai/sdk';
import puppeteer from 'puppeteer-core';
const client = new Scrapeless({ apiKey: process.env.SCRAPELESS_API_KEY });
const { browserWSEndpoint } = await client.browser.create({
proxyCountry: 'US', // 固定出口 --- 区域影响页面的渲染
sessionTTL: 300, // 秒数;足够的时间让补充的XHR加载
});
const browser = await puppeteer.connect({ browserWSEndpoint });
const page = await browser.newPage();
一个实际案例的注释:同一个用户资料在一个出口区域反复超时,在另一个出口区域第一次尝试就成功渲染。如果渲染卡住,首先切换 proxyCountry。
第二步 --- 从补充JSON中抓取用户资料
配置文件、其统计信息以及首次帖子在页面加载时已经存在------在#__UNIVERSAL_DATA_FOR_REHYDRATION__脚本标签内。等待该节点,解析它,并读取webapp.user-detail范围:
javascriptCopy
await page.goto('https://www.tiktok.com/@oddanimalspecimens', {
waitUntil: 'domcontentloaded',
timeout: 120000,
});
await page.waitForSelector('#__UNIVERSAL_DATA_FOR_REHYDRATION__', { timeout: 60000 });
const userInfo = await page.evaluate(() => {
const el = document.getElementById('__UNIVERSAL_DATA_FOR_REHYDRATION__');
const data = JSON.parse(el.textContent);
return data.__DEFAULT_SCOPE__['webapp.user-detail'].userInfo;
});
console.log(userInfo.user.uniqueId, '---', userInfo.stats);
这将逐字返回TikTok的userInfo对象------一个user对象和一个stats对象。无需DOM抓取;你正在读取TikTok为呈现其自身页面而提供的数据。
第3步 --- 从XHR捕获帖子和评论
第一页之后的帖子和所有评论线程在页面启动并滚动时以XHR形式到达。模式是,在导航之前附加一个response监听器,滚动以触发获取,然后解析JSON主体:
javascriptCopy
const xhrCalls = [];
page.on('response', async (resp) => {
const rt = resp.request().resourceType();
if (rt !== 'xhr' && rt !== 'fetch') return;
try {
xhrCalls.push({ url: resp.url(), body: await resp.text() });
} catch { /* 有些主体无法读取;跳过 */ }
});
await page.goto('https://www.tiktok.com/@oddanimalspecimens', { waitUntil: 'domcontentloaded' });
// 滚动以触发懒加载的帖子/评论
for (let i = 0; i < 5; i++) {
await page.evaluate(() => window.scrollBy(0, document.body.scrollHeight));
await new Promise((r) => setTimeout(r, 2000));
}
// 筛选项目/评论列表端点并JSON.parse主体
const itemLists = xhrCalls.filter((c) => /\/api\/(post|comment)\//.test(c.url));
从那里你将JSON.parse每个捕获的主体并提取项目或评论数组。这与配置文件的渲染-然后-读取网络方法相同,只是通过滚动驱动,而不是单个水合块。
你得到的结果
个人资料提取返回TikTok的userInfo对象。下面的形状正是实时运行所发出的;计数是真实捕获,随着时间推移而变化:
jsonCopy
{
"user": {
"id": "...",
"uniqueId": "oddanimalspecimens",
"nickname": "Odd Animal Specimens",
"avatarLarger": "https://...",
"signature": "...",
"verified": false,
"secUid": "...",
"privateAccount": false
},
"stats": {
"followerCount": 4000000,
"followingCount": 9,
"heartCount": 78000000,
"videoCount": 179,
"diggCount": 0,
"friendCount": 6
}
}
// 形状是TikTok的userInfo逐字;作为"..."显示的字符串字段是一个示例,计数是真实捕获,随着时间推移而变化。
一些诚实的观察:
user对象非常丰富 --- TikTok包含数十个字段(设置,secUid,关系标志)。读取你需要的那几项;忽略其余部分。heart和heartCount同时出现 ,并且携带相同的总数 --- 使用heartCount。- 在规模上计数取整。 大账户报告取整总数(例如4,000,000),与TikTok在UI中显示的方式相同。
- 区域对渲染很重要 ,不仅仅是数据。固定
proxyCountry并给会话足够的TTL。
结论:每个TikTok表面上的一个模式
抓取TikTok归根结底是两个读取:解析#__UNIVERSAL_DATA_FOR_REHYDRATION__以获取页面中已经存在的内容,并捕获XHR响应以获取滚动时的水合内容。个人资料、帖子、评论、搜索和频道都是该单一渲染-然后提取循环的变体。在Scrapeless抓取浏览器上运行它是使渲染发生的原因------抗检测的Chromium加上住宅出口,这样TikTok便能提供真实数据,而不是一个空壳。有关同一SDK-over-CDP模式的电子商务变体,请参阅本人主页或scrapeless官网相关博客查看Etsy抓取器指南;抓取浏览器产品页面和文档涵盖完整的SDK范围。固定区域,延长滚动密集表面的TTL,并读取TikTok已经提供的JSON。
准备构建您的AI驱动数据管道了吗?
加入我们的社区,申请免费计划并与构建社交数据管道的开发者连接:Discord · Telegram。
在 app.scrapeless.com 注册以免费获取抓取浏览器的运行时,并根据您的工作流程需求调整上述模式以适应配置文件、查询和频道。请查看 定价 获取规模信息。
常见问题
问:抓取 TikTok 合法吗?
收集公开可见的数据通常是可以的,但规则因管辖权而异,TikTok 的服务条款适用。仅抓取公共数据,查看服务条款,并咨询法律顾问以适应您的用例。
问:为什么我的抓取返回空页面或超时?
两个常见原因:TikTok 路由您的出站区域和会话在 hydration 完成前结束。固定 proxyCountry(如果渲染挂起则切换)并提高 sessionTTL。
问:我需要代理吗?
是的。TikTok 对 IP 声誉的权重很高;通过 proxyCountry 固定住宅出站,以便页面能够渲染并激活 XHRs。
问:如何获取第一页以外的帖子?
滚动页面以触发懒加载的 XHR 请求,并使用 response 监听器捕获它们,然后解析 JSON 内容------重水合数据块仅包含第一批数据。
问:DOM 标记发生变化,我的选择器失效了。现在怎么办?
依靠 JSON 数据源------重水合数据块和 XHR 内容------而不是抓取渲染的网格。它们的变化远低于视觉标记。只需重新检查您仍然需要的少数选择器。
问:我可以针对 TikTok 运行多少个工作线程?
保持并发适度------每个主机少量会话------以确保 IP 声誉信号保持清晰。
问:我可以在没有 AI 代理的情况下运行此工具吗?
可以。这是 Scrapeless SDK 加上纯 Puppeteer 通过 CDP------不需要代理。