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.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,
+};