|
@@ -9,6 +9,7 @@ import (
|
|
|
"net/http"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
+ "time"
|
|
|
)
|
|
|
|
|
|
// RedBookNoteResponse 小红书链接接收结构体
|
|
@@ -19,12 +20,17 @@ type RedBookNoteResponse struct {
|
|
|
Data struct {
|
|
|
Items []struct {
|
|
|
NoteCard struct {
|
|
|
+ Title string `json:"title"` // 名称
|
|
|
+ Time int64 `json:"time"` // 时间戳
|
|
|
InteractInfo struct {
|
|
|
LikedCount string `json:"liked_count"` // 点赞数
|
|
|
CommentCount string `json:"comment_count"` // 评论数
|
|
|
CollectedCount string `json:"collected_count"` // 收藏数
|
|
|
ShareCount string `json:"share_count"` // 分享数
|
|
|
} `json:"interact_info"`
|
|
|
+ ImageList []struct {
|
|
|
+ UrlDefault string `json:"url_default"` // 主图
|
|
|
+ } `json:"image_list"`
|
|
|
} `json:"note_card"`
|
|
|
} `json:"items"`
|
|
|
} `json:"data"`
|
|
@@ -45,19 +51,18 @@ type WeiboPostResponse struct {
|
|
|
}
|
|
|
|
|
|
// parseNoteStats 小红书接收方法
|
|
|
-func parseNoteStats(jsonData []byte) (likes, comments, collects, shares string, err error) {
|
|
|
- var resp RedBookNoteResponse
|
|
|
+func parseNoteStats(jsonData []byte) (respData *RedBookNoteResponse, err error) {
|
|
|
+ var resp *RedBookNoteResponse
|
|
|
+ resp = &RedBookNoteResponse{}
|
|
|
if err := json.Unmarshal(jsonData, &resp); err != nil {
|
|
|
- return "", "", "", "", fmt.Errorf("JSON解析失败: %v", err)
|
|
|
+ return nil, fmt.Errorf("JSON解析失败: %v", err)
|
|
|
}
|
|
|
|
|
|
// 检查数据是否存在
|
|
|
if len(resp.Data.ResponseBody.Data.Items) == 0 {
|
|
|
- return "", "", "", "", errors.New("未找到笔记数据")
|
|
|
+ return nil, errors.New("未找到笔记数据")
|
|
|
}
|
|
|
-
|
|
|
- stats := resp.Data.ResponseBody.Data.Items[0].NoteCard.InteractInfo
|
|
|
- return stats.LikedCount, stats.CommentCount, stats.CollectedCount, stats.ShareCount, nil
|
|
|
+ return resp, nil
|
|
|
}
|
|
|
|
|
|
// parseWeiboStats 微博接收方法
|
|
@@ -83,7 +88,7 @@ func parseWeiboStats(jsonData []byte) (likes, comments, reposts, reads string, e
|
|
|
}
|
|
|
|
|
|
// GetRedBookLinkDetail 获取小红书链接数据详情
|
|
|
-func GetRedBookLinkDetail(ctx context.Context, noteId string, xSecToken string) (int, int, int, int, error) {
|
|
|
+func GetRedBookLinkDetail(ctx context.Context, noteId string, xSecToken string) (*RedBookNoteResponse, error) {
|
|
|
|
|
|
url := "http://120.46.92.62:6888/api/xhs/note_detail"
|
|
|
method := "POST"
|
|
@@ -103,7 +108,7 @@ func GetRedBookLinkDetail(ctx context.Context, noteId string, xSecToken string)
|
|
|
jsonData, err := json.Marshal(requestBody)
|
|
|
if err != nil {
|
|
|
fmt.Println("JSON编码失败:", err)
|
|
|
- return 0, 0, 0, 0, err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
|
|
|
payload := strings.NewReader(string(jsonData))
|
|
@@ -112,46 +117,43 @@ func GetRedBookLinkDetail(ctx context.Context, noteId string, xSecToken string)
|
|
|
req, err := http.NewRequest(method, url, payload)
|
|
|
|
|
|
if err != nil {
|
|
|
- // fmt.Println(err)
|
|
|
- return 0, 0, 0, 0, err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
req.Header.Add("Cookie", "abRequestId=87d8ec6f-314f-5914-af1d-21296bf34c5f; a1=197a0b8a06djmry91j6taqhe0kcutwojdcu15nypd50000386246; webId=ae01b40eef7780a778b6624a3b295f4f; gid=yjW08S8Y2yffyjW08DY08SIvKfV6FvjyVK907UfAxd8TSy2879ElV2888qYKJ4K82qfKy82D; webBuild=4.68.0; acw_tc=0a4a2d6e17510033969926232e7b39d55058a43fb37e1ddc19745e6642f746; websectiga=7750c37de43b7be9de8ed9ff8ea0e576519e8cd2157322eb972ecb429a7735d4; sec_poison_id=b4fae566-5d78-4269-85c6-28fc28ec20d3; xsecappid=xhs-pc-web; web_session=040069b22467fb3084e74f796b3a4b11424ee0; loadts=1751003476156; unread={%22ub%22:%22685b56850000000020019eef%22%2C%22ue%22:%22685df405000000002201fd00%22%2C%22uc%22:14}")
|
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
|
|
res, err := client.Do(req)
|
|
|
if err != nil {
|
|
|
- // fmt.Println(err)
|
|
|
- return 0, 0, 0, 0, err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
defer res.Body.Close()
|
|
|
|
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
|
if err != nil {
|
|
|
- // fmt.Println(err)
|
|
|
- return 0, 0, 0, 0, err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- fmt.Println(string(body))
|
|
|
+ // fmt.Println(string(body))
|
|
|
|
|
|
// 提取互动数据
|
|
|
- likes, comments, collects, shares, err := parseNoteStats(body)
|
|
|
- if err != nil {
|
|
|
+ redBookNoteStatus, redBookNoteStatusErr := parseNoteStats(body)
|
|
|
+ if redBookNoteStatusErr != nil {
|
|
|
//fmt.Println("解析互动数据失败:", err)
|
|
|
- return 0, 0, 0, 0, err
|
|
|
- }
|
|
|
-
|
|
|
- like, likeErr := strconv.Atoi(likes)
|
|
|
- if likeErr != nil {
|
|
|
- }
|
|
|
- comment, commentErr := strconv.Atoi(comments)
|
|
|
- if commentErr != nil {
|
|
|
- }
|
|
|
- collect, collectErr := strconv.Atoi(collects)
|
|
|
- if collectErr != nil {
|
|
|
- }
|
|
|
- share, shareErr := strconv.Atoi(shares)
|
|
|
- if shareErr != nil {
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- return like, comment, collect, share, nil
|
|
|
+ fmt.Println(redBookNoteStatus, redBookNoteStatusErr)
|
|
|
+ //like, likeErr := strconv.Atoi(redBookNoteStatus.Data.ResponseBody.Data.Items[0].NoteCardInteractInfo.LikedCount)
|
|
|
+ //if likeErr != nil {
|
|
|
+ //}
|
|
|
+ //comment, commentErr := strconv.Atoi(comments)
|
|
|
+ //if commentErr != nil {
|
|
|
+ //}
|
|
|
+ //collect, collectErr := strconv.Atoi(collects)
|
|
|
+ //if collectErr != nil {
|
|
|
+ //}
|
|
|
+ //share, shareErr := strconv.Atoi(shares)
|
|
|
+ //if shareErr != nil {
|
|
|
+ //}
|
|
|
+ return redBookNoteStatus, nil
|
|
|
}
|
|
|
|
|
|
// GetWeiBoLinkDetail 获取微博链接数据详情
|
|
@@ -231,24 +233,19 @@ type WeiboPostsResponse struct {
|
|
|
Data struct {
|
|
|
Cards []struct {
|
|
|
Mblog struct {
|
|
|
+ Id string `json:"id"`
|
|
|
+ Text string `json:"text"`
|
|
|
+ CreatedAt string `json:"created_at"`
|
|
|
+ AttitudesCount int `json:"attitudes_count"` // 点赞数
|
|
|
+ CommentsCount int `json:"comments_count"` // 评论数
|
|
|
+ RepostsCount int `json:"reposts_count"` // 转发数
|
|
|
RetweetedStatus struct {
|
|
|
- Id string `json:"id"`
|
|
|
- Text string `json:"text"`
|
|
|
- CreatedAt string `json:"created_at"`
|
|
|
- AttitudesCount int `json:"attitudes_count"` // 点赞数
|
|
|
- CommentsCount int `json:"comments_count"` // 评论数
|
|
|
- RepostsCount int `json:"reposts_count"` // 转发数
|
|
|
- PendingApproval int `json:"pending_approval"` // 待审数
|
|
|
- PicIds []string `json:"pic_ids"` // 图片ID列表
|
|
|
- PageInfo struct {
|
|
|
- Type string `json:"type"` // 内容类型(video/article)
|
|
|
- PagePic struct {
|
|
|
- Url string `json:"url"` // 封面图URL
|
|
|
- } `json:"page_pic"`
|
|
|
- MediaInfo struct {
|
|
|
- StreamUrl string `json:"stream_url"` // 视频流地址
|
|
|
- } `json:"media_info"`
|
|
|
- } `json:"page_info"`
|
|
|
+ Id string `json:"id"`
|
|
|
+ Text string `json:"text"`
|
|
|
+ CreatedAt string `json:"created_at"`
|
|
|
+ AttitudesCount int `json:"attitudes_count"` // 点赞数
|
|
|
+ CommentsCount int `json:"comments_count"` // 评论数
|
|
|
+ RepostsCount int `json:"reposts_count"` // 转发数
|
|
|
} `json:"retweeted_status"`
|
|
|
} `json:"mblog"`
|
|
|
} `json:"cards"`
|
|
@@ -264,47 +261,46 @@ type WeiboPostStats struct {
|
|
|
Likes int `json:"likes"` // 点赞数
|
|
|
Comments int `json:"comments"` // 评论数
|
|
|
Reposts int `json:"reposts"` // 转发数
|
|
|
- HasImages bool `json:"has_images"` // 是否有图片
|
|
|
- HasVideo bool `json:"has_video"` // 是否有视频
|
|
|
CoverImageUrl string `json:"cover_image_url"` // 封面图URL(视频/文章)
|
|
|
- VideoUrl string `json:"video_url"` // 视频地址(如果有)
|
|
|
+ VideoUrl string `json:"video_url"` // 视频地址
|
|
|
}
|
|
|
|
|
|
// parseWeiboPosts 解析微博多条数据
|
|
|
-func parseWeiboPosts(body []byte) ([]WeiboPostStats, error) {
|
|
|
+func parseWeiboPosts(body []byte) ([]WeiboPostStats, int, error) {
|
|
|
var resp WeiboPostsResponse
|
|
|
if err := json.Unmarshal(body, &resp); err != nil {
|
|
|
- return nil, fmt.Errorf("JSON解析失败: %v", err)
|
|
|
+ return nil, 0, fmt.Errorf("JSON解析失败: %v", err)
|
|
|
}
|
|
|
|
|
|
if resp.Code != 200 {
|
|
|
- return nil, fmt.Errorf("API返回错误: %s", resp.Msg)
|
|
|
+ return nil, 0, fmt.Errorf("API返回错误: %s", resp.Msg)
|
|
|
}
|
|
|
|
|
|
var posts []WeiboPostStats
|
|
|
- for i, card := range resp.Data.Data.Cards {
|
|
|
- if i >= 5 { // 只取前5条
|
|
|
+ var count int
|
|
|
+ count = 0
|
|
|
+ for _, card := range resp.Data.Data.Cards {
|
|
|
+ if count >= 5 { // 最多取5条
|
|
|
break
|
|
|
}
|
|
|
|
|
|
- mblog := card.Mblog.RetweetedStatus
|
|
|
- hasVideo := mblog.PageInfo.Type == "video"
|
|
|
+ mblog := card.Mblog
|
|
|
+ // 如果是转发微博,使用转发的内容
|
|
|
+ if mblog.RetweetedStatus.Id != "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
|
|
|
posts = append(posts, WeiboPostStats{
|
|
|
- Id: mblog.Id,
|
|
|
- Text: mblog.Text,
|
|
|
- CreatedAt: mblog.CreatedAt,
|
|
|
- Likes: mblog.AttitudesCount,
|
|
|
- Comments: mblog.CommentsCount,
|
|
|
- Reposts: mblog.RepostsCount,
|
|
|
- HasImages: len(mblog.PicIds) > 0,
|
|
|
- HasVideo: hasVideo,
|
|
|
- CoverImageUrl: mblog.PageInfo.PagePic.Url,
|
|
|
- VideoUrl: mblog.PageInfo.MediaInfo.StreamUrl,
|
|
|
+ Id: mblog.Id,
|
|
|
+ Text: mblog.Text,
|
|
|
+ CreatedAt: mblog.CreatedAt,
|
|
|
+ Likes: mblog.AttitudesCount,
|
|
|
+ Comments: mblog.CommentsCount,
|
|
|
+ Reposts: mblog.RepostsCount,
|
|
|
})
|
|
|
+ count++
|
|
|
}
|
|
|
- //fmt.Println(posts)
|
|
|
- return posts, nil
|
|
|
+ return posts, count, nil
|
|
|
}
|
|
|
|
|
|
// GetWeiBoHistoryList 微博历史作品数据
|
|
@@ -351,11 +347,12 @@ func GetWeiBoHistoryList(ctx context.Context, homePageUrl string) ([]WeiboPostSt
|
|
|
fmt.Println(err)
|
|
|
return nil, err
|
|
|
}
|
|
|
- hisData, hisErr := parseWeiboPosts(body)
|
|
|
- if hisErr != nil {
|
|
|
- fmt.Println(hisErr)
|
|
|
+ historyData, historyCount, historyErr := parseWeiboPosts(body)
|
|
|
+ if historyErr != nil {
|
|
|
+ fmt.Println(historyErr)
|
|
|
}
|
|
|
- return hisData, nil
|
|
|
+ fmt.Println(historyCount)
|
|
|
+ return historyData, nil
|
|
|
}
|
|
|
|
|
|
// RedBookPostsResponse 小红书多条数据接收结构体
|
|
@@ -374,6 +371,17 @@ type RedBookPostsResponse struct {
|
|
|
} `json:"data"`
|
|
|
}
|
|
|
|
|
|
+type RedBookPostStats struct {
|
|
|
+ Id string `json:"id"`
|
|
|
+ CreatedAt string `json:"created_at"`
|
|
|
+ Text string `json:"text"` // 名称
|
|
|
+ Likes string `json:"likes"` // 点赞数
|
|
|
+ Comments string `json:"comments"` // 评论数
|
|
|
+ Collects string `json:"collects"` // 收藏数
|
|
|
+ CoverImageUrl string `json:"cover_image_url"` // 封面图URL(视频/文章)
|
|
|
+ VideoUrl string `json:"video_url"` // 视频地址
|
|
|
+}
|
|
|
+
|
|
|
func parseRedBookPosts(body []byte) ([]string, []string, error) {
|
|
|
var resp RedBookPostsResponse
|
|
|
if err := json.Unmarshal(body, &resp); err != nil {
|
|
@@ -397,7 +405,8 @@ func parseRedBookPosts(body []byte) ([]string, []string, error) {
|
|
|
}
|
|
|
|
|
|
// GetRedBookHistoryList 小红书历史作品列表
|
|
|
-func GetRedBookHistoryList(ctx context.Context, homePageUrl string) ([]RedBookNoteResponse, error) {
|
|
|
+func GetRedBookHistoryList(ctx context.Context, homePageUrl string) ([]*RedBookPostStats, error) {
|
|
|
+ var redBookPostStats []*RedBookPostStats
|
|
|
url := "http://120.46.92.62:6888/api/xhs/user_post"
|
|
|
method := "POST"
|
|
|
|
|
@@ -445,8 +454,30 @@ func GetRedBookHistoryList(ctx context.Context, homePageUrl string) ([]RedBookNo
|
|
|
return nil, linkErr
|
|
|
}
|
|
|
if linkList != nil {
|
|
|
- fmt.Println(linkList)
|
|
|
- fmt.Println(GetRedBookLinkDetail(ctx, linkList[2], tokenList[2]))
|
|
|
+ for i, _ := range linkList {
|
|
|
+ redBookNoteStatus, redBookNoteStatusErr := GetRedBookLinkDetail(ctx, linkList[i], tokenList[i])
|
|
|
+ if redBookNoteStatusErr != nil {
|
|
|
+ return nil, redBookNoteStatusErr
|
|
|
+ }
|
|
|
+ if redBookNoteStatus != nil {
|
|
|
+ var xhsStatus *RedBookPostStats
|
|
|
+ xhsStatus = &RedBookPostStats{}
|
|
|
+ xhsStatus.Likes = redBookNoteStatus.Data.ResponseBody.Data.Items[0].NoteCard.InteractInfo.LikedCount
|
|
|
+ xhsStatus.Comments = redBookNoteStatus.Data.ResponseBody.Data.Items[0].NoteCard.InteractInfo.CommentCount
|
|
|
+ xhsStatus.Collects = redBookNoteStatus.Data.ResponseBody.Data.Items[0].NoteCard.InteractInfo.CollectedCount
|
|
|
+ if len(redBookNoteStatus.Data.ResponseBody.Data.Items[0].NoteCard.ImageList) > 0 {
|
|
|
+ xhsStatus.CoverImageUrl = redBookNoteStatus.Data.ResponseBody.Data.Items[0].NoteCard.ImageList[0].UrlDefault
|
|
|
+ }
|
|
|
+ seconds := redBookNoteStatus.Data.ResponseBody.Data.Items[0].NoteCard.Time / 1000
|
|
|
+ nanos := (redBookNoteStatus.Data.ResponseBody.Data.Items[0].NoteCard.Time % 1000) * 1e6
|
|
|
+ // 转换为北京时间
|
|
|
+ utcTime := time.Unix(seconds, nanos).UTC()
|
|
|
+ beijingTime := utcTime.In(time.FixedZone("CST", 8*3600))
|
|
|
+ xhsStatus.CreatedAt = beijingTime.Format("2006-01-02 15:04:05")
|
|
|
+ xhsStatus.Text = redBookNoteStatus.Data.ResponseBody.Data.Items[0].NoteCard.Title
|
|
|
+ redBookPostStats = append(redBookPostStats, xhsStatus)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- return nil, nil
|
|
|
+ return redBookPostStats, nil
|
|
|
}
|