diff --git a/docs/new-media.md b/docs/new-media.md index 63101c3b6..ff102be64 100644 --- a/docs/new-media.md +++ b/docs/new-media.md @@ -3423,15 +3423,15 @@ column 为 third 时可选的 category: ### 分类 - - -### 标签 - - + ### 搜索 - + + +### 用户文章 + + ## 上下游 News\&Market diff --git a/lib/v2/ruancan/category.js b/lib/v2/ruancan/category.js new file mode 100644 index 000000000..a2a7edbee --- /dev/null +++ b/lib/v2/ruancan/category.js @@ -0,0 +1,8 @@ +const fetchFeed = require('./utils'); + +module.exports = async (ctx) => { + const category = ctx.params.category; + const currentUrl = `/cat/${category}`; + + ctx.state.data = await fetchFeed(ctx, currentUrl); +}; diff --git a/lib/v2/ruancan/index.js b/lib/v2/ruancan/index.js index b10a014ff..e6897946b 100644 --- a/lib/v2/ruancan/index.js +++ b/lib/v2/ruancan/index.js @@ -1,59 +1,7 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const { parseDate } = require('@/utils/parse-date'); +const fetchFeed = require('./utils'); module.exports = async (ctx) => { - const todo = ctx.params.do ?? ''; - const keyword = ctx.params.keyword ?? ''; + const currentUrl = ''; - const rootUrl = 'https://www.ruancan.com'; - const currentUrl = `${rootUrl}${todo ? (todo === 'search' ? `/page/1?s=${keyword}` : `/${todo}/${keyword}`) : ''}`; - - const response = await got({ - method: 'get', - url: currentUrl, - }); - - const $ = cheerio.load(response.data); - - let items = $('.entry-title a') - .toArray() - .map((item) => { - item = $(item); - - return { - title: item.text(), - link: item.attr('href'), - }; - }); - - items = await Promise.all( - items.map((item) => - ctx.cache.tryGet(item.link, async () => { - const detailResponse = await got({ - method: 'get', - url: item.link, - }); - - const content = cheerio.load(detailResponse.data); - - content('.entry-content .heateorSssClear').first().nextAll().remove(); - content('.adsbygoogle, .heateorSssClear').remove(); - - item.description = content('.entry-content').html(); - item.category = content('.cat-links a') - .toArray() - .map((a) => content(a).text()); - item.pubDate = parseDate(content('meta[property="article:published_time"]').attr('content')); - - return item; - }) - ) - ); - - ctx.state.data = { - title: $('title').text(), - link: currentUrl, - item: items, - }; + ctx.state.data = await fetchFeed(ctx, currentUrl); }; diff --git a/lib/v2/ruancan/maintainer.js b/lib/v2/ruancan/maintainer.js index a4c1f4cb6..741611ac7 100644 --- a/lib/v2/ruancan/maintainer.js +++ b/lib/v2/ruancan/maintainer.js @@ -1,6 +1,6 @@ module.exports = { - '/ruancan': ['nczitzk'], - '/ruancan/sort/:sort?': ['nczitzk'], - '/ruancan/tag/:tag': ['nczitzk'], + '/ruancan/category/:category?': ['nczitzk'], '/ruancan/search/:keyword?': ['nczitzk'], + '/ruancan/user/:id': ['nczitzk'], + '/ruancan': ['nczitzk'], }; diff --git a/lib/v2/ruancan/radar.js b/lib/v2/ruancan/radar.js index 32118f14f..22d1fd1b5 100644 --- a/lib/v2/ruancan/radar.js +++ b/lib/v2/ruancan/radar.js @@ -11,14 +11,20 @@ module.exports = { { title: '分类', docs: 'https://docs.rsshub.app/new-media.html#ruan-can-fen-lei', - source: ['/sort/:sort', '/'], - target: '/ruancan/sort/:sort', + source: ['/cat/:category', '/'], + target: '/ruancan/category/:category', }, { - title: '标签', - docs: 'https://docs.rsshub.app/new-media.html#ruan-can-biao-qian', - source: ['/tag/:tag', '/'], - target: '/ruancan/tag/:tag', + title: '搜索', + docs: 'https://docs.rsshub.app/new-media.html#ruan-can-sou-suo', + source: ['/'], + target: (params, url) => `/ruancan/search/${new URL(url).searchParams.get('s')}`, + }, + { + title: '用户文章', + docs: 'https://docs.rsshub.app/new-media.html#ruan-can-yong-hu-wen-zhang', + source: ['/i/:id', '/'], + target: '/ruancan/user/:id', }, ], }, diff --git a/lib/v2/ruancan/router.js b/lib/v2/ruancan/router.js index 85893161d..d8e764530 100644 --- a/lib/v2/ruancan/router.js +++ b/lib/v2/ruancan/router.js @@ -1,3 +1,6 @@ module.exports = function (router) { - router.get('/:do?/:keyword?', require('./index')); + router.get('/category/:category?', require('./category')); + router.get('/search/:keyword?', require('./search')); + router.get('/user/:id', require('./user')); + router.get('/', require('./index')); }; diff --git a/lib/v2/ruancan/search.js b/lib/v2/ruancan/search.js new file mode 100644 index 000000000..ec3d7ff58 --- /dev/null +++ b/lib/v2/ruancan/search.js @@ -0,0 +1,8 @@ +const fetchFeed = require('./utils'); + +module.exports = async (ctx) => { + const keyword = ctx.params.keyword; + const currentUrl = `/?s=${keyword}`; + + ctx.state.data = await fetchFeed(ctx, currentUrl); +}; diff --git a/lib/v2/ruancan/user.js b/lib/v2/ruancan/user.js new file mode 100644 index 000000000..3117e0ee0 --- /dev/null +++ b/lib/v2/ruancan/user.js @@ -0,0 +1,8 @@ +const fetchFeed = require('./utils'); + +module.exports = async (ctx) => { + const id = ctx.params.id; + const currentUrl = `/i/${id}`; + + ctx.state.data = await fetchFeed(ctx, currentUrl); +}; diff --git a/lib/v2/ruancan/utils.js b/lib/v2/ruancan/utils.js new file mode 100644 index 000000000..465776162 --- /dev/null +++ b/lib/v2/ruancan/utils.js @@ -0,0 +1,66 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx, currentUrl) => { + const rootUrl = 'https://www.ruancan.com'; + currentUrl = `${rootUrl}${currentUrl}`; + + const response = await got({ + method: 'get', + url: currentUrl, + }); + + const $ = cheerio.load(response.data); + + let items = $('.item-title a') + .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 15) + .toArray() + .map((item) => { + item = $(item); + + return { + title: item.text(), + link: item.attr('href'), + }; + }); + + items = await Promise.all( + items.map((item) => + ctx.cache.tryGet(item.link, async () => { + const detailResponse = await got({ + method: 'get', + url: item.link, + }); + + const content = cheerio.load(detailResponse.data); + + content('.entry-copyright').remove(); + + content('.entry-content div').each(function () { + if (/^ruanc-\d+/.test(content(this).attr('id'))) { + content(this).remove(); + } + }); + + content('figure').each(function () { + content(this).html(``); + }); + + item.description = content('.entry-content').html(); + item.category = content('.entry-info a[rel="category tag"]') + .toArray() + .map((c) => content(c).text()); + item.pubDate = parseDate(content('.entry-info .entry-date').attr('datetime')); + + return item; + }) + ) + ); + + return { + title: $('title').text(), + link: currentUrl, + item: items, + }; +};