| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 |
- #!/usr/bin/env node
- /**
- * 暴力探测 TikHub Douyin 搜索 endpoint 命名
- * 目标:找到一个能按关键词返回视频列表的 cookie-free 接口
- */
- 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 req(hostname, pathUrl, method = 'GET', body = null) {
- return new Promise((resolve) => {
- const headers = { Authorization: `Bearer ${TOKEN}`, Accept: 'application/json' };
- if (body) {
- headers['Content-Type'] = 'application/json';
- headers['Content-Length'] = Buffer.byteLength(body);
- }
- const r = https.request({ hostname, path: pathUrl, method, headers }, (res) => {
- const chunks = [];
- res.on('data', (c) => chunks.push(c));
- res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(chunks).toString('utf-8') }));
- });
- r.on('error', (e) => resolve({ status: 0, body: e.message }));
- r.setTimeout(15000, () => { r.destroy(); resolve({ status: 0, body: 'timeout' }); });
- if (body) r.write(body);
- r.end();
- });
- }
- const kw = '猴菇饮';
- const kwE = encodeURIComponent(kw);
- // 根据 TikHub 抖音 endpoint 命名模式(app/v3 + web)
- const candidates = [
- // 直连 api.tikhub.io(官方域名)
- { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_general_search_result?keyword=${kwE}&offset=0&count=20&sort_type=0&publish_time=0` },
- { host: 'api.tikhub.io', path: `/api/v1/douyin/web/fetch_general_search_result?keyword=${kwE}&offset=0&count=20` },
- { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_video_search?keyword=${kwE}&offset=0&count=20` },
- { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_user_search?keyword=${kwE}&offset=0&count=20` },
- { host: 'api.tikhub.io', path: `/api/v1/douyin/web/fetch_hot_search_list` },
- { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_hot_search_list` },
- { host: 'api.tikhub.io', path: `/api/v1/douyin/web/fetch_word_hot_search_list?keyword=${kwE}` },
- // fmode 网关(已知工作)
- { host: 'server.fmode.cn', path: `/thapi/v1/douyin/app/v3/fetch_general_search_result?keyword=${kwE}&offset=0&count=20&sort_type=0&publish_time=0` },
- { host: 'server.fmode.cn', path: `/thapi/v1/douyin/web/fetch_general_search_result?keyword=${kwE}&offset=0&count=20` },
- { host: 'server.fmode.cn', path: `/thapi/v1/douyin/app/v3/fetch_video_search?keyword=${kwE}&offset=0&count=20` },
- { host: 'server.fmode.cn', path: `/thapi/v1/douyin/web/fetch_word_hot_search_list?keyword=${kwE}` },
- { host: 'server.fmode.cn', path: `/thapi/v1/douyin/web/fetch_hot_search_list` },
- // 关键词热度趋势
- { host: 'api.tikhub.io', path: `/api/v1/douyin/web/fetch_keyword_hot_trend?keyword=${kwE}` },
- { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_keyword_hot_trend?keyword=${kwE}` },
- ];
- (async () => {
- console.log(`🔎 keyword = "${kw}"`);
- console.log(`🔑 token = ${TOKEN.slice(0, 10)}...\n`);
- for (const c of candidates) {
- const r = await req(c.host, c.path);
- let summary;
- try {
- const j = JSON.parse(r.body);
- const d = j.data ?? j;
- if (Array.isArray(d?.data)) summary = `data=Array(${d.data.length})`;
- else if (d?.aweme_list) summary = `aweme_list=${d.aweme_list.length}`;
- else if (d?.business_data) summary = `business_data=${Array.isArray(d.business_data) ? d.business_data.length : 'obj'}`;
- else if (d?.word_list) summary = `word_list=${d.word_list.length}`;
- else if (d?.hot_search_list) summary = `hot_search=${d.hot_search_list.length}`;
- else if (d?.detail) summary = `detail="${String(d.detail).slice(0, 80)}"`;
- else if (j.code) summary = `code=${j.code} msg=${(j.message || j.msg || '').slice(0, 60)}`;
- else summary = 'keys=' + Object.keys(d || j).slice(0, 5).join(',');
- } catch (e) {
- summary = r.body.slice(0, 100);
- }
- const flag = r.status === 200 ? '✅' : (r.status === 404 ? '✗' : '⚠');
- console.log(`${flag} [${r.status}] ${c.host}${c.path.slice(0, 75)}`);
- console.log(` → ${summary}\n`);
- await new Promise((r) => setTimeout(r, 500));
- }
- })();
|