#!/usr/bin/env node /** * 用 POST + JSON 测试 Douyin-Search-API V2 接口 * 关键线索:不需要 cookie,POST body 参数 */ const fs = require('fs'); const path = require('path'); const os = require('os'); const https = require('https'); const TIKHUB_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 post(apiPath, body) { return new Promise((resolve) => { const payload = JSON.stringify(body); const opts = { hostname: 'api.tikhub.io', path: apiPath, method: 'POST', headers: { Authorization: `Bearer ${TIKHUB_TOKEN}`, 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload), Accept: 'application/json', }, }; const req = https.request(opts, (res) => { const chunks = []; res.on('data', (c) => chunks.push(c)); res.on('end', () => { const text = Buffer.concat(chunks).toString('utf-8'); try { resolve({ status: res.statusCode, json: JSON.parse(text) }); } catch (e) { resolve({ status: res.statusCode, json: { _raw: text.slice(0, 300) } }); } }); }); req.on('error', (e) => resolve({ status: 0, json: { _error: e.message } })); req.setTimeout(30000, () => { req.destroy(); resolve({ status: 0, json: { _error: 'timeout' } }); }); req.write(payload); req.end(); }); } const candidatePaths = [ // more granular attempts '/api/v1/douyin/fetch_video_search_v2', '/api/v1/douyin/fetch_user_search_v2', '/api/v1/douyin/app/v2/fetch_video_search_v2', '/api/v1/douyin/app/v2/fetch_user_search_v2', '/api/v1/douyin/app/v1/fetch_video_search_v2', '/api/v1/douyin/app/v1/fetch_user_search_v2', // 按图中 apifox 路径 id 尝试完整路径 '/api/v1/douyin_search/v2/api_v1_douyin_search_fetch_video_search_v2_post', '/api/v1/douyin_search/v2/api_v1_douyin_search_fetch_user_search_v2_post', // 重试已知的 fetch_user_search_result_v2(曾 200),看 POST 是否不同 '/api/v1/douyin/web/fetch_user_search_result_v2', // 官方 API v3 带 _v2 后缀 '/api/v1/douyin/app/v3/fetch_video_search_result_v2', '/api/v1/douyin/app/v3/fetch_user_search_result_v2', // Web V3 / V2 '/api/v3/douyin/search/fetch_video_search_v2', '/api/v3/douyin/search/fetch_user_search_v2', ]; (async () => { const keyword = '康恩贝'; console.log(`Testing search for "${keyword}"\n`); const results = []; for (const p of candidatePaths) { const body = p.includes('user') ? { keyword, cursor: 0, sort_type: 0, publish_time: 0, search_id: '' } : { keyword, cursor: 0, sort_type: '0', publish_time: '0', filter_duration: '0', content_type: '', search_id: '', backtrace: '' }; const r = await post(p, body); const code = r.json?.code; const msg = (r.json?.message_zh || r.json?.message || r.json?.detail?.message_zh || r.json?.detail?.message || '').slice(0, 80); const data = r.json?.data; const dataKeys = data && typeof data === 'object' && !Array.isArray(data) ? Object.keys(data).slice(0, 8).join(',') : (Array.isArray(data) ? `arr[${data.length}]` : typeof data); const row = { path: p, status: r.status, code, msg, dataKeys }; results.push(row); console.log(` [${r.status}] ${p}`); console.log(` code=${code} msg="${msg}" data=${dataKeys}`); await new Promise((res) => setTimeout(res, 600)); } fs.writeFileSync('data/dy-v2-probe.json', JSON.stringify(results, null, 2), 'utf-8'); })();