摘要:批量导出在360quake中使用过的搜索语句

直接在控制台使用下面代码的即可

(async () => {
  const PAGE_SIZE = 10; // 使用你提供的分页大小
  const DELAY = 300;    // 每次请求间隔,单位毫秒
  const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

  const url = new URL(
    "https://quake.360.net/api/user/log/query?size=10&start=0&query_type=Query&os=Windows&os_version=10.0&date=2026%2F10%2F20%208%3A41%3A26&UUID=874f4617-b6a1-4dfc-bed4-e55e24b71b0a"
  );

  // 保存到全局变量,中途出错也可以查看已提取的结果。
  const queries = [];
  window.quakeQueryStrings = queries;
  window.quakeExportStop = false;

  let start = 0;
  let total = Infinity;
  const seenIds = new Set();

  try {
    while (start < total) {
      if (window.quakeExportStop) {
        console.warn("已停止,已提取结果保存在 window.quakeQueryStrings");
        return;
      }

      url.searchParams.set("size", String(PAGE_SIZE));
      url.searchParams.set("start", String(start));

      const response = await fetch(url.href, {
        method: "GET",
        headers: {
          accept: "application/json, text/plain, */*",
          authorization: "233"
        },
        credentials: "include",
        cache: "no-store"
      });

      if (!response.ok) {
        throw new Error(`请求失败:HTTP ${response.status}`);
      }

      const result = await response.json();

      if (result.code !== 0 || !Array.isArray(result.data)) {
        throw new Error(result.message || "接口返回格式异常");
      }

      const reportedTotal = Number(result.meta?.pagination?.total);
      if (Number.isFinite(reportedTotal) && reportedTotal >= 0) {
        total = reportedTotal;
      }

      const rows = result.data;
      if (rows.length === 0) {
        if (Number.isFinite(total) && start < total) {
          throw new Error(`提前返回空页:已读取 ${start} 条,总数 ${total}`);
        }
        break;
      }

      // 防止接口忽略分页参数,反复返回同一页。
      if (rows.every(row => row.id && seenIds.has(row.id))) {
        throw new Error("接口返回重复页面,已停止,请检查分页参数");
      }

      for (const row of rows) {
        if (row.id && seenIds.has(row.id)) continue;
        if (row.id) seenIds.add(row.id);

        if (typeof row.original_query_string === "string") {
          queries.push({
            time: row.time,
            query: row.original_query_string
          });
        }
      }

      start += rows.length;
      console.log(`已读取 ${start}/${total} 条,已提取 ${queries.length} 条查询语句`);

      if (start < total) await sleep(DELAY);
    }

    const blob = new Blob([JSON.stringify(queries, null, 2)], {
      type: "application/json;charset=utf-8"
    });
    const downloadUrl = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = downloadUrl;
    link.download = "quake_original_query_strings.json";
    document.body.appendChild(link);
    link.click();
    link.remove();
    setTimeout(() => URL.revokeObjectURL(downloadUrl), 60_000);

    console.log(`提取完成,共 ${queries.length} 条,文件已触发下载。`);
  } catch (error) {
    console.error("提取未完成:", error);
    console.log(`已保留 ${queries.length} 条:window.quakeQueryStrings`);
  }
})();