diff --git a/docs/social-media.md b/docs/social-media.md index debb7a081..ff520fbde 100644 --- a/docs/social-media.md +++ b/docs/social-media.md @@ -784,6 +784,20 @@ rule +## 小红书 + +### 用户笔记和专辑 + + + +### 用户专辑 + + + +### 专辑 + + + ## 知乎 ### 收藏夹 diff --git a/lib/router.js b/lib/router.js index 501b7d5f6..e78588945 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1953,6 +1953,10 @@ router.get('/zhanqi/room/:id', require('./routes/zhanqi/room')); // 酒云网 router.get('/wineyun/:category', require('./routes/wineyun')); +// 小红书 +router.get('/xiaohongshu/user/:user_id/:category', require('./routes/xiaohongshu/user')); +router.get('/xiaohongshu/board/:board_id', require('./routes/xiaohongshu/board')); + // 重磅原创-每经网 router.get('/nbd/daily', require('./routes/nbd/article')); diff --git a/lib/routes/xiaohongshu/board.js b/lib/routes/xiaohongshu/board.js new file mode 100644 index 000000000..903594ef2 --- /dev/null +++ b/lib/routes/xiaohongshu/board.js @@ -0,0 +1,28 @@ +const { getContent } = require('@/routes/xiaohongshu/util'); + +const JSON5 = require('json5'); + +module.exports = async (ctx) => { + const url = `https://www.xiaohongshu.com/board/${ctx.params.board_id}`; + const content = await getContent(url); + const regex = /_SSR_STATE__\S*=([\s\S]+)<\/script>/gm; + const match = regex.exec(content); + const main = JSON5.parse(match[1]).Main; + + const albumInfo = main.albumInfo; + const title = `${albumInfo.name}`; + const description = `${albumInfo.desc}`; + + const list = main.notesDetail; + const resultItem = list.map((item) => ({ + title: `${item.title}`, + link: `https://www.xiaohongshu.com/discovery/item/${item.id}`, + description: `
${item.title}`, + })); + ctx.state.data = { + title: title, + link: url, + item: resultItem, + description: description, + }; +}; diff --git a/lib/routes/xiaohongshu/user.js b/lib/routes/xiaohongshu/user.js new file mode 100644 index 000000000..047020ec2 --- /dev/null +++ b/lib/routes/xiaohongshu/user.js @@ -0,0 +1,49 @@ +const { getContent } = require('@/routes/xiaohongshu/util'); + +const cheerio = require('cheerio'); +const JSON5 = require('json5'); + +module.exports = async (ctx) => { + const userId = ctx.params.user_id; + const category = ctx.params.category; + const url = `https://www.xiaohongshu.com/user/profile/${userId}`; + + const content = await getContent(url); + const $ = cheerio.load(content); + const description = $('head title'); + const regex = /_SSR_STATE__\S*=([\s\S]+)<\/script>/gm; + const match = regex.exec(content); + const main = JSON5.parse(match[1]).Main; + const userDetail = main.userDetail; + if (category === 'notes') { + const list = main.notesDetail; + const resultItem = list.map((item) => ({ + title: `${item.title}`, + link: `${item.link}`, + description: `
${item.title}`, + })); + + const title = `小红书-${userDetail.nickname}-笔记`; + ctx.state.data = { + title: title, + link: url, + item: resultItem, + description: description, + }; + } else if (category === 'album') { + const list = main.albumDetail; + const resultItem = list.map((item) => ({ + title: `${item.title}`, + link: `https://www.xiaohongshu.com/board/${item.id}`, + description: item.images.map((it) => ``).join(`
`), + })); + + const title = `小红书-${userDetail.nickname}-专辑`; + ctx.state.data = { + title: title, + link: url, + item: resultItem, + description: description, + }; + } +}; diff --git a/lib/routes/xiaohongshu/util.js b/lib/routes/xiaohongshu/util.js new file mode 100644 index 000000000..ad18262ff --- /dev/null +++ b/lib/routes/xiaohongshu/util.js @@ -0,0 +1,18 @@ +const delay = (timeout = 1000) => new Promise((resolve) => setTimeout(resolve, timeout)); + +async function getContent(url) { + const browser = await require('@/utils/puppeteer')(); + try { + const page = await browser.newPage(); + await page.goto(url); + await delay(1000); + const content = await page.content(); + return content.replace('"RedAppLayout":undefined,', ''); // fix json parse + } finally { + browser.close(); + } +} + +module.exports = { + getContent, +};