diff --git a/docs/en/traditional-media.md b/docs/en/traditional-media.md index 0acd797781..9fe39a5e53 100644 --- a/docs/en/traditional-media.md +++ b/docs/en/traditional-media.md @@ -574,6 +574,20 @@ Language +## The Atlantic + +### News + + + +| Popular | Latest | Politics | Technology | Business | +| ------------ | ------ | -------- | ---------- | -------- | +| most-popular | latest | politics | technology | business | + +More categories (except photo) can be found within the navigation bar at + + + ## The Economist ### Category diff --git a/docs/traditional-media.md b/docs/traditional-media.md index cdb5bb6e1f..eef02620f7 100644 --- a/docs/traditional-media.md +++ b/docs/traditional-media.md @@ -468,6 +468,20 @@ Solidot 提供的 feed: +## The Atlantic + +### News + + + +| Popular | Latest | Politics | Technology | Business | +| ------------ | ------ | -------- | ---------- | -------- | +| most-popular | latest | politics | technology | business | + +More categories (except photo) can be found within the navigation bar at + + + ## The Economist ### 分类 diff --git a/lib/v2/theatlantic/maintainer.js b/lib/v2/theatlantic/maintainer.js new file mode 100644 index 0000000000..1b6cbaac98 --- /dev/null +++ b/lib/v2/theatlantic/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/:category': ['NavePnow'], +}; diff --git a/lib/v2/theatlantic/news.js b/lib/v2/theatlantic/news.js new file mode 100644 index 0000000000..85f29deea6 --- /dev/null +++ b/lib/v2/theatlantic/news.js @@ -0,0 +1,33 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { getArticleDetails } = require('./utils'); +module.exports = async (ctx) => { + const host = 'https://www.theatlantic.com'; + const category = ctx.params.category; + const url = `${host}/${category}/`; + const response = await got({ + method: 'get', + url, + }); + const $ = cheerio.load(response.data); + const contents = JSON.parse($('script#__NEXT_DATA__').text()).props.pageProps.urqlState; + const firstKey = Object.keys(contents)[0]; + const data = JSON.parse(contents[firstKey].data); + let list = Object.values(data)[0].river.edges; + list = list.filter((item) => !item.node.url.startsWith('https://www.theatlantic.com/photo')); + list = list.map((item) => { + const data = {}; + data.title = item.node.title; + data.link = item.node.url; + data.pubDate = item.node.datePublished; + return data; + }); + const items = await getArticleDetails(list, ctx); + + ctx.state.data = { + title: `The Atlantic - ${category.toUpperCase()}`, + link: url, + description: `The Atlantic - ${category.toUpperCase()}`, + item: items, + }; +}; diff --git a/lib/v2/theatlantic/radar.js b/lib/v2/theatlantic/radar.js new file mode 100644 index 0000000000..a8288a387b --- /dev/null +++ b/lib/v2/theatlantic/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'theatlantic.com': { + _name: 'The Atlantic', + www: [ + { + title: '新闻', + docs: 'https://docs.rsshub.app/traditional-media.html#the-atlantic', + source: '/:category', + target: '/theatlantic/:category', + }, + ], + }, +}; diff --git a/lib/v2/theatlantic/router.js b/lib/v2/theatlantic/router.js new file mode 100644 index 0000000000..d146629a4d --- /dev/null +++ b/lib/v2/theatlantic/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/:category', require('./news')); +}; diff --git a/lib/v2/theatlantic/templates/article-description.art b/lib/v2/theatlantic/templates/article-description.art new file mode 100644 index 0000000000..c64df2e205 --- /dev/null +++ b/lib/v2/theatlantic/templates/article-description.art @@ -0,0 +1,19 @@ +
+ {{@ item.caption}} +
+ {{ if item.imgUrl }} +
+ {{item.imgAlt}} +
{{item.imgCaption}}
+
+ {{ /if }} + {{each item.content}} + {{@ $value.innerHtml}} + {{if !$value.tagName}} +
+
+ {{/if}} + {{/each}} + +
+ diff --git a/lib/v2/theatlantic/utils.js b/lib/v2/theatlantic/utils.js new file mode 100644 index 0000000000..feb3f55e71 --- /dev/null +++ b/lib/v2/theatlantic/utils.js @@ -0,0 +1,53 @@ +const cheerio = require('cheerio'); +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); +const UA = require('@/utils/rand-user-agent')({ browser: 'chrome', os: 'android', device: 'mobile' }); + +const getArticleDetails = async (items, ctx) => { + const list = await Promise.all( + items.map((item) => + ctx.cache.tryGet(item.link, async () => { + const url = item.link; + const response = await got({ + url, + method: 'get', + headers: { + 'User-Agent': UA, + }, + }); + const html = response.data; + const $ = cheerio.load(html); + let data = JSON.parse($('script#__NEXT_DATA__').text()); + + const list = data.props.pageProps.urqlState; + const keyWithContent = Object.keys(list).filter((key) => list[key].data.includes('content')); + data = JSON.parse(list[keyWithContent].data).article; + item.category = data.categories.map((category) => category.slug); + data.channels.forEach((channel) => { + item.category.push(channel.slug); + }); + item.content = data.content.filter((item) => item.innerHtml !== undefined && item.innerHtml !== ''); + item.caption = data.dek; + item.imgUrl = data.leadArt.image?.url; + item.imgAlt = data.leadArt.image?.altText; + item.imgCaption = data.leadArt.image?.attributionText; + item.description = art(path.join(__dirname, 'templates/article-description.art'), { + item, + }); + return { + title: item.title, + pubDate: parseDate(item.pubDate), + link: item.link, + description: item.description, + }; + }) + ) + ); + return list; +}; + +module.exports = { + getArticleDetails, +};