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:
parent
07c7d139a2
commit
4b105834fb
|
|
@ -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'));
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
@ -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)),
|
||||
};
|
||||
};
|
||||
|
|
@ -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)),
|
||||
};
|
||||
};
|
||||
|
|
@ -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)),
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
module.exports = {
|
||||
'': ['nczitzk'],
|
||||
'/:id?': ['nczitzk'],
|
||||
'/author/:id': ['nczitzk'],
|
||||
'/channel/:id?': ['nczitzk'],
|
||||
'/label/:name': ['nczitzk'],
|
||||
};
|
||||
|
|
@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
@ -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('./'));
|
||||
};
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
@ -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}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue