diff --git a/docs/en/travel.md b/docs/en/travel.md
index 15e423cb7..87939d236 100644
--- a/docs/en/travel.md
+++ b/docs/en/travel.md
@@ -56,6 +56,14 @@ For airport IATA code please refer to [Wikipedia List of airports by IATA code](
+## National Geographic
+
+### Latest Stories
+
+
+
## New Museum
diff --git a/lib/v2/nationalgeographic/latest-stories.js b/lib/v2/nationalgeographic/latest-stories.js
new file mode 100644
index 000000000..c5205a6b3
--- /dev/null
+++ b/lib/v2/nationalgeographic/latest-stories.js
@@ -0,0 +1,53 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const { art } = require('@/utils/render');
+const { join } = require('path');
+
+const findNatgeo = ($) =>
+ JSON.parse(
+ $('script')
+ .text()
+ .match(/\['__natgeo__'\]=({.*?});/)[1]
+ );
+
+module.exports = async (ctx) => {
+ const currentUrl = 'https://www.nationalgeographic.com/pages/topic/latest-stories';
+ const response = await got(currentUrl);
+ const $ = cheerio.load(response.data);
+ const items = await Promise.all(
+ findNatgeo($)
+ .page.content.hub.frms.flatMap((e) => e.mods)
+ .flatMap((m) => m.tiles?.filter((t) => t.ctas[0]?.text === 'natgeo.ctaText.read'))
+ .filter((i) => i)
+ .map((i) => ({
+ title: i.title,
+ link: i.ctas[0].url,
+ category: i.tags.map((t) => t.name),
+ }))
+ .map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const response = await got(item.link);
+ const $ = cheerio.load(response.data);
+ const mods = findNatgeo($).page.content.article.frms.find((f) => f.cmsType === 'ArticleBodyFrame').mods;
+ const bodyTile = mods.find((m) => m.edgs[0].cmsType === 'ArticleBodyTile').edgs[0];
+
+ item.author = bodyTile.schma.athrs.map((a) => a.name).join(', ');
+ item.description = art(join(__dirname, 'templates/stories.art'), {
+ ldMda: bodyTile.ldMda,
+ description: bodyTile.dscrptn,
+ body: bodyTile.bdy,
+ });
+ item.pubDate = parseDate(bodyTile.pbDt);
+
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: $('meta[property="og:title"]').attr('content'),
+ link: currentUrl,
+ item: items.filter((item) => item !== null),
+ };
+};
diff --git a/lib/v2/nationalgeographic/maintainer.js b/lib/v2/nationalgeographic/maintainer.js
new file mode 100644
index 000000000..c732b952c
--- /dev/null
+++ b/lib/v2/nationalgeographic/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/latest-stories': ['miles170'],
+};
diff --git a/lib/v2/nationalgeographic/radar.js b/lib/v2/nationalgeographic/radar.js
new file mode 100644
index 000000000..5446bb6a5
--- /dev/null
+++ b/lib/v2/nationalgeographic/radar.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'nationalgeographic.com': {
+ _name: 'National Geographic',
+ www: [
+ {
+ title: 'Latest Stories',
+ docs: 'https://docs.rsshub.app/traditional-media.html#hua-shi-xin-wen',
+ source: '/pages/topic/latest-stories',
+ target: '/nationalgeographic/latest-stories',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/nationalgeographic/router.js b/lib/v2/nationalgeographic/router.js
new file mode 100644
index 000000000..98738d787
--- /dev/null
+++ b/lib/v2/nationalgeographic/router.js
@@ -0,0 +1,3 @@
+module.exports = function (router) {
+ router.get('/latest-stories', require('./latest-stories'));
+};
diff --git a/lib/v2/nationalgeographic/templates/stories.art b/lib/v2/nationalgeographic/templates/stories.art
new file mode 100644
index 000000000..6a6d842a8
--- /dev/null
+++ b/lib/v2/nationalgeographic/templates/stories.art
@@ -0,0 +1,44 @@
+{{ if ldMda }}
+
+
+ {{ ldMda.caption }}
+
+{{ /if }}
+
+{{ if description }}
+
{{ description }}
+{{ /if }}
+
+{{ each body b }}
+ {{ if b.type === 'p' }}
+ {{@ b.cntnt.mrkup }}
+ {{ else if b.type === 'inline' }}
+ {{ if b.cntnt.cmsType === 'image' }}
+
+
+ {{@ b.cntnt.caption }}
+
+ {{ else if b.cntnt.cmsType === 'imagegroup' }}
+ {{ each b.cntnt.images img }}
+
+
+ {{@ img.caption }}
+
+ {{ /each }}
+ {{ else if b.cntnt.cmsType === 'editorsNote' }}
+ {{@ b.cntnt.note }}
+ {{ else if b.cntnt.cmsType === 'listicle' }}
+ {{@ b.cntnt.text }}
+ {{ else if b.cntnt.cmsType === 'pullquote' }}
+ {{@ b.cntnt.quote }}
+ {{ else if b.cntnt.cmsType === 'source' }}
+ {{ b.cntnt.src }}
+ {{ else if b.cntnt.cmsType === 'video' }}
+
+
+ {{@ b.cntnt.title }}
+ {{@ b.cntnt.description }}
+
+ {{ /if }}
+ {{ /if }}
+{{ /each }}