diff --git a/README.md b/README.md index 4e0dfebcc3..5fcf5cc564 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,13 @@ RSSHub 是一个轻量、易于扩展的 RSS 生成器,可以给任何奇奇 - 会员购作品 - 排行榜 - 话题(频道/标签) +- Bangumi + - 放送列表 + - 条目的吐槽箱 + - 条目的评论 + - 条目的讨论 + - 现实人物的新作品 + - 小组话题的新回复 - 微博 - 博主 - 关键词 diff --git a/docs/README.md b/docs/README.md index 999b05d504..67d4bc25d8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -474,6 +474,49 @@ rid: 排行榜分区 id,默认 0 参数: 无 +### 条目的吐槽箱 + +举例: + +路由: `/bangumi/subject/:id/comments` + +参数: + +- id:条目 ID。在条目页面的地址栏查看 +- minLength:以查询字符串(query string)的形式指定。用于过滤掉内容长度小于指定值的吐槽 + +### 条目的评论 + +举例: + +路由: `/bangumi/subject/:id/blogs` + +参数: id - 条目 ID。在条目页面的地址栏查看 + +### 条目的讨论 + +举例: + +路由: `/bangumi/subject/:id/topics` + +参数: id - 条目 ID。在条目页面的地址栏查看 + +### 现实人物的新作品 + +举例: + +路由: `/bangumi/person/:id` + +参数: id - 人物 ID。在人物页面的地址栏查看 + +### 小组话题的新回复 + +举例: + +路由: `/bangumi/topic/:id` + +参数: id - 话题 ID。在话题页面地址栏查看 + ## 微博 ### 博主 @@ -1929,3 +1972,14 @@ IATA 国际航空运输协会机场代码,参见[维基百科 国际航空运 参数: type,必选,目前支持两种,`hot` 代表热门游记,`latest` 代表最新游记 + + +## 中国地震局 + +### 地震速报 + +举例: + +路由: `/earthquake` + +参数: 无 diff --git a/router.js b/router.js index d6dc8085ae..3aaf63681a 100755 --- a/router.js +++ b/router.js @@ -109,6 +109,9 @@ router.get('/bilibili/topic/:topic', require('./routes/bilibili/topic')); // bangumi router.get('/bangumi/calendar/today', require('./routes/bangumi/calendar/today')); +router.get('/bangumi/subject/:id/:type', require('./routes/bangumi/subject')); +router.get('/bangumi/person/:id', require('./routes/bangumi/person')); +router.get('/bangumi/topic/:id', require('./routes/bangumi/group/reply.js')); // 微博 router.get('/weibo/user/:uid', require('./routes/weibo/user')); @@ -431,4 +434,7 @@ router.get('/wechat/wasi/:id', require('./routes/wechat/wasi')); // 马蜂窝 router.get('/mafengwo/note/:type', require('./routes/mafengwo/note')); +// 中国地震局震情速递(与地震台网同步更新) +router.get('/earthquake', require('./routes/earthquake')); + module.exports = router; diff --git a/routes/bangumi/group/reply.js b/routes/bangumi/group/reply.js new file mode 100644 index 0000000000..6cd403d81e --- /dev/null +++ b/routes/bangumi/group/reply.js @@ -0,0 +1,34 @@ +const axios = require('../../../utils/axios'); +const cheerio = require('cheerio'); + +module.exports = async (ctx) => { + // bangumi.tv未提供获取小组话题的API,因此仍需要通过抓取网页来获取 + const topicID = ctx.params.id; + const link = `https://bgm.tv/group/topic/${topicID}`; + const html = (await axios.get(link)).data; + const $ = cheerio.load(html); + const title = $('#pageHeader h1').text(); + const latestReplies = $('.row_reply') + .slice(-10) + .map((i, el) => { + const $el = $(el); + return { + id: $el.attr('id'), + author: $el.find('.userInfo .l').text(), + content: $el.find('.reply_content .message').html(), + }; + }) + .get() + .reverse(); + + ctx.state.data = { + title: `${title}的最新回复`, + link, + item: latestReplies.map((c) => ({ + title: `${c.author}回复了小组话题《${title}》`, + description: c.content, + guid: c.id, + link, + })), + }; +}; diff --git a/routes/bangumi/person/index.js b/routes/bangumi/person/index.js new file mode 100644 index 0000000000..391983c108 --- /dev/null +++ b/routes/bangumi/person/index.js @@ -0,0 +1,33 @@ +const axios = require('../../../utils/axios'); +const cheerio = require('cheerio'); + +module.exports = async (ctx) => { + // bangumi.tv未提供获取“人物信息”的API,因此仍需要通过抓取网页来获取 + const personID = ctx.params.id; + const link = `https://bgm.tv/person/${personID}/works?sort=date`; + const html = (await axios.get(link)).data; + const $ = cheerio.load(html); + const personName = $('.nameSingle a').text(); + const works = $('.item') + .map((i, el) => { + const $el = $(el); + const $workEl = $el.find('.l'); + return { + work: $workEl.text(), + workURL: `https://bgm.tv/${$workEl.attr('href')}`, + workInfo: $el.find('p.info').text(), + job: $el.find('.badge_job').text(), + }; + }) + .get(); + + ctx.state.data = { + title: `${personName}参与的作品`, + link, + item: works.map((c) => ({ + title: `${personName}以${c.job}的身份参与了作品《${c.work}》`, + description: c.workInfo, + link: c.workURL, + })), + }; +}; diff --git a/routes/bangumi/subject/comments.js b/routes/bangumi/subject/comments.js new file mode 100644 index 0000000000..a521ee15db --- /dev/null +++ b/routes/bangumi/subject/comments.js @@ -0,0 +1,38 @@ +const axios = require('../../../utils/axios'); +const cheerio = require('cheerio'); + +module.exports = async (subjectID, minLength) => { + // bangumi.tv未提供获取“吐槽(comments)”的API,因此仍需要通过抓取网页来获取 + const link = `https://bgm.tv/subject/${subjectID}/comments`; + const html = (await axios.get(link)).data; + const $ = cheerio.load(html); + const title = $('.nameSingle') + .find('a') + .text(); + const comments = $('.item') + .map((i, el) => { + const $el = $(el); + const $rateEl = $el.find('.starsinfo'); + let rate = null; + if ($rateEl.length > 0) { + rate = $rateEl.attr('class').match(/sstars(\d)/)[1]; + } + return { + user: $el.find('.l').text(), + rate: rate ? rate / 2 : '无', + content: $el.find('p').text(), + }; + }) + .get() + .filter((obj) => obj.content.length >= minLength); + return { + title: `${title}的Bangumi吐槽箱`, + link, + item: comments.map((c) => ({ + title: `${c.user}的吐槽`, + description: `【评分:${c.rate}】 ${c.content}`, + guid: c.user, + link, + })), + }; +}; diff --git a/routes/bangumi/subject/index.js b/routes/bangumi/subject/index.js new file mode 100644 index 0000000000..3b111e8d10 --- /dev/null +++ b/routes/bangumi/subject/index.js @@ -0,0 +1,22 @@ +const getComments = require('./comments.js'); +const getFromAPI = require('./offcial-subject-api.js'); + +module.exports = async (ctx) => { + const id = ctx.params.id; + let response; + switch (ctx.params.type) { + case 'comments': + response = await getComments(id, Number(ctx.request.query.minLength) || 0); + break; + case 'blogs': + response = await getFromAPI('blog')(id); + break; + case 'topics': + response = await getFromAPI('topic')(id); + break; + default: + throw Error(`暂不支持对${ctx.params.type}的订阅`); + } + + ctx.state.data = response; +}; diff --git a/routes/bangumi/subject/offcial-subject-api.js b/routes/bangumi/subject/offcial-subject-api.js new file mode 100644 index 0000000000..6d464ede90 --- /dev/null +++ b/routes/bangumi/subject/offcial-subject-api.js @@ -0,0 +1,28 @@ +const axios = require('../../../utils/axios'); +module.exports = (type) => { + const mapping = { + blog: { + en: 'reviews', + cn: '评论', + }, + topic: { + en: 'board', + cn: '讨论', + }, + }; + + return async (subjectID) => { + // 官方提供的条目API文档见https://github.com/bangumi/api/blob/master/docs-raw/Subject-API.md + const url = `https://api.bgm.tv/subject/${subjectID}?responseGroup=large`; + const subjectInfo = (await axios.get(url)).data; + return { + title: `${subjectInfo.name_cn || subjectInfo.name}的Bangumi${mapping[type].cn}`, + link: `https://bgm.tv/subject/${subjectInfo.id}/${mapping[type].en}`, + item: subjectInfo[type].map((article) => ({ + title: `${article.user.nickname}:${article.title}`, + description: article.summary || '', + link: article.url, + })), + }; + }; +}; diff --git a/routes/earthquake/index.js b/routes/earthquake/index.js new file mode 100644 index 0000000000..52576540d2 --- /dev/null +++ b/routes/earthquake/index.js @@ -0,0 +1,49 @@ +const axios = require('../../utils/axios'); +const cheerio = require('cheerio'); + +module.exports = async (ctx) => { + const link = 'http://www.cea.gov.cn/publish/dizhenj/464/479/index.html'; + const html = (await axios.get(link)).data; + const $ = cheerio.load(html); + const $items = $('.list_main_right_conbg_con li'); + const items = await Promise.all($items + .toArray() + .map(async (el) => { + const $el = $(el); + const url = 'http://www.cea.gov.cn' + $el.find('a').attr('href'); + let html = await ctx.cache.get(url); + if (!html) { + html = (await axios.get(url)).data; + ctx.cache.set(url, html, 3 * 24 * 60 * 60); + } + const $1 = cheerio.load(html); + const $content = $1('.detail_main_right_conbg_con > div') + .find('script') + .remove() + .end(); + const $container = $('
'); + $container.append(`

${$content.text().replace('()', '')}

`); + $content.find('img') + .each((i, el) => { + const $el = $(el); + const src = $el.attr('src'); + $el.attr('src', 'http://www.cea.gov.cn' + src); + $el.attr('width', ''); + }) + .appendTo($('

')) + .parent() + .appendTo($container); + + return { + title: $el.find('a').text(), + link: url, + description: $container.html() + }; + })); + + ctx.state.data = { + title: '中国地震局震情速递', + link, + item: items + }; +};