delphi/python 实现小红书xhs用户作品列表和图片/视频无水印解析

news2024/11/26 16:23:53

技术学习,请勿用与非法用途!!!

成品图

用户作品列表接口
/api/sns/web/v1/user_posted?num=30&cursor=&user_id=642bf0850000000011022c4e&image_scenes=
http Get方式,请求头需要带上x-s x-t签名验证

笔记明细接口
/api/sns/web/v1/feed
data = {"source_note_id":"64356527000000001303282b", "image_scenes":["CRD_PRV_WEBP","CRD_WM_WEBP"]}
http Post方式,请求头需要带上x-s x-t签名验证
x-s算法部分是js+python完成

unit ApiXHS;

interface
uses
  Windows, Messages, SysUtils, Classes, Forms, OverbyteIcsWSocket,
  OverbyteIcsHttpProt, OverbyteIcsSuperObject, OverbyteIcsUtils, OverbyteIcsWndControl;

type

  // 笔记信息
  PNoteItem = ^TNoteItem;
  TNoteItem = record
    user_id: string; //用户id
    nickname: string; //昵称
    avatar: string; //头像

    note_id: string;
    collected_count: Integer; //收藏数
    liked_count: Integer; //点赞数
    comment_count: Integer; //评论数
    share_count: Integer; //分享数
    create_time: TDateTime; //创建时间
    note_type: string; //类型   video -- 或 普通 normal
    title: string; //标题
    desc: string;
    image_list: TStrings; //图片列表或视频封面
    video_addr: string; //视频地址
    tag_list: TStrings;
  end;


type
  // 小红书接口--仅学习交流 请勿用于非法用途
  TXHS = class
  private
    function GetCookie: string;
    procedure SetCookie(const Value: string);

  protected
    http: TSslHttpCli;
    ssl: TSslContext;
    _a1: string;
  public
    constructor Create;
    destructor Destroy; override;

    function get_xs( 算法\/: jeomoo168 ): Boolean;
    function request(url, api, data: string; var json: ISuperObject; var s: string): Boolean; //请求
    function note_info(p: PNoteItem; var s: string): Boolean; //笔记信息
    function parse_noteId(url: string): string; //笔记链接->解析对于的笔记id
    function parse_userId(url: string): string; //作者主页链接->解析对于的用户id

    function user_posted(user_id: string;
      var cursor, s: string; var has_more: Boolean; items: TList): Boolean; //获取作者作品列表


    property cookie: string read GetCookie write SetCookie;
    property a1: string read _a1 write _a1;
  end;


procedure InitProfileItem(pi: PProfileItem);
procedure InitNoteItem(p: PNoteItem);


implementation
uses
  ShellAPI;

procedure InitProfileItem(pi: PProfileItem);
begin
  pi.user_id := '';
  pi.nickname := '';
  pi.avatar := '';
  pi.desc := '';
  pi.ipLocation := '';
  pi.follows := 0;
  pi.fans := 0;
  pi.interaction := 0;
  pi.gender := -1;
end;


procedure InitNoteItem(p: PNoteItem);
begin
  if p = nil then exit;
  p.user_id := '';
  p.nickname := '';
  p.avatar := '';

//  p.note_id := '';
  p.collected_count := 0;
  p.liked_count := 0;
  p.comment_count := 0;
  p.share_count := 0;

  p.create_time := Now;
  p.note_type := '';
  p.title := '';
  p.desc := '';
  p.video_addr := '';

  p.image_list := TStringList.Create;
  p.tag_list := TStringList.Create;
end;


//******************************************************************************
{ TXHS }

constructor TXHS.Create;
begin
  inherited;
  _a1 := '';
  ssl := TSslContext.Create(nil);
  http := TSslHttpCli.Create(nil);
  http.SslContext := ssl;
  http.Agent := 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.188';
  http.Accept := 'application/json, text/plain, */*';
  http.AcceptLanguage := 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6';
  http.Reference := 'https://www.xiaohongshu.com';
  http.ContentTypePost := 'application/json;charset=UTF-8'; 
  http.Timeout := 30; //默认30秒
end;

destructor TXHS.Destroy;
begin
  ssl.Free;
  http.Free;
  inherited;
end;

function TXHS.GetCookie: string;
begin
  Result := http.Cookie;
end;

procedure TXHS.SetCookie(const Value: string);
var
  k: Integer;
begin
  http.Cookie := Value;
  k := 1;
  _a1 := ParseTag(Value, 'a1=', ';', k);
end;

function TXHS.get_xs(api, data: string; var xs, xt, s: string): Boolean;
var
  json: ISuperObject;
begin
  Result := False;
  s := '';
  if a1 = '' then exit; //未设置cookie
 Result := js-get-xs('x-s生成算法\/: jeomoo168', s);
  try
    json := SO(s);
  except
    json := SO('{}');
  end;
  xs := json.S['X-s'];
  xt := json.S['X-t'];
end;

function TXHS.request(url, api, data: string; var json: ISuperObject; var s: string): Boolean;
var
  xs, xt: string; // , v
  buf: UTF8String;
begin
  Result := False;
  json := SO('{}');
  s := '';
  xs := '';
  xt := '';
  data := StringReplace(data, ' ', '', [rfReplaceAll]); //data字符串中不要有空格!!! 不然会请求失败
  if api <> '' then //api不为空时 需要x-s x-t
  begin
    Result := get_xs(api, EscapeChars(data), xs, xt, s);
    if not Result then exit;
  end;
                       //  xs:=''; xt:='';
  http.URL := url + api;
  http.ExtraHeaders.Clear;
  //http.ExtraHeaders.Add('Accept-Encoding: gzip, deflate'); //!!! 不能加这个 加了 返回数据被压缩了
  http.ExtraHeaders.Add('authority: edith.xiaohongshu.com');
  http.ExtraHeaders.Add('origin: https://www.xiaohongshu.com');
  http.ExtraHeaders.Add('Content-Type: application/json;charset=UTF-8');
  http.ExtraHeaders.Add('x-s: ' + xs);
  http.ExtraHeaders.Add('x-t: ' + xt);
  //http.ExtraHeaders.Add('x-s-common: ');

  http.RcvdStream := TMemoryStream.Create; // For answer
  try
    if data = '' then
      http.Get
    else
    begin
      //j2 := SO(data);
      //v := j2.S['keyword']; //搜索关键词
      //if v <> '' then
      //begin
      //  v := utf8encode(v); //关键词utf8编码
      // data := Format('{"keyword":"%s","note_type":0,"page":1,"page_size":20,"search_id":"2ci5066wl1gu6kr4q9apa","sort":"general","image_scenes":"FD_PRV_WEBP,FD_WM_WEBP"}',          [v]);
      //end;
      data := utf8encode(data);
      http.SendStream := TMemoryStream.Create;
      http.SendStream.Write(PChar(data)^, Length(data));
      http.SendStream.Position := 0; // Send from start!
      http.Post;
    end;
  except
    on e: Exception do
    begin
      s := Format('%s', [Trim(e.Message)]);
      exit;
    end;
  end;

  if http.StatusCode <> 200 then
    if not SameText(http.RequestDoneErrorStr, 'No Error') then
    begin
      s := http.RequestDoneErrorStr;
      exit;
    end;

  SetLength(buf, http.RcvdStream.Size);
  Move((http.RcvdStream as TMemoryStream).Memory^, buf[1], http.RcvdStream.Size);
  http.RcvdStream.Free;
  http.RcvdStream := nil;
  if http.SendStream <> nil then
  begin
    http.SendStream.Free;
    http.SendStream := nil;
  end;
  s := Trim(Utf8ToStringA(buf)); //utf8解码   Utf8Decode
  try
    json := SO(s);
    Result := json.B['success'];
  except
    json := SO('{}');
  end;

//  if Pos('Not Acceptable',s)>0 then     s:=s+Format('  xs=%s',[xs]);
end;


function TXHS.note_info(p: PNoteItem; var s: string): Boolean;
var
  url, api, data: string;
  json, info, vi: ISuperObject;
  va: TSuperArray;
  i: Integer;
begin
  Result := False;
  s := '';
  if p = nil then exit;
  InitNoteItem(p);
  url := 'https://edith.xiaohongshu.com';
  api := '/api/sns/web/v1/feed';
  data := '{"source_note_id":"' + p.note_id + '","image_scenes":["CRD_PRV_WEBP","CRD_WM_WEBP"]}';
  Result := request(url, api, data, json, s); //'Not Acceptable'
  if not Result then exit;
  info := json.O['data'].A['items'][0].O['note_card'];
  if info=nil then exit; //解析失败 有可能接口更新了

  p.user_id := info.O['user'].S['user_id'];
  p.nickname := info.O['user'].S['nickname'];
  p.avatar := info.O['user'].S['avatar'];

  p.collected_count := info.O['interact_info'].I['collected_count'];
  p.comment_count := info.O['interact_info'].I['comment_count'];
  p.share_count := info.O['interact_info'].I['share_count'];
  p.liked_count := info.O['interact_info'].I['liked_count'];

  p.desc := info.S['desc'];
  p.create_time := GetTime_DateTime(info.I['time']);
  p.title := info.S['title'];
  p.note_type := info.S['type'];

  // image_list
  va := info.A['image_list'];
  for i := 0 to va.Length - 1 do
  begin
    vi := va[i];
    p.image_list.Add(vi.A['info_list'][1].S['url']);
  end;

  // tag_list
  va := info.A['tag_list'];
  for i := 0 to va.Length - 1 do
  begin
    vi := va[i];
    p.tag_list.Add(vi.S['name']);
  end;

  if p.note_type = 'video' then
    p.video_addr := 'https://sns-video-bd.xhscdn.com/' + info.O['video'].O['consumer'].S['origin_video_key'];
end;

// 笔记链接->解析对于的笔记id
function TXHS.parse_noteId(url: string): string;
var
  s: string;
  json: ISuperObject;
  k: Integer;
begin
  Result := '';
  if url = '' then exit;
  if Pos('xiaohongshu.com/explore/', url) > 0 then
  begin
    k := Pos('?', url);
    if k > 0 then
      url := Copy(url, 1, k - 1);
    url := url + '/';
    k := 1;
    Result := ParseTag(url, '/explore/', '/', k);
    exit;
  end;

  // http://xhslink.com/xxxxx 格式
  try
    http.FollowRelocation := False;
    request(url, '', '', json, s);
  finally
    http.FollowRelocation := True;
  end;

  //<a href="https://www.xiaohongshu.com/discovery/item/6558d9300000000032xxxxx?app_platform=ios&amp;app_version=8.14.3&amp;share_from_user_hidden=true&amp;type=normal&amp;xhsshare=CopyLink&amp;appuid=5beaa1f00ac0a40001f2d248&amp;apptime=1700968043">Temporary Redirect</a>.
  //或
  //Redirecting to <a href="/discovery/item/6558d930000000003200698c">/discovery/item/6558d9300000000032xxxxx</a>.
  k := Pos('?', s);
  if k > 0 then
    s := Copy(s, 1, k - 1) + '"';

  k := 1;
  Result := ParseTag(s, 'discovery/item/', '"', k);
end;

// 作者主页链接->解析对于的用户id
function TXHS.parse_userId(url: string): string;
var
  k: Integer;
begin
// https://www.xiaohongshu.com/user/profile/5565692bb7ba2219xxxxx
// https://www.xiaohongshu.com/user/profile/5565692bb7ba221xxxxxx?xhsshare=CopyLink&appuid=5beaa1f00ac0a40001f2d248&apptime=1700990774
  Result := '';
  if url = '' then exit;
  if Pos('xiaohongshu.com/user/profile/', url) = 0 then exit;

  k := Pos('?', url);
  if k > 0 then
    url := Copy(url, 1, k - 1);
  url := url + '/';
  k := 1;
  Result := ParseTag(url, '/profile/', '/', k);
  exit;
end;

//获取作者作品列表
function TXHS.user_posted(user_id: string; var cursor, s: string; var has_more: Boolean; items: TList): Boolean;
var
  api, data, url: string;
  json, vi: ISuperObject;
  va: TSuperArray;
  p: PNoteItem;
  i: Integer;
begin
  items.Clear;
  has_more := False;
  url := 'https://edith.xiaohongshu.com';
  api := Format('/api/sns/web/v1/user_posted?num=30&cursor=%s&user_id=%s&image_scenes=',
    [cursor, user_id]);
  data := '';
  Result := request(url, api, data, json, s);
  if not Result then exit;
  if json.O['data'] = nil then exit;

  cursor := json.O['data'].S['cursor'];
  has_more := json.O['data'].B['has_more'];

  va := json.O['data'].A['notes'];
  for i := 0 to va.Length - 1 do
  begin
    Application.ProcessMessages;
    vi := va[i];
    New(p);
    InitNoteItem(p);
    Items.Add(p);
    p.user_id := vi.O['user'].S['user_id'];
    p.nickname := vi.O['user'].S['nickname'];
    p.avatar := vi.O['user'].S['avatar'];

    p.liked_count := vi.O['interact_info'].I['liked_count'];
    p.note_id := vi.S['note_id'];
    p.title := vi.S['display_title'];
    p.note_type := vi.S['type'];
  end;
end;

end.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1296400.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树的层序遍历[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你二叉树的根节点root&#xff0c;返回其节点值的 层序遍历 。&#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],…

【算法每日一练]-图论(保姆级教程篇12 tarjan篇)#POJ3352道路建设 #POJ2553图的底部 #POJ1236校园网络 #缩点

目录&#xff1a; 今天知识点 加边使得无向图图变成双连通图 找出度为0的强连通分量 加边使得有向图变成强连通图 将有向图转成DAG图进行dp POJ3352&#xff1a;道路建设 思路&#xff1a; POJ2553&#xff1a;图的底部 思路&#xff1a; POJ1236校园网络 思路&#x…

[ndss 2023]确保联邦敏感主题分类免受中毒攻击

Securing Federated Sensitive Topic Classification against Poisoning Attacks 摘要 我们提出了一种基于联邦学习 (FL) 的解决方案&#xff0c;用于构建能够检测包含敏感内容的 URL 的分布式分类器&#xff0c;即与健康、政治信仰、性取向等类别相关的内容。尽管这样的分类器…

docker:搭建私有仓库

文章目录 1、拉取镜像2、运行容器3、测试成功4、修改daemon.json5、重启docker 服务6、上传镜像到私有仓库6.1 标记某个镜像为私有仓库镜像6.2 上传镜像到私有仓库 其他注意项 1、拉取镜像 docker pull registry2、运行容器 docker run -di --nameregistry -p 5000:5000 regi…

3DCAT+上汽奥迪:打造新零售汽车配置器实时云渲染解决方案

在 5G、云计算等技术飞速发展的加持下&#xff0c;云渲染技术迎来了突飞猛进的发展。在这样的背景下&#xff0c;3DCAT应运而生&#xff0c;成为了业内知名的实时云渲染服务商之一。 交互式3D实时云看车作为云渲染技术的一种使用场景&#xff0c;也逐步成为一种新的看车方式&a…

HttpComponents: 领域对象的设计

1. HTTP协议 1.1 HTTP请求 HTTP请求由请求头、请求体两部分组成&#xff0c;请求头又分为请求行(request line)和普通的请求头组成。通过浏览器的开发者工具&#xff0c;我们能查看请求和响应的详情。 下面是一个HTTP请求发送的完整内容。 POST https://track.abc.com/v4/tr…

金融银行业更适合申请哪种SSL证书?

在当今数字化时代&#xff0c;金融行业的重要性日益增加。越来越多的金融交易和敏感信息在线进行&#xff0c;金融银行机构必须采取必要的措施来保护客户数据的安全。SSL证书作为一种重要的安全技术工具&#xff0c;可以帮助金融银行机构加密数据传输&#xff0c;验证网站身份&…

html中一个div中平均一行分配四个盒子,可展开与收起所有的盒子

html中一个div中平均一行分配四个盒子&#xff0c;可展开与收起所有的盒子 1.截图显示部分 2.代码展示部分 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"wid…

物易管预测性维护平台3.6.0版本上线,工况数据处理、设备故障模型、数据可视化等方面带来全新功能体验

物易管设备预测性维护平台V3.6.0版本近日正式发布上线&#xff0c;相较V3.5.0版本次主要新增优化设备工况数据接入、工况数据模型训练、数据可视化以及设备监测详情优化四个板块。新版本在处理工况数据、设备故障模型、数据分析展示以及设备监测方面带来全新的体验。 01设备工况…

【投稿】期刊选择

一、期刊影响力评价方法 只要投稿的期刊&#xff0c;被上述三个索引收录&#xff0c;那就说明该期刊的影响力是得到认可的。 二、如何选择合适的期刊 研究工作和目标期刊进行权衡。

[GXYCTF2019]禁止套娃1

提示 git泄露无参数rce &#xff01;&#xff01;注意需要python3环境 github里dirsearch工具下载位置 ###可能需要开节点才能打开 百度网盘dirsearch下载地址 ###如果github里下载不了可以在网盘下载 提取码sx5d 只给了flag在哪里呢&#xff0c;那么应该就是要让…

电子学会C/C++编程等级考试2021年09月(五级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:抓牛 农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上,农夫起始位于点N(0<=N<=100000),牛位于点K(0<=K<=100000)。农夫有两种移动方式: 1、从X移动到X-1或X+1,每次移动花费一分钟 2、从X移动到2*X,每…

Axios 拦截器实战教程:简单易懂

Axios 提供了一种称为 “拦截器&#xff08;interceptors&#xff09;” 的功能&#xff0c;使我们能够在请求或响应被发送或处理之前对它们进行全局处理。拦截器为我们提供了一种简洁而强大的方式来转换请求和响应、进行错误处理、添加认证信息等操作。在本文中&#xff0c;我…

案例063:基于微信小程序的传染病防控宣传系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder …

如何进行更好的面试回复之缓存函数在项目中的性能优化?

缓存函数是一种提高函数性能的技术&#xff0c;在函数被调用时&#xff0c;会将计算结果缓存起来&#xff0c;以便在后续的调用中直接返回缓存的结果&#xff0c;从而减少了重复计算的时间。 缓存函数的实现通常包括两个步骤&#xff1a; 判断缓存是否存在&#xff1a;在函数被…

Mysql 命令行导出SQL文件和导入文件

1-导出SQL文件 要导出 MySQL 数据库到一个 SQL 文件&#xff0c;你可以使用 mysqldump 工具&#xff0c;它是 MySQL 的一个命令行工具&#xff0c;以下是一些步骤&#xff1a; 打开终端&#xff0c;并使用以下命令来执行导出操作&#xff1a; mysqldump -u wqzbxh -h 1.137.15…

DAP数据集成与算法模型如何结合使用

企业信息化建设会越来越完善&#xff0c;越来越体系化&#xff0c;当今数据时代背景下更加强调、重视数据的价值&#xff0c;以数据说话&#xff0c;通过数据为企业提升渠道转化率、改善企业产品、实现精准运营&#xff0c;为企业打造自助模式的数据分析成果&#xff0c;以数据…

无人机巡山护林,林业无人机智能助力绿色守护

随着全球环保意识的不断提高&#xff0c;无人机巡山护林已经成为解决森林巡检难题的一种独特而高效的方式。在我国&#xff0c;各地正积极探索无人机在森林防火、病虫害监测以及生态调查等领域的创新应用。随着无人机技术的不断演进&#xff0c;其在推动森林保护和可持续发展方…

高性能和多级高可用,云原生数据库 GaiaDB 架构设计解析

1 云原生数据库和 GaiaDB 目前&#xff0c;云原生数据库已经被各行各业大规模投入到实际生产中&#xff0c;最终的目标都是「单机 分布式一体化」。但在演进路线上&#xff0c;当前主要有两个略有不同的路径。 一种是各大公有云厂商选择的优先保证上云兼容性的路线。它基于存…

工作上Redis安装及配置

下载redis软件 第一步&#xff1a;解压压缩包 tar -zxvf redis-7.0.14.tar.gz 第二步&#xff1a;移动redis存放目录&#xff08;结合个人需求而定&#xff01;&#xff09; redis-7.0.14&#xff1a;解压后的文件路径 /usr/local&#xff1a;移动后的文件路径 mv redis-7.0.…