#!/usr/bin/env node /** * 抖音账号搜索探测:用 TikHub /fetch_user_search_result 搜用户 * 对 6 个候选关键词搜索,返回 Top 3 用户(昵称 / sec_uid / 粉丝) */ 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+/, ''); const COOKIE = fs.readFileSync(path.resolve('data/douyin-cookie.txt'), 'utf-8').trim(); function get(apiPath, params) { params.cookie = COOKIE; const qs = Object.entries(params) .filter(([, v]) => v !== undefined && v !== null && v !== '') .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`) .join('&'); return new Promise((resolve) => { const opts = { hostname: 'api.tikhub.io', path: `${apiPath}?${qs}`, method: 'GET', headers: { Authorization: `Bearer ${TIKHUB_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, json: { _raw: body.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.end(); }); } const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); // 关键词 × 产品 候选列表 const candidates = [ // liver 肝纯片 { product: 'liver', role: '品牌竞品号', keyword: '葵花护肝片' }, { product: 'liver', role: '品牌竞品号', keyword: '葵花药业' }, { product: 'liver', role: '垂类博主', keyword: '肝病科' }, { product: 'liver', role: '垂类博主', keyword: '护肝科普' }, // probiotic 儿童乳酸菌 { product: 'probiotic', role: '品牌竞品号', keyword: '妈咪爱' }, { product: 'probiotic', role: '品牌竞品号', keyword: '亿活益生菌' }, { product: 'probiotic', role: '品牌竞品号', keyword: '合生元' }, { product: 'probiotic', role: '垂类博主', keyword: '儿科医生' }, { product: 'probiotic', role: '垂类博主', keyword: '崔玉涛' }, // monkey 猴菇饮 { product: 'monkey', role: '品牌竞品号', keyword: '康恩贝' }, { product: 'monkey', role: '品牌竞品号', keyword: '太阳神' }, { product: 'monkey', role: '品牌竞品号', keyword: '三九胃泰' }, { product: 'monkey', role: '垂类博主', keyword: '胃肠科医生' }, { product: 'monkey', role: '垂类博主', keyword: '养胃' }, ]; (async () => { const out = []; for (const c of candidates) { await sleep(1200); console.log(`\n[${c.product} · ${c.role}] 搜 "${c.keyword}"`); // TikHub 的用户搜索接口(v2 可用) const r = await get('/api/v1/douyin/web/fetch_user_search_result_v2', { keyword: c.keyword, search_channel: 'aweme_user_web', sort_type: 0, publish_time: 0, page: 1, count: 10, }); const data = r.json?.data; const users = Array.isArray(data) ? data : (data?.user_list || data?.users || data?.data || []); if (!Array.isArray(users) || users.length === 0) { console.log(' ❌ no users. raw:', JSON.stringify(r.json).slice(0, 250)); out.push({ ...c, users: [], error: r.json?.message_zh || r.json?.message || 'empty' }); continue; } const top = users.slice(0, 3).map((u) => { const uinfo = u.user_info || u; return { nickname: uinfo.nickname, sec_uid: uinfo.sec_uid, uid: uinfo.uid, follower_count: uinfo.follower_count, custom_verify: uinfo.custom_verify, signature: (uinfo.signature || '').replace(/\n/g, ' ').slice(0, 60), share_url: uinfo.sec_uid ? `https://www.douyin.com/user/${uinfo.sec_uid}` : '', }; }); console.log(' top candidates:'); top.forEach((t, i) => { console.log(` #${i + 1} ${t.nickname} · 粉丝 ${(t.follower_count || 0).toLocaleString()} · ${t.custom_verify || '-'}`); console.log(` sec_uid: ${t.sec_uid}`); }); out.push({ ...c, users: top }); } const outPath = path.resolve('data', 'dy-user-candidates.json'); fs.writeFileSync(outPath, JSON.stringify(out, null, 2), 'utf-8'); console.log('\n\n✓ Wrote', outPath); console.log('共搜 ' + out.length + ' 个关键词'); })();