@@ -135,3 +135,36 @@ type Comment struct {
|
||||
SubComments []Comment `json:"subComments"`
|
||||
ShowTags []string `json:"showTags"`
|
||||
}
|
||||
|
||||
// UserProfileResponse 用户详情页完整响应
|
||||
type UserProfileResponse struct {
|
||||
UserBasicInfo UserBasicInfo `json:"userBasicInfo"`
|
||||
Interactions []UserInteractions `json:"interactions"`
|
||||
Feeds []Feed `json:"feeds"`
|
||||
}
|
||||
|
||||
// UserPageData 用户的详细信息
|
||||
type UserPageData struct {
|
||||
RawValue struct {
|
||||
Interactions []UserInteractions `json:"interactions"`
|
||||
BasicInfo UserBasicInfo `json:"basicInfo"`
|
||||
} `json:"_rawValue"`
|
||||
}
|
||||
|
||||
// UserBasicInfo 用户的基本信息
|
||||
type UserBasicInfo struct {
|
||||
Gender int `json:"gender"`
|
||||
IpLocation string `json:"ipLocation"`
|
||||
Desc string `json:"desc"`
|
||||
Imageb string `json:"imageb"`
|
||||
Nickname string `json:"nickname"`
|
||||
Images string `json:"images"`
|
||||
RedId string `json:"redId"`
|
||||
}
|
||||
|
||||
// UserInteractions 用户的 关注 粉丝 收藏量
|
||||
type UserInteractions struct {
|
||||
Type string `json:"type"` // follows fans interaction
|
||||
Name string `json:"name"` // 关注 粉丝 获赞与收藏
|
||||
Count string `json:"count"` // 数量
|
||||
}
|
||||
|
||||
71
xiaohongshu/user_profile.go
Normal file
71
xiaohongshu/user_profile.go
Normal file
@@ -0,0 +1,71 @@
|
||||
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()
|
||||
|
||||
page.MustWait(`() => window.__INITIAL_STATE__ !== undefined`)
|
||||
|
||||
// 获取 window.__INITIAL_STATE__ 并转换为 JSON 字符串
|
||||
result := page.MustEval(`() => {
|
||||
if (window.__INITIAL_STATE__) {
|
||||
return JSON.stringify(window.__INITIAL_STATE__);
|
||||
}
|
||||
return "";
|
||||
}`).String()
|
||||
|
||||
if result == "" {
|
||||
return nil, fmt.Errorf("__INITIAL_STATE__ not found")
|
||||
}
|
||||
// 定义响应结构并直接反序列化
|
||||
var initialState = struct {
|
||||
User struct {
|
||||
UserPageData UserPageData `json:"userPageData"`
|
||||
Notes struct {
|
||||
Feeds [][]Feed `json:"_rawValue"` // 帖子为双重数组
|
||||
} `json:"notes"`
|
||||
} `json:"user"`
|
||||
}{}
|
||||
if err := json.Unmarshal([]byte(result), &initialState); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal __INITIAL_STATE__: %w", err)
|
||||
}
|
||||
response := &UserProfileResponse{
|
||||
UserBasicInfo: initialState.User.UserPageData.RawValue.BasicInfo,
|
||||
Interactions: initialState.User.UserPageData.RawValue.Interactions,
|
||||
}
|
||||
// 添加用户贴子
|
||||
for _, feeds := range initialState.User.Notes.Feeds {
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user