diff --git a/lib/v2/routledge/book-series.js b/lib/v2/routledge/book-series.js
new file mode 100644
index 000000000..f876a10d7
--- /dev/null
+++ b/lib/v2/routledge/book-series.js
@@ -0,0 +1,78 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const { art } = require('@/utils/render');
+const { join } = require('path');
+
+module.exports = async (ctx) => {
+ const { bookName, bookId } = ctx.params;
+ const baseUrl = 'https://www.routledge.com';
+ const pageUrl = `${baseUrl}/${bookName}/book-series/${bookId}`;
+ const { data: response } = await got(pageUrl, {
+ headers: {
+ accept: 'text/html, */*; q=0.01',
+ },
+ searchParams: {
+ publishedFilter: 'alltitles',
+ pd: 'published,forthcoming',
+ pg: 1,
+ pp: 12,
+ so: 'pub',
+ view: 'list',
+ },
+ });
+ const $ = cheerio.load(response);
+
+ const list = $('.row.book')
+ .toArray()
+ .map((item) => {
+ item = $(item);
+ const title = item.find('h3 a');
+ const description = item.find('p.description');
+ const meta = item.find('p.description').prev().text().split('\n');
+ return {
+ title: title.text(),
+ link: title.attr('href'),
+ description: description.text(),
+ pubDate: parseDate(meta.pop().trim(), 'MMMM DD, YYYY'),
+ author: meta
+ .map((i) => i.trim())
+ .filter((i) => i)
+ .join(', '),
+ };
+ });
+
+ const items = await Promise.all(
+ list.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const { data } = await got(item.link);
+ const $ = cheerio.load(data);
+ const isbn = $('meta[property="books:isbn"]').attr('content');
+ const { data: image } = await got('https://productimages.routledge.com', {
+ searchParams: {
+ isbn,
+ size: 'amazon',
+ ext: 'jpg',
+ },
+ });
+
+ const description = $('.sticky-div');
+ description.find('button.accordion-button').contents().unwrap();
+ description.find('.fa-shopping-cart').parent().parent().remove();
+
+ item.description = art(join(__dirname, 'templates/description.art'), {
+ image,
+ description: description.html(),
+ });
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: $('head title').text(),
+ description: $('head meta[name="description"]').attr('content'),
+ link: pageUrl,
+ item: items,
+ };
+};
diff --git a/lib/v2/routledge/maintainer.js b/lib/v2/routledge/maintainer.js
new file mode 100644
index 000000000..7c3f1201e
--- /dev/null
+++ b/lib/v2/routledge/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/:bookName/book-series/:bookId': ['TonyRL'],
+};
diff --git a/lib/v2/routledge/radar.js b/lib/v2/routledge/radar.js
new file mode 100644
index 000000000..2eb39553b
--- /dev/null
+++ b/lib/v2/routledge/radar.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'routledge.com': {
+ _name: 'Routledge',
+ '.': [
+ {
+ title: 'Book Series',
+ docs: 'https://docs.rsshub.app/routes/journals#routledge',
+ source: ['/:bookName/book-series/:bookId'],
+ target: '/routledge/:bookName/book-series/:bookId',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/routledge/router.js b/lib/v2/routledge/router.js
new file mode 100644
index 000000000..51935b6e9
--- /dev/null
+++ b/lib/v2/routledge/router.js
@@ -0,0 +1,3 @@
+module.exports = (router) => {
+ router.get('/:bookName/book-series/:bookId', require('./book-series'));
+};
diff --git a/lib/v2/routledge/templates/description.art b/lib/v2/routledge/templates/description.art
new file mode 100644
index 000000000..d7f9b4d48
--- /dev/null
+++ b/lib/v2/routledge/templates/description.art
@@ -0,0 +1,7 @@
+{{ if image }}
+
+{{ /if }}
+
+{{ if description }}
+{{@ description }}
+{{ /if }}
diff --git a/website/docs/routes/journal.mdx b/website/docs/routes/journal.mdx
index 8ab97e320..efcec0bdf 100644
--- a/website/docs/routes/journal.mdx
+++ b/website/docs/routes/journal.mdx
@@ -411,6 +411,12 @@ In `https://pubmed.ncbi.nlm.nih.gov/trending/?filter=simsearch1.fha&filter=pubt.
+## Routledge {#routledge}
+
+### Book Series {#routledge-book-series}
+
+
+
## Royal Society of Chemistry {#royal-society-of-chemistry}
### Journal {#royal-society-of-chemistry-journal}