diff --git a/lib/router.js b/lib/router.js index 5425b45ddeae43..71aa9f8e7bf097 100644 --- a/lib/router.js +++ b/lib/router.js @@ -2665,7 +2665,7 @@ router.get('/netflix/newsroom/:category?/:region?', lazyloadRouteHandler('./rout router.get('/sbs/chinese/:category?/:id?/:dialect?/:language?', lazyloadRouteHandler('./routes/sbs/chinese')); // QuestMobile -router.get('/questmobile/report/:category?/:label?', lazyloadRouteHandler('./routes/questmobile/report')); +// router.get('/questmobile/report/:category?/:label?', lazyloadRouteHandler('./routes/questmobile/report')); // Fashion Network router.get('/fashionnetwork/news/:sectors?/:categories?/:language?', lazyloadRouteHandler('./routes/fashionnetwork/news.js')); diff --git a/lib/routes/questmobile/report.js b/lib/routes/questmobile/report.js deleted file mode 100644 index 214e10262c1120..00000000000000 --- a/lib/routes/questmobile/report.js +++ /dev/null @@ -1,121 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -const categories = { - 0: '全部行业', - 10: '移动视频', - 1: '移动社交', - 2: '移动购物', - 17: '系统工具', - 21: '新闻资讯', - 11: '移动音乐', - 5: '生活服务', - 16: '数字阅读', - 4: '汽车服务', - 12: '拍摄美化', - 8: '旅游服务', - 22: '健康美容', - 23: '医疗服务', - 14: '教育学习', - 3: '金融理财', - 9: '办公商务', - 19: '智能设备', - 20: '手机游戏', - 26: '出行服务', - 29: '内容平台', -}; - -const labels = { - 0: '全部标签', - 75: '5G', - 74: '双十一', - 73: '直播带货', - 72: '电商平台', - 71: '新蓝领', - 70: '市场竞争', - 69: 'KOL', - 68: '品牌营销', - 67: '互联网研究', - 66: '广告效果', - 65: '媒介策略', - 64: 'App和小程序', - 63: 'App增长', - 62: '小程序数据', - 61: '移动大数据', - 60: '互联网报告', - 59: '数据报告', - 58: '互联网数据', - 57: '智能终端', - 56: '小程序', - 55: '私域流量', - 54: '运动消费', - 53: '用户争夺', - 52: '运动健身', - 48: '新消费', - 42: '增长模式', - 41: '下沉', - 36: '新中产', - 31: '银发族', - 30: '粉丝经济', - 29: '泛娱乐', - 28: '网购少女', - 27: '二次元', - 26: '兴趣圈层', - 25: '大学生', - 23: '广告营销', - 22: 'Z世代', - 18: '付费用户', - 17: '精细化运营', - 14: '00后', - 11: '90后', - 10: '春节报告', - 9: '低幼经济', - 7: '季度报告', - 6: '年度报告', - 5: '全景生态', - 2: '消费者洞察', -}; - -module.exports = async (ctx) => { - const category = ctx.params.category || '0'; - const label = ctx.params.label || '0'; - - const rootUrl = 'https://www.questmobile.com.cn'; - const currentUrl = `${rootUrl}/api/v1/research/reports?categoryId=${category}&labelId=${label}&version=0¤tPage=1&limit=15`; - const response = await got({ - method: 'get', - url: currentUrl, - }); - - const list = response.data.data.map((item) => ({ - title: item.title, - pubDate: Date.parse(item.publishTime), - link: `${rootUrl}/research/report-new/${item.id}/`, - })); - - const items = await Promise.all( - list.map((item) => - ctx.cache.tryGet(item.link, async () => { - const detailResponse = await got({ - method: 'get', - url: item.link, - }); - const content = cheerio.load(detailResponse.data); - - content('img[_ngcontent-c11]').each(function () { - content(this).attr('alt', ''); - }); - - item.description = content('.text').html(); - - return item; - }) - ) - ); - - ctx.state.data = { - title: `${categories[category]}, ${labels[label]} - 行业研究报告 - QuestMobile`, - link: currentUrl, - item: items, - }; -}; diff --git a/lib/v2/questmobile/maintainer.js b/lib/v2/questmobile/maintainer.js new file mode 100644 index 00000000000000..204040069b5d16 --- /dev/null +++ b/lib/v2/questmobile/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/report/:industry?/:label?': ['nczitzk'], +}; diff --git a/lib/v2/questmobile/radar.js b/lib/v2/questmobile/radar.js new file mode 100644 index 00000000000000..6ab6b9df2a130b --- /dev/null +++ b/lib/v2/questmobile/radar.js @@ -0,0 +1,18 @@ +module.exports = { + 'questmobile.com.cn': { + _name: 'QuestMobile', + '.': [ + { + title: '行业研究报告', + docs: 'https://docs.rsshub.app/routes/new-media#questmobile-hang-ye-yan-jiu-bao-gao', + source: ['/research/reports/:industry/:label'], + target: (params) => { + const industry = params.industry; + const label = params.label; + + return `/questmobile/report/${industry}/${label}`; + }, + }, + ], + }, +}; diff --git a/lib/v2/questmobile/report.js b/lib/v2/questmobile/report.js new file mode 100644 index 00000000000000..bd7ba5e362cbc9 --- /dev/null +++ b/lib/v2/questmobile/report.js @@ -0,0 +1,127 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); + +/** + * Parses a tree array and returns an array of objects containing the key-value pairs. + * @param {Array} tree - The tree to parse. + * @param {Array} result - The result array to store the parsed key-value pairs. Default is an empty array. + * + * @returns {Array} - An array of objects containing the key-value pairs. + */ +const parseTree = (tree, result = []) => { + tree.forEach((obj) => { + const { key, value, children } = obj; + result.push({ key, value }); + + if (children && children.length > 0) { + parseTree(children, result); + } + }); + + return result; +}; + +module.exports = async (ctx) => { + const { industry, label } = ctx.params; + const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 50; + + const rootUrl = 'https://www.questmobile.com.cn'; + const apiUrl = new URL('api/v2/report/article-list', rootUrl).href; + const apiTreeUrl = new URL('api/v2/report/industry-label-tree', rootUrl).href; + + const { + data: { + data: { industryTree, labelTree }, + }, + } = await got(apiTreeUrl); + + const industries = parseTree(industryTree); + const labels = parseTree(labelTree); + + const industryObj = industry ? industries.find((i) => i.key === industry || i.value === industry) : undefined; + const labelObj = label ? labels.find((i) => i.key === label || i.value === label) : industryObj ? undefined : labels.find((i) => i.key === industry || i.value === industry); + + const industryId = industryObj?.key ?? -1; + const labelId = labelObj?.key ?? -1; + + const currentUrl = new URL(`research/reports/${industryObj?.key ?? -1}/${labelObj?.key ?? -1}`, rootUrl).href; + + const { data: response } = await got(apiUrl, { + searchParams: { + version: 0, + pageSize: limit, + pageNo: 1, + industryId, + labelId, + }, + }); + + let items = response.data.slice(0, limit).map((item) => ({ + title: item.title, + link: new URL(`research/report/${item.id}`, rootUrl).href, + description: art(path.join(__dirname, 'templates/description.art'), { + image: { + src: item.coverImgUrl, + alt: item.title, + }, + introduction: item.introduction, + description: item.content, + }), + category: [...(item.industryList ?? []), ...(item.labelList ?? [])], + guid: `questmobile-report#${item.id}`, + pubDate: parseDate(item.publishTime), + })); + + 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('div.text div.daoyu').remove(); + + item.title = content('div.title h1').text(); + item.description += art(path.join(__dirname, 'templates/description.art'), { + description: content('div.text').html(), + }); + item.author = content('div.source') + .text() + .replace(/^.*?:/, ''); + item.category = content('div.hy, div.keyword') + .find('span') + .toArray() + .map((c) => content(c).text()); + item.pubDate = parseDate(content('div.data span').prop('datetime')); + + return item; + }) + ) + ); + + const { data: currentResponse } = await got(currentUrl); + + const $ = cheerio.load(currentResponse); + + const author = $('meta[property="og:title"]').prop('content').split(/-/)[0]; + const categories = [industryObj?.value, labelObj?.value].filter((c) => c); + const image = $(`img[alt="${author}"]`).prop('src'); + const icon = $('link[rel="shortcut icon"]').prop('href'); + + ctx.state.data = { + item: items, + title: `${author}${categories.length === 0 ? '' : ` - ${categories.join(' - ')}`}`, + link: currentUrl, + description: $('meta[property="og:description"]').prop('content'), + language: 'zh', + image, + icon, + logo: icon, + subtitle: $('meta[name="keywords"]').prop('content'), + author, + allowEmpty: true, + }; +}; diff --git a/lib/v2/questmobile/router.js b/lib/v2/questmobile/router.js new file mode 100644 index 00000000000000..337e9db84dc900 --- /dev/null +++ b/lib/v2/questmobile/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/report/:industry?/:label?', require('./report')); +}; diff --git a/lib/v2/questmobile/templates/description.art b/lib/v2/questmobile/templates/description.art new file mode 100644 index 00000000000000..dd4ad1216ff8a5 --- /dev/null +++ b/lib/v2/questmobile/templates/description.art @@ -0,0 +1,17 @@ +{{ if image?.src }} + +{{ /if }} + +{{ if introduction }} +
{{ introduction }}+{{ /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 a9386ec61122d2..a63a8c61ad237b 100644 --- a/website/docs/routes/new-media.mdx +++ b/website/docs/routes/new-media.mdx @@ -1610,79 +1610,121 @@ Compared to the official one, this feed: ### 行业研究报告 {#questmobile-hang-ye-yan-jiu-bao-gao} -