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 }} +
+ {{ image.alt }} +
+{{ /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} - + -行业 - -| 全部行业 | 移动视频 | 移动社交 | 移动购物 | -| -------- | -------- | -------- | -------- | -| 0 | 10 | 1 | 2 | - -| 系统工具 | 新闻资讯 | 移动音乐 | 生活服务 | -| -------- | -------- | -------- | -------- | -| 17 | 21 | 11 | 5 | - -| 数字阅读 | 汽车服务 | 拍摄美化 | 旅游服务 | -| -------- | -------- | -------- | -------- | -| 16 | 4 | 12 | 8 | - -| 健康美容 | 医疗服务 | 教育学习 | 金融理财 | -| -------- | -------- | -------- | -------- | -| 22 | 23 | 14 | 3 | - -| 办公商务 | 智能设备 | 手机游戏 | 出行服务 | 内容平台 | -| -------- | -------- | -------- | -------- | -------- | -| 9 | 19 | 20 | 26 | 29 | - -标签 - -| 全部标签 | 5G | 双十一 | 直播带货 | 电商平台 | -| -------- | -- | ------ | -------- | -------- | -| 0 | 75 | 74 | 73 | 72 | - -| 新蓝领 | 市场竞争 | KOL | 品牌营销 | 互联网研究 | -| ------ | -------- | --- | -------- | ---------- | -| 71 | 70 | 69 | 68 | 67 | - -| 广告效果 | 媒介策略 | App 和小程序 | App 增长 | -| -------- | -------- | ------------ | -------- | -| 66 | 65 | 64 | 63 | - -| 小程序数据 | 移动大数据 | 互联网报告 | 数据报告 | -| ---------- | ---------- | ---------- | -------- | -| 62 | 61 | 60 | 59 | +:::tip -| 互联网数据 | 智能终端 | 小程序 | 私域流量 | -| ---------- | -------- | ------ | -------- | -| 58 | 57 | 56 | 55 | +若订阅行业 [互联网行业](https://www.questmobile.com.cn/research/reports/1/-1),网址为 `https://www.questmobile.com.cn/research/reports/1/-1` +参数 industry 为 `互联网行业` 或 `1`,此时路由为 [`/questmobile/report/互联网行业`](https://rsshub.app/questmobile/report/互联网行业) 或 [`/questmobile/report/1/-1`](https://rsshub.app/questmobile/report/1/-1)。 -| 运动消费 | 用户争夺 | 运动健身 | 新消费 | -| -------- | -------- | -------- | ------ | -| 54 | 53 | 52 | 48 | +若订阅标签 [榜单](https://www.questmobile.com.cn/research/reports/-1/11),网址为 `https://www.questmobile.com.cn/research/reports/-1/11` +参数 label 为 `榜单` 或 `11`,此时路由为 [`/questmobile/report/榜单`](https://rsshub.app/questmobile/report/榜单) 或 [`/questmobile/report/-1/11`](https://rsshub.app/questmobile/report/-1/11)。 -| 增长模式 | 下沉 | 新中产 | 银发族 | -| -------- | ---- | ------ | ------ | -| 42 | 41 | 36 | 31 | +若订阅行业和标签 [品牌领域 - 互联网经济](https://www.questmobile.com.cn/research/reports/2/1),网址为 `https://www.questmobile.com.cn/research/reports/2/1` +参数 industry 为 `品牌领域` 或 `2`,参数 label 为 `互联网经济` 或 `1`,此时路由为 [`/questmobile/report/品牌领域/互联网经济`](https://rsshub.app/questmobile/report/品牌领域/互联网经济) 或 [`/questmobile/report/2/1`](https://rsshub.app/questmobile/report/2/1),甚至 [`/questmobile/report/品牌领域/1`](https://rsshub.app/questmobile/report/品牌领域/1)。 -| 粉丝经济 | 泛娱乐 | 网购少女 | 二次元 | -| -------- | ------ | -------- | ------ | -| 30 | 29 | 28 | 27 | - -| 兴趣圈层 | 大学生 | 广告营销 | Z 世代 | -| -------- | ------ | -------- | ------ | -| 26 | 25 | 23 | 22 | - -| 付费用户 | 精细化运营 | 00 后 | 90 后 | -| -------- | ---------- | ----- | ----- | -| 18 | 17 | 14 | 11 | +::: -| 春节报告 | 低幼经济 | 季度报告 | 年度报告 | -| -------- | -------- | -------- | -------- | -| 10 | 9 | 7 | 6 | +
+ 全部行业和标签 + + #### 行业 + + | 互联网行业 | 移动社交 | 移动视频 | 移动购物 | 系统工具 | + | ----- | ---- | ---- | ---- | ---- | + | 1 | 1001 | 1002 | 1003 | 1004 | + + | 出行服务 | 金融理财 | 生活服务 | 移动音乐 | 新闻资讯 | + | ---- | ---- | ---- | ---- | ---- | + | 1005 | 1006 | 1007 | 1008 | 1009 | + + | 办公商务 | 手机游戏 | 实用工具 | 数字阅读 | 教育学习 | + | ---- | ---- | ---- | ---- | ---- | + | 1010 | 1011 | 1012 | 1013 | 1014 | + + | 汽车服务 | 拍摄美化 | 智能设备 | 旅游服务 | 健康美容 | + | ---- | ---- | ---- | ---- | ---- | + | 1015 | 1016 | 1017 | 1018 | 1020 | + + | 育儿母婴 | 主题美化 | 医疗服务 | 品牌领域 | 美妆品牌 | + | ---- | ---- | ---- | ---- | ---- | + | 1022 | 1023 | 1024 | 2 | 2001 | + + | 母婴品牌 | 家电品牌 | 食品饮料品牌 | 汽车品牌 | 服饰箱包品牌 | + | ---- | ---- | ------ | ---- | ------ | + | 2002 | 2003 | 2004 | 2005 | 2006 | + + #### 标签 + + | 互联网经济 | 圈层经济 | 粉丝经济 | 银发经济 | 儿童经济 | + | ----- | ---- | ---- | ---- | ---- | + | 1 | 1001 | 1002 | 1004 | 1005 | + + | 萌宠经济 | 她经济 | 他经济 | 泛娱乐经济 | 下沉市场经济 | + | ---- | ---- | ---- | ----- | ------ | + | 1007 | 1009 | 1010 | 1011 | 1012 | + + | 内容经济 | 订阅经济 | 会员经济 | 居家经济 | 到家经济 | + | ---- | ---- | ---- | ---- | ---- | + | 1013 | 1014 | 1015 | 1016 | 1017 | + + | 颜值经济 | 闲置经济 | 旅游经济 | 人群洞察 | 00后 | + | ---- | ---- | ------------------- | ---- | ---- | + | 1018 | 1020 | 1622842051677753346 | 2 | 2002 | + + | Z世代 | 银发族 | 宝妈宝爸 | 萌宠人群 | 运动达人 | + | ---- | ---- | ---- | ---- | ---- | + | 2003 | 2004 | 2005 | 2007 | 2008 | + + | 女性消费 | 男性消费 | 游戏人群 | 二次元 | 新中产 | + | ---- | ---- | ---- | ---- | ---- | + | 2009 | 2010 | 2012 | 2013 | 2014 | + + | 下沉市场用户 | 大学生 | 数字化营销 | 广告效果 | 品牌营销 | + | ------ | ---- | ----- | ---- | ---- | + | 2018 | 2022 | 3 | 3001 | 3002 | + + | 全域营销 | 私域流量 | 新媒体营销 | KOL生态 | 内容营销 | + | ---- | ---- | ----- | ----- | ---- | + | 3003 | 3004 | 3005 | 3006 | 3008 | + + | 直播电商 | 短视频带货 | 娱乐营销 | 营销热点 | 双11电商大促 | + | ---- | ----- | ------------------- | ---- | ------- | + | 3009 | 3010 | 1630464311158738945 | 4 | 4001 | + + | 618电商大促 | 春节营销 | 五一假期营销 | 热点事件盘点 | 消费热点 | + | ------- | ---- | ------ | ------ | ---- | + | 4002 | 4003 | 4004 | 4007 | 5 | + + | 时尚品牌 | 连锁餐饮 | 新式茶饮 | 智能家电 | 国潮品牌 | + | ---- | ---- | ---- | ---- | ---- | + | 5001 | 5002 | 5003 | 5004 | 5007 | + + | 白酒品牌 | 精益运营 | 媒介策略 | 用户争夺 | 精细化运营 | + | ------------------- | ---- | ---- | ---- | ----- | + | 1622841828310093825 | 6 | 6001 | 6002 | 6003 | + + | 用户分层 | 增长黑马 | 社交裂变 | 新兴领域 | 新能源汽车 | + | ---- | ---- | ---- | ---- | ----- | + | 6004 | 6005 | 6007 | 7 | 7001 | + + | 智能汽车 | 新消费 | AIoT | 产业互联网 | AIGC | + | ---- | ---- | ---- | ----- | ------------------- | + | 7002 | 7003 | 7004 | 7005 | 1645677998450511873 | + + | OTT应用 | 智能电视 | 全景数据 | 全景生态 | 微信小程序 | + | ------------------- | ------------------- | ---- | ---- | ----- | + | 1676063510499528705 | 1676063630293045249 | 8 | 8001 | 8002 | + + | 支付宝小程序 | 百度智能小程序 | 企业流量 | 抖音小程序 | 手机终端 | + | ------ | ------- | ------------------- | ------------------- | ---- | + | 8003 | 8004 | 1671052842096496642 | 1676063017220018177 | 9 | + + | 智能终端 | 国产终端 | 5G手机 | 盘点 | 季度报告 | + | ---- | ---- | ---- | --- | ----- | + | 9001 | 9002 | 9003 | 10 | 10001 | -| 全景生态 | 消费者洞察 | -| -------- | ---------- | -| 5 | 2 | +