diff --git a/docs/new-media.md b/docs/new-media.md
index 748db8178..f7c7b82ec 100644
--- a/docs/new-media.md
+++ b/docs/new-media.md
@@ -1297,6 +1297,20 @@ Supported sub-sites:
+## 艾莱资讯
+
+### 世界轨道交通资讯网
+
+
+
+::: tip 提示
+
+默认抓取前 20 条,可通过 `?limit=` 改变。
+
+:::
+
+
+
## 爱范儿 ifanr
### 爱范儿频道
diff --git a/lib/v2/ally/maintainer.js b/lib/v2/ally/maintainer.js
new file mode 100644
index 000000000..10df219e9
--- /dev/null
+++ b/lib/v2/ally/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/rail/:category?/:topic?': ['Rongronggg9'],
+};
diff --git a/lib/v2/ally/radar.js b/lib/v2/ally/radar.js
new file mode 100644
index 000000000..a90254cff
--- /dev/null
+++ b/lib/v2/ally/radar.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'ally.net.cn': {
+ _name: '艾莱资讯',
+ rail: [
+ {
+ title: '世界轨道交通资讯网',
+ docs: 'https://docs.rsshub.app/new-media.html#ai-lai-zi-xun',
+ source: ['/', '/html/:category?/:topic?'],
+ target: '/ally/rail/:category?/:topic?',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/ally/rail.js b/lib/v2/ally/rail.js
new file mode 100644
index 000000000..a0ddae442
--- /dev/null
+++ b/lib/v2/ally/rail.js
@@ -0,0 +1,109 @@
+const cheerio = require('cheerio');
+const got = require('@/utils/got');
+const { parseDate } = require('@/utils/parse-date');
+const timezone = require('@/utils/timezone');
+
+module.exports = async (ctx) => {
+ // http://rail.ally.net.cn/sitemap.html
+ const { category, topic } = ctx.params;
+ const rootUrl = 'http://rail.ally.net.cn';
+ const pageUrl = category ? (topic ? `${rootUrl}/html/${category}/${topic}/` : `${rootUrl}/html/${category}/`) : rootUrl;
+
+ const response = await got.get(pageUrl);
+ const $ = cheerio.load(response.data);
+ let title = $('.container .regsiter a') // what a typo...
+ .get()
+ .slice(1) // drop "首页"
+ .reduce((prev, curr) => (prev ? `${prev} - ${$(curr).text()}` : $(curr).text()), '');
+ title = title || (category && topic ? `${category} - ${topic}` : category) || '首页';
+ let links = [
+ // list page: http://rail.ally.net.cn/html/lujuzixun/
+ $('.left .hynewsO h2 a').get(),
+ // multi-sub-topic page: http://rail.ally.net.cn/html/hyzix/
+ $('.left .list_content_c').find('.new_hy_focus_con_tit a, .new_hy_list_name a').get(),
+ // multi-sub-topic page 2: http://rail.ally.net.cn/html/foster/
+ $('.left').find('.nnewslistpic a, .nnewslistinfo dd a').get(),
+ // data list page: http://rail.ally.net.cn/html/tongjigongbao/
+ $('.left .list_con .datacountTit a').get(),
+ // home page: http://rail.ally.net.cn
+ $('.container_left').find('dd a, h1 a, ul.slideshow li a').get(),
+ ].reduce((a, b) => a.concat(b), []);
+ if (!links.length) {
+ // try aggressively sniffing links, e.g. http://rail.ally.net.cn/html/InviteTen/
+ links = $('.left a, .container_left a').get();
+ }
+
+ let items = links
+ .map((link) => {
+ link = $(link);
+ const url = link.attr('href');
+ const urlMatch = url && url.match(/\/html\/(\d{4})\/\w+_(\d{4})\/\d+\.html/);
+ if (!urlMatch) {
+ return null;
+ }
+ const title = link.text();
+ return {
+ title,
+ link: url.startsWith('/') ? `${rootUrl}${url}` : url,
+ pubDate: timezone(parseDate(`${urlMatch[1]}${urlMatch[2]}`), 8),
+ };
+ })
+ .filter(Boolean)
+ .reduce((prev, curr) => (prev.length && prev[prev.length - 1].link === curr.link ? prev : [...prev, curr]), [])
+ .sort((a, b) => b.pubDate - a.pubDate)
+ .slice(0, ctx.query.limit || 20);
+
+ items = await Promise.all(
+ items.map(
+ async (item) =>
+ await ctx.cache.tryGet(item.link, async () => {
+ const response = await got(item.link);
+ const $ = cheerio.load(response.data);
+ // fix weird format
+ let description = '';
+ const content = $('div.content_all');
+ if (content.length) {
+ content
+ .eq(content.length - 1) // some pages have "summary"
+ .contents()
+ .each((_, child) => {
+ const $child = $(child);
+ let innerHtml;
+ if (child.name === 'div') {
+ innerHtml = $child.html();
+ innerHtml = innerHtml && innerHtml.trim();
+ description += !innerHtml || innerHtml === ' ' ? (description ? '
' : '') : innerHtml;
+ } else {
+ // bare text node or something else
+ description += $child.toString().trim();
+ }
+ });
+ } else {
+ // http://rail.ally.net.cn/html/2022/InviteTen_0407/4686.html
+ description = $('div.content div').first().html();
+ }
+
+ description = description.replace(/\s*
\s*$/, ''); // trim
at the end
+ const info = $('.content > em span');
+ return {
+ title: $('.content > h2').text() || item.title,
+ description,
+ // pubDate: timezone(parseDate(info.eq(0).text()), 8),
+ pubDate: item.pubDate,
+ author: info
+ .eq(1)
+ .text()
+ .replace(/^来源:/, ''),
+ link: item.link,
+ };
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: `世界轨道交通资讯网 - ${title}`,
+ link: pageUrl,
+ item: items,
+ description: $('head > meta[name="description"]').attr('content'),
+ };
+};
diff --git a/lib/v2/ally/router.js b/lib/v2/ally/router.js
new file mode 100644
index 000000000..1bbacb1d6
--- /dev/null
+++ b/lib/v2/ally/router.js
@@ -0,0 +1,3 @@
+module.exports = function (router) {
+ router.get('/rail/:category?/:topic?', require('./rail'));
+};