From 7ad67ad56dce475d5412c7997986e41fe73b554b Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 17 Jul 2026 02:32:37 +0800 Subject: [PATCH] fix(route/xiaohongshu): use cover as url (#22740) --- lib/routes/xiaohongshu/user.ts | 19 ++++++++------ lib/routes/xiaohongshu/util.ts | 46 ++++++++++++++-------------------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/lib/routes/xiaohongshu/user.ts b/lib/routes/xiaohongshu/user.ts index 4550898e4..031d54f84 100644 --- a/lib/routes/xiaohongshu/user.ts +++ b/lib/routes/xiaohongshu/user.ts @@ -98,14 +98,17 @@ async function getUserFeeds(url: string, category: string) { const renderNote = (notes) => notes.flatMap((n) => - n.map(({ id, noteCard }) => ({ - title: noteCard.displayTitle, - link: new URL(noteCard.noteId || id, url).href, - guid: noteCard.displayTitle, - description: `
${noteCard.displayTitle}`, - author: noteCard.user.nickname, - upvotes: noteCard.interactInfo.likedCount, - })) + n.map(({ noteCard }) => { + const coverUrl = noteCard.cover.infoList.pop().url; + return { + title: noteCard.displayTitle, + link: coverUrl, + guid: noteCard.displayTitle, + description: `
${noteCard.displayTitle}`, + author: noteCard.user.nickname, + upvotes: noteCard.interactInfo.likedCount, + }; + }) ); const renderCollect = (collect) => { if (!collect) { diff --git a/lib/routes/xiaohongshu/util.ts b/lib/routes/xiaohongshu/util.ts index a3084ff28..cf1b40e2e 100644 --- a/lib/routes/xiaohongshu/util.ts +++ b/lib/routes/xiaohongshu/util.ts @@ -1,3 +1,4 @@ +import type { CheerioAPI } from 'cheerio'; import { load } from 'cheerio'; import { config } from '@/config'; @@ -77,17 +78,22 @@ const getUser = (url, cache) => await page.goto(url, { waitUntil: 'domcontentloaded', }); - await page.waitForSelector('div.reds-tab-item:nth-child(2), #red-captcha'); + try { + await page.waitForSelector('div.reds-tab-item:nth-child(2), .fe-verify-box', { timeout: 3000 }); + } catch { + // + } - if (await page.$('#red-captcha')) { + if (await page.$('.fe-verify-box')) { throw new CaptchaError('小红书风控校验,请稍后再试'); } - const initialState = await page.evaluate(() => (window as any).__INITIAL_STATE__); + const content = await page.content(); + const initialState = JSON.parse(extractInitialState(load(content))); if (!(await page.$('.lock-icon'))) { - await page.click('div.reds-tab-item:nth-child(2)'); try { + await page.click('div.reds-tab-item:nth-child(2)', { timeout: 3000 }); const response = await page.waitForResponse( (res) => { const req = res.request(); @@ -105,6 +111,10 @@ const getUser = (url, cache) => userPageData = userPageData._rawValue || userPageData; notes = notes._rawValue || notes; + if (!userPageData.basicInfo) { + throw new Error(`小红书未返回用户数据,请稍后再试: ${JSON.stringify(userPageData.result)}`); + } + return { userPageData, notes, collect }; } finally { await destroy(); @@ -299,38 +309,20 @@ async function getUserWithCookie(url: string) { } // Add helper function to extract initial state -function extractInitialState($) { - let script = $('script') - .filter((i, script) => { - const text = script.children[0]?.data; - return text?.startsWith('window.__INITIAL_STATE__='); - }) - .text(); - script = script.slice('window.__INITIAL_STATE__='.length); +function extractInitialState($: CheerioAPI) { + let script = $('script:contains("window.__INITIAL_STATE__=")').text(); + script = script.slice(script.indexOf('window.__INITIAL_STATE__=') + 'window.__INITIAL_STATE__='.length); script = script.replaceAll('undefined', 'null'); return script; } // Add helper function to extract initial SSR state -function extractInitialSsrState($) { - let script = $('script') - .filter((i, script) => { - const text = script.children[0]?.data; - return text?.includes('window.__INITIAL_SSR_STATE__='); - }) - .text(); +function extractInitialSsrState($: CheerioAPI) { + const script = $('script:contains("window.__INITIAL_SSR_STATE__=")').text(); const match = script.match(/window\.__INITIAL_SSR_STATE__\s*=\s*(\{[\s\S]*?\})\s*(?:;|$)/); if (match) { return match[1].replaceAll('undefined', 'null'); } - // Fallback: try simple extraction - const startMarker = 'window.__INITIAL_SSR_STATE__='; - const startIndex = script.indexOf(startMarker); - if (startIndex !== -1) { - script = script.slice(startIndex + startMarker.length); - script = script.replaceAll('undefined', 'null'); - return script; - } throw new Error('Cannot extract __INITIAL_SSR_STATE__'); }