diff --git a/docs/traditional-media.md b/docs/traditional-media.md
index 17bb39843..79c8b7052 100644
--- a/docs/traditional-media.md
+++ b/docs/traditional-media.md
@@ -2071,6 +2071,10 @@ category 对应的关键词有
+### 作者
+
+
+
## 卫报 The Guardian
通过提取文章全文,以提供比官方源更佳的阅读体验。
diff --git a/lib/v2/cw/author.js b/lib/v2/cw/author.js
new file mode 100644
index 000000000..85fe02f16
--- /dev/null
+++ b/lib/v2/cw/author.js
@@ -0,0 +1,18 @@
+const { baseUrl, parsePage } = require('./utils');
+
+module.exports = async (ctx) => {
+ const browser = await require('@/utils/puppeteer')();
+
+ const { $, items } = await parsePage('author', browser, ctx);
+
+ await browser.close();
+
+ ctx.state.data = {
+ title: $('head title').text(),
+ description: $('.authorTxt').text(),
+ link: `${baseUrl}/author/${ctx.params.channel}`,
+ image: $('.authorPhoto img').attr('src') || `${baseUrl}/assets_new/img/fbshare.jpg'`,
+ language: $('meta[property="og:locale"]').attr('content'),
+ item: items,
+ };
+};
diff --git a/lib/v2/cw/maintainer.js b/lib/v2/cw/maintainer.js
index 51a7e83af..7aaacc960 100644
--- a/lib/v2/cw/maintainer.js
+++ b/lib/v2/cw/maintainer.js
@@ -1,4 +1,5 @@
module.exports = {
+ '/author/:channel': ['TonyRL'],
'/master/:channel': ['TonyRL'],
'/sub/:channel': ['TonyRL'],
'/today': ['TonyRL'],
diff --git a/lib/v2/cw/master.js b/lib/v2/cw/master.js
index aa34918c1..9fe3b1c00 100644
--- a/lib/v2/cw/master.js
+++ b/lib/v2/cw/master.js
@@ -1,34 +1,16 @@
-const cheerio = require('cheerio');
-const { baseUrl, parseList, parseItems, getCookie, setCookies } = require('./utils');
+const { baseUrl, parsePage } = require('./utils');
module.exports = async (ctx) => {
- const { channel } = ctx.params;
- const pageUrl = `${baseUrl}/masterChannel.action`;
-
const browser = await require('@/utils/puppeteer')();
- const cookie = await getCookie(browser, ctx.cache.tryGet);
- const page = await browser.newPage();
- await page.setRequestInterception(true);
- page.on('request', (request) => {
- request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort();
- });
- await setCookies(page, cookie, 'cw.com.tw');
- await page.goto(`${pageUrl}?idMasterChannel=${channel}`, {
- waitUntil: 'domcontentloaded',
- });
- const response = await page.evaluate(() => document.documentElement.innerHTML);
- await page.close();
- const $ = cheerio.load(response);
-
- const list = parseList($, ctx.query.limit ? Number(ctx.query.limit) : 12);
- const items = await parseItems(list, browser, ctx.cache.tryGet);
+ const { $, items } = await parsePage('master', browser, ctx);
await browser.close();
ctx.state.data = {
title: $('head title').text(),
description: $('meta[name=description]').attr('content'),
+ link: `${baseUrl}/masterChannel.action?idMasterChannel=${ctx.params.channel}`,
image: `${baseUrl}/assets_new/img/fbshare.jpg`,
language: $('meta[property="og:locale"]').attr('content'),
item: items,
diff --git a/lib/v2/cw/radar.js b/lib/v2/cw/radar.js
index 9fa3a27e4..c1869514e 100644
--- a/lib/v2/cw/radar.js
+++ b/lib/v2/cw/radar.js
@@ -20,6 +20,12 @@ module.exports = {
source: ['/subchannel.action'],
target: (_, url) => `/cw/sub/${new URL(url).searchParams.get('idSubChannel')}`,
},
+ {
+ title: '作者',
+ docs: 'https://docs.rsshub.app/traditional-media.html#tian-xia-za-zhi',
+ source: ['/author/:channel'],
+ target: '/cw/author/:channel',
+ },
],
},
};
diff --git a/lib/v2/cw/router.js b/lib/v2/cw/router.js
index 6d7edc690..7d3536b6c 100644
--- a/lib/v2/cw/router.js
+++ b/lib/v2/cw/router.js
@@ -1,4 +1,5 @@
module.exports = (router) => {
+ router.get('/author/:channel', require('./author'));
router.get('/master/:channel', require('./master'));
router.get('/sub/:channel', require('./sub'));
router.get('/today', require('./today'));
diff --git a/lib/v2/cw/sub.js b/lib/v2/cw/sub.js
index 134ff6166..768624324 100644
--- a/lib/v2/cw/sub.js
+++ b/lib/v2/cw/sub.js
@@ -1,34 +1,16 @@
-const cheerio = require('cheerio');
-const { baseUrl, parseList, parseItems, getCookie, setCookies } = require('./utils');
+const { baseUrl, parsePage } = require('./utils');
module.exports = async (ctx) => {
- const { channel } = ctx.params;
- const pageUrl = `${baseUrl}/subchannel.action`;
-
const browser = await require('@/utils/puppeteer')();
- const cookie = await getCookie(browser, ctx.cache.tryGet);
- const page = await browser.newPage();
- await page.setRequestInterception(true);
- page.on('request', (request) => {
- request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort();
- });
- await setCookies(page, cookie, 'cw.com.tw');
- await page.goto(`${pageUrl}?idSubChannel=${channel}`, {
- waitUntil: 'domcontentloaded',
- });
- const response = await page.evaluate(() => document.documentElement.innerHTML);
- await page.close();
- const $ = cheerio.load(response);
-
- const list = parseList($, ctx.query.limit ? Number(ctx.query.limit) : 12);
- const items = await parseItems(list, browser, ctx.cache.tryGet);
+ const { $, items } = await parsePage('sub', browser, ctx);
await browser.close();
ctx.state.data = {
title: $('head title').text(),
description: $('meta[name=description]').attr('content'),
+ link: `${baseUrl}/subchannel.action?idSubChannel=${ctx.params.channel}`,
image: `${baseUrl}/assets_new/img/fbshare.jpg`,
language: $('meta[property="og:locale"]').attr('content'),
item: items,
diff --git a/lib/v2/cw/today.js b/lib/v2/cw/today.js
index d878b3245..7bd01787b 100644
--- a/lib/v2/cw/today.js
+++ b/lib/v2/cw/today.js
@@ -1,33 +1,16 @@
-const cheerio = require('cheerio');
-const { baseUrl, parseList, parseItems, getCookie, setCookies } = require('./utils');
+const { baseUrl, parsePage } = require('./utils');
module.exports = async (ctx) => {
- const pageUrl = `${baseUrl}/today`;
-
const browser = await require('@/utils/puppeteer')();
- const cookie = await getCookie(browser, ctx.cache.tryGet);
- const page = await browser.newPage();
- await page.setRequestInterception(true);
- page.on('request', (request) => {
- request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort();
- });
- await setCookies(page, cookie, 'cw.com.tw');
- await page.goto(pageUrl, {
- waitUntil: 'domcontentloaded',
- });
- const response = await page.evaluate(() => document.documentElement.innerHTML);
- await page.close();
- const $ = cheerio.load(response);
-
- const list = parseList($, ctx.query.limit ? Number(ctx.query.limit) : 30);
- const items = await parseItems(list, browser, ctx.cache.tryGet);
+ const { $, items } = await parsePage('today', browser, ctx);
await browser.close();
ctx.state.data = {
title: $('head title').text(),
description: $('meta[name=description]').attr('content'),
+ link: `${baseUrl}/today`,
image: `${baseUrl}/assets_new/img/fbshare.jpg`,
language: $('meta[property="og:locale"]').attr('content'),
item: items,
diff --git a/lib/v2/cw/utils.js b/lib/v2/cw/utils.js
index a3735a61d..32ce93626 100644
--- a/lib/v2/cw/utils.js
+++ b/lib/v2/cw/utils.js
@@ -1,10 +1,30 @@
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
const { getCookies, setCookies } = require('@/utils/puppeteer-utils');
+const logger = require('@/utils/logger');
let cookie;
const baseUrl = 'https://www.cw.com.tw';
+const pathMap = {
+ today: {
+ pageUrl: () => '/today',
+ limit: 30,
+ },
+ master: {
+ pageUrl: (channel) => `/masterChannel.action?idMasterChannel=${channel}`,
+ limit: 12,
+ },
+ sub: {
+ pageUrl: (channel) => `/subchannel.action?idSubChannel=${channel}`,
+ limit: 12,
+ },
+ author: {
+ pageUrl: (channel) => `/author/${channel}`,
+ limit: 10,
+ },
+};
+
const getCookie = async (browser, tryGet) => {
if (!cookie) {
cookie = await tryGet('cw:cookie', async () => {
@@ -13,6 +33,7 @@ const getCookie = async (browser, tryGet) => {
page.on('request', (request) => {
request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort();
});
+ logger.debug(`Requesting ${baseUrl}/user/get/cookie-bar`);
await page.goto(`${baseUrl}/user/get/cookie-bar`, {
waitUntil: 'domcontentloaded',
});
@@ -24,6 +45,31 @@ const getCookie = async (browser, tryGet) => {
return cookie;
};
+const parsePage = async (path, browser, ctx) => {
+ const pageUrl = `${baseUrl}${pathMap[path].pageUrl(ctx.params.channel)}`;
+
+ const cookie = await getCookie(browser, ctx.cache.tryGet);
+ const page = await browser.newPage();
+ await page.setRequestInterception(true);
+ page.on('request', (request) => {
+ request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort();
+ });
+ await setCookies(page, cookie, 'cw.com.tw');
+ logger.debug(`Requesting ${pageUrl}`);
+ await page.goto(pageUrl, {
+ waitUntil: 'domcontentloaded',
+ });
+
+ const response = await page.evaluate(() => document.documentElement.innerHTML);
+ await page.close();
+ const $ = cheerio.load(response);
+
+ const list = parseList($, ctx.query.limit ? Number(ctx.query.limit) : pathMap[path].limit);
+ const items = await parseItems(list, browser, ctx.cache.tryGet);
+
+ return { $, items };
+};
+
const parseList = ($, limit) =>
$('.caption')
.toArray()
@@ -47,6 +93,7 @@ const parseItems = (list, browser, tryGet) =>
request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort();
});
await setCookies(page, cookie, 'cw.com.tw');
+ logger.debug(`Requesting ${item.link}`);
await page.goto(item.link, {
waitUntil: 'domcontentloaded',
});
@@ -77,8 +124,10 @@ const parseItems = (list, browser, tryGet) =>
module.exports = {
baseUrl,
+ pathMap,
getCookie,
setCookies,
+ parsePage,
parseList,
parseItems,
};