probe-douyin-search2.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #!/usr/bin/env node
  2. /** 深入探测两条路径的完整 response 结构,锁定抖音搜索可用接口 */
  3. const fs = require('fs');
  4. const path = require('path');
  5. const os = require('os');
  6. const https = require('https');
  7. const TIKHUB_TOKEN = JSON.parse(fs.readFileSync(
  8. path.join(os.homedir(), '.openclaw', 'skills', 'xiaohongshu-search-notes', 'api-config.json'),
  9. 'utf-8'
  10. )).endpoint.headers.Authorization.replace(/^Bearer\s+/, '');
  11. const VOC_TOKEN = JSON.parse(fs.readFileSync(
  12. path.join(os.homedir(), '.openclaw', 'voc-credentials.json'), 'utf-8'
  13. )).vocToken;
  14. function req(hostname, pathUrl, method, body, token) {
  15. return new Promise((resolve) => {
  16. const headers = { Authorization: `Bearer ${token}`, Accept: 'application/json' };
  17. const opts = { hostname, path: pathUrl, method, headers };
  18. if (body) {
  19. headers['Content-Type'] = 'application/json';
  20. headers['Content-Length'] = Buffer.byteLength(body);
  21. }
  22. const r = https.request(opts, (res) => {
  23. const chunks = [];
  24. res.on('data', (c) => chunks.push(c));
  25. res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(chunks).toString('utf-8') }));
  26. });
  27. r.on('error', (e) => resolve({ status: 0, body: e.message }));
  28. r.setTimeout(30000, () => { r.destroy(); resolve({ status: 0, body: 'timeout' }); });
  29. if (body) r.write(body);
  30. r.end();
  31. });
  32. }
  33. function summarize(obj, depth = 0, prefix = '') {
  34. if (depth > 4) return '...';
  35. if (obj == null) return String(obj);
  36. if (Array.isArray(obj)) {
  37. if (obj.length === 0) return '[]';
  38. return `Array(${obj.length}) [0]=${summarize(obj[0], depth + 1, prefix + ' ')}`;
  39. }
  40. if (typeof obj === 'object') {
  41. const keys = Object.keys(obj);
  42. if (keys.length === 0) return '{}';
  43. const parts = keys.slice(0, 6).map((k) => {
  44. const v = obj[k];
  45. if (typeof v === 'string') return `${k}:"${v.slice(0, 30)}"`;
  46. if (typeof v === 'number') return `${k}:${v}`;
  47. if (typeof v === 'boolean') return `${k}:${v}`;
  48. if (Array.isArray(v)) return `${k}:Arr(${v.length})`;
  49. if (typeof v === 'object' && v != null) return `${k}:{${Object.keys(v).slice(0, 3).join(',')}}`;
  50. return `${k}:${typeof v}`;
  51. });
  52. return `{${parts.join(', ')}${keys.length > 6 ? ', ...' : ''}}`;
  53. }
  54. return typeof obj === 'string' ? `"${obj.slice(0, 40)}"` : String(obj);
  55. }
  56. (async () => {
  57. const kw = '江中猴菇';
  58. console.log(`🔎 keyword = "${kw}"\n`);
  59. // —— 1) TikHub direct on fmode /thapi/v1/ proxy ——
  60. console.log('━━━ 1) TikHub proxy /thapi/v1 (GET, TikHub token) ━━━');
  61. const r1 = await req('server.fmode.cn',
  62. `/thapi/v1/douyin/app/v3/fetch_general_search_result?keyword=${encodeURIComponent(kw)}&offset=0&count=20&sort_type=0&publish_time=0`,
  63. 'GET', null, TIKHUB_TOKEN);
  64. console.log(`status = ${r1.status}`);
  65. if (r1.status === 200) {
  66. try {
  67. const j = JSON.parse(r1.body);
  68. console.log('outer:', summarize(j));
  69. const inner = j.data ?? j;
  70. console.log('data:', summarize(inner));
  71. if (inner?.data) console.log('data.data:', summarize(inner.data));
  72. if (inner?.data?.[0]) {
  73. const v = inner.data[0];
  74. console.log('sample[0] keys:', Object.keys(v).slice(0, 20).join(', '));
  75. if (v.aweme_info) {
  76. console.log('aweme_info:', summarize(v.aweme_info));
  77. }
  78. }
  79. } catch (e) { console.log('PARSE ERR', e.message, r1.body.slice(0, 300)); }
  80. } else { console.log(r1.body.slice(0, 400)); }
  81. console.log();
  82. // —— 2) voc-social gateway (POST, voc token) ——
  83. console.log('━━━ 2) voc-social gateway (POST, vocToken) ━━━');
  84. const searchBody = JSON.stringify({
  85. keyword: kw, cursor: 0, sort_type: '0', publish_time: '0',
  86. filter_duration: '0', content_type: '0', search_id: '', backtrace: '',
  87. });
  88. const r2 = await req('server.fmode.cn',
  89. '/api/voc-social/douyin/search/fetch_general_search_v2',
  90. 'POST', searchBody, VOC_TOKEN);
  91. console.log(`status = ${r2.status}`);
  92. if (r2.status === 200) {
  93. try {
  94. const j = JSON.parse(r2.body);
  95. console.log('outer:', summarize(j));
  96. if (j?.data) console.log('data:', summarize(j.data));
  97. if (j?.data?.data) console.log('data.data:', summarize(j.data.data));
  98. } catch (e) { console.log('PARSE ERR', e.message, r2.body.slice(0, 300)); }
  99. } else { console.log(r2.body.slice(0, 400)); }
  100. console.log();
  101. // —— 3) Try official api.tikhub.io directly ——
  102. console.log('━━━ 3) api.tikhub.io direct (GET, TikHub token) ━━━');
  103. const r3 = await req('api.tikhub.io',
  104. `/api/v1/douyin/app/v3/fetch_general_search_result?keyword=${encodeURIComponent(kw)}&offset=0&count=20&sort_type=0&publish_time=0`,
  105. 'GET', null, TIKHUB_TOKEN);
  106. console.log(`status = ${r3.status}`);
  107. if (r3.status === 200) {
  108. try {
  109. const j = JSON.parse(r3.body);
  110. console.log('outer:', summarize(j));
  111. const inner = j.data ?? j;
  112. if (inner?.data) console.log('data.data:', summarize(inner.data));
  113. if (Array.isArray(inner?.data) && inner.data[0]) {
  114. const v = inner.data[0];
  115. console.log('first aweme keys:', Object.keys(v).slice(0, 15).join(', '));
  116. if (v.aweme_info || v.aweme_mix_info) {
  117. const info = v.aweme_info || v.aweme_mix_info;
  118. console.log('aweme_info keys:', Object.keys(info).slice(0, 20).join(', '));
  119. console.log('aweme_id:', info.aweme_id, '| author:', info.author?.nickname, '| stats:', summarize(info.statistics || {}));
  120. }
  121. }
  122. } catch (e) { console.log('PARSE ERR', e.message, r3.body.slice(0, 300)); }
  123. } else { console.log(r3.body.slice(0, 400)); }
  124. })();