diff --git a/docs/README.md b/docs/README.md
index 1aa531ff6..0aae2d5b2 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -2317,3 +2317,14 @@ category 对应的关键词有
> board 和 build 可在[这里](http://api.ineal.me/tss/status)查看
+
+### 9To5
+
+
+
+支持分站:
+| Mac | Google | Toys |
+| --- | ------ | ---- |
+| Mac | Google | Toys |
+
+
diff --git a/docs/en/README.md b/docs/en/README.md
index 5fae59fce..7eba38825 100644
--- a/docs/en/README.md
+++ b/docs/en/README.md
@@ -398,3 +398,14 @@ Provides a better reading experience (full text articles) over the official one.
Provides a better reading experience (full text articles) over the official one.
+
+### 9To5
+
+
+
+Supported sub-sites:
+| Mac | Google | Toys |
+| --- | ------ | ---- |
+| Mac | Google | Toys |
+
+
diff --git a/router.js b/router.js
index bd4bb6c13..97bc2afdd 100644
--- a/router.js
+++ b/router.js
@@ -807,4 +807,7 @@ router.get('/tssstatus/:board/:build', require('./routes/tssstatus'));
router.get('/anime1/anime/:time/:name', require('./routes/anime1/anime'));
router.get('/anime1/search/:keyword', require('./routes/anime1/search'));
+// 9to5
+router.get('/9to5/:type', require('./routes/9to5/subsite'));
+
module.exports = router;
diff --git a/routes/9to5/subsite.js b/routes/9to5/subsite.js
new file mode 100644
index 000000000..ea624f5bf
--- /dev/null
+++ b/routes/9to5/subsite.js
@@ -0,0 +1,62 @@
+const axios = require('../../utils/axios');
+const utils = require('./utils');
+const Parser = require('rss-parser');
+const parser = new Parser();
+
+module.exports = async (ctx) => {
+ let feedUrl,
+ title = '9To5',
+ link,
+ description;
+
+ switch (ctx.params.type) {
+ case 'mac':
+ feedUrl = 'https://9to5mac.com/feed';
+ title += 'Mac';
+ description = 'Apple News & Mac Rumors Breaking All Day';
+ break;
+
+ case 'google':
+ feedUrl = 'https://9to5google.com/feed';
+ title += 'Google';
+ description = 'Google, Pixel news, Android, Home, Chrome OS, apps, more';
+ break;
+
+ case 'toys':
+ feedUrl = 'https://9to5toys.com/feed';
+ title += 'Toys';
+ description = 'New Gear, reviews and deals';
+ break;
+
+ default:
+ break;
+ }
+
+ const feed = await parser.parseURL(feedUrl);
+
+ const items = await Promise.all(
+ feed.items.splice(0, 10).map(async (item) => {
+ const response = await axios({
+ method: 'get',
+ url: item.link,
+ });
+ const description = utils.ProcessFeed(response.data);
+
+ const single = {
+ title: item.title,
+ description,
+ pubDate: item.pubDate,
+ link: item.link,
+ author: item['dc:creator'],
+ };
+ return Promise.resolve(single);
+ })
+ );
+
+ ctx.state.data = {
+ title,
+ link,
+ description,
+ item: items,
+ };
+};
diff --git a/routes/9to5/utils.js b/routes/9to5/utils.js
new file mode 100644
index 000000000..846a5c95b
--- /dev/null
+++ b/routes/9to5/utils.js
@@ -0,0 +1,31 @@
+const cheerio = require('cheerio');
+
+const ProcessFeed = (data) => {
+ const $ = cheerio.load(data);
+ const content = $('div[itemprop="articleBody"]');
+
+ // remove useless DOMs
+ content
+ .find('hr')
+ .nextAll()
+ .remove();
+
+ content.find('hr, ins.adsbygoogle, script').each((i, e) => {
+ $(e).remove();
+ });
+
+ content.find('div').each((i, e) => {
+ if ($(e)[0].attribs.class) {
+ const classes = $(e)[0].attribs.class;
+ if (classes.match(/th\w{8}\sth\w{8}/g)) {
+ $(e).remove();
+ }
+ }
+ });
+
+ return content.html();
+};
+
+module.exports = {
+ ProcessFeed,
+};