#!/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'); })();