灵猴市集Linghou Market

中国政府网政策搜索

中国政府网gov-policy-search

用途 读取中国政府网政策搜索结果页,返回指定关键词对应的政策搜索结果列表。 执行前提 先打开中国政府网政策搜索结果页,并确保页面 URL 中的 searchWord 与参数 query 一致,例如: https://sousuo.www.gov.cn/sousuo/search.shtml?code=17da70961a7&dataTypeId=107&s…

政策搜索公开读取
版本1.0.0
扫描passed
更新2026-06-28
超时30000ms

匹配范围

*://sousuo.www.gov.cn/**://www.gov.cn/*

排除范围

未声明

能力声明

dom.read

代码

源码已展开
1return await (async () => {2  const input = typeof params === "object" && params !== null ? params : {};3  const query = cleanText(input.query);4  const limit = readLimit(input.limit, 10, 50);5 6  if (!query) {7    throw new Error("参数 query 必须是非空字符串。");8  }9 10  const searchUrl = buildSearchUrl(query);11  const currentUrl = new URL(location.href);12  const currentQuery = cleanText(13    currentUrl.searchParams.get("searchWord") ||14      (document.querySelector(".js_search_input") || {}).value15  );16 17  if (location.hostname !== "sousuo.www.gov.cn" && parseItems(document, limit).length === 0) {18    throw new Error("请先打开中国政府网搜索结果页后执行:" + searchUrl);19  }20 21  if (currentQuery && currentQuery !== query) {22    throw new Error("当前页面搜索词为“" + currentQuery + "”,请打开匹配 query 的搜索页:" + searchUrl);23  }24 25  const items = await waitForItems(limit, 8000);26  return {27    query,28    searchUrl,29    count: items.length,30    items,31  };32 33  function buildSearchUrl(searchWord) {34    const url = new URL("https://sousuo.www.gov.cn/sousuo/search.shtml");35    url.searchParams.set("code", "17da70961a7");36    url.searchParams.set("dataTypeId", "107");37    url.searchParams.set("searchWord", searchWord);38    return url.toString();39  }40 41  async function waitForItems(maxItems, timeoutMs) {42    const startedAt = Date.now();43    let latest = [];44 45    while (Date.now() - startedAt < timeoutMs) {46      latest = parseItems(document, maxItems);47      if (latest.length > 0 || isNoResultVisible()) {48        return latest;49      }50      await delay(250);51    }52 53    return parseItems(document, maxItems);54  }55 56  function parseItems(root, maxItems) {57    const nodes = Array.from(58      root.querySelectorAll(".basic_result_content .item, .js_basic_result_content .item")59    );60    const rows = [];61    const seen = new Set();62 63    for (const node of nodes) {64      const link =65        node.querySelector("a.title[href], a.guizhang-title[href], a.log-anchor[href]") ||66        node.querySelector("a[href]");67      if (!link) {68        continue;69      }70 71      const title = titleFromLink(link);72      const url = absoluteUrl(link.getAttribute("href"), document.baseURI);73      if (!title || !url) {74        continue;75      }76 77      const key = url + "\n" + title;78      if (seen.has(key)) {79        continue;80      }81      seen.add(key);82 83      const summaryNode = node.querySelector(84        ".description .detail, .description p, .detail, .line_clamp_two"85      );86      const sourceNode = node.querySelector(".source");87      const text = cleanText(node.textContent);88      rows.push({89        index: rows.length + 1,90        title,91        date: extractDate(text),92        source: sourceNode ? cleanText(sourceNode.textContent) : "",93        summary: summaryNode ? cleanText(summaryNode.textContent) : "",94        url,95      });96 97      if (rows.length >= maxItems) {98        break;99      }100    }101 102    return rows;103  }104 105  function titleFromLink(link) {106    const clone = link.cloneNode(true);107    clone.querySelectorAll(".type_title").forEach((node) => node.remove());108    return cleanText(link.getAttribute("title") || clone.textContent || link.textContent);109  }110 111  function isNoResultVisible() {112    const node = document.querySelector(".search_noResult, .js_search_noResult1");113    if (!node) {114      return false;115    }116    const style = getComputedStyle(node);117    return style.display !== "none" && style.visibility !== "hidden";118  }119 120  function delay(ms) {121    return new Promise((resolve) => setTimeout(resolve, ms));122  }123 124  function readLimit(value, fallback, max) {125    if (value === undefined || value === null || value === "") {126      return fallback;127    }128    const numberValue = Number(value);129    if (!Number.isInteger(numberValue)) {130      throw new Error("参数 limit 必须是整数。");131    }132    if (numberValue < 1 || numberValue > max) {133      throw new Error("参数 limit 必须在 1 到 " + max + " 之间。");134    }135    return numberValue;136  }137 138  function cleanText(value) {139    return String(value || "").replace(/\s+/g, " ").trim();140  }141 142  function absoluteUrl(value, baseUrl) {143    if (!value) {144      return "";145    }146    try {147      return new URL(String(value), baseUrl).toString();148    } catch (error) {149      return "";150    }151  }152 153  function extractDate(value) {154    const text = cleanText(value);155    const match =156      text.match(/\d{4}-\d{1,2}-\d{1,2}/) ||157      text.match(/\d{4}年\d{1,2}月\d{1,2}日/) ||158      text.match(/\d{4}[./]\d{1,2}[./]\d{1,2}/);159    return match ? match[0] : "";160  }161})();