用 Java 写了个 Hexo 使用 GitHub Actions 自动提交网站到搜索引擎

657 阅读5分钟

写在前面

偶然在逛 Bing 的 Webmasters 的时候,发现多了个 IndexNow 原先一直用的 Hexo-SEO-AutoPush 但是有新东西了,和乐特子提了一下,他让我 PR,可惜 JavaScript 不太熟,索性自己用 JAVA 写了个,也顺便学习了 XML 解析器和 XPath 表达式

项目地址:sudojia/hexo-auto-submit-urls

开发思路

  1. 获取 RSS 地址
  2. 进行解析 RSS 并使用 XPath 表达式提取其中的 ID 标签
    /**
     * 从XML内容中提取id值。
     * 首先解析XML内容,然后使用XPath表达式定位到id元素的文本内容,并将这些内容收集到一个列表中。
     *
     * @param xmlContent 要解析的XML内容,作为字符串提供。
     * @param ids        用于收集提取到的id值的列表。
     * @throws Exception 如果解析XML或执行XPath表达式时发生错误,则抛出异常。
     */
    private static void extractIds(String xmlContent, List<String> ids) throws Exception {
        // 创建一个文档工厂实例,用于构建XML文档解析器
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        // 使用文档工厂创建一个文档构建器
        DocumentBuilder db = dbf.newDocumentBuilder();
        // 将XML内容解析为DOM文档
        Document doc = db.parse(new ByteArrayInputStream(xmlContent.getBytes(StandardCharsets.UTF_8)));
    
        // 创建XPath工厂,用于生成XPath实例
        XPathFactory xPathFactory = XPathFactory.newInstance();
        // 创建XPath实例,用于评估XPath表达式
        XPath xpath = xPathFactory.newXPath();
        // 编译XPath表达式,用于定位id元素的文本内容
        XPathExpression expr = xpath.compile("//feed/entry/id/text()");
        // 执行XPath表达式并获取匹配的节点列表
        NodeList nodeList = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
        // 遍历节点列表,将id文本添加到收集列表中
        for (int i = 0; i < nodeList.getLength(); i++) {
            ids.add(nodeList.item(i).getNodeValue());
        }
    }
    
  3. 最后将这些 ID 循环写入 urls.txt 文件
    /**
     * 将一串ID写入到指定的文本文件中。
     * 每个ID占一行。
     *
     * @param ids 包含需要写入文件的所有ID的列表。
     * @throws IOException 在写入文件过程中发生IO异常。
     */
    private static void writeIdsToFile(List<String> ids) throws IOException {
        // 使用BufferedWriter来提高文件写入性能,通过FileWriter指定写入的文件路径
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(TXT_FILE_PATH))) {
            // 遍历ids列表,将每个ID写入文件,如果ID不是最后一个,则在后面添加换行符
            for (int i = 0; i < ids.size(); i++) {
                // 写入当前ID
                writer.write(ids.get(i));
                if (i < ids.size() - 1) {
                    // 如果当前ID不是最后一个,则写入换行符
                    writer.newLine();
                }
            }
        }
    }
    
  4. 使用并发批量提交 urls.txt 的文本内容到不同的搜索引擎
    /**
     * 向多个搜索引擎提交URLs。
     *
     * @param host          主机地址
     * @param siteUrl       网站的URL
     * @param indexNowkey   IndexNow的密钥
     * @param keyLocation   密钥存储位置
     * @param bingApiKey    Bing搜索引擎的API密钥
     * @param baiduApiKey   百度搜索引擎的API密钥
     * @param googleKey     Google搜索引擎的json文件内容
     * @param indexNowCount 向IndexNow提交的URL数量
     * @param bingCount     向Bing提交的URL数量
     * @param baiDuCount    向百度提交的URL数量
     */
    private static void submitUrls(String host, String siteUrl, String indexNowkey, String keyLocation, String bingApiKey, String baiduApiKey, String googleKey, Integer indexNowCount, Integer bingCount, Integer baiDuCount) {
        List<String> urlList;
        try {
            // 从文件中读取 urls 列表
            urlList = ReptileRssTools.readUrlsFromFile();
            if (urlList.isEmpty()) {
                // 如果URL列表为空或读取失败, 取消提交
                LOGGER.log(Level.SEVERE, "URL 列表为空或读取 rss 失败, 取消提交!");
                return;
            }
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "读取 URL 列表时发生异常" + e.getMessage());
            return;
        }
        // 创建一个固定大小为 5 的线程池,用于并发提交URL到不同的搜索引擎
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        // 向IndexNow提交URL任务
        executorService.submit(() -> {
            try {
                AutoSubmitUrlServiceImpl.pushIndexNowUrl(urlList, host, indexNowkey, keyLocation, indexNowCount);
            } catch (Exception e) {
                LOGGER.log(Level.SEVERE, "提交 URL 到 IndexNow 时发生异常", e.getMessage());
            }
        });
        // 向Bing提交URL任务
        executorService.submit(() -> {
            try {
                AutoSubmitUrlServiceImpl.pushBingUrl(urlList, siteUrl, bingApiKey, bingCount);
            } catch (Exception e) {
                LOGGER.log(Level.SEVERE, "提交 URL 到 Bing 时发生异常", e.getMessage());
            }
        });
        // 向百度提交URL任务
        executorService.submit(() -> {
            try {
                AutoSubmitUrlServiceImpl.pushBaiduUrl(urlList, siteUrl, baiduApiKey, baiDuCount);
            } catch (Exception e) {
                LOGGER.log(Level.SEVERE, "提交 URL 到 Baidu 时发生异常", e.getMessage());
            }
        });
        // 向Google提交URL任务
        executorService.submit(() -> {
            try {
                AutoSubmitUrlServiceImpl.pushGoogleUrl(urlList, googleKey);
            } catch (Exception e) {
                LOGGER.log(Level.SEVERE, "提交 URL 到 Google 时发生异常", e.getMessage());
            }
        });
        // 关闭线程池,并等待所有任务完成
        executorService.shutdown();
        try {
            // 长时间等待所有任务完成,以确保所有提交任务执行完毕
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            // 如果在等待线程池关闭时线程被中断时,尝试恢复中断状态
            LOGGER.log(Level.SEVERE, "线程池等待终止时发生异常", e.getMessage());
            Thread.currentThread().interrupt();
        }
    }
    

如何使用

  1. 本项目是利用 hexo-generator-feed 生成的 RSS 进行解析并获取文章列表,所以需要你在 Hexo 安装该插件

    npm install hexo-generator-feed --save
    

    然后在博客的配置文件里添加如下配置

    feed:
      enable: true
      type: atom
      path: atom.xml
      limit: 0
      hub:
      content: true
      content_limit:
      content_limit_delim: ' '
      order_by: -date
      icon: 图标.icon # 修改你的图标
      autodiscovery: true
      template:
    

    详情见插件地址:hexojs/hexo-generator-feed

  2. Fork】本项目并在仓库的 settings -> Secrets and variables -> Actions -> New repository secret 添加环境变量

  3. 最后在 Actions 运行 Workflows 文件

    image-20240512181416458

参数说明

SecretsValue 说明
RSS_URLrss 地址,eg:https://example.org/atom.xml
INDEX_NOW_KEYwww.bing.com/indexnow/ge… 获取 KEY 并把文件下载到本地,放在 Hexo 根目录的 source 下
eg: 6b0e36ed24d24c68a6f3415c41ee849a
BING_KEYwww.bing.com/webmasters/… 设置 -> API 访问 -> 查看 API 密钥
BAIDU_KEYziyuan.baidu.com/linksubmit/… 只要 token 的值
GOOGLE_KEYJSON 内容,到 json.cn 压缩代码
BOT_TOKEN通过 @BotFather 获取机器人 token
CHAT_ID和它对话获取 chat_id @userinfobot

提交数量:

因本项目是获取你的 RSS 地址,所以你 RSS 有多少条文章链接,就提交多少条,如需要设置提交数量可在 KEY 的后面加个 , 隔开数量

注意是英文的逗号,另 GOOGLE_KEY 没有写提交数量,默认就是你的 RSS 文章链接数量

eg: hCvGPEqkx7L5hJur,100

特别说明:

  1. INDEX_NOW_KEY: Secrets 变量里不仅需要填入 KEY,还需要你把它那个文件下载并放入到网站的根目录,可以参考官方的请求示例:keyLocation

    POST /IndexNow HTTP/1.1
    Content-Type: application/json; charset=utf-8
    Host: api.indexnow.org
    {
      "host": "www.example.org",
      "key": "960ca71a21c141f7a4deb8edcb256bf4",
      "keyLocation": "https://www.example.org/960ca71a21c141f7a4deb8edcb256bf4.txt",
      "urlList": [
          "https://www.example.org/url1",
          "https://www.example.org/folder/url2",
          "https://www.example.org/url3"
          ]
    }
    
  2. BAIDU_KEY: 百度提交的好像大部分站长都是 10 条,所以本项目百度提交默认为 10 条,有需求的可以在 token 后加个 ,提交的数量

    hCvGPEqkx7L5hJur,100
    
  3. GOOGLE_KEY: 前提你需要创建项目并启用 Web Search Indexing API,最后创建密钥时把 JSON 文件下载到本地,前置操作可以参考【如何使用google index api来自动提交url】挺详细的。

    然后打开【json.cn】压缩 JSON 代码,并填入压缩后的代码

    image-20240512184012784

    image-20240512184140509

结语

该收录的自然会收录,瞧不上的自然瞧不上

文章作者: GarveyZhong

文章链接: blog.imzjw.cn/posts/3ed40…

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 小嘉的部落格!