| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- #!/usr/bin/env node
- /**
- * TikTok 第二轮探测:视频详情 / 评论 / 用户画像 / 用户作品
- * 先用 fetch_general_search_result 搜 "milk thistle" 拿一个 aweme_id + sec_uid,
- * 然后用这俩去测深度端点
- */
- const fs = require('fs');
- const path = require('path');
- const os = require('os');
- const https = require('https');
- const TOKEN = JSON.parse(
- fs.readFileSync(
- path.join(os.homedir(), '.openclaw', 'skills', 'xiaohongshu-search-notes', 'api-config.json'),
- 'utf-8'
- )
- ).endpoint.headers.Authorization.replace(/^Bearer\s+/, '');
- function get(apiPath) {
- return new Promise((resolve) => {
- const opts = {
- hostname: 'api.tikhub.io',
- path: apiPath,
- method: 'GET',
- headers: { Authorization: `Bearer ${TOKEN}`, Accept: 'application/json' },
- };
- const req = https.request(opts, (res) => {
- const chunks = [];
- res.on('data', (c) => chunks.push(c));
- res.on('end', () => {
- const body = Buffer.concat(chunks).toString('utf-8');
- try { resolve({ status: res.statusCode, json: JSON.parse(body) }); }
- catch (e) { resolve({ status: res.statusCode, text: body.slice(0, 300) }); }
- });
- });
- req.on('error', (e) => resolve({ status: 0, error: e.message }));
- req.setTimeout(45000, () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
- req.end();
- });
- }
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
- (async () => {
- // 第一步:拿一个视频样本
- console.log('Step 1: Search "milk thistle" to get sample aweme_id + sec_uid');
- const s = await get('/api/v1/tiktok/app/v3/fetch_general_search_result?keyword=milk%20thistle&cursor=0&count=10');
- fs.writeFileSync('data/tiktok-probe-search-raw.json', JSON.stringify(s.json, null, 2).slice(0, 12000), 'utf-8');
- const d = s.json?.data;
- console.log(' data type:', typeof d, 'isArray:', Array.isArray(d));
- if (d && typeof d === 'object') {
- console.log(' data keys:', Array.isArray(d) ? `arr[${d.length}]` : Object.keys(d).slice(0, 8).join(','));
- }
- // 统一找一个 aweme_info:可能在 arr 顶层,可能在 data.data,可能在 data.aweme_list
- let videoList = [];
- if (Array.isArray(d)) videoList = d;
- else if (Array.isArray(d?.data)) videoList = d.data;
- else if (Array.isArray(d?.aweme_list)) videoList = d.aweme_list;
- else if (Array.isArray(d?.item_list)) videoList = d.item_list;
- console.log(' videoList length:', videoList.length);
- const firstVideo = videoList.find((x) => x?.aweme_info) || videoList[0];
- const videoInfo = firstVideo?.aweme_info || firstVideo;
- const aweme_id = videoInfo?.aweme_id;
- const sec_uid = videoInfo?.author?.sec_uid;
- const author_unique_id = videoInfo?.author?.unique_id;
- console.log(' aweme_id:', aweme_id, '| sec_uid:', sec_uid?.slice(0, 20) + '...', '| unique:', author_unique_id);
- console.log(' video desc:', (videoInfo?.desc || '').slice(0, 80));
- console.log(' video stats:', JSON.stringify(videoInfo?.statistics || {}).slice(0, 150));
- if (videoInfo) fs.writeFileSync('data/tiktok-probe-sample-video.json', JSON.stringify(videoInfo, null, 2).slice(0, 8000), 'utf-8');
- if (!aweme_id) {
- console.log('❌ no aweme_id found, aborting depth probe. See data/tiktok-probe-search-raw.json for raw');
- return;
- }
- // 第二步:试深度端点
- const depthPaths = [
- // 视频详情
- `/api/v1/tiktok/app/v3/fetch_one_video?aweme_id=${aweme_id}`,
- `/api/v1/tiktok/app/v3/fetch_video_detail?aweme_id=${aweme_id}`,
- // 视频评论
- `/api/v1/tiktok/app/v3/fetch_video_comments?aweme_id=${aweme_id}&cursor=0&count=10`,
- // 作者资料
- `/api/v1/tiktok/app/v3/handler_user_profile?sec_user_id=${sec_uid}`,
- `/api/v1/tiktok/app/v3/fetch_user_profile?sec_user_id=${sec_uid}`,
- `/api/v1/tiktok/web/fetch_user_profile?unique_id=${author_unique_id}`,
- // 作者作品
- `/api/v1/tiktok/app/v3/fetch_user_post_videos?sec_user_id=${sec_uid}&max_cursor=0&count=10`,
- `/api/v1/tiktok/app/v3/fetch_user_post?sec_user_id=${sec_uid}&cursor=0&count=10`,
- ];
- const results = [];
- for (const p of depthPaths) {
- await sleep(700);
- const r = await get(p);
- const code = r.json?.code;
- const msg = (r.json?.message_zh || r.json?.message || '').slice(0, 60);
- const d = r.json?.data;
- let hint = '';
- if (d) {
- if (Array.isArray(d)) hint = `arr[${d.length}]`;
- else if (typeof d === 'object') {
- hint = 'keys=' + Object.keys(d).slice(0, 7).join(',');
- const inner = d.aweme_detail || d.comments || d.aweme_list || d.user || d.user_info;
- if (inner) {
- if (Array.isArray(inner)) hint += ` inner=arr[${inner.length}]`;
- else hint += ' inner_keys=' + Object.keys(inner).slice(0, 5).join(',');
- }
- }
- }
- console.log(`[${r.status}] ${p.split('?')[0].replace('/api/v1/tiktok/', '')}`);
- console.log(` code=${code} msg="${msg}" ${hint}`);
- results.push({ path: p, status: r.status, code, msg, hint });
- }
- fs.writeFileSync('data/tiktok-probe2.json', JSON.stringify(results, null, 2), 'utf-8');
- console.log('\n✓ data/tiktok-probe2.json');
- })();
|