gen_reply_crawl.js 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. /**
  2. * 根据合并后的评论 JSON,生成「抓取回复最多的前 N 条评论的子回复」的云机脚本
  3. * 用法: node gen_reply_crawl.js <comments.json> <outSh> [topN] [perCommentLimit]
  4. */
  5. const fs = require('fs');
  6. const inFile = process.argv[2];
  7. const outSh = process.argv[3];
  8. const topN = parseInt(process.argv[4] || '30', 10);
  9. const perLimit = process.argv[5] || '0'; // 0=每条评论的回复抓全
  10. const comments = JSON.parse(fs.readFileSync(inFile, 'utf8'));
  11. const cands = comments.filter(c => (c.replyCount || 0) > 0)
  12. .sort((a, b) => b.replyCount - a.replyCount)
  13. .slice(0, topN);
  14. console.log('候选(有回复)评论:', comments.filter(c => (c.replyCount || 0) > 0).length, '条');
  15. console.log('本轮抓取前', cands.length, '条,声明回复合计',
  16. cands.reduce((s, c) => s + c.replyCount, 0), '条');
  17. console.log('预计请求数 ≈', cands.reduce((s, c) => s + Math.ceil(c.replyCount / 50), 0),
  18. '页 →', Math.round(cands.reduce((s, c) => s + Math.ceil(c.replyCount / 50), 0) * 3.5 / 60), '分钟');
  19. const lines = [];
  20. lines.push('#!/system/bin/sh');
  21. lines.push('# 抓取指定评论的子回复(串行,含空页容错/断点续传)');
  22. lines.push('# 由 gen_reply_crawl.js 自动生成 ' + new Date().toISOString());
  23. lines.push('PX="http://127.0.0.1:2080"');
  24. lines.push('VID="7552202593888603410"');
  25. lines.push('DIR=/sdcard/Download/_replies');
  26. lines.push('LOG=/sdcard/Download/_reply.log');
  27. lines.push('PER=' + perLimit);
  28. lines.push('mkdir -p $DIR');
  29. lines.push('echo "REPLY START $(date)" > $LOG');
  30. lines.push('');
  31. lines.push('grab() { # $1=comment_id $2=declared');
  32. lines.push(' cid=$1; dec=$2; cursor=0; empty=0; got=0; pages=0');
  33. lines.push(' cap=$((dec + 100))');
  34. lines.push(' while [ $cursor -le $cap ]; do');
  35. lines.push(' if [ "$PER" -gt 0 ] && [ $got -ge $PER ]; then break; fi');
  36. lines.push(' pages=$((pages+1))');
  37. lines.push(' f="$DIR/${cid}_$cursor.json"');
  38. lines.push(' if [ -s "$f" ] && grep -q \'"code":0\' "$f"; then');
  39. lines.push(' n=$(grep -o \'"video_id"\' "$f" | wc -l)');
  40. lines.push(' got=$((got+n)); empty=0');
  41. lines.push(' echo " cid=$cid cursor=$cursor SKIP n=$n" >> $LOG');
  42. lines.push(' if [ $got -ge $dec ] && [ $n -lt 50 ]; then break; fi');
  43. lines.push(' cursor=$((cursor+50)); continue');
  44. lines.push(' fi');
  45. lines.push(' a=1; n=0');
  46. lines.push(' while [ $a -le 4 ]; do');
  47. lines.push(' curl -s --compressed -m 40 -x "$PX" "https://www.tikwm.com/api/comment/reply?video_id=$VID&comment_id=$cid&count=50&cursor=$cursor" -o "$f"');
  48. lines.push(' if grep -q \'"code":0\' "$f" 2>/dev/null; then');
  49. lines.push(' n=$(grep -o \'"video_id"\' "$f" | wc -l); [ $n -gt 0 ] && break');
  50. lines.push(' fi');
  51. lines.push(' rm -f "$f"; a=$((a+1)); sleep 2');
  52. lines.push(' done');
  53. lines.push(' if [ $n -gt 0 ]; then');
  54. lines.push(' got=$((got+n)); empty=0');
  55. lines.push(' # 已达标 且 本页未满(末页) → 收工');
  56. lines.push(' if [ $got -ge $dec ] && [ $n -lt 50 ]; then cursor=$((cursor+50)); break; fi');
  57. lines.push(' else');
  58. lines.push(' empty=$((empty+1))');
  59. lines.push(' [ $empty -ge 5 ] && break');
  60. lines.push(' fi');
  61. lines.push(' cursor=$((cursor+50)); sleep 0.4');
  62. lines.push(' done');
  63. lines.push(' echo "REPLY cid=$cid 声明=$dec 实得=$got 页=$pages" >> $LOG');
  64. lines.push('}');
  65. lines.push('');
  66. cands.forEach(c => {
  67. lines.push('grab ' + c.cid + ' ' + (c.replyCount || 0) + ' # ' + String(c.text || '').replace(/[\r\n]/g, ' ').slice(0, 40));
  68. });
  69. lines.push('');
  70. lines.push('echo "REPLY DONE $(date)" >> $LOG');
  71. lines.push('echo "DONE" > /sdcard/Download/_reply.done');
  72. fs.writeFileSync(outSh, lines.join('\n'), 'utf8');
  73. console.log('\n已生成:', outSh);