diff --git a/docs/traditional-media.md b/docs/traditional-media.md index ec94098f4..e90c269ca 100644 --- a/docs/traditional-media.md +++ b/docs/traditional-media.md @@ -1629,6 +1629,30 @@ category 对应的关键词有 +## 南方网 + +### 南方 +(按栏目 ID) + + + +::: tip 提示 +若此处输入的是栏目 ID(而非南方号 ID),则该接口会返回与输入栏目相关联栏目的文章。例如,输入栏目 ID `38`(广州),则返回的结果还会包含 ID 为 `3547`(市长报道集)的文章。 +::: + +1. `pc.nfapp.southcn.com` 下的文章页面,可通过 url 查看,例: 的栏目 ID 为 `13707`。 +2. `static.nfapp.southcn.com` 下的文章页面,可查看网页源代码,搜索 `columnid`。 +3. 列出了部分栏目,`id` 即为栏目 ID。 + + + +### 南方 +(按作者) + + + +作者的 UUID 只可通过 `static.nfapp.southcn.com` 下的文章页面获取。点击文章下方的作者介绍,进入该作者的个人主页,即可从 url 中获取。 + + + ## 南方周末 ### 新闻分类 diff --git a/lib/v2/southcn/maintainer.js b/lib/v2/southcn/maintainer.js new file mode 100644 index 000000000..dd0067dac --- /dev/null +++ b/lib/v2/southcn/maintainer.js @@ -0,0 +1,4 @@ +module.exports = { + '/nfapp/column/:column?': ['TimWu007'], + '/nfapp/reporter/:reporter': ['TimWu007'], +}; diff --git a/lib/v2/southcn/nfapp/column.js b/lib/v2/southcn/nfapp/column.js new file mode 100644 index 000000000..2931c0633 --- /dev/null +++ b/lib/v2/southcn/nfapp/column.js @@ -0,0 +1,35 @@ +const got = require('@/utils/got'); +const timezone = require('@/utils/timezone'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); +const { parseArticle } = require('./utils'); + +module.exports = async (ctx) => { + const columnId = ctx.params.column ?? 38; + const currentUrl = `https://api.nfapp.southcn.com/nfplus-manuscript-web/article/list?columnId=${columnId}&nfhSubCount=1&pageNum=1&pageSize=20`; // this api only returns up to 10 articles + + const { data: response } = await got(currentUrl); + + const list = response.data.list + .filter((i) => i.articleType === 0) + .map((item) => ({ + title: '【' + item.columnName + '】' + item.title, + description: art(path.join(__dirname, '../templates/description.art'), { + thumb: item.picMiddle, + description: item.summary === '详见内文' ? '' : item.summary, + }), + pubDate: timezone(parseDate(item.updateTime), +8), + link: `http://pc.nfapp.southcn.com/${item.columnId}/${item.articleId}.html`, + articleId: item.articleId, + shareUrl: item.shareUrl, + })); + + const items = await Promise.all(list.map((item) => parseArticle(item, ctx.cache.tryGet))); + + ctx.state.data = { + title: `南方+`, + link: `https://m.nfapp.southcn.com/${columnId}`, + item: items, + }; +}; diff --git a/lib/v2/southcn/nfapp/reporter.js b/lib/v2/southcn/nfapp/reporter.js new file mode 100644 index 000000000..34e5a7741 --- /dev/null +++ b/lib/v2/southcn/nfapp/reporter.js @@ -0,0 +1,33 @@ +const got = require('@/utils/got'); +const timezone = require('@/utils/timezone'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const { parseArticle } = require('./utils'); +const path = require('path'); + +module.exports = async (ctx) => { + const reporterId = ctx.params.reporter; + const currentUrl = `https://api.nfapp.southcn.com/nanfang_if/reporter/list?reporterUuid=${reporterId}&pageSize=20&pageNo=1&origin=0`; + + const { data: response } = await got(currentUrl); + + const list = response.data.reportInfo.articleInfo.map((item) => ({ + title: '【' + item.releaseColName + '】' + item.title, + description: art(path.join(__dirname, '../templates/description.art'), { + thumb: item.picMiddle, + description: item.attAbstract, + }), + pubDate: timezone(parseDate(item.publishtime), +8), + link: `http://pc.nfapp.southcn.com/${item.colID}/${item.fileId}.html`, + articleId: item.fileId, + shareUrl: item.shareUrl, + })); + + const items = await Promise.all(list.map((item) => parseArticle(item, ctx.cache.tryGet))); + + ctx.state.data = { + title: `南方+ - ${response.data.reportInfo.reporterName}`, + link: `https://static.nfapp.southcn.com/apptpl/reporterWorksList/index.html?reporterUuid=${reporterId}`, + item: items, + }; +}; diff --git a/lib/v2/southcn/nfapp/utils.js b/lib/v2/southcn/nfapp/utils.js new file mode 100644 index 000000000..d92543215 --- /dev/null +++ b/lib/v2/southcn/nfapp/utils.js @@ -0,0 +1,23 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); + +const parseArticle = (item, tryGet) => + tryGet(item.link, async () => { + const detailResponse = await got({ + method: 'get', + url: item.link, + }); + + const $ = cheerio.load(detailResponse.data); + + $('.article-nfh-icon, .article-crumb, .article-share, .article-copyright').remove(); + + item.description += $('#content').html() ?? ''; + item.author = $('meta[name="author"]').attr('content'); + + return item; + }); + +module.exports = { + parseArticle, +}; diff --git a/lib/v2/southcn/radar.js b/lib/v2/southcn/radar.js new file mode 100644 index 000000000..5155d7402 --- /dev/null +++ b/lib/v2/southcn/radar.js @@ -0,0 +1,12 @@ +module.exports = { + 'nfapp.southcn.com': { + _name: '南方网', + '.': [ + { + title: '南方+', + docs: 'https://docs.rsshub.app/traditional-media.html#nan-fang-wang', + source: ['/'], + }, + ], + }, +}; diff --git a/lib/v2/southcn/router.js b/lib/v2/southcn/router.js new file mode 100644 index 000000000..3fd129644 --- /dev/null +++ b/lib/v2/southcn/router.js @@ -0,0 +1,4 @@ +module.exports = (router) => { + router.get('/nfapp/column/:column?', require('./nfapp/column')); + router.get('/nfapp/reporter/:reporter', require('./nfapp/reporter')); +}; diff --git a/lib/v2/southcn/templates/description.art b/lib/v2/southcn/templates/description.art new file mode 100644 index 000000000..e94386a36 --- /dev/null +++ b/lib/v2/southcn/templates/description.art @@ -0,0 +1,6 @@ +{{ if thumb }} + +{{ /if }} +{{ if description }} +

{{ description }}

+{{ /if }} \ No newline at end of file