package service import ( "context" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "strconv" "strings" "time" ) // RedBookNoteResponse 小红书接收结构体 type RedBookNoteResponse struct { Code int `json:"code"` Data struct { ResponseBody struct { Data struct { Items []struct { NoteCard struct { 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"` } `json:"note_card"` } `json:"items"` } `json:"data"` } `json:"response_body"` } `json:"data"` } // WeiboPostResponse 微博接收结构体 type WeiboPostResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data struct { AttitudesCount int `json:"attitudes_count"` // 点赞数 CommentsCount int `json:"comments_count"` // 评论数 RepostsCount int `json:"reposts_count"` // 转发数 ReadsCount int `json:"reads_count"` // 浏览数 } `json:"data"` } // parseNoteStats 小红书接收方法 func parseNoteStats(jsonData []byte) (likes, comments, collects, shares string, err error) { var resp RedBookNoteResponse if err := json.Unmarshal(jsonData, &resp); err != nil { return "", "", "", "", fmt.Errorf("JSON解析失败: %v", err) } // 检查数据是否存在 if len(resp.Data.ResponseBody.Data.Items) == 0 { return "", "", "", "", errors.New("未找到笔记数据") } stats := resp.Data.ResponseBody.Data.Items[0].NoteCard.InteractInfo return stats.LikedCount, stats.CommentCount, stats.CollectedCount, stats.ShareCount, nil } // parseWeiboStats 微博接收方法 func parseWeiboStats(jsonData []byte) (likes, comments, reposts, reads string, err error) { var resp WeiboPostResponse // 解析JSON if err := json.Unmarshal(jsonData, &resp); err != nil { return "", "", "", "", fmt.Errorf("JSON解析失败: %v", err) } // 检查状态码(微博通常用 code=200 表示成功) if resp.Code != 200 { return "", "", "", "", fmt.Errorf("API返回错误: %s", resp.Msg) } // 返回数值(转为字符串,保持与原函数一致) return strconv.Itoa(resp.Data.AttitudesCount), strconv.Itoa(resp.Data.CommentsCount), strconv.Itoa(resp.Data.RepostsCount), strconv.Itoa(resp.Data.ReadsCount), nil } // GetRedBookLinkDetail 获取小红书链接数据详情 func GetRedBookLinkDetail(ctx context.Context, shareText string) (int, int, int, int, error) { url := "http://120.46.92.62:6888/api/xhs/note_detail" method := "POST" // 构造请求体结构 requestBody := struct { ShareText string `json:"share_text"` Proxy string `json:"proxy"` }{ ShareText: shareText, Proxy: "", } jsonData, err := json.Marshal(requestBody) if err != nil { fmt.Println("JSON编码失败:", err) return 0, 0, 0, 0, err } payload := strings.NewReader(string(jsonData)) client := &http.Client{} req, err := http.NewRequest(method, url, payload) if err != nil { // fmt.Println(err) return 0, 0, 0, 0, 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 } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { // fmt.Println(err) return 0, 0, 0, 0, err } fmt.Println(string(body)) // 提取互动数据 likes, comments, collects, shares, err := parseNoteStats(body) if err != 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 like, comment, collect, share, nil } // GetWeiBoLinkDetail 获取微博链接数据详情 func GetWeiBoLinkDetail(ctx context.Context, shareText string) (int, int, int, int, error) { url := "http://120.46.92.62:6888/api/weibo/post_detail" method := "POST" fmt.Println(shareText) requestBody := struct { Id string `json:"id"` ShareText string `json:"share_text"` Proxy string `json:"proxy"` }{ Id: "", ShareText: shareText, Proxy: "", } jsonData, err := json.Marshal(requestBody) if err != nil { fmt.Println("JSON编码失败:", err) return 0, 0, 0, 0, err } payload := strings.NewReader(string(jsonData)) client := &http.Client{} req, err := http.NewRequest(method, url, payload) fmt.Println(req) if err != nil { //fmt.Println(err) return 0, 0, 0, 0, err } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err != nil { fmt.Println(err) return 0, 0, 0, 0, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { fmt.Println(err) return 0, 0, 0, 0, err } // fmt.Println(string(body)) likes, comments, collects, shares, err := parseWeiboStats(body) if err != 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 like, comment, collect, share, nil } // WeiboPostsResponse 微博多条数据接收结构体 type WeiboPostsResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data 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"` // 转发数 } `json:"retweeted_status"` } `json:"mblog"` } `json:"cards"` } `json:"data"` } `json:"data"` } // WeiboPostStats 单条微博的互动数据 type WeiboPostStats struct { Id string `json:"id"` CreatedAt string `json:"created_at"` Text string `json:"text"` // 名称 Likes int `json:"likes"` // 点赞数 Comments int `json:"comments"` // 评论数 Reposts int `json:"reposts"` // 转发数 CoverImageUrl string `json:"cover_image_url"` // 封面图URL(视频/文章) VideoUrl string `json:"video_url"` // 视频地址 } // parseWeiboPosts 解析微博多条数据 func parseWeiboPosts(body []byte) ([]WeiboPostStats, int, error) { var resp WeiboPostsResponse if err := json.Unmarshal(body, &resp); err != nil { return nil, 0, fmt.Errorf("JSON解析失败: %v", err) } if resp.Code != 200 { return nil, 0, fmt.Errorf("API返回错误: %s", resp.Msg) } var posts []WeiboPostStats var count int count = 0 for _, card := range resp.Data.Data.Cards { if count >= 5 { // 最多取5条 break } 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, }) count++ } return posts, count, nil } // GetWeiBoHistoryList 微博历史作品数据 func GetWeiBoHistoryList(ctx context.Context, homePageUrl string) ([]WeiboPostStats, error) { url := "http://120.46.92.62:6888/api/weibo/user_post" // 替换为实际API地址 method := "POST" requestBody := struct { Uid string `json:"uid"` ShareText string `json:"share_text"` SinceId string `json:"since_id"` Proxy string `json:"proxy"` }{ ShareText: homePageUrl, Proxy: "", } client := &http.Client{} jsonData, err := json.Marshal(requestBody) if err != nil { fmt.Println("JSON编码失败:", err) return nil, err } payload := strings.NewReader(string(jsonData)) req, err := http.NewRequest(method, url, payload) if err != nil { fmt.Println(err) return nil, err } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err != nil { fmt.Println(err) return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { fmt.Println(err) return nil, err } historyData, historyCount, historyErr := parseWeiboPosts(body) if historyErr != nil { fmt.Println(historyErr) } fmt.Println(historyCount) return historyData, nil } // RedBookPostsResponse 小红书多条数据接收结构体 type RedBookPostsResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data struct { ResponseBody struct { Data struct { Note []struct { NoteId string `json:"note_id"` XsecToken string `json:"xsec_token"` } `json:"notes"` } `json:"data"` } `json:"response_body"` } `json:"data"` } func parseRedBookPosts(body []byte) ([]string, []string, error) { var resp RedBookPostsResponse if err := json.Unmarshal(body, &resp); err != nil { return nil, nil, fmt.Errorf("JSON解析失败: %v", err) } if resp.Code != 200 { return nil, nil, fmt.Errorf("API返回错误: %s", resp.Msg) } var linkList []string var secTokenList []string for i, card := range resp.Data.ResponseBody.Data.Note { if i >= 5 { // 只取前5条 break } linkList = append(linkList, card.NoteId) secTokenList = append(secTokenList, card.XsecToken) } return linkList, secTokenList, nil } // RedBookNoteResponseByNoteId 小红书链接接收结构体 type RedBookNoteResponseByNoteId struct { Code int `json:"code"` Data struct { ResponseBody 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"` } `json:"response_body"` } `json:"data"` } // parseNoteStats 小红书接收方法 func parseNoteStatsByNoteId(jsonData []byte) (respData *RedBookNoteResponseByNoteId, err error) { var resp *RedBookNoteResponseByNoteId resp = &RedBookNoteResponseByNoteId{} if err := json.Unmarshal(jsonData, &resp); err != nil { return nil, fmt.Errorf("JSON解析失败: %v", err) } // 检查数据是否存在 if len(resp.Data.ResponseBody.Data.Items) == 0 { return nil, errors.New("未找到笔记数据") } return resp, nil } // GetRedBookLinkDetailByNoteId 根据noteID获取小红书链接数据详情 func GetRedBookLinkDetailByNoteId(ctx context.Context, noteId string, xSecToken string) (*RedBookNoteResponseByNoteId, error) { url := "http://120.46.92.62:6888/api/xhs/note_detail" method := "POST" // 构造请求体结构 requestBody := struct { NoteId string `json:"note_id"` XSecToken string `json:"xsec_token"` ShareText string `json:"share_text"` Proxy string `json:"proxy"` }{ NoteId: noteId, XSecToken: xSecToken, Proxy: "", } jsonData, err := json.Marshal(requestBody) if err != nil { fmt.Println("JSON编码失败:", err) return nil, err } payload := strings.NewReader(string(jsonData)) client := &http.Client{} req, err := http.NewRequest(method, url, payload) if err != nil { 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 { return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } // fmt.Println(string(body)) // 提取互动数据 redBookNoteStatus, redBookNoteStatusErr := parseNoteStatsByNoteId(body) if redBookNoteStatusErr != nil { //fmt.Println("解析互动数据失败:", err) return nil, err } 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 } 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"` // 视频地址 } // GetRedBookHistoryList 小红书历史作品列表 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" requestBody := struct { UserId string `json:"user_id"` Cursor string `json:"cursor"` Proxy string `json:"proxy"` }{ UserId: homePageUrl, Proxy: "", } client := &http.Client{} jsonData, err := json.Marshal(requestBody) if err != nil { fmt.Println("JSON编码失败:", err) return nil, err } payload := strings.NewReader(string(jsonData)) req, err := http.NewRequest(method, url, payload) if err != nil { fmt.Println(err) return nil, err } req.Header.Add("Cookie", "abRequestId=023e90e1-a95a-58db-830a-d66c0cc67f60; a1=19566f0f66eglh42hy8peqvpak3xqglp49289p8xb50000109075; webId=d189df044ca380c74b5b97bc300db5a1; gid=yj2KKiyyyY4Yyj2KKi8iKjUYKdk1x4JxvYUdJCDAhU0TqY28CAk1Uj888y8j8W288q4Jif4i; acw_tc=0a4a949617517192833111453ec142bc6ab7f9b09590524b2d59d413f0ffb7; websectiga=a9bdcaed0af874f3a1431e94fbea410e8f738542fbb02df1e8e30c29ef3d91ac; sec_poison_id=9bc0c279-b964-4448-87df-d08559819919; web_session=0400698c340cec15e62be2555c3a4b29a1365a; xsecappid=xhs-pc-web; webBuild=4.71.0; loadts=1751719579486; unread={%22ub%22:%22685be6260000000015023bc6%22%2C%22ue%22:%2268617298000000001c0337aa%22%2C%22uc%22:23}") req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err != nil { fmt.Println(err) return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { fmt.Println(err) return nil, err } linkList, tokenList, linkErr := parseRedBookPosts(body) if linkErr != nil { return nil, linkErr } if linkList != nil { for i, _ := range linkList { redBookNoteStatus, redBookNoteStatusErr := GetRedBookLinkDetailByNoteId(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 redBookPostStats, nil }