From e252310f2350a4b1272d07ae0fb71476b7d3b701 Mon Sep 17 00:00:00 2001 From: kurihada Date: Mon, 2 Mar 2026 15:00:49 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E5=87=86=E7=A1=AE?= =?UTF-8?q?=E7=9A=84=20feedCount=20=E5=92=8C=20shareCount=20=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=EF=BC=8C=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7=E4=B8=BB?= =?UTF-8?q?=E9=A1=B5=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 UserProfile.feedCount(tab 选择器已失效,改用 feeds.length 展示) - 删除 FeedDetail.shareCount(实际无法获取分享数) - 用户信息栏新增"主页"按钮,点击查看当前登录用户主页 - extractInitialState 补充 userProfile/homeFeed/explore key 提取 --- src/platforms/xiaohongshu/feed-detail.ts | 10 --------- src/platforms/xiaohongshu/feeds.ts | 4 ++-- src/platforms/xiaohongshu/selectors.ts | 3 --- src/platforms/xiaohongshu/types.ts | 2 -- src/platforms/xiaohongshu/user-profile.ts | 25 +---------------------- web/src/api/types.ts | 2 -- web/src/components/feed/FeedDetail.tsx | 1 - web/src/components/feed/UserCard.tsx | 3 +-- web/src/pages/XiaohongshuPage.tsx | 5 +++++ 9 files changed, 9 insertions(+), 46 deletions(-) diff --git a/src/platforms/xiaohongshu/feed-detail.ts b/src/platforms/xiaohongshu/feed-detail.ts index b8e4724..8d7a69c 100644 --- a/src/platforms/xiaohongshu/feed-detail.ts +++ b/src/platforms/xiaohongshu/feed-detail.ts @@ -109,8 +109,6 @@ interface RawNoteInteract { collected_count?: string; commentCount?: string; comment_count?: string; - shareCount?: string; - share_count?: string; } interface RawNoteUser { @@ -335,10 +333,6 @@ function parseDetailFromState( const commentCount = parseCountString( interact?.commentCount ?? interact?.comment_count ?? '0', ); - const shareCount = parseCountString( - interact?.shareCount ?? interact?.share_count ?? '0', - ); - // Timestamps const createTimeRaw = noteData.time ?? noteData.createTime ?? noteData.create_time; const createTime = createTimeRaw @@ -377,7 +371,6 @@ function parseDetailFromState( likeCount, collectCount, commentCount, - shareCount, isLiked: false, isFavorited: false, createTime, @@ -495,8 +488,6 @@ async function scrapeDetailFromDom( const likeCount = await extractCount(page, SEL.likeCount); const collectCount = await extractCount(page, SEL.collectCount); const commentCount = await extractCount(page, SEL.commentCount); - const shareCount = await extractCount(page, SEL.shareCount); - // Create time const createTime = await page .$eval(SEL.createTime, (el) => el.textContent?.trim() ?? '') @@ -534,7 +525,6 @@ async function scrapeDetailFromDom( likeCount, collectCount, commentCount, - shareCount, isLiked: false, isFavorited: false, createTime, diff --git a/src/platforms/xiaohongshu/feeds.ts b/src/platforms/xiaohongshu/feeds.ts index 343c5c2..12fe3da 100644 --- a/src/platforms/xiaohongshu/feeds.ts +++ b/src/platforms/xiaohongshu/feeds.ts @@ -166,7 +166,7 @@ async function extractInitialState(page: Page): Promise { if (!s || typeof s !== 'object') return null; try { // structuredClone strips Vue proxies and produces a plain object. - const plain = structuredClone({ noteData: s.noteData, note: s.note, feed: s.feed, feeds: s.feeds, user: s.user }); + const plain = structuredClone({ noteData: s.noteData, note: s.note, feed: s.feed, feeds: s.feeds, user: s.user, userProfile: s.userProfile, homeFeed: s.homeFeed, explore: s.explore }); return JSON.stringify(plain); } catch { // structuredClone may fail on some Vue internals — fall back to @@ -175,7 +175,7 @@ async function extractInitialState(page: Page): Promise { const MAX_DEPTH = 20; const seen = new Set(); const result = JSON.stringify( - { noteData: s.noteData, note: s.note, feed: s.feed, feeds: s.feeds, user: s.user }, + { noteData: s.noteData, note: s.note, feed: s.feed, feeds: s.feeds, user: s.user, userProfile: s.userProfile, homeFeed: s.homeFeed, explore: s.explore }, function (_key, value) { if (typeof value === 'function') return undefined; if (typeof value === 'object' && value !== null) { diff --git a/src/platforms/xiaohongshu/selectors.ts b/src/platforms/xiaohongshu/selectors.ts index a9f0257..f872de8 100644 --- a/src/platforms/xiaohongshu/selectors.ts +++ b/src/platforms/xiaohongshu/selectors.ts @@ -72,7 +72,6 @@ export const XHS_SELECTORS = { /** Comment count. */ commentCount: '.engage-bar .chat-wrapper .count', /** Share count. */ - shareCount: '.engage-bar .share-wrapper .count', /** Publish / create time text. */ createTime: '.note-scroller .bottom-container .date', /** IP location. */ @@ -122,8 +121,6 @@ export const XHS_SELECTORS = { ipLocation: '.user-info .user-ip', /** Follower / following / interaction count elements. */ followCount: '.user-info .user-interactions > div', - /** Note count (displayed somewhere on the profile page). */ - noteCountTab: '.reds-tab-item', /** Individual feed items on the user profile. */ feedItem: '.feeds-container .note-item', }, diff --git a/src/platforms/xiaohongshu/types.ts b/src/platforms/xiaohongshu/types.ts index 672e769..acddc0d 100644 --- a/src/platforms/xiaohongshu/types.ts +++ b/src/platforms/xiaohongshu/types.ts @@ -53,7 +53,6 @@ export interface FeedDetail { likeCount: number; collectCount: number; commentCount: number; - shareCount: number; isLiked: boolean; isFavorited: boolean; createTime: string; @@ -89,7 +88,6 @@ export interface UserProfile { follows: number; fans: number; interaction: number; - feedCount: number; feeds: Feed[]; } diff --git a/src/platforms/xiaohongshu/user-profile.ts b/src/platforms/xiaohongshu/user-profile.ts index bb8b72f..c73cd65 100644 --- a/src/platforms/xiaohongshu/user-profile.ts +++ b/src/platforms/xiaohongshu/user-profile.ts @@ -158,7 +158,7 @@ export async function getUserProfile( if (initialState) { const profile = parseProfileFromState(initialState, userId, xsecToken); if (profile) { - log.info({ userId, feedCount: profile.feeds.length }, 'Extracted user profile from __INITIAL_STATE__'); + log.info({ userId, feedsCount: profile.feeds.length }, 'Extracted user profile from __INITIAL_STATE__'); return profile; } log.debug('__INITIAL_STATE__ found but no profile data extracted, falling back to DOM'); @@ -215,12 +215,6 @@ function parseProfileFromState( const fans = toNumber(interactions?.fans ?? userInfo.fans ?? 0); const interaction = toNumber(interactions?.interaction ?? userInfo.interaction ?? 0); - // Note count. - const feedCount = toNumber( - userPageData?.noteCount ?? userPageData?.note_count ?? - userInfo.noteCount ?? userInfo.note_count ?? 0, - ); - // Notes / feeds on the profile page. const rawNotes: RawProfileNote[] = userPageData?.notes ?? state.userProfile?.notes ?? []; @@ -238,7 +232,6 @@ function parseProfileFromState( follows, fans, interaction, - feedCount, feeds, }; } @@ -359,21 +352,6 @@ async function scrapeProfileFromDom( const fans = parseCountString(dataCounts[1] ?? '0'); const interaction = parseCountString(dataCounts[2] ?? '0'); - // Note count from tab — use a string expression to run in browser context - // without needing DOM types in our TypeScript config. - const feedCount = await page - .$$eval(SEL.noteCountTab, (tabs) => { - for (const tab of tabs) { - const text = tab.textContent ?? ''; - if (text.includes('\u7B14\u8BB0')) { - const match = text.match(/\d+/); - return match ? parseInt(match[0], 10) : 0; - } - } - return 0; - }) - .catch(() => 0); - // Scrape feed items on the profile page. const feedElements = await page.$$(SEL.feedItem); const feeds: Feed[] = []; @@ -430,7 +408,6 @@ async function scrapeProfileFromDom( follows, fans, interaction, - feedCount, feeds, }; } diff --git a/web/src/api/types.ts b/web/src/api/types.ts index 54ecb12..ddf860b 100644 --- a/web/src/api/types.ts +++ b/web/src/api/types.ts @@ -42,7 +42,6 @@ export interface FeedDetail { likeCount: number; collectCount: number; commentCount: number; - shareCount: number; isLiked: boolean; isFavorited: boolean; createTime: string; @@ -74,7 +73,6 @@ export interface UserProfile { follows: number; fans: number; interaction: number; - feedCount: number; feeds: Feed[]; } diff --git a/web/src/components/feed/FeedDetail.tsx b/web/src/components/feed/FeedDetail.tsx index 3d71dfb..bd0117e 100644 --- a/web/src/components/feed/FeedDetail.tsx +++ b/web/src/components/feed/FeedDetail.tsx @@ -194,7 +194,6 @@ export function FeedDetail({ feedId, xsecToken, onClose, onUserClick }: Props) { { label: '点赞', value: detail.likeCount }, { label: '收藏', value: detail.collectCount }, { label: '评论', value: detail.commentCount }, - { label: '分享', value: detail.shareCount }, ].map((s) => (

{formatNumber(s.value)}

diff --git a/web/src/components/feed/UserCard.tsx b/web/src/components/feed/UserCard.tsx index a087317..209a925 100644 --- a/web/src/components/feed/UserCard.tsx +++ b/web/src/components/feed/UserCard.tsx @@ -54,12 +54,11 @@ export function UserCard({ userId, xsecToken, onFeedSelect }: Props) {
{/* Stats */} -
+
{[ { label: '关注', value: profile.follows }, { label: '粉丝', value: profile.fans }, { label: '获赞与收藏', value: profile.interaction }, - { label: '笔记', value: profile.feedCount }, ].map((s) => (

{formatNumber(s.value)}

diff --git a/web/src/pages/XiaohongshuPage.tsx b/web/src/pages/XiaohongshuPage.tsx index fa26487..f4a779b 100644 --- a/web/src/pages/XiaohongshuPage.tsx +++ b/web/src/pages/XiaohongshuPage.tsx @@ -261,6 +261,11 @@ export function XiaohongshuPage() { {status.userId.slice(0, 12)}… )}
+ {status.userId && ( + + )}