Files
xiaohongshu-mcp/xiaohongshu/user_profile.go
zy df623caf18 fix get data panic (#244)
* fix: 修复 data 获取时的循环引用错误
2025-10-16 23:00:57 +08:00

123 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package xiaohongshu
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/go-rod/rod"
)
type UserProfileAction struct {
page *rod.Page
}
func NewUserProfileAction(page *rod.Page) *UserProfileAction {
pp := page.Timeout(60 * time.Second)
return &UserProfileAction{page: pp}
}
// UserProfile 获取用户基本信息及帖子
func (u *UserProfileAction) UserProfile(ctx context.Context, userID, xsecToken string) (*UserProfileResponse, error) {
page := u.page.Context(ctx)
searchURL := makeUserProfileURL(userID, xsecToken)
page.MustNavigate(searchURL)
page.MustWaitStable()
return u.extractUserProfileData(page)
}
// extractUserProfileData 从页面中提取用户资料数据的通用方法
func (u *UserProfileAction) extractUserProfileData(page *rod.Page) (*UserProfileResponse, error) {
page.MustWait(`() => window.__INITIAL_STATE__ !== undefined`)
userDataResult := page.MustEval(`() => {
if (window.__INITIAL_STATE__ &&
window.__INITIAL_STATE__.user &&
window.__INITIAL_STATE__.user.userPageData) {
const userPageData = window.__INITIAL_STATE__.user.userPageData;
const data = userPageData.value !== undefined ? userPageData.value : userPageData._value;
if (data) {
return JSON.stringify(data);
}
}
return "";
}`).String()
if userDataResult == "" {
return nil, fmt.Errorf("user.userPageData.value not found in __INITIAL_STATE__")
}
// 2. 获取用户帖子window.__INITIAL_STATE__.user.notes.value
notesResult := page.MustEval(`() => {
if (window.__INITIAL_STATE__ &&
window.__INITIAL_STATE__.user &&
window.__INITIAL_STATE__.user.notes) {
const notes = window.__INITIAL_STATE__.user.notes;
// 优先使用 valuegetter如果不存在则使用 _value内部字段
const data = notes.value !== undefined ? notes.value : notes._value;
if (data) {
return JSON.stringify(data);
}
}
return "";
}`).String()
if notesResult == "" {
return nil, fmt.Errorf("user.notes.value not found in __INITIAL_STATE__")
}
// 解析用户信息
var userPageData struct {
Interactions []UserInteractions `json:"interactions"`
BasicInfo UserBasicInfo `json:"basicInfo"`
}
if err := json.Unmarshal([]byte(userDataResult), &userPageData); err != nil {
return nil, fmt.Errorf("failed to unmarshal userPageData: %w", err)
}
// 解析帖子数据(帖子为双重数组)
var notesFeeds [][]Feed
if err := json.Unmarshal([]byte(notesResult), &notesFeeds); err != nil {
return nil, fmt.Errorf("failed to unmarshal notes: %w", err)
}
// 组装响应
response := &UserProfileResponse{
UserBasicInfo: userPageData.BasicInfo,
Interactions: userPageData.Interactions,
}
// 添加用户帖子(展平双重数组)
for _, feeds := range notesFeeds {
if len(feeds) != 0 {
response.Feeds = append(response.Feeds, feeds...)
}
}
return response, nil
}
func makeUserProfileURL(userID, xsecToken string) string {
return fmt.Sprintf("https://www.xiaohongshu.com/user/profile/%s?xsec_token=%s&xsec_source=pc_note", userID, xsecToken)
}
func (u *UserProfileAction) GetMyProfileViaSidebar(ctx context.Context) (*UserProfileResponse, error) {
page := u.page.Context(ctx)
// 创建导航动作
navigate := NewNavigate(page)
// 通过侧边栏导航到个人主页
if err := navigate.ToProfilePage(ctx); err != nil {
return nil, fmt.Errorf("failed to navigate to profile page via sidebar: %w", err)
}
// 等待页面加载完成并获取 __INITIAL_STATE__
page.MustWaitStable()
return u.extractUserProfileData(page)
}