diff --git a/assets/radar-rules.js b/assets/radar-rules.js index 48c8667bc..cd468907b 100644 --- a/assets/radar-rules.js +++ b/assets/radar-rules.js @@ -166,11 +166,6 @@ 'xiaomi.cn': { _name: '小米社区', www: [{ title: '圈子', docs: 'https://docs.rsshub.app/bbs.html#xiao-mi-she-qu', source: '/board/:boardId', target: '/mi/bbs/board/:boardId' }] }, '163.com': { _name: '网易', - ds: [{ title: '大神', docs: 'https://docs.rsshub.app/game.html#wang-yi-da-shen', source: '/user/:id', target: '/netease/ds/:id' }], - open: [ - { title: '公开课 - 精品课程', docs: 'https://docs.rsshub.app/study.html#wang-yi-gong-kai-ke', source: '/', target: '/open163/vip' }, - { title: '公开课 - 最新课程', docs: 'https://docs.rsshub.app/study.html#wang-yi-gong-kai-ke', source: '/', target: '/open163/latest' }, - ], music: [ { title: '云音乐 - 用户歌单', diff --git a/docs/game.md b/docs/game.md index bebe6070a..e3d7f32de 100644 --- a/docs/game.md +++ b/docs/game.md @@ -759,7 +759,7 @@ Example:`https://www.iyingdi.com/tz/people/55547` ,id 是 `55547` ### 用户发帖 - + ## 小黑盒 diff --git a/docs/new-media.md b/docs/new-media.md index 4d3792e0b..8dbb6ebce 100644 --- a/docs/new-media.md +++ b/docs/new-media.md @@ -3792,7 +3792,7 @@ column 为 third 时可选的 category: ### 栏目 - + | 分类 | 编号 | | ---- | ---- | @@ -3819,7 +3819,7 @@ column 为 third 时可选的 category: ### 更新 - + 1. 在[网易号搜索页面](https://dy.163.com/v2/media/tosearch.html) 搜索想要订阅的网易号。 2. 打开网易号的任意文章。 @@ -3827,9 +3827,9 @@ column 为 third 时可选的 category: -## 网易号(通用) +### 网易号(通用) - + 优先使用方法一,若是网易号搜索页面搜不到的小众网易号(文章页面不含`data-wemediaid`)则可使用此法。 触发反爬会只抓取到标题,建议自建。 @@ -3844,7 +3844,7 @@ column 为 third 时可选的 category: ### 今日关注 - + ::: tip 提示 @@ -3856,7 +3856,7 @@ column 为 third 时可选的 category: ### 排行榜 - + ::: tip 提示 @@ -3878,7 +3878,7 @@ column 为 third 时可选的 category: ### 专栏 - + | 轻松一刻 | 槽值 | 人间 | 大国小民 | 三三有梗 | 数读 | 看客 | 下划线 | 谈心社 | 哒哒 | 胖编怪聊 | 曲一刀 | 今日之声 | 浪潮 | 沸点 | | ---- | -- | -- | ---- | ---- | -- | -- | --- | --- | -- | ---- | --- | ---- | -- | -- | @@ -3888,7 +3888,7 @@ column 为 third 时可选的 category: ### 人间 - + | 特写 | 记事 | 大写 | 好读 | 看客 | | ----- | ----- | ----- | ----- | ----- | diff --git a/docs/study.md b/docs/study.md index bb6ed7389..707b3a01f 100644 --- a/docs/study.md +++ b/docs/study.md @@ -472,10 +472,6 @@ path="/ctfhub/upcoming/:limit?" -### 最新课程 - - - ## 下厨房 ### 用户作品 diff --git a/lib/radar-rules.js b/lib/radar-rules.js index bc8324a08..e59134e44 100644 --- a/lib/radar-rules.js +++ b/lib/radar-rules.js @@ -397,28 +397,6 @@ module.exports = { }, '163.com': { _name: '网易', - ds: [ - { - title: '大神', - docs: 'https://docs.rsshub.app/game.html#wang-yi-da-shen', - source: '/user/:id', - target: '/netease/ds/:id', - }, - ], - open: [ - { - title: '公开课 - 精品课程', - docs: 'https://docs.rsshub.app/study.html#wang-yi-gong-kai-ke', - source: '/', - target: '/open163/vip', - }, - { - title: '公开课 - 最新课程', - docs: 'https://docs.rsshub.app/study.html#wang-yi-gong-kai-ke', - source: '/', - target: '/open163/latest', - }, - ], music: [ { title: '云音乐 - 用户歌单', diff --git a/lib/router.js b/lib/router.js index 0911642e1..0641a8b14 100644 --- a/lib/router.js +++ b/lib/router.js @@ -2894,18 +2894,18 @@ router.get('/futunn/highlights', lazyloadRouteHandler('./routes/futunn/highlight // 网易新闻 // router.get('/netease/news/rank/:category?/:type?/:time?', lazyloadRouteHandler('./routes/netease/news/rank')); -router.get('/netease/news/special/:type?', lazyloadRouteHandler('./routes/netease/news/special')); +// router.get('/netease/news/special/:type?', lazyloadRouteHandler('./routes/netease/news/special')); // 网易 - 网易号 -router.get('/netease/dy/:id', lazyloadRouteHandler('./routes/netease/dy')); -router.get('/netease/dy2/:id', lazyloadRouteHandler('./routes/netease/dy2')); +// router.get('/netease/dy/:id', lazyloadRouteHandler('./routes/netease/dy')); +// router.get('/netease/dy2/:id', lazyloadRouteHandler('./routes/netease/dy2')); // 网易大神 -router.get('/netease/ds/:id', lazyloadRouteHandler('./routes/netease/ds')); +// router.get('/netease/ds/:id', lazyloadRouteHandler('./routes/netease/ds')); // 网易公开课 -router.get('/open163/vip', lazyloadRouteHandler('./routes/netease/open/vip')); -router.get('/open163/latest', lazyloadRouteHandler('./routes/netease/open/latest')); +// router.get('/open163/vip', lazyloadRouteHandler('./routes/netease/open/vip')); +// router.get('/open163/latest', lazyloadRouteHandler('./routes/netease/open/latest')); // Boston.com router.get('/boston/:tag?', lazyloadRouteHandler('./routes/boston/index')); diff --git a/lib/routes/netease/ds.js b/lib/routes/netease/ds.js deleted file mode 100644 index c5ea468e1..000000000 --- a/lib/routes/netease/ds.js +++ /dev/null @@ -1,41 +0,0 @@ -const url = require('url'); -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const date = require('@/utils/date'); - -const root_url = 'https://ds.163.com/'; - -module.exports = async (ctx) => { - const id = ctx.params.id; - if (!id) { - throw Error('Bad parameter. See https://docs.rsshub.app/game.html#wang-yi-da-shen'); - } - - const current_url = url.resolve(root_url, '/user/' + id); - const response = await got({ - method: 'get', - url: current_url, - }); - - const $ = cheerio.load(response.data); - const list = $('main.user-page__main div.feed-card') - .map((_, item) => { - item = $(item); - const a = item.find('a.feed-card__link-hide'); - const desc = item.find('div.feed-card__body'); - const title = desc.find('div.feed-text').text(); - return { - title, - link: a.attr('href'), - description: desc.html(), - pubDate: date(item.find('time.userbar__time').text().trim()), - }; - }) - .get(); - - ctx.state.data = { - title: $('title').text(), - link: current_url, - item: list, - }; -}; diff --git a/lib/routes/netease/dy.js b/lib/routes/netease/dy.js deleted file mode 100644 index da60a8472..000000000 --- a/lib/routes/netease/dy.js +++ /dev/null @@ -1,54 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const date = require('@/utils/date'); -const iconv = require('iconv-lite'); - -module.exports = async (ctx) => { - const { id } = ctx.params; - - const response = await got.get(`http://dy.163.com/v2/article/list.do?pageNo=1&wemediaId=${id}&size=10`); - const charset = response.headers['content-type'].split('=')[1]; - const list = response.data.data.list; - let author, link; - - const items = await Promise.all( - list.map(async (e) => { - if (e.docid) { - e.link = 'http://dy.163.com/v2/article/detail/' + e.docid + '.html'; - } - - author = e.source ? e.source : ''; - - const html = await ctx.cache.tryGet(e.link, async () => - iconv.decode( - ( - await got.get(e.link, { - responseType: 'buffer', - }) - ).data, - charset - ) - ); - const $ = cheerio.load(html, { decodeEntities: false }); - - const article = $('.post_body'); - - const single = { - title: e.title, - link: e.link, - description: article.html(), - pubDate: date(e.ptime, 8), - author: e.source, - }; - - return Promise.resolve(single); - }) - ); - - ctx.state.data = { - title: `网易号 - ${author}`, - link, - description: '', - item: items, - }; -}; diff --git a/lib/routes/netease/dy2.js b/lib/routes/netease/dy2.js deleted file mode 100644 index 059db7c9a..000000000 --- a/lib/routes/netease/dy2.js +++ /dev/null @@ -1,62 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const iconv = require('iconv-lite'); -const { parseDate } = require('@/utils/parse-date'); - -module.exports = async (ctx) => { - const { id } = ctx.params; - const limit = ctx.query.limit || 5; - const url = `https://www.163.com/dy/media/${id}.html`; - - const response = await got(url, { responseType: 'buffer' }); - - const charset = response.headers['content-type'].split('=')[1]; - const data = iconv.decode(response.data, charset); - const $ = cheerio.load(data, { decodeEntities: false }); - - const list = $('.media_articles ul li') - .slice(0, limit) - .map((_, item) => { - item = $(item); - const a = item.find('h2.media_article_title a'); - return { - title: a.text(), - link: a.attr('href'), - pubDate: parseDate(item.find('.media_article_date').text()), - }; - }) - .get(); - - const items = await Promise.all( - list.map(async (item) => { - const itemData = await ctx.cache.tryGet(item.link, () => - got(item.link, { - responseType: 'buffer', - }).then((it) => it.data) - ); - const content = cheerio.load(itemData, { decodeEntities: false }); - const postBody = content('.post_body'); - postBody.find('p, br, section').each((_, elem) => elem.attribs && Object.keys(elem.attribs).forEach((attr) => content(elem).removeAttr(attr))); - postBody - .parent() - .find('*') - .contents() - .filter((_, elem) => elem.type === 'comment') - .remove(); - return { - title: item.title, - link: item.link, - description: postBody.html(), - pubDate: item.pubDate, - }; - }) - ); - - ctx.state.data = { - title: $('.media_info h1').text(), - link: url, - description: $('.media_degist').text(), - item: items, - author: $('.media_name').text(), - }; -}; diff --git a/lib/routes/netease/open/latest.js b/lib/routes/netease/open/latest.js deleted file mode 100644 index e324efc77..000000000 --- a/lib/routes/netease/open/latest.js +++ /dev/null @@ -1,68 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -module.exports = async (ctx) => { - const list_response = await got.get('https://c.open.163.com/open/getLatestMovies.do?callback='); - - const list = JSON.parse(list_response.data.match(/\((.*?)\)/)[1]) || []; - - const parseContent = (htmlString) => { - const $ = cheerio.load(htmlString); - - const author = $('.m-courseinfo__side ul > li:nth-child(2)'); - - const images = $('img'); - for (let k = 0; k < images.length; k++) { - const testRealURL = $(images[k]) - .attr('src') - .match(/\?url=([^&]+)/); - if (testRealURL) { - $(images[k]).replaceWith(``); - } - } - - const content = $('.m-courseinfo__side'); - $('.i-container__title').remove(); - - return { - author: author.text().trim(), - description: content.html(), - pubDate: new Date(), - }; - }; - - const out = await Promise.all( - list.map(async (item) => { - const link = item.url; - - const cache = await ctx.cache.get(link); - if (cache) { - return Promise.resolve(JSON.parse(cache)); - } - - const rssitem = { - title: item.title, - link, - }; - - try { - const response = await got.get(link); - const result = parseContent(response.data); - - rssitem.author = result.author; - rssitem.description = result.description; - rssitem.pubDate = result.pubDate; - } catch (err) { - return Promise.resolve(''); - } - ctx.cache.set(link, JSON.stringify(rssitem)); - return Promise.resolve(rssitem); - }) - ); - - ctx.state.data = { - title: '网易公开课 - 最新课程', - link: 'https://open.163.com/', - item: out.filter((item) => item !== ''), - }; -}; diff --git a/lib/routes/netease/open/vip.js b/lib/routes/netease/open/vip.js deleted file mode 100644 index 7f6122d66..000000000 --- a/lib/routes/netease/open/vip.js +++ /dev/null @@ -1,39 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -module.exports = async (ctx) => { - const url = 'https://vip.open.163.com'; - - const list_response = await got.get(url); - const $ = cheerio.load(list_response.data); - - const list = $('.CourseListItem_ListItem') - .map((index, ele) => { - $('.CourseListItem_oriPrice', ele).remove(); - const price = $('.CourseListItem_price', ele).text().trim(); - return { - title: price + ' - ' + $('.CourseListItem_title', ele).text(), - link: url + $(ele).attr('href'), - }; - }) - .get(); - - const item = await Promise.all( - list.slice(0, 10).map((item) => - ctx.cache.tryGet(item.link, async () => { - const res = await got.get(item.link); - const s = cheerio.load(res.data); - s('img').css('max-width', '100%'); - const toc = s('.CourseCommonDetail_contentItem').eq(1).html(); - item.description = `
${toc}` + s('.CourseCommonDetail_contentItem').first().html() + `
`; - return item; - }) - ) - ); - - ctx.state.data = { - title: '网易公开课 - 精品课程', - link: url, - item, - }; -}; diff --git a/lib/v2/163/ds.js b/lib/v2/163/ds.js new file mode 100644 index 000000000..62597b0c3 --- /dev/null +++ b/lib/v2/163/ds.js @@ -0,0 +1,33 @@ +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); + +const root_url = 'https://inf.ds.163.com'; + +module.exports = async (ctx) => { + const id = ctx.params.id; + + const current_url = `${root_url}/v1/web/feed/basic/getSomeOneFeeds?feedTypes=1,2,3,4,6,7,10,11&someOneUid=${id}`; + const response = await got({ + method: 'get', + url: current_url, + }); + const data = response.data.result.feeds; + + const list = data.map((feed) => ({ + title: JSON.parse(feed.content).body.text, + link: `https://ds.163.com/feed/${feed.id}`, + description: art(path.resolve(__dirname, 'templates/ds.art'), { + text: JSON.parse(feed.content).body.text, + medias: JSON.parse(feed.content).body.media, + }), + pubDate: parseDate(feed.updateTime), + })); + + ctx.state.data = { + title: `${response.data.result.userInfos[0].user.nick} 的动态`, + link: `https://ds.163.com/user/${id}`, + item: list, + }; +}; diff --git a/lib/v2/163/dy.js b/lib/v2/163/dy.js new file mode 100644 index 000000000..a99467a7e --- /dev/null +++ b/lib/v2/163/dy.js @@ -0,0 +1,29 @@ +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); +const { parseDyArticle } = require('./utils'); + +module.exports = async (ctx) => { + const { id } = ctx.params; + + const response = await got(`https://dy.163.com/v2/article/list.do?pageNo=1&wemediaId=${id}&size=10`); + const charset = response.headers['content-type'].split('=')[1]; + + const list = response.data.data.list.map((e) => ({ + title: e.title, + link: 'https://www.163.com/dy/article/' + e.docid + '.html', + pubDate: timezone(parseDate(e.ptime), 8), + author: e.source, + imgsrc: e.imgsrc, + })); + + const items = await Promise.all(list.map((e) => parseDyArticle(charset, e, ctx.cache.tryGet))); + + ctx.state.data = { + title: `网易号 - ${list[0].author}`, + link: items[0].feedLink, + description: items[0].feedDescription, + image: items[0].feedImage, + item: items, + }; +}; diff --git a/lib/v2/163/dy2.js b/lib/v2/163/dy2.js new file mode 100644 index 000000000..61724026e --- /dev/null +++ b/lib/v2/163/dy2.js @@ -0,0 +1,42 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const iconv = require('iconv-lite'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); +const { parseDyArticle } = require('./utils'); + +module.exports = async (ctx) => { + const { id } = ctx.params; + const limit = ctx.query.limit ?? 30; + const url = `https://www.163.com/dy/media/${id}.html`; + + const response = await got(url, { responseType: 'buffer' }); + + const charset = response.headers['content-type'].split('=')[1]; + const data = iconv.decode(response.data, charset); + const $ = cheerio.load(data); + + const list = $('.tab_content ul li') + .slice(0, limit) + .toArray() + .map((item) => { + item = $(item); + return { + title: item.find('h4 a').text(), + link: item.find('a').first().attr('href'), + pubDate: timezone(parseDate(item.find('.time').text()), 8), + imgsrc: item.find('a img').attr('src'), + }; + }); + + const items = await Promise.all(list.map((item) => parseDyArticle(charset, item, ctx.cache.tryGet))); + + ctx.state.data = { + title: `${$('head title').text()} - 网易号`, + link: url, + description: $('.icon_line.desc').text(), + image: $('.head_img').attr('src'), + item: items, + author: $('h2').text(), + }; +}; diff --git a/lib/v2/netease/exclusive.js b/lib/v2/163/exclusive.js similarity index 100% rename from lib/v2/netease/exclusive.js rename to lib/v2/163/exclusive.js diff --git a/lib/v2/netease/maintainer.js b/lib/v2/163/maintainer.js similarity index 54% rename from lib/v2/netease/maintainer.js rename to lib/v2/163/maintainer.js index 8b73669a8..c5168ec4b 100644 --- a/lib/v2/netease/maintainer.js +++ b/lib/v2/163/maintainer.js @@ -1,6 +1,11 @@ module.exports = { '/exclusive/:id?': ['nczitzk'], + '/ds/:id': ['luyuhuang'], + '/dy/:id': ['HendricksZheng'], + '/dy2/:id': ['mjysci'], + '/news/rank/:category?/:type?/:time?': ['nczitzk'], + '/news/special/:type?': ['nczitzk'], + '/open/vip': ['hoilc'], '/renjian/:category?': ['nczitzk'], '/today/:need_content?': ['nczitzk'], - '/news/rank/:category?/:type?/:time?': ['nczitzk'], }; diff --git a/lib/v2/netease/rank.js b/lib/v2/163/news/rank.js similarity index 77% rename from lib/v2/netease/rank.js rename to lib/v2/163/news/rank.js index 2c51fcc3d..442ecda9e 100644 --- a/lib/v2/netease/rank.js +++ b/lib/v2/163/news/rank.js @@ -3,7 +3,7 @@ const cheerio = require('cheerio'); const iconv = require('iconv-lite'); const { parseDate } = require('@/utils/parse-date'); -const rootUrl = 'http://news.163.com'; +const rootUrl = 'https://news.163.com'; const config = { whole: { @@ -23,7 +23,7 @@ const config = { title: '体育', }, money: { - link: 'http://money.163.com/special/002526BH/rank.html', + link: 'https://money.163.com/special/002526BH/rank.html', title: '财经', }, tech: { @@ -99,21 +99,27 @@ module.exports = async (ctx) => { const list = $('div.tabContents') .eq(timeRange[time].index + (category === 'whole' ? (type === 'click' ? -1 : 2) : type === 'click' ? 0 : 2)) .find('table tbody tr td a') - .map((_, item) => { + .toArray() + .map((item) => { item = $(item); return { link: item.attr('href'), }; - }) - .get(); + }); const items = await Promise.all( list.map((item) => ctx.cache.tryGet(item.link, async () => { try { - const category = item.link.split('.163.com')[0].split('//').pop().split('.').pop(); - const link = `https://3g.163.com/${category}/article/${item.link.split('/').pop()}`; + let link; + if (category === 'auto' || category === 'house' || category === 'travel') { + const category = item.link.split('.163.com')[0].split('//').pop().split('.').pop(); + link = `https://3g.163.com/${category}/article/${item.link.split('/').pop()}`; + } else { + const pathname = new URL(item.link).pathname; + link = `https://3g.163.com${pathname}`; + } const detailResponse = await got({ method: 'get', @@ -121,16 +127,19 @@ module.exports = async (ctx) => { }); const content = cheerio.load(detailResponse.data); - content('.bot_word').remove(); + content('.bot_word, .js-open-app, .s-img').remove(); content('video').each(function () { content(this).attr('src', content(this).attr('data-src')); }); + content('.article-body .image-lazy').each((_, elem) => { + elem.attribs.src = elem.attribs['data-src'] ? elem.attribs['data-src'] : elem.attribs.src; + }); item.title = content('meta[property="og:title"]').attr('content').replace('_手机网易网', ''); item.pubDate = parseDate(content('meta[property="og:release_date"]').attr('content')); - item.description = content('.content').html(); + item.description = content('.article-body').html(); } catch (err) { - return Promise.resolve(''); + return ''; } return item; @@ -138,17 +147,9 @@ module.exports = async (ctx) => { ) ); - for (let i = 0; i < items.length; i++) { - if (items[i] === undefined) { - items.splice(i, 1); - } else if (items[i].title === undefined) { - items.splice(i, 1); - } - } - ctx.state.data = { title: `网易新闻${timeRange[time].title}${type === 'click' ? '点击' : '跟帖'}榜 - ${cfg.title}`, link: currentUrl, - item: items, + item: items.filter((item) => item), }; }; diff --git a/lib/routes/netease/news/special.js b/lib/v2/163/news/special.js similarity index 80% rename from lib/routes/netease/news/special.js rename to lib/v2/163/news/special.js index a5fc44e43..093423ec2 100644 --- a/lib/routes/netease/news/special.js +++ b/lib/v2/163/news/special.js @@ -1,5 +1,5 @@ const got = require('@/utils/got'); -const date = require('@/utils/date'); +const { parseDate } = require('@/utils/parse-date'); const cheerio = require('cheerio'); const typeMap = { 1: '轻松一刻', @@ -73,8 +73,8 @@ module.exports = async (ctx) => { default: break; } - const url = `https://3g.163.com/touch/reconstruct/article/list/${type}/0-10.html`; - const response = await got.get(url); + const url = `https://3g.163.com/touch/reconstruct/article/list/${type}/0-20.html`; + const response = await got(url); const data = response.data; const matches = data.replace(/\s/g, '').match(/artiList\((.*?)\]\}\)/); const articlelist0 = matches[1].replace(/".*?wangning/, '"articles') + ']}'; @@ -82,11 +82,8 @@ module.exports = async (ctx) => { const articles = articlelist.articles; const items = await Promise.all( - articles.map(async (article) => { - const title = article.title; + articles.map((article) => { let url = article.url; - const time0 = article.ptime; - const time = time0.slice(0, 10) + ' ' + time0.slice(10); if (url === null || article.skipType === 'video') { const skipurl = article.skipURL; const vid = skipurl.match(/vid=(.*?)$/); @@ -94,20 +91,16 @@ module.exports = async (ctx) => { url = `https://3g.163.com/exclusive/video/${vid[1]}.html`; } } - const content = await ctx.cache.tryGet(url, async () => { - const article_response = await got.get(url); + return ctx.cache.tryGet(url, async () => { + const article_response = await got(url); const $ = cheerio.load(article_response.data); - return $('div[class="content"]').html() ? $('div[class="content"]').html() : $('div[class="video"]').html(); - }); + article.link = url; + article.description = $('.article-body').html() ? $('.article-body').html() : $('div[class="video"]').html(); + article.pubDate = parseDate(article.ptime); - const list = { - title, - link: url, - description: content, - pubDate: date(time), - }; - return Promise.resolve(list); + return article; + }); }) ); diff --git a/lib/v2/163/open/vip.js b/lib/v2/163/open/vip.js new file mode 100644 index 000000000..6cbdfd406 --- /dev/null +++ b/lib/v2/163/open/vip.js @@ -0,0 +1,63 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); + +module.exports = async (ctx) => { + const url = 'https://vip.open.163.com'; + + const list_response = await got(url); + const $ = cheerio.load(list_response.data); + const initialState = JSON.parse( + $('script') + .text() + .match(/window\.__INITIAL_STATE__=(.*);\(function\(\)\{var/)[1] + ); + + const list = Object.values(initialState.courseindex.myModules).flatMap((mod) => + mod.contents.map((item) => ({ + title: `${item.title} - ${item.subtitle}`, + author: item.authorName, + pubDate: parseDate(item.publishTime, 'x'), + link: `${url}/courses/${item.courseUid}/`, + courseUid: item.courseUid, + category: mod.name, + })) + ); + + const items = await Promise.all( + list.map((item) => + ctx.cache.tryGet(item.link, async () => { + const { + data: { data }, + } = await got.post(`${url}/open/trade/pc/course/getCourseInfo.do`, { + form: { + courseUid: item.courseUid, + version: 1, + }, + }); + + const $ = cheerio.load(data.courseInfo.description, null, false); + $('img').each((_, img) => { + img.attribs.src = img.attribs.src.split('?')[0]; + delete img.attribs.width; + }); + + item.category = [item.category, data.courseInfo.firstClassifyName, data.courseInfo.secondClassifyName]; + item.description = art(path.join(__dirname, '../templates/open.art'), { + data, + description: $.html(), + }); + + return item; + }) + ) + ); + + ctx.state.data = { + title: '网易公开课 - 精品课程', + link: url, + item: items, + }; +}; diff --git a/lib/v2/163/radar.js b/lib/v2/163/radar.js new file mode 100644 index 000000000..8e5880e56 --- /dev/null +++ b/lib/v2/163/radar.js @@ -0,0 +1,69 @@ +const dy2 = { + title: '网易号(通用)', + docs: 'https://docs.rsshub.app/new-media.html#wang-yi-hao', + source: ['/dy/media/:id', '/news/sub/:id'], + target: (params) => `/163/dy2/${params.id.replace('.html', '')}`, +}; + +module.exports = { + '163.com': { + _name: '网易', + '.': [dy2], + '3g': [ + dy2, + { + title: '独家栏目', + docs: 'https://docs.rsshub.app/new-media.html#wang-yi-du-jia-lan-mu', + source: ['/touch/exclusive/sub/:id'], + target: '/163/exclusive/:id?', + }, + ], + 'c.m': [dy2], + ds: [ + { + title: '大神', + docs: 'https://docs.rsshub.app/game.html#wang-yi-da-shen', + source: '/user/:id', + target: '/163/ds/:id', + }, + ], + m: [ + { + title: '今日关注', + docs: 'https://docs.rsshub.app/new-media.html#wang-yi-xin-wen-jin-ri-guan-zhu', + source: ['/'], + target: '/163/today', + }, + ], + news: [ + { + title: '排行榜', + docs: 'https://docs.rsshub.app/new-media.html#wang-yi-xin-wen-pai-hang-bang', + }, + ], + renjian: [ + { + title: '人间', + docs: 'https://docs.rsshub.app/new-media.html#wang-yi-xin-wen-ren-jian', + source: ['/:category', '/'], + target: '/163/renjian/:category?', + }, + ], + 'vip.open': [ + { + title: '公开课 精品课程', + docs: 'https://docs.rsshub.app/study.html#wang-yi-gong-kai-ke', + source: ['/'], + target: '/163/open/vip', + }, + ], + 'wp.m': [ + { + title: '今日关注', + docs: 'https://docs.rsshub.app/new-media.html#wang-yi-xin-wen', + source: ['/163/html/newsapp/todayFocus/index.html', '/'], + target: '/163/today', + }, + ], + }, +}; diff --git a/lib/v2/netease/renjian.js b/lib/v2/163/renjian.js similarity index 77% rename from lib/v2/netease/renjian.js rename to lib/v2/163/renjian.js index 3f5f78b28..41be6770a 100644 --- a/lib/v2/netease/renjian.js +++ b/lib/v2/163/renjian.js @@ -2,6 +2,7 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); const iconv = require('iconv-lite'); const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); const titles = { texie: '特写', @@ -14,8 +15,8 @@ const titles = { module.exports = async (ctx) => { const category = ctx.params.category ?? 'texie'; - const rootUrl = 'http://renjian.163.com'; - const currentUrl = `${rootUrl}/special/renjian_${category}`; + const rootUrl = 'https://renjian.163.com'; + const currentUrl = `${rootUrl}/special/renjian_${category}/`; const response = await got({ method: 'get', @@ -30,21 +31,21 @@ module.exports = async (ctx) => { const urls = data.match(/url:"(.*)",/g); if (urls) { - items = urls.slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 15).map((item) => ({ + items = urls.slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 50).map((item) => ({ link: item.match(/url:"(.*)",/)[1], })); } else { const $ = cheerio.load(data); items = $('.article h3 a') - .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 15) + .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 50) + .toArray() .map((_, item) => { item = $(item); return { link: item.attr('href'), }; - }) - .get(); + }); } items = await Promise.all( @@ -57,9 +58,11 @@ module.exports = async (ctx) => { const content = cheerio.load(detailResponse.data); item.title = content('h1').text(); - item.author = content('.author_txt name').text(); + item.author = content('script') + .text() + .match(/renjian_author = '(.*)'/)[1]; item.description = content('#endText').html() ?? content('#content').html(); - item.pubDate = parseDate(content('.pub_time').text() ?? content('.post_info').text().split('来源:')[0].trim()); + item.pubDate = timezone(parseDate(content('.pub_time').text() ?? content('.post_info').text().split('来源:')[0].trim()), 8); return item; }) diff --git a/lib/v2/netease/router.js b/lib/v2/163/router.js similarity index 51% rename from lib/v2/netease/router.js rename to lib/v2/163/router.js index f93652a8c..b23f3a7f2 100644 --- a/lib/v2/netease/router.js +++ b/lib/v2/163/router.js @@ -1,6 +1,11 @@ module.exports = function (router) { router.get('/exclusive/:id?', require('./exclusive')); + router.get('/ds/:id', require('./ds')); + router.get('/dy/:id', require('./dy')); + router.get('/dy2/:id', require('./dy2')); + router.get('/news/rank/:category?/:type?/:time?', require('./news/rank')); + router.get('/news/special/:type?', require('./news/special')); + router.get('/open/vip', require('./open/vip')); router.get('/renjian/:category?', require('./renjian')); router.get('/today/:need_content?', require('./today')); - router.get('/news/rank/:category?/:type?/:time?', require('./rank')); }; diff --git a/lib/v2/163/templates/ds.art b/lib/v2/163/templates/ds.art new file mode 100644 index 000000000..8a493d218 --- /dev/null +++ b/lib/v2/163/templates/ds.art @@ -0,0 +1,7 @@ +{{ text }} + +{{ each medias }} + {{ if $value.mimeType.indexOf('image') > -1 }} + + {{ /if }} +{{ /each }} diff --git a/lib/v2/163/templates/dy.art b/lib/v2/163/templates/dy.art new file mode 100644 index 000000000..c78f313c9 --- /dev/null +++ b/lib/v2/163/templates/dy.art @@ -0,0 +1,6 @@ +{{ if imgsrc }} +
+{{ /if }} +{{ if postBody }} +{{@ postBody }} +{{ /if }} diff --git a/lib/v2/netease/templates/exclusive.art b/lib/v2/163/templates/exclusive.art similarity index 100% rename from lib/v2/netease/templates/exclusive.art rename to lib/v2/163/templates/exclusive.art diff --git a/lib/v2/163/templates/open.art b/lib/v2/163/templates/open.art new file mode 100644 index 000000000..b6fae699d --- /dev/null +++ b/lib/v2/163/templates/open.art @@ -0,0 +1,12 @@ +{{ set chapterList = data.movieChapterList.length ? data.movieChapterList : data.audioChapterList; }} +{{ if chapterList }} +
+{{ each chapterList chapter chapterIndex }} +

第{{ chapterIndex + 1 }}章 {{ chapter.title }}

+ {{ each chapter.contentList content contentIndex }} +

{{ contentIndex + 1 }} {{ content.title }}

+ {{ /each }} +{{ /each }} +
+{{ /if }} +{{@ description }} diff --git a/lib/v2/netease/today.js b/lib/v2/163/today.js similarity index 91% rename from lib/v2/netease/today.js rename to lib/v2/163/today.js index b14f008b5..7b15609db 100644 --- a/lib/v2/netease/today.js +++ b/lib/v2/163/today.js @@ -35,8 +35,8 @@ module.exports = async (ctx) => { content('.bot_word').remove(); - content('figure[data-echo]').each(function () { - content(this).html(``); + content('img').each((_, img) => { + img.attribs.src = img.attribs['data-src'] ? img.attribs['data-src'] : img.attribs.src; }); item.description = content('.content, article').html(); diff --git a/lib/v2/163/utils.js b/lib/v2/163/utils.js new file mode 100644 index 000000000..d952bc736 --- /dev/null +++ b/lib/v2/163/utils.js @@ -0,0 +1,40 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const iconv = require('iconv-lite'); +const { art } = require('@/utils/render'); +const path = require('path'); + +const parseDyArticle = (charset, item, tryGet) => + tryGet(item.link, async () => { + const response = await got(item.link, { + responseType: 'buffer', + }); + + const html = iconv.decode(response.data, charset); + const $ = cheerio.load(html); + + $('.post_main img').each((_, i) => { + if (!i.attribs.src) { + return; + } + const url = new URL(i.attribs.src); + if (url.host === 'nimg.ws.126.net') { + i.attribs.src = url.searchParams.get('url'); + } + }); + + item.description = art(path.join(__dirname, 'templates/dy.art'), { + imgsrc: item.imgsrc?.split('?')[0], + postBody: $('.post_body').html(), + }); + + item.feedLink = $('.post_wemedia_name a').attr('href'); + item.feedDescription = $('.post_wemedia_title').text(); + item.feedImage = $('.post_wemedia_avatar img').attr('src'); + + return item; + }); + +module.exports = { + parseDyArticle, +}; diff --git a/lib/v2/netease/radar.js b/lib/v2/netease/radar.js deleted file mode 100644 index 41ea928b5..000000000 --- a/lib/v2/netease/radar.js +++ /dev/null @@ -1,37 +0,0 @@ -module.exports = { - '163.com': { - _name: '网易', - '': [ - { - title: '独家栏目', - docs: 'https://docs.rsshub.app/new-media.html#wang-yi-du-jia-lan-mu', - source: ['/'], - target: '/netease/exclusive/:id?', - }, - ], - renjian: [ - { - title: '人间', - docs: 'https://docs.rsshub.app/new-media.html#wang-yi-xin-wen-ren-jian', - source: ['/:category', '/'], - target: '/netease/renjian/:category?', - }, - ], - news: [ - { - title: '排行榜', - docs: 'https://docs.rsshub.app/new-media.html#wang-yi-xin-wen-pai-hang-bang', - source: ['/:category', '/'], - target: '/netease/news/rank/:category?/:type?/:time?', - }, - ], - m: [ - { - title: '今日关注', - docs: 'https://docs.rsshub.app/new-media.html#wang-yi-xin-wen-jin-ri-guan-zhu', - source: ['/'], - target: '/netease/today/:need_content?', - }, - ], - }, -};