找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 8|回复: 0

百度热搜榜定时采集入库关键词监控/排名变动

[复制链接]
发表于 半小时前 | 显示全部楼层 |阅读模式
百度热搜榜定时采集入库关键词监控/排名变动

QQ截图20261022124445.png

QQ截图20261022124432.png

QQ截图20261022124420.png

  1. /**
  2. * 案例18:百度热搜榜定时采集入库 + 关键词监控/排名变动报警(纯 printl 日志版,输出到 H5 日志终端)
  3. *
  4. * 经典实用案例"热搜榜监控+关键词盯盘提醒",转换成 AIWROK 实现:
  5. *   okHttp 请求百度热搜公开 JSON 接口(top.baidu.com/api/board,无需Key)
  6. *   → 取 Top N 榜单(标题/排名/热度) → SQLite 入库(hot_records 表) → 逐条完整打印到 H5 终端
  7. *
  8. * 案例15/16/17 是"数值监控"(价格/温度/汇率),本案例换成"文本榜单监控",
  9. * 三个新知识点:
  10. * 1. okHttp.setHeader("User-Agent", ...):部分公开接口会校验 UA,这是第一次用到请求头;
  11. * 2. 关键词命中报警:标题包含监控词(如"AI")就打 ⚠ 并附链接,做舆情/盯盘提醒的雏形;
  12. * 3. 排名变动对比:用对象存上一轮榜单,本轮逐个对比出 新上榜/上升/下降/掉榜,
  13. *    这是"有状态采集"——脚本记得上一轮数据才能算变化,和前面纯数值入库不同。
  14. *
  15. * 接口实测说明(电脑直连验证过):
  16. *   https://top.baidu.com/api/board?platform=pc&tab=realtime 返回 50 条,
  17. *   data.cards[0].content[i] 含 word(标题)/index(排名)/hotScore(热度)/desc(摘要)/url(链接)。
  18. */

  19. // ========== 配置区(改这里) ==========
  20. var 监控关键词列表 = ["AI", "小米", "微信"];   // 命中任一关键词就打 ⚠ 报警(改这里盯你自己的词)
  21. var 取榜单条数 = 10;                          // 每轮取前 10 条(演示打印量刚好)
  22. var 排名变动阈值 = 2;                         // 排名比上一轮变动 >= 2 位提示 ↑/↓
  23. var 采集间隔秒 = 30;                          // 每 30 秒采一轮(录像教学用;长期挂机可改 300)
  24. var SQLite路径 = "/sdcard/热搜榜.db";
  25. var 表名 = "hot_records";

  26. // ========== 全局变量顶部声明 ==========
  27. var gStop = false;
  28. var 数据库连接 = null;
  29. var 数据库可用 = false;
  30. var 累计入库条数 = 0;
  31. var 上一轮榜单 = {};            // { "标题": 排名 } 首轮为空对象,不做变动对比

  32. // ========== importClass 只能放最外层 ==========
  33. importClass(android.database.sqlite.SQLiteDatabase);
  34. importClass(java.io.File);

  35. // ========== 工具函数(先定义后调用) ==========

  36. // 连接 SQLite(零配置;如需远程库可仿照案例14扩展 mysql 分支)
  37. function dbConnect() {
  38.     try {
  39.         数据库连接 = SQLiteDatabase.openOrCreateDatabase(new File(SQLite路径), null);
  40.         数据库可用 = true;
  41.         printl("[DB] ✅ SQLite 已打开: " + SQLite路径);
  42.     } catch (e) {
  43.         数据库可用 = false;
  44.         printl("[DB] ⚠ 打开失败(本轮只打印不入库): " + e);
  45.     }
  46. }

  47. // 建表(存在则跳过):rank/word/hot_score/collected_at
  48. function dbEnsureTable() {
  49.     if (!数据库可用) return;
  50.     try {
  51.         var sql = "CREATE TABLE IF NOT EXISTS " + 表名 + " (" +
  52.             "id INTEGER PRIMARY KEY AUTOINCREMENT," +
  53.             "rank INTEGER, word TEXT, hot_score INTEGER, collected_at INTEGER)";
  54.         数据库连接.execSQL(sql);
  55.         printl("[DB] ✅ 表已就绪: " + 表名);
  56.     } catch (e) {
  57.         printl("[DB] ⚠ 建表失败: " + e);
  58.         数据库可用 = false;
  59.     }
  60. }

  61. // 单条榜单入库
  62. function dbInsertHot(rank, word, hotScore) {
  63.     if (!数据库可用) return false;
  64.     try {
  65.         var stmt = 数据库连接.compileStatement("INSERT INTO " + 表名 +
  66.             " (rank, word, hot_score, collected_at) VALUES (?,?,?,?)");
  67.         stmt.bindLong(1, rank);
  68.         stmt.bindString(2, "" + word);
  69.         stmt.bindLong(3, hotScore);
  70.         stmt.bindLong(4, time.nowStamp());   // nowStamp=毫秒数字;time.now()是格式化字符串不能bindLong
  71.         stmt.executeInsert();
  72.         stmt.close();
  73.         return true;
  74.     } catch (e) {
  75.         printl("[DB] ❌ 入库失败: " + e);
  76.         return false;
  77.     }
  78. }

  79. function dbClose() {
  80.     if (数据库可用 && 数据库连接 != null) {
  81.         try { 数据库连接.close(); } catch (e) {}
  82.         数据库连接 = null;
  83.     }
  84. }

  85. // 拉取一轮热搜榜:返回数组 [{排名,标题,热度,链接}],失败返回 null
  86. function 拉取热搜() {
  87.     var url = "https://top.baidu.com/api/board?platform=pc&tab=realtime";

  88.     try {
  89.         printl("📡 正在请求百度热搜接口(实时榜)...");
  90.         var http = new okHttp();
  91.         http.setTimeOut(5000, 8000, 5000);   // 必须设超时,防网络挂死(踩坑经验)
  92.         // 新知识点:setHeader 带浏览器 UA,公开接口常校验 UA(本接口实测不带也通,带上更稳)
  93.         http.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36");
  94.         printl("📡 等待接口返回(最长8秒)...");
  95.         var text = "" + http.get(url);
  96.         printl("📡 接口已返回,开始解析 JSON...");
  97.         var obj = JSON.parse(text);
  98.         if (!obj || !obj.success || !obj.data || !obj.data.cards || !obj.data.cards[0] || !obj.data.cards[0].content) {
  99.             printl("[API] ⚠ 接口结构异常: " + text.substring(0, 120));
  100.             return null;
  101.         }
  102.         var 全部 = obj.data.cards[0].content;
  103.         var 取数 = 取榜单条数;
  104.         if (取数 > 全部.length) 取数 = 全部.length;
  105.         var 结果 = [];
  106.         for (var i = 0; i < 取数; i++) {
  107.             var 项 = 全部[i];
  108.             var 热度 = parseInt(项.hotScore);
  109.             if (isNaN(热度)) 热度 = 0;
  110.             结果.push({ 排名: i + 1, 标题: "" + 项.word, 热度: 热度, 链接: "" + 项.url });
  111.         }
  112.         printl("✅ 解析完成,榜单共 " + 全部.length + " 条,本轮取前 " + 结果.length + " 条");
  113.         return 结果;
  114.     } catch (e) {
  115.         printl("[API] ❌ 拉取失败: " + e);
  116.         return null;
  117.     }
  118. }

  119. // 逐条完整打印采集结果(演示用:排名/标题/热度全打出来)
  120. function 打印采集结果(榜单数组) {
  121.     printl("📋 本轮采集结果(百度热搜 Top " + 榜单数组.length + "):");
  122.     for (var i = 0; i < 榜单数组.length; i++) {
  123.         var s = 榜单数组[i];
  124.         printl("   [" + s.排名 + "] " + s.标题 + " | 热度 " + s.热度);
  125.     }
  126. }

  127. // 关键词命中报警:标题包含任一监控词就打 ⚠ 并附链接(舆情/盯盘提醒雏形)
  128. function 检查关键词(榜单数组) {
  129.     var 命中数 = 0;
  130.     for (var i = 0; i < 榜单数组.length; i++) {
  131.         var s = 榜单数组[i];
  132.         for (var k = 0; k < 监控关键词列表.length; k++) {
  133.             var kw = 监控关键词列表[k];
  134.             if (s.标题.indexOf(kw) >= 0) {
  135.                 命中数++;
  136.                 printl("⚠ 报警:关键词「" + kw + "」上榜! [" + s.排名 + "] " + s.标题);
  137.                 printl("   🔗 " + s.链接);
  138.             }
  139.         }
  140.     }
  141.     if (命中数 === 0) {
  142.         printl("🔕 关键词检查:Top " + 榜单数组.length + " 未命中监控词(" + 监控关键词列表.join("/") + ")");
  143.     }
  144. }

  145. // 排名变动对比:与上一轮榜单比,算出新上榜/上升/下降/掉榜(有状态采集)
  146. function 检查排名变动(榜单数组) {
  147.     var 本轮集合 = {};
  148.     for (var i = 0; i < 榜单数组.length; i++) {
  149.         var s = 榜单数组[i];
  150.         本轮集合[s.标题] = s.排名;
  151.         var 旧排名 = 上一轮榜单[s.标题];
  152.         if (旧排名 == null) {
  153.             printl("🆕 新上榜:[" + s.排名 + "] " + s.标题);
  154.         } else {
  155.             var 变动 = 旧排名 - s.排名;   // 正数=名次前进
  156.             if (变动 >= 排名变动阈值) {
  157.                 printl("📈 上升:[" + 旧排名 + "→" + s.排名 + "] " + s.标题);
  158.             } else if (-变动 >= 排名变动阈值) {
  159.                 printl("📉 下降:[" + 旧排名 + "→" + s.排名 + "] " + s.标题);
  160.             }
  161.         }
  162.     }
  163.     // 掉榜检测:上一轮有、本轮没有
  164.     for (var 词 in 上一轮榜单) {
  165.         if (本轮集合[词] == null) {
  166.             printl("🚪 掉榜:" + 词);
  167.         }
  168.     }
  169.     上一轮榜单 = 本轮集合;   // 本轮存为下一轮对比基准
  170. }

  171. // ========== 主流程 ==========
  172. printl("===== 案例18 百度热搜定时采集入库 启动 =====");

  173. // 步骤0:连库建表
  174. dbConnect();
  175. dbEnsureTable();
  176. printl("⚙ 配置:取前 " + 取榜单条数 + " 条 · 监控词(" + 监控关键词列表.join("/") + ") · 排名变动阈值 " +
  177.     排名变动阈值 + " 位 · 采集间隔 " + 采集间隔秒 + " 秒");

  178. // 停止按钮清理(顺序:先置标志,stopAllRuning 放最后)
  179. event.onStopBtnEvent(function () {
  180.     gStop = true;
  181.     dbClose();
  182.     runTime.stopAllRuning();
  183. });

  184. // 步骤1:定时循环采集
  185. var 轮次 = 0;
  186. while (!gStop) {
  187.     轮次++;
  188.     printl("\n---- 第 " + 轮次 + " 轮采集 ----");

  189.     var 榜单数组 = 拉取热搜();
  190.     if (榜单数组 != null && 榜单数组.length > 0) {
  191.         // 1) 逐条完整打印采集结果(演示重点:榜单标题全打出来)
  192.         打印采集结果(榜单数组);

  193.         // 2) 入库
  194.         var 本轮入库 = 0;
  195.         printl("💾 开始逐条写入数据库(表: " + 表名 + ")...");
  196.         for (var k = 0; k < 榜单数组.length; k++) {
  197.             var q = 榜单数组[k];
  198.             if (dbInsertHot(q.排名, q.标题, q.热度)) {
  199.                 本轮入库++;
  200.                 累计入库条数++;
  201.                 printl("   ✓ 已入库 [" + 本轮入库 + "/" + 榜单数组.length + "] [" + q.排名 + "] " + q.标题);
  202.             } else {
  203.                 printl("   ✗ 入库失败: " + q.标题);
  204.             }
  205.         }
  206.         printl("[DB] 本轮入库 " + 本轮入库 + " 条 | 累计 " + 累计入库条数 + " 条 | 库文件: " + SQLite路径);

  207.         // 3) 关键词报警 + 排名变动对比
  208.         检查关键词(榜单数组);
  209.         if (轮次 === 1) {
  210.             printl("ℹ 首轮已记录榜单基准,从第 2 轮起显示 新上榜/上升/下降/掉榜");
  211.         }
  212.         检查排名变动(榜单数组);
  213.     } else {
  214.         printl("⚠ 本轮无数据,跳过入库(" + time.StampToTime(time.nowStamp()) + ")");
  215.     }

  216.     // 分片等待下一轮:每 5 秒打一条进度日志(画面不空,方便观察/录像)
  217.     printl("⏳ 本轮全部完成," + 采集间隔秒 + " 秒后自动开始下一轮");
  218.     var 剩余 = 采集间隔秒;
  219.     while (剩余 > 0 && !gStop) {
  220.         var 步长 = 剩余 >= 5 ? 5 : 剩余;
  221.         printl("⏳ 等待下一轮...剩余 " + 剩余 + " 秒(期间脚本在待机,到点自动拉热搜→入库→对比排名)");
  222.         sleep.second(秒 = 步长);
  223.         剩余 = 剩余 - 步长;
  224.     }
  225. }

  226. dbClose();
  227. printl("===== 流程结束:共 " + 轮次 + " 轮,累计入库 " + 累计入库条数 + " 条 =====");
复制代码


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|

GMT+8, 2026-10-23 07:16

安卓脚本软件 · 键鼠盒子 · HID · 机械臂

快速回复 返回顶部 返回列表

随机推荐 · 热帖 / 新帖 / 随便看看

社区会员(随机展示)