写在前面

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

项目地址:

开发思路

  1. 获取 RSS 地址

  2. 进行解析 RSS 并使用 XPath 表达式提取其中的 ID 标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    /**
    * 从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 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * 将一串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 的文本内容到不同的搜索引擎

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    /**
    * 向多个搜索引擎提交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 安装该插件

    1
    npm install hexo-generator-feed --save

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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_KEYhttps://www.bing.com/indexnow/getstarted#implementation 获取 KEY 并把文件下载到本地,放在 Hexo 根目录的 source 下
eg: 6b0e36ed24d24c68a6f3415c41ee849a
BING_KEYhttps://www.bing.com/webmasters/home 设置 -> API 访问 -> 查看 API 密钥
BAIDU_KEYhttps://ziyuan.baidu.com/linksubmit/index 只要 token 的值
GOOGLE_KEYJSON 内容,到 json.cn 压缩代码
BOT_TOKEN通过 @BotFather 获取机器人 token
CHAT_ID和它对话获取 chat_id @userinfobot

提交数量:

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

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

1
eg: hCvGPEqkx7L5hJur,100

特别说明:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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 后加个 ,提交的数量

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

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

    image-20240512184012784

    image-20240512184140509

结语

该收录的自然会收录,瞧不上的自然瞧不上 —— 孙泽康

话虽如此,但是有新东西,我不用,我难受😭