Featured image of post 手搓RSSHub路由失败

手搓RSSHub路由失败

前几天在NameCheap白嫖 .news 域名,注册了个 hy2.news,想着也没啥用就搭建一下个人动态页面。既然是动态的,那就得把 Wordpress 请出来,然后用 RSS 插件实现。页面地址:Hyruo News

页面是搭好了,RSS 来源成了问题,于是摸索着先从最近几个月混得比较活跃的 Nodeseek 论坛搞起。结果搞半天还是失败了。


RSSHub 手搓路由方法

RSSHub 官网的开发路由教程比较跳跃,主要过程如下。

前期工作

  1. 克隆 RSSHub 仓库 (电脑慢的话要半个小时以上) git clone https://github.com/DIYgod/RSSHub.git
  2. 安装最新版本 Node.js (版本要大于 22)Node.js 官网地址
  3. 安装依赖项 pnpm i
  4. 运行 pnpm run dev

开发路由

开发路由就比较简单,打开 RSSHub\lib\routes 目录,在下边新建一个文件夹,比如 nodeseek,然后在该文件夹中添加两个文件 namespace.ts custom.ts 就完事。

  1. namespace.ts 文件

这个文件照着官方教程就行,不然就随便在 lib\routes 目录下边复制别人的改改。示例如下:

1
2
3
4
5
6
7
import type { Namespace } from '@/types';

export const namespace: Namespace = {
    name: 'nodeseek',
    url: 'nodeseek.com',
    lang: 'zh-CN',
};
  1. custom.ts 文件

这是开发路由的主文件,文件名可以按照目标网站结构来命名,看看其他文件夹就懂,难度主要是在具体内容上。示例如下:

  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
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { Route } from '@/types';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import logger from '@/utils/logger';
import puppeteer from '@/utils/puppeteer';
import cache from '@/utils/cache';

export const route: Route = {
    path: '/user/:userId',
    categories: ['bbs'],
    example: '/nodeseek/user/1',
    parameters: { userId: '用户 ID,例如 1' },
    features: {
        requireConfig: false,
        requirePuppeteer: true, // 启用 Puppeteer
        antiCrawler: true, // 启用反爬虫
        supportBT: false,
        supportPodcast: false,
        supportScihub: false,
    },
    radar: [
        {
            source: ['nodeseek.com/space/:userId'],
            target: '/user/:userId',
        },
    ],
    name: 'NodeSeek 用户话题',
    maintainers: ['你的名字'],
    handler: async (ctx) => {
        const userId = ctx.req.param('userId');
        const baseUrl = 'https://www.nodeseek.com';
        const userUrl = `${baseUrl}/space/${userId}#/discussions`;

        // 导入 puppeteer 工具类并初始化浏览器实例
        const browser = await puppeteer();
        // 打开一个新标签页
        const page = await browser.newPage();

        // 设置请求头
        await page.setExtraHTTPHeaders({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
            Referer: baseUrl,
            Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        });

        // 访问目标链接
        logger.http(`Requesting ${userUrl}`);
        await page.goto(userUrl, {
            waitUntil: 'networkidle2', // 等待页面完全加载
        });

        // 模拟滚动页面(如果需要)
        await page.evaluate(() => {
            window.scrollBy(0, window.innerHeight);
        });

        // 等待帖子列表加载
        await page.waitForSelector('a[href^="/post-"]', { timeout: 7000 });

        // 获取页面的 HTML 内容
        const response = await page.content();
        const $ = load(response);

        // 提取帖子列表
        let items = $('a[href^="/post-"]')
            .toArray()
            .map((item) => {
                const $item = $(item);
                const title = $item.find('span').text().trim();
                const link = `${baseUrl}${$item.attr('href')}`;
                return {
                    title,
                    link,
                };
            });

        // 排除页脚的两个固定链接
        const excludedLinks = ['/post-6797-1', '/post-6800-1'];
        items = items.filter((item) => !excludedLinks.includes(new URL(item.link).pathname));

        // 最多提取 15 个帖子
        items = items.slice(0, 15);

        // 打印提取的帖子列表
        console.log('提取的帖子列表:', items); // 调试信息

        // 如果帖子列表为空,可能是页面未加载动态内容
        if (items.length === 0) {
            throw new Error('无法获取帖子列表,请检查页面结构');
        }

        // 获取每个帖子的内容
        items = await Promise.all(
            items.map((item) =>
                cache.tryGet(item.link, async () => {
                    // 打开一个新标签页
                    const postPage = await browser.newPage();

                    // 设置请求头
                    await postPage.setExtraHTTPHeaders({
                        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
                        Referer: baseUrl,
                    });

                    // 访问帖子链接
                    logger.http(`Requesting ${item.link}`);
                    await postPage.goto(item.link, {
                        waitUntil: 'networkidle2', // 等待页面完全加载
                    });

                    // 获取帖子内容
                    const postHtml = await postPage.content();
                    const $post = load(postHtml);

                    item.description = $post('article.post-content').html();
                    item.pubDate = parseDate($post('time').attr('datetime'));

                    // 关闭帖子页面
                    await postPage.close();

                    return item;
                })
            )
        );

        // 关闭浏览器实例
        await browser.close();

        // 返回 RSS 数据
        return {
            title: `NodeSeek 用户 ${userId} 的话题`,
            link: userUrl,
            item: items,
        };
    },
};

本次主要问题总结

简单说本次手搓 nodeseek 路由失败原因,主要就是没能突破 nodeseek 的反爬和 cloudflare 盾限制。

RSSHub 可以使用的极限方法就是利用 Puppeteer 来模拟浏览器行为反爬,问题是机器模拟行为很容易在攻防中被 cf 这种平台识别出来。

我在本地测试时,大概有个 50% 的成功率,这还是在本地更新最新版本 Puppeteer 的情况下,如果用 RSSHub 官方依赖中的 Puppeteer 版本,成功率不足 10%。考虑提交至 RSSHub 还要经过双重审核,这成功率没法看了。

最终只能先忍痛放弃先。

RSShub 无法获取文章列表

被反爬程序挡住,无法访问页面信息

被 CF 挡了

屋漏偏逢连夜雨

今早一起床发现天塌了,6 个 *.US.KG 免费域名因上级域名被停止解析而崩盘。

今天中午发现天又塌了一遍。好不容易弄好的 .news 域名又被官方暂停了。邮件发过来让我好好解释下为啥在注册过程中个人信息出现变更,然后强行将 NS 解析到鬼都不认识的 IP 上。

好吧,这个是我自己问题。一开始注册时直接套浏览器自动填单程序,一不小心全给填了真实信息。然后想着改回来,一改就出异常了。

PS:下午 *.us.kg 又恢复正常。但感觉不会再爱它了。

All textual works on this website are protected by copyright, and the authors reserve all rights. The photos on this website, unless specifically stated, licensed under the CC BY-NC-ND 4.0 license.
Built with Hugo & Stack, Powered by Github.
全站共 301 篇文章 合计 825630 字
本站已加入BLOGS·CN