diff --git a/lib/v2/logclub/index.js b/lib/v2/logclub/index.js
new file mode 100644
index 00000000000000..01d0ca5c752e8e
--- /dev/null
+++ b/lib/v2/logclub/index.js
@@ -0,0 +1,134 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const { art } = require('@/utils/render');
+const path = require('path');
+
+module.exports = async (ctx) => {
+ const { category = 'news' } = ctx.params;
+ const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 11;
+
+ const rootUrl = 'https://www.logclub.com';
+ const currentUrl = new URL(category, rootUrl).href;
+
+ const { data: response } = await got(currentUrl);
+
+ const $ = cheerio.load(response);
+
+ let items = $('li.layui-row, li.layui-timeline-item')
+ .slice(0, limit)
+ .toArray()
+ .map((item) => {
+ item = $(item);
+
+ const a = item.find('div.newslist-txt h3 a, a.article_title').first();
+ const image = item.find('img.img-hover').prop('src')?.split(/\?/)[0] ?? undefined;
+
+ return {
+ title: a.text(),
+ link: new URL(a.prop('href'), rootUrl).href,
+ description: art(path.join(__dirname, 'templates/description.art'), {
+ image: {
+ src: image,
+ alt: a.text(),
+ },
+ intro: item.find('p.newslist-intro, div.newslist-info-intro').text(),
+ }),
+ itunes_item_image: image,
+ };
+ });
+
+ items = await Promise.all(
+ items.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const { data: detailResponse } = await got(item.link);
+
+ const content = cheerio.load(detailResponse);
+
+ content('a.dl_file').each((_, el) => {
+ el = content(el);
+ el.parent().remove();
+ });
+ content('img').each((_, el) => {
+ el = content(el);
+ el.replaceWith(
+ art(path.join(__dirname, 'templates/description.art'), {
+ image: {
+ src: el.prop('src')?.split(/\?/)[0] ?? undefined,
+ alt: el.prop('title'),
+ },
+ })
+ );
+ });
+
+ item.title = content('h1, div.current_video_title').first().text();
+
+ item.enclosure_url = content('video#ref_video').prop('src');
+ if (item.enclosure_url) {
+ item.enclosure_type = `video/${item.enclosure_url.split(/\./).pop()}`;
+ }
+
+ item.description += art(path.join(__dirname, 'templates/description.art'), {
+ video: {
+ poster: item.itunes_item_image,
+ src: item.enclosure_url,
+ type: item.enclosure_type,
+ },
+ description: content('div.article-cont').html(),
+ });
+ item.author = content('div.article-info-r a')
+ .toArray()
+ .map((a) => content(a).text())
+ .join('/');
+ item.category = [
+ ...new Set([
+ ...content('div.article-label-r a.label')
+ .toArray()
+ .map((c) => content(c).text()),
+ ...(content('meta[name="keywords"]')
+ .prop('content')
+ ?.split(/\s?,\s?/) ?? []),
+ ]),
+ ].filter((c) => c);
+
+ if (content('span.aritlceIn-time').length === 0) {
+ item.pubDate = parseDate(
+ content(
+ content('div.video_info_item, div.lc-infos div')
+ .toArray()
+ .filter((i) => /\d{4}-\d{2}-\d{2}/.test(content(i).text()))
+ .pop()
+ )
+ .text()
+ .split(/:/)
+ .pop()
+ .trim()
+ );
+ } else {
+ item.pubDate = parseDate(content('span.aritlceIn-time').text().trim());
+ }
+
+ return item;
+ })
+ )
+ );
+
+ const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href;
+ const subtitle = $('meta[name="keywords"]').prop('content');
+ const author = subtitle.split(/,/)[0];
+
+ ctx.state.data = {
+ item: items,
+ title: $('title').text().split(/-/)[0].trim(),
+ link: currentUrl,
+ description: $('meta[name="description"]').prop('content'),
+ language: 'zh',
+ image: new URL($('div.logo_img img').prop('src'), rootUrl).href,
+ icon,
+ logo: icon,
+ subtitle: subtitle.replace(/,/g, ''),
+ author,
+ itunes_author: author,
+ itunes_category: 'News',
+ };
+};
diff --git a/lib/v2/logclub/maintainer.js b/lib/v2/logclub/maintainer.js
new file mode 100644
index 00000000000000..68c46e20c708f0
--- /dev/null
+++ b/lib/v2/logclub/maintainer.js
@@ -0,0 +1,9 @@
+module.exports = {
+ '/company/:id': ['nczitzk'],
+ '/columnist/articleList/:id': ['nczitzk'],
+ '/lc_report/:id?': ['nczitzk'],
+ '/news/:id?': ['nczitzk'],
+ '/original': ['nczitzk'],
+ '/recruit': ['nczitzk'],
+ '/tender': ['nczitzk'],
+};
diff --git a/lib/v2/logclub/radar.js b/lib/v2/logclub/radar.js
new file mode 100644
index 00000000000000..675254d7cc9cf0
--- /dev/null
+++ b/lib/v2/logclub/radar.js
@@ -0,0 +1,139 @@
+module.exports = {
+ 'logclub.com': {
+ _name: '罗戈网',
+ '.': [
+ {
+ title: '资讯',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/:id', '/news'],
+ target: (params) => {
+ const id = params.id;
+
+ return `/logclub/news${id ? `/${id}` : ''}`;
+ },
+ },
+ {
+ title: '资讯 - 供应链',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/10-16'],
+ target: '/logclub/news/10-16',
+ },
+ {
+ title: '资讯 - 快递',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/11'],
+ target: '/logclub/news/11',
+ },
+ {
+ title: '资讯 - 快运/运输',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/30'],
+ target: '/logclub/news/30',
+ },
+ {
+ title: '资讯 - 仓储/地产',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/9'],
+ target: '/logclub/news/9',
+ },
+ {
+ title: '资讯 - 物流综合',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/32'],
+ target: '/logclub/news/32',
+ },
+ {
+ title: '资讯 - 国际与跨境物流',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/114'],
+ target: '/logclub/news/114',
+ },
+ {
+ title: '资讯 - 科技创新',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/107'],
+ target: '/logclub/news/107',
+ },
+ {
+ title: '资讯 - 绿色供应链',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/213'],
+ target: '/logclub/news/213',
+ },
+ {
+ title: '资讯 - 低碳物流',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/214'],
+ target: '/logclub/news/214',
+ },
+ {
+ title: '资讯 - 碳中和碳达峰',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zi-xun',
+ source: ['/news/215'],
+ target: '/logclub/news/215',
+ },
+ {
+ title: '招聘',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zhao-pin',
+ source: ['/recruit'],
+ target: '/logclub/recruit',
+ },
+ {
+ title: '报告',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-bao-gao',
+ source: ['/lc_report'],
+ target: (params, url, document) => {
+ const id = document
+ ?.querySelector('li.layui-this[id]')
+ ?.id?.replace(/_/g, ' ')
+ .replace(/\b\w/g, (c) => c.toUpperCase())
+ .replace(/\s/g, '');
+
+ return `/logclub/lc_report${id ? `/${id}` : ''}`;
+ },
+ },
+ {
+ title: '报告 - 罗戈研究出品',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-bao-gao',
+ source: ['/lc_report'],
+ target: '/logclub/lc_report/Report',
+ },
+ {
+ title: '报告 - 物流报告',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-bao-gao',
+ source: ['/lc_report'],
+ target: '/logclub/lc_report/IndustryReport',
+ },
+ {
+ title: '报告 - 绿色双碳报告',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-bao-gao',
+ source: ['/lc_report'],
+ target: '/logclub/lc_report/GreenDualCarbonReport',
+ },
+ {
+ title: '招投标',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zhao-tou-biao',
+ source: ['/tender'],
+ target: '/logclub/tender',
+ },
+ {
+ title: '原创',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zhao-yuan-chuang',
+ source: ['/original'],
+ target: '/logclub/original',
+ },
+ {
+ title: '大企业',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zhao-da-qi-ye',
+ source: ['/company/:id'],
+ target: '/logclub/company/:id',
+ },
+ {
+ title: '专家说',
+ docs: 'https://docs.rsshub.app/routes/new-media#luo-ge-wang-zhao-zhuan-jia-shuo',
+ source: ['/columnist/articleList/:id'],
+ target: '/logclub/columnist/articleList/:id',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/logclub/report.js b/lib/v2/logclub/report.js
new file mode 100644
index 00000000000000..1abd9b1b97f938
--- /dev/null
+++ b/lib/v2/logclub/report.js
@@ -0,0 +1,98 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const timezone = require('@/utils/timezone');
+const { parseDate } = require('@/utils/parse-date');
+const { art } = require('@/utils/render');
+const path = require('path');
+
+module.exports = async (ctx) => {
+ const { id = 'Report' } = ctx.params;
+ const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 11;
+
+ const rootUrl = 'https://www.logclub.com';
+ const currentUrl = new URL('lc_report', rootUrl).href;
+ const apiUrl = new URL(`front/lc_report/load${id}List`, rootUrl).href;
+
+ const { data: response } = await got.post(apiUrl, {
+ json: {
+ page: 1,
+ },
+ });
+
+ let items = response.list.slice(0, limit).map((item) => ({
+ title: item.title,
+ link: new URL(`front/lc_report/get_report_info/${item.id}`, rootUrl).href,
+ description: art(path.join(__dirname, 'templates/description.art'), {
+ image: {
+ src: item.img_url?.split(/\?/)[0] ?? undefined,
+ alt: item.title,
+ },
+ }),
+ author: item.author,
+ category: [item.channel_name],
+ guid: `logclub-report-${item.id}`,
+ pubDate: timezone(parseDate(item.release_time), +8),
+ }));
+
+ items = await Promise.all(
+ items.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const { data: detailResponse } = await got(item.link);
+
+ const content = cheerio.load(detailResponse);
+
+ content('img').each((_, el) => {
+ el = content(el);
+ el.replaceWith(
+ art(path.join(__dirname, 'templates/description.art'), {
+ image: {
+ src: el.prop('src')?.split(/\?/)[0] ?? undefined,
+ alt: el.prop('title'),
+ },
+ })
+ );
+ });
+
+ item.title = content('h1').first().text();
+ item.description += art(path.join(__dirname, 'templates/description.art'), {
+ description: content('div.article-cont').html(),
+ });
+ item.author = content('div.lc-infos a')
+ .toArray()
+ .map((a) => content(a).text())
+ .join('/');
+ item.category = [
+ ...new Set([
+ ...(item.category ?? []),
+ ...content('div.article-label-r a.label')
+ .toArray()
+ .map((c) => content(c).text()),
+ ]),
+ ].filter((c) => c);
+
+ return item;
+ })
+ )
+ );
+
+ const { data: currentResponse } = await got(currentUrl);
+
+ const $ = cheerio.load(currentResponse);
+
+ const title = $('div.this_nav').text().trim();
+ const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href;
+ const subtitle = $('meta[name="keywords"]').prop('content');
+
+ ctx.state.data = {
+ item: items,
+ title: `${$('title').text()}${title}`,
+ link: currentUrl,
+ description: $('meta[name="description"]').prop('content'),
+ language: 'zh',
+ image: new URL($('div.logo_img img').prop('src'), rootUrl).href,
+ icon,
+ logo: icon,
+ subtitle: subtitle.replace(/,/g, ''),
+ author: subtitle.split(/,/)[0],
+ };
+};
diff --git a/lib/v2/logclub/router.js b/lib/v2/logclub/router.js
new file mode 100644
index 00000000000000..fa03f16902fffb
--- /dev/null
+++ b/lib/v2/logclub/router.js
@@ -0,0 +1,5 @@
+module.exports = (router) => {
+ router.get('/lc_report/:id?', require('./report'));
+ router.get('/report/:id?', require('./report'));
+ router.get('/:category*', require('./'));
+};
diff --git a/lib/v2/logclub/templates/description.art b/lib/v2/logclub/templates/description.art
new file mode 100644
index 00000000000000..eedb4179b066ae
--- /dev/null
+++ b/lib/v2/logclub/templates/description.art
@@ -0,0 +1,33 @@
+{{ if image?.src }}
+
+
{{ intro }}
+{{ /if }} + +{{ if video?.src }} + +{{ /if }} + +{{ if description }} + {{@ description }} +{{ /if }} \ No newline at end of file diff --git a/website/docs/routes/new-media.mdx b/website/docs/routes/new-media.mdx index 8eda702dc6baac..c94ca05d0fe11a 100644 --- a/website/docs/routes/new-media.mdx +++ b/website/docs/routes/new-media.mdx @@ -4300,6 +4300,268 @@ column 为 third 时可选的 category: +## 罗戈网 {#luo-ge-wang} + +### 资讯 {#luo-ge-wang-zi-xun} + +