feat(route): 创业邦资讯 (#13083)

* feat(route): 创业邦资讯

* fix: remove async

* fix docs

* fix typo

* fix typo

* apply suggestions from code review

* update lib/v2/cyzone/index.js

---------
This commit is contained in:
Ethan Shen 2023-08-29 09:21:41 +08:00 committed by GitHub
parent 07c7d139a2
commit 4b105834fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 252 additions and 112 deletions

View File

@ -634,8 +634,8 @@ router.get('/jingdong/zhongchou/:type/:status/:sort', lazyloadRouteHandler('./ro
router.get('/allpoetry/:order?', lazyloadRouteHandler('./routes/allpoetry/order'));
// 创业邦
router.get('/cyzone/author/:id', lazyloadRouteHandler('./routes/cyzone/author'));
router.get('/cyzone/label/:name', lazyloadRouteHandler('./routes/cyzone/label'));
// router.get('/cyzone/author/:id', lazyloadRouteHandler('./routes/cyzone/author'));
// router.get('/cyzone/label/:name', lazyloadRouteHandler('./routes/cyzone/label'));
// 政府
// router.get('/gov/zhengce/zuixin', lazyloadRouteHandler('./routes/gov/zhengce/zuixin'));

View File

@ -1,55 +0,0 @@
const cheerio = require('cheerio');
const got = require('@/utils/got');
module.exports = async (ctx) => {
const { id } = ctx.params;
const url = `https://www.cyzone.cn/author/${id}`;
const res = await got.get(url);
const $ = cheerio.load(res.data);
const list = $('.article-item');
const out = await Promise.all(
list
.map(async (index, elem) => {
const $elem = $(elem);
const $title = $elem.find('.item-title');
const link = 'https:' + $title.attr('href');
const pubDate = new Date(+$elem.find('.time').attr('data-time') * 1000).toUTCString();
const item = {
title: $title.text(),
pubDate,
link,
};
const key = `cyzone-${link}`;
const value = await ctx.cache.get(key);
if (value) {
item.description = value;
} else {
const storyDetail = await got.get(item.link);
const data = storyDetail.data;
const $ = cheerio.load(data);
$('.article-content img').each(function () {
const $img = $(this);
const src = $img.attr('src');
if (!src.startsWith('http')) {
$img.attr('src', 'https:' + src);
}
});
item.description = $('.article-content').html();
ctx.cache.set(key, item.description);
}
return item;
})
.get()
);
ctx.state.data = {
title: `创业邦-作者 ${$('.ss-title').text()}`,
link: url,
description: '创业邦致力于成为中国创业者的信息平台和服务平台,帮助中国创业者实现创业梦想。创业邦为创业者和风险投资人提供各种创业类最新资讯和实用知识手册,打造创业者和投资人的社交平台。',
item: out,
};
};

View File

@ -1,53 +0,0 @@
const cheerio = require('cheerio');
const got = require('@/utils/got');
module.exports = async (ctx) => {
const name = ctx.params.name;
const url = `https://www.cyzone.cn/label/${encodeURIComponent(name)}/`;
const res = await got.get(url);
const $ = cheerio.load(res.data);
const list = $('.article-item');
const out = await Promise.all(
list
.map(async (index, elem) => {
const $elem = $(elem);
const $title = $elem.find('.item-title');
const link = 'https:' + $title.attr('href');
const pubDate = new Date(+$elem.find('.time').attr('data-time') * 1000).toUTCString();
const item = {
title: $title.text(),
pubDate,
link,
};
const key = `cyzone-${link}`;
const value = await ctx.cache.get(key);
if (value) {
item.description = value;
} else {
const storyDetail = await got.get(item.link);
const data = storyDetail.data;
const $ = cheerio.load(data);
$('.article-content img').each(function () {
const $img = $(this);
const src = $img.attr('src');
if (!src.startsWith('http')) {
$img.attr('src', 'https:' + src);
}
});
item.description = $('.article-content').html() || '内容已删除或未通过审核';
ctx.cache.set(key, item.description);
}
return item;
})
.get()
);
ctx.state.data = {
title: `创业邦-#${name}#`,
link: url,
item: out,
};
};

20
lib/v2/cyzone/author.js Normal file
View File

@ -0,0 +1,20 @@
const { rootUrl, apiRootUrl, processItems, getInfo } = require('./util');
module.exports = async (ctx) => {
const { id } = ctx.params;
const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 5;
const apiUrl = new URL('v2/author/author/detail', apiRootUrl).href;
const currentUrl = new URL(`author/${id}`, rootUrl).href;
const items = await processItems(apiUrl, limit, ctx.cache.tryGet, {
author_id: id,
});
ctx.state.data = {
...{
item: items,
},
...(await getInfo(currentUrl, ctx.cache.tryGet)),
};
};

27
lib/v2/cyzone/index.js Normal file
View File

@ -0,0 +1,27 @@
const { rootUrl, apiRootUrl, processItems, getInfo } = require('./util');
module.exports = async (ctx) => {
const { id = 'news' } = ctx.params;
const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 5;
const apiUrl = new URL(`v2/content/channel/${id === 'news' ? 'getArticle' : 'detail'}`, apiRootUrl).href;
const currentUrl = new URL(`channel/${id}`, rootUrl).href;
const items = await processItems(
apiUrl,
limit,
ctx.cache.tryGet,
id === 'news'
? {}
: {
channel_id: id,
}
);
ctx.state.data = {
...{
item: items,
},
...(await getInfo(currentUrl, ctx.cache.tryGet)),
};
};

20
lib/v2/cyzone/label.js Normal file
View File

@ -0,0 +1,20 @@
const { rootUrl, apiRootUrl, processItems, getInfo } = require('./util');
module.exports = async (ctx) => {
const { name } = ctx.params;
const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 5;
const apiUrl = new URL('v2/content/tag/tagList', apiRootUrl).href;
const currentUrl = new URL(`label/${name}`, rootUrl).href;
const items = await processItems(apiUrl, limit, ctx.cache.tryGet, {
tag: name,
});
ctx.state.data = {
...{
item: items,
},
...(await getInfo(currentUrl, ctx.cache.tryGet)),
};
};

View File

@ -0,0 +1,7 @@
module.exports = {
'': ['nczitzk'],
'/:id?': ['nczitzk'],
'/author/:id': ['nczitzk'],
'/channel/:id?': ['nczitzk'],
'/label/:name': ['nczitzk'],
};

25
lib/v2/cyzone/radar.js Normal file
View File

@ -0,0 +1,25 @@
module.exports = {
'cyzone.cn': {
_name: '创业邦',
'.': [
{
title: '资讯',
docs: 'https://docs.rsshub.app/routes/new-media#chuang-ye-bang-zi-xun',
source: ['/channel/:id', '/'],
target: '/cyzone/:id',
},
{
title: '作者',
docs: 'https://docs.rsshub.app/routes/new-media#chuang-ye-bang-zuo-zhe',
source: ['/author/:id', '/'],
target: '/cyzone/author/:id',
},
{
title: '标签',
docs: 'https://docs.rsshub.app/routes/new-media#chuang-ye-bang-biao-qian',
source: ['/label/:name', '/'],
target: '/cyzone/label/:name',
},
],
},
};

6
lib/v2/cyzone/router.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = function (router) {
router.get('/author/:id', require('./author'));
router.get('/channel/:id?', require('./'));
router.get('/label/:name', require('./label'));
router.get('/:id?', require('./'));
};

125
lib/v2/cyzone/util.js Normal file
View File

@ -0,0 +1,125 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
const rootUrl = 'https://www.cyzone.cn';
const apiRootUrl = 'https://api1.cyzone.cn';
const apiShowUrl = new URL('v2/content/app_content/show', apiRootUrl).href;
/**
* Retrieves information from a given URL using a provided tryGet function.
* @param {string} url - The URL to retrieve information from.
* @param {Function} tryGet - The tryGet function that handles the retrieval process.
* @returns {Promise<Object>} - A promise that resolves to an object containing the retrieved information.
*/
const getInfo = (url, tryGet) =>
tryGet(url, async () => {
const { data: response } = await got(url);
const $ = cheerio.load(response);
const avatar = $('img.avatar')?.prop('src')?.split('?')[0] ?? undefined;
const icon = new URL($('link[rel="icon"]')?.prop('href'), rootUrl).href;
const image = new URL($('div.logo img')?.prop('src'), rootUrl).href;
return {
title: $('title').text(),
link: url,
description: $('meta[name="description"]').prop('content'),
language: 'zh-cn',
image: avatar || image,
icon,
logo: icon,
subtitle: $('meta[name="keywords"]').prop('content'),
author: $('meta[name="app-mobile-web-app-title"]').prop('content'),
};
});
/**
* Process the item list and return the resulting array.
* @param {string} apiUrl - The URL of the API.
* @param {number} limit - The limit of the results.
* @param {function} tryGet - The tryGet function that handles the retrieval process.
* @param {...Object} searchParams - The search parameter objects.
* @returns {Promise<Array>} - The processed item array.
*/
const processItems = async (apiUrl, limit, tryGet, ...searchParams) => {
// Merge search parameters
searchParams = {
...searchParams.reduce(
(result, object) => ({
...result,
...object,
}),
{}
),
...{
size: limit,
},
};
const { data: response } = await got(apiUrl, {
searchParams,
});
let items = (response.data?.article ?? response.data?.data ?? response.data).slice(0, limit).map((item) => {
item = item.item ?? item;
return {
title: item.title,
link: /^\/\//.test(item.url) ? `https:${item.url}` : item.url,
description: item.description,
category: [item.category_name, ...(item.tags?.split(',') ?? [])],
guid: item.content_id,
pubDate: parseDate(item.published_at * 1000),
upvotes: item.votes ?? 0,
};
});
items = await Promise.all(
items.map((item) =>
tryGet(`cyzone-${item.guid}`, async () => {
const { data: detailResponse } = await got.post(apiShowUrl, {
json: {
content_id: item.guid,
},
});
const data = detailResponse.data;
const categories = [data.category, ...item.category, ...(data.tags?.split(',') ?? [])];
const content = cheerio.load(data.content);
content('img').each(function () {
if (content(this).prop('src')) {
content(this).prop('src', content(this).prop('src').split('?')[0]);
} else {
content(this).remove();
}
});
item.title = data.title;
item.link = /^\/\//.test(data.link_url) ? `https:${data.link_url}` : data.link_url;
item.description = content.html();
item.author = data.author_name ?? data.author;
item.category = [...new Set(categories)].filter((c) => c);
item.guid = `cyzone-${item.guid}`;
item.pubDate = parseDate(data.published_at * 1000);
item.upvotes = data.votes ?? 0;
return item;
})
)
);
return items;
};
module.exports = {
rootUrl,
apiRootUrl,
getInfo,
processItems,
};

View File

@ -2312,13 +2312,31 @@ Supported sub-sites:
## 创业邦 {#chuang-ye-bang}
### 资讯 {#chuang-ye-bang-zi-xun}
<Route author="nczitzk" example="/cyzone" path="/cyzone/:id?" paramsDesc={['频道 id可在对应频道页 URL 中找到默认为 news即最新资讯']}>
| 最新 | 快鲤鱼 | 创投 | 科创板 | 汽车 |
| ---- | ------ | ---- | ------ | ---- |
| news | 5 | 14 | 13 | 8 |
| 海外 | 消费 | 科技 | 医疗 | 文娱 |
| ---- | ---- | ---- | ---- | ---- |
| 10 | 9 | 7 | 27 | 11 |
| 城市 | 政策 | 特写 | 干货 | 科技股 |
| ---- | ---- | ---- | ---- | ------ |
| 16 | 15 | 6 | 12 | 33 |
</Route>
### 作者 {#chuang-ye-bang-zuo-zhe}
<Route author="xyqfer" example="/cyzone/author/1225562" path="/cyzone/author/:id" paramsDesc={['作者 id']}/>
<Route author="xyqfer nczitzk" example="/cyzone/author/1225562" path="/cyzone/author/:id" paramsDesc={['作者 id可在对应作者页 URL 中找到']}/>
### 标签 {#chuang-ye-bang-biao-qian}
<Route author="LogicJake" example="/cyzone/label/创业邦周报" path="/cyzone/label/:name" paramsDesc={['标签名称']}/>
<Route author="LogicJake nczitzk" example="/cyzone/label/创业邦周报" path="/cyzone/label/:name" paramsDesc={['标签名称,可在对应标签页 URL 中找到']}/>
## 創新拿鐵 {#chuang-xin-na-tie}