c# 通过webView2模拟登陆小红书网页版,执行关键字搜索,以及解决X-s,X-t,X-S-Common签名验证【2023年5月8日】

一、搜索接口及原理
1、搜索接口

url=https://edith.xiaohongshu.com/api/sns/web/v1/search/notes

2、数据

  var data={
    "keyword": "",        //搜索关键字:比如:风景视频
    "page": 1,              //返回第几页数据,1表示返回第一页数据;
    "page_size": 20,     //20表示一页数据有多少个笔记(视频)
    "search_id": "2blciviwfc1cdapvnknmj",        //搜索id,根据当前时间生成的唯一字符串;
    "sort": "general",                                         //排序方式:general 普通排序;
    "note_type": 0                                            //笔记类型:0表示:视频;
   };

3、请求头:

headers = {
  'authority':"edith.xiaohongshu.com",
  'method':"post",
  'Content-Type':"application/json;charset=utf8",
  'accept':"application/json, text/plain, */*",
  'accept-encoding':"gzip, deflate, br",
  'accept-language':"zh-CN,zh;q=0.9",
  "cache-control":"no-cache",
  "origin":"https://www.xiaohongshu.com",
  "pragma":"no-cache",
  "sec-ch-ua":"\"Not_A Brand\";v=\"99\", \"Google Chrome\";v=\"109\", \"Chromium\";v=\"109\"",
  "sec-ch-ua-mobile":"?0",
  "sec-ch-ua-platform":"\"Windows\"",
  "sec-fetch-dest":"empty",
  "sec-fetch-mode":"cors",
  "sec-fetch-site":"same-site",
  'cookie':"a1=186d30820a4m09cb6glhxe1aqks2olv1l97ow1gun50000408881;  web_session=040069b3b3f6625dade26f8d1d364b44f72187;  ",
  'referer':"https://www.xiaohongshu.com",
  "X-t":1683343307335,
  "X-s":"O6MKZYwvOYsLZBFisgqUZ61KsYF+OY1lsl1WZYZBsjM3",
  'X-S-Common':"2UQAPsHC+aIjqArjwjHjNsQhPsHCH0rjNsQhPaHCH0P1PjhIHjIj2eHjwjQgynEDJ74AHjIj2ePjwjQhyoPTqBPT49pjHjIj2ecjwjHUN0P1PaHVHdWMH0ijP/W98ePIweHIG/zTPeS0G08dJBYh8/bYqnTAPfRV40bVw/464Abd4nhMPeZIPecIweWhPjHVHdW9H0il+0WUw/Z9PeHlw/HlNsQh+UHCHDRd8rQOyd+P/MSLpAbk8o8AypzenfkAaAZUprql87+knDQlyMkxwnSyn/LAHjIj2eWjwjQQPAYUaBzdq9k6qB4Q4fpA8b878FSet9RQzLlTcSiM8/+n4MYP8F8LagY/P9Ql4FpUzfpS2BcI8nT1GFbC/L88JdbFyrSiafprwLMra7pFLDDAa7+8J7QgabmFz7Qjp0mcwp4fanD68p40+fp8qgzELLbILrDA+9p3JpHlLLI3+LSk+d+DJfRAyfRL+gSl4bYlqg48qDQlJFShtUTozBD6qM8FyFShPo+h4g4U+obFyLS3qd4QyaRAy9+0PFSe/B8QPFRSPopFJeHIzbkA/epSzb+t8nkn4AmQynpS2b87/sTc4BRUqgziLBMt8/8VzURQ4DzranYlpB4n4FRtqgzMagYbGMk/89pn4gcEag898p8n4bkQy9lNanVhqfEm+d+8pgbn2juM8nkn4ok6LApAL9+Up7kD+np/p9+cagWM8Lzc4rkQzLES8opFJBQCzrpwwL4oagY68pzM49l1J/+S2BzbLrS3pFRQyLYTqgPhzrSezor3zS86anS0ynpc4epQcMki+rQwqFzIa9phqgzma/+mq9kd+7+gGAFRHjIj2eDjwjFl+echPeLFPeDANsQhP/Zjw0rFKc==",
   'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
}

cookie中的
a1客户端标识,参与X-S-Common签名;web_session登陆会话标识,登陆小红书时会产生此字段,大概1个月有效。
X-t,X-s必须的,由data和url签名产生;X-S-Common必须,由a1,X-t,X-s,MINI_BROSWER_INFO_KEY共同签名产生。

4、具备以上数据后,向服务器发送post请求。

var result = winning.getPostResultEx(url, data_json, headers);

如果参数错误,将返回:

{"code":-1,"success":false}

如果未登陆或web_session失效,将返回未登陆提示;
如果请求成功,将返回第一页(或某一页)(一页20个笔记)json数据:

{"msg":"成功","data":{"has_more":true,"items":[{"model_type":"note","note_card":{"cover":{"height":1706,"width":1280,"url":"https://sns-img-qc.xhscdn.com/1000g0081jf9ote2ao04g5o8kppjg8f48774fb60","trace_id":"1000g0081jf9ote2ao04g5o8kppjg8f48774fb60","file_id":"1000g0081jf9ote2ao04g5o8kppjg8f48774fb60"},"type":"normal","display_title":"美女多多","user":{"nick_name":"雄","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/632952c77cccf41054bf74ec.jpg?imageView2/2/w/80/format/jpg","user_id":"6114ce670000000001003c88","nickname":"雄"},"interact_info":{"liked":false,"liked_count":"1805"}},"id":"6374df81000000001c036084"},{"id":"64195495000000001301092f","model_type":"note","note_card":{"type":"normal","user":{"avatar":"https://sns-avatar-qc.xhscdn.com/avatar/640142a9d27b886b440aea46.jpg?imageView2/2/w/80/format/jpg","user_id":"5cb9cc050000000017030ea4","nickname":"私拍女神","nick_name":"私拍女神"},"interact_info":{"liked":false,"liked_count":"311"},"cover":{"height":1646,"width":1234,"url":"https://sns-img-qc.xhscdn.com/1000g00827hn8oi4fm0005n5ppg2lu3l4q54q030","trace_id":"1000g00827hn8oi4fm0005n5ppg2lu3l4q54q030","file_id":"1000g00827hn8oi4fm0005n5ppg2lu3l4q54q030"}}},{"id":"6432dffc0000000012030dbf","model_type":"note","note_card":{"type":"normal","display_title":"18岁🌸还拿不下你?","user":{"nick_name":"一粒甜豆","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/642e6a60a61cee6d85ad3ec5.jpg?imageView2/2/w/80/format/jpg","user_id":"63082e80000000001501da4b","nickname":"一粒甜豆"},"interact_info":{"liked":false,"liked_count":"14965"},"cover":{"trace_id":"1000g0082algar80h40605oo85q05bmibn1pdc48","file_id":"1000g0082algar80h40605oo85q05bmibn1pdc48","height":1706,"width":1280,"url":"https://sns-img-qc.xhscdn.com/1000g0082algar80h40605oo85q05bmibn1pdc48"}}},{"note_card":{"interact_info":{"liked":false,"liked_count":"368"},"cover":{"file_id":"1000g0082cah7sh4gu0005od9do18d59mjp9nm30","height":1760,"width":1320,"url":"https://sns-img-qc.xhscdn.com/1000g0082cah7sh4gu0005od9do18d59mjp9nm30","trace_id":"1000g0082cah7sh4gu0005od9do18d59mjp9nm30"},"type":"normal","display_title":"海滩 | 粉色女孩","user":{"nick_name":"AI小影","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/6453b3f6fe6243cd283e30b0.jpg?imageView2/2/w/80/format/jpg","user_id":"61a96e020000000021029536","nickname":"AI小影"}},"id":"6440733d0000000027011028","model_type":"note"},{"id":"63d69298000000001d022f88","model_type":"note","note_card":{"interact_info":{"liked":false,"liked_count":"1681"},"cover":{"height":1920,"width":1440,"url":"https://sns-img-qc.xhscdn.com/1000g0081vcuhmp8es0005o7bmt885c21irp600o","trace_id":"1000g0081vcuhmp8es0005o7bmt885c21irp600o","file_id":"1000g0081vcuhmp8es0005o7bmt885c21irp600o"},"type":"normal","display_title":"爱烫头的40岁阿姨","user":{"nick_name":"四季奶青milk","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/6354c63051daefab555b06cd.jpg?imageView2/2/w/80/format/jpg","user_id":"60ebb750000000002002b041","nickname":"四季奶青milk"}}},{"id":"63491ee00000000006029200","model_type":"note","note_card":{"type":"normal","display_title":"美女素材","user":{"user_id":"6231c1150000000010005b32","nickname":"素材分享","nick_name":"素材分享","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/6373751ea5559e15b5803c3b.jpg?imageView2/2/w/80/format/jpg"},"interact_info":{"liked":false,"liked_count":"734"},"cover":{"width":1080,"url":"https://sns-img-qc.xhscdn.com/0302ae01ktx0duoo7ci010zhr7edmyyg2g","trace_id":"0302ae01ktx0duoo7ci010zhr7edmyyg2g","file_id":"0302ae01ktx0duoo7ci010zhr7edmyyg2g","height":1439}}},{"id":"63bd3627000000001f023037","model_type":"note","note_card":{"type":"normal","display_title":"美女图片","user":{"nickname":"疏玲玲🌸","nick_name":"疏玲玲🌸","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/53ba1722242c3e9344db7b0fc2e1005f.jpg?imageView2/2/w/80/format/jpg","user_id":"629d81f0000000002102ae28"},"interact_info":{"liked":false,"liked_count":"211"},"cover":{"file_id":"1000g0081s9s5nimeq05g5oktg7o8dbh8lujuqqg","height":1706,"width":1280,"url":"https://sns-img-qc.xhscdn.com/1000g0081s9s5nimeq05g5oktg7o8dbh8lujuqqg","trace_id":"1000g0081s9s5nimeq05g5oktg7o8dbh8lujuqqg"}}},{"id":"643367d1000000000800d696","model_type":"note","note_card":{"type":"video","user":{"nick_name":"W丽人青蓝W","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/64254adc4af4649956a01daf.jpg?imageView2/2/w/80/format/jpg","user_id":"63e5b3b5000000002702b06b","nickname":"W丽人青蓝W"},"interact_info":{"liked":false,"liked_count":"295"},"cover":{"trace_id":"1000g0082ani1ulgh00305ov5meqptc3b8j2ango","file_id":"1000g0082ani1ulgh00305ov5meqptc3b8j2ango","height":1000,"width":750,"url":"https://sns-img-qc.xhscdn.com/1000g0082ani1ulgh00305ov5meqptc3b8j2ango"}}},{"id":"642fe63b0000000013033795","model_type":"note","note_card":{"cover":{"trace_id":"1000g0082a9sckp2go05g5nsald6081vg02qt9g0","file_id":"1000g0082a9sckp2go05g5nsald6081vg02qt9g0","height":2560,"width":1920,"url":"https://sns-img-qc.xhscdn.com/1000g0082a9sckp2go05g5nsald6081vg02qt9g0"},"type":"video","display_title":"双倍快乐呀","user":{"avatar":"https://sns-avatar-qc.xhscdn.com/avatar/62b6d0693c746fe743dd31de.jpg?imageView2/2/w/80/format/jpg","user_id":"5f8aab4c00000000010007f0","nickname":"特效小哥灰豆","nick_name":"特效小哥灰豆"},"interact_info":{"liked":false,"liked_count":"2588"}}},{"id":"62d96e1c000000001200f212","model_type":"note","note_card":{"user":{"nick_name":"宋琳琳","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/603d00834b428e2d0816097b.jpg?imageView2/2/w/80/format/jpg","user_id":"603cb94a0000000001005a5a","nickname":"宋琳琳"},"interact_info":{"liked":false,"liked_count":"11048"},"cover":{"width":720,"url":"https://sns-img-qc.xhscdn.com/01027l01kohqj14olei011dwfsn1j30qxg","trace_id":"01027l01kohqj14olei011dwfsn1j30qxg","file_id":"01027l01kohqj14olei011dwfsn1j30qxg","height":961},"type":"video","display_title":"迷迷糊糊发个视频!"}},{"note_card":{"user":{"nick_name":"华籍美鱼","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/613cf784cdb961c1162ad2dd.jpg?imageView2/2/w/80/format/jpg","user_id":"5a3735774eacab0d3461e3ac","nickname":"华籍美鱼"},"interact_info":{"liked":false,"liked_count":"5218"},"cover":{"file_id":"030338016i01k7cvelo010zr9980v4zf9o","height":1440,"width":1080,"url":"https://sns-img-qc.xhscdn.com/030338016i01k7cvelo010zr9980v4zf9o","trace_id":"030338016i01k7cvelo010zr9980v4zf9o"},"type":"video","display_title":"☀️喜欢阳光"},"id":"64084a230000000013013647","model_type":"note"},{"note_card":{"user":{"nick_name":"居居","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/644bd7f61300c5cf9d3786e1.jpg?imageView2/2/w/80/format/jpg","user_id":"5f602bc70000000001004977","nickname":"居居"},"interact_info":{"liked":false,"liked_count":"10314"},"cover":{"trace_id":"1000g00827dh7psifo0005nr05f3g8ibnim0droo","file_id":"1000g00827dh7psifo0005nr05f3g8ibnim0droo","height":1440,"width":1080,"url":"https://sns-img-qc.xhscdn.com/1000g00827dh7psifo0005nr05f3g8ibnim0droo"},"type":"video","display_title":"好纯欲!"},"id":"641842140000000014025707","model_type":"note"},{"id":"640da05a0000000027000ded","model_type":"note","note_card":{"type":"normal","user":{"nick_name":"心弦","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/64128a90fc6d6dfb889f9caa.jpg?imageView2/2/w/80/format/jpg","user_id":"634290d70000000018029c84","nickname":"心弦"},"interact_info":{"liked":false,"liked_count":"467"},"cover":{"file_id":"1000g00826408ra6fm05g5oq2i3bm5744cmkaqsg","height":1706,"width":1280,"url":"https://sns-img-qc.xhscdn.com/1000g00826408ra6fm05g5oq2i3bm5744cmkaqsg","trace_id":"1000g00826408ra6fm05g5oq2i3bm5744cmkaqsg"}}},{"id":"64092a94000000001203ffab","model_type":"note","note_card":{"type":"normal","display_title":"清冷系美女","user":{"nick_name":"泡芙不芙","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/63cbba9cf49cc7b05d2f1b3f.jpg?imageView2/2/w/80/format/jpg","user_id":"615e8d45000000001f038367","nickname":"泡芙不芙"},"interact_info":{"liked":false,"liked_count":"7567"},"cover":{"file_id":"03033901krmp1p0mqg7010ij29dcem4gli","height":1706,"width":1280,"url":"https://sns-img-qc.xhscdn.com/03033901krmp1p0mqg7010ij29dcem4gli","trace_id":"03033901krmp1p0mqg7010ij29dcem4gli"}}},{"id":"6441c5440000000013009e24","model_type":"note","note_card":{"interact_info":{"liked":false,"liked_count":"48"},"cover":{"file_id":"1000g0082cfm9ivkh806g5okqirjmsguqdduudlo","height":2560,"width":1920,"url":"https://sns-img-qc.xhscdn.com/1000g0082cfm9ivkh806g5okqirjmsguqdduudlo","trace_id":"1000g0082cfm9ivkh806g5okqirjmsguqdduudlo"},"type":"video","display_title":"清纯美少女","user":{"avatar":"https://sns-avatar-qc.xhscdn.com/avatar/629a96e7000000001b0243da.jpg?imageView2/2/w/80/format/jpg","user_id":"629a96e7000000001b0243da","nickname":"心心相印","nick_name":"心心相印"}}},{"note_card":{"type":"video","user":{"nickname":"小红薯柯柯","nick_name":"小红薯柯柯","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/63dd18b7627a7faf54860ec5.jpg?imageView2/2/w/80/format/jpg","user_id":"63dd12140000000026006855"},"interact_info":{"liked_count":"29","liked":false},"cover":{"file_id":"1000g008207ot5kafi0605out28a9gq2ltnesej0","height":960,"width":720,"url":"https://sns-img-qc.xhscdn.com/1000g008207ot5kafi0605out28a9gq2ltnesej0","trace_id":"1000g008207ot5kafi0605out28a9gq2ltnesej0"}},"id":"63dd72b7000000000d015817","model_type":"note"},{"model_type":"note","note_card":{"interact_info":{"liked":false,"liked_count":"360"},"cover":{"file_id":"1000g00828n5ivcofe06g481srpi2rpg0kuh5gk0","height":2560,"width":1920,"url":"https://sns-img-qc.xhscdn.com/1000g00828n5ivcofe06g481srpi2rpg0kuh5gk0","trace_id":"1000g00828n5ivcofe06g481srpi2rpg0kuh5gk0"},"type":"video","user":{"nick_name":"似梦飞花","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/62cedd038aee919605303931.jpg?imageView2/2/w/80/format/jpg","user_id":"5713642daed758612a88e600","nickname":"似梦飞花"}},"id":"6422eaba0000000012030de9"},{"id":"642bc2dc000000001203f040","model_type":"note","note_card":{"interact_info":{"liked_count":"479","liked":false},"cover":{"height":1797,"width":1348,"url":"https://sns-img-qc.xhscdn.com/1000g00829pn3jkqh000040ci7c3bgd5fb89p5o8","trace_id":"1000g00829pn3jkqh000040ci7c3bgd5fb89p5o8","file_id":"1000g00829pn3jkqh000040ci7c3bgd5fb89p5o8"},"type":"normal","display_title":"这都拿不下你?这也太显身材了好辣妹","user":{"nick_name":"Yyyyi","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/626e51259759af6e5b3d89e1.jpg?imageView2/2/w/80/format/jpg","user_id":"551fc6b89c5a6e5a0f3d34af","nickname":"Yyyyi"}}},{"model_type":"note","note_card":{"user":{"nick_name":"天嘉✨","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/63dcb264cfead81aea1bce25.jpg?imageView2/2/w/80/format/jpg","user_id":"6333b029000000001501e7d6","nickname":"天嘉✨"},"interact_info":{"liked":false,"liked_count":"2253"},"cover":{"trace_id":"03033o01kwpjei1pgba010xx08u0ybcsrp","file_id":"03033o01kwpjei1pgba010xx08u0ybcsrp","height":769,"width":576,"url":"https://sns-img-qc.xhscdn.com/03033o01kwpjei1pgba010xx08u0ybcsrp"},"type":"video","display_title":"是谁在偷看美女睡觉"},"id":"641d56080000000011010302"},{"id":"642c0a3d0000000011013313","model_type":"note","note_card":{"display_title":"简简单单的大美女穿搭!超显身材的一套~~","user":{"nick_name":"安野萘缨","avatar":"https://sns-avatar-qc.xhscdn.com/avatar/60965f6b231383d089ecfc3d.jpg?imageView2/2/w/80/format/jpg","user_id":"5d3ff38d00000000120302cc","nickname":"安野萘缨"},"interact_info":{"liked":false,"liked_count":"1316"},"cover":{"file_id":"1000g00829qpv7hsgq0005n9vue6km0mctkmsa9g","height":1440,"width":1080,"url":"https://sns-img-qc.xhscdn.com/1000g00829qpv7hsgq0005n9vue6km0mctkmsa9g","trace_id":"1000g00829qpv7hsgq0005n9vue6km0mctkmsa9g"},"type":"video"}}]},"code":0,"success":true}

json数据中的"has_more":true表示还有下一页,可再次请求第二页数据....直到"has_more":false

5、X-s,X-t签名方法:

function sign_s(data)
{
var args = '/api/sns/web/v1/search/notes';
var t=JSON.parse(data);
var o = _sign(args, t);
//需要X-s,X-t签名算法的+v:metabycf
return o;
}

可见,X-s,X-t对搜索接口url及post数据data签名,返回结果如下:

{ X-t: 1683156321085, X-s: 'sYZksj1GsgspOjTb1BVks6FLslOU121KsBTK0YTGOgc3'}

6、X-S-Common签名方法:

  var A={
    "s0": 5,
    "s1": "",
    "x0": "1",
    "x1": "3.2.0",
    "x2": "Windows",
    "x3": "xhs-pc-web",
    "x4": "2.3.1",
    "x5": "186d30820a4m09cb6glhxe1aqks2olv1l97ow1gun50000408882",
    "x6": 1683159186565,
    "x7": "O6MKZYwvOYsLZBFisgqUZ61KsYF+OY1lsl1WZYZBsjM3",
    "x8": "I38rHdgsjopgIvesdVwgIC+oIELmBZ5e3VwXLgFTIxS3bqwErFeexd0ekncAzMFYnqthIhJeSnMDKutRI3KsYorWHPtGrbV0P9WfIi/eWc6eYqtyQApPI37ekmR1QL+5Ii6sdnosjoT5yqtXqqwYrBqoIx++GDi/sVtkIx0sxuwr4qtiIkrwIi/skcc3ICLfI3Oe0utl20DZsL5eDSJejVw0IieexVwL+PtorqthPmoeTYG/IvDDIhqTgVwOJqtuIxEcZSgeVut9IvveVVtZIklKIk8rnA6sYUqVz/5ejVtzoS5sScrWzd5eSWcPIx5eDqt/IEKeDPtIrmWhNnLGIxoeTuwlnm3exdEPIkWOIiHmqs8DICDq+FVoIiciePt5ICZh4BNsDpKexqtAI3Asjg7sWc==",
    "x9": -1675349166,
    "x10": 6
  };

其中:
s0,s1,x0,x1,x2,x3,x4固定;
x5即是cookie中的a1字段;
x6既是X-t;
x7既是X-s;
x8既是MINI_BROSWER_INFO_KEY客户端浏览器信息标识;
x9为:x6,x7,x8签名产生:

  var e=A["x6"]+A["x7"]+A["x8"];
  var mcr1=mcr(e);
  var res=mcr1(e);
  A['x9']=res;

X-S-Common为A字符串的base64编码:

  var str = JSON.stringify(A);
  var base = new BASE64();
  var comm= base.encode(str);
  //需要X-S-Common签名算法的+v:metabycf

二、c# WebView2简介


  Microsoft Edge WebView2 控件允许在本机应用中嵌入 web 技术(HTML、CSS 以及 JavaScript)。 WebView2 控件使用 Microsoft Edge 作为绘制引擎,以在本机应用中显示 web 内容。
虽说无法跨平台,但是在windows应用下做为原浏览器控件替代品还是不错的。

安装webview2
   打开NuGet,搜索WebView2,安装之后,可以看到左侧就有了webview2控件,可以直接拖到窗体内。

三、通过c# WebView2获取cookie信息的方法:


private Dictionary<string, string> mCookies = new Dictionary<string, string>();//保存Cookie到字典中

/// <summary>
        /// WebView2异步获取cookie
        /// </summary>
        /// <param name="url">与cookie关联的域名</param>
        private async void getCookie(string url)
        {
            List<CoreWebView2Cookie> cookieList = await webView.CoreWebView2.CookieManager.GetCookiesAsync(url);
            mCookies.Clear();
            for (int i = 0; i < cookieList.Count; ++i)
            {
                CoreWebView2Cookie cookie = webView.CoreWebView2.CookieManager.CreateCookieWithSystemNetCookie(cookieList[i].ToSystemNetCookie());
                mCookies.Add(cookie.Name, cookie.Value);
            }

        }

        /// <summary>
        /// 提取cookie中的一个字段;
        /// </summary>
        /// <param name="url">域名</param>
        /// <param name="key">关键字,如:web_session</param>
        /// <param name="t">延时(没用到)</param>
        /// <returns></returns>
        public string getCookieEx(string url, string key, int t)
        {
            getCookie(url);
            if (mCookies.ContainsKey(key))
            {
                string cookies = "";
                foreach (var cookie in mCookies)
                {
                    cookies += cookie.Key + "=" + cookie.Value + ";";
                }
                cookies = key + "=" + mCookies[key];
                return cookies;
            }


            return null;
        }

四、WebView2中C#和JavaScript代码互操作

1.需要创建一个ScriptHost对象,并注册到WebView2中:

   /// <summary>
    /// 网页调用C#方法
    /// </summary>
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    public class ScriptHost

2.在WebView2初始化完成事件中注册ScriptHost对象

   /// <summary>
        /// CoreWebView2初始化完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        private void webView_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
        {
            //注册winning,winasync脚本c#互操作
            webView.CoreWebView2.AddHostObjectToScript("scriptHost", scriptHost);
            //注册全局变量winning 同步操作;winasync:异步操作;
            webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("var winasync= window.chrome.webview.hostObjects.scriptHost;");
            webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("var winning= window.chrome.webview.hostObjects.sync.scriptHost;");
        }

3.ScriptHost中暴露的公共方法,都可以在前端JavaScript中调用:

        /// <summary>
        /// 日志记录(JavaScript前端调用
        /// </summary>
        /// <param name="message">JavaScript前端信息</param>
        public void log(string message)
        {
            Log.i(message);//记录到文本文件中
            //MessageBox.Show(message);
        }
//JavaScript 代码..............................................
winning.log(data);//JavaScript端调用(同步调用);

五、与javascript交互

using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using System.Threading;
using System.Security.Policy;
using System.Net.Http;
using System.Net.Http.Headers;

namespace XhsVideo
{
    /// <summary>
    /// 网页调用C#方法,需要完整源码的+v:metabycf
    /// </summary>
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    public class ScriptHost
    {
        
        private WebView2 webView;
        private Dictionary<string, string> mCookies = new Dictionary<string, string>();//保存Cookie到字典中
        private string mUrl;
        public static ScriptHost scriptHost = null;
        private ScriptHost(WebView2 webView)
        {
            scriptHost = this;
            this.webView = webView;
            //注册事件侦听,加载页面完成时,获取cookie;
            this.webView.NavigationCompleted += new System.EventHandler<Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs>(this.webView_NavigationCompleted);
            //webView初始化完成后注册与JavaScript交互
            this.webView.CoreWebView2InitializationCompleted += new System.EventHandler<Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs>(this.webView_CoreWebView2InitializationCompleted);
        }
        public static ScriptHost GetScriptHost(WebView2 webView)
        {
            if (scriptHost == null) scriptHost = new ScriptHost(webView);
            return scriptHost;
        }
        /// <summary>
        /// 日志记录(JavaScript前端调用
        /// </summary>
        /// <param name="message">JavaScript前端信息</param>
        public void log(string message)
        {
            Log.i(message);//记录到文本文件中
            //MessageBox.Show(message);
        }
        /// <summary>
        /// 提取cookie中的一个字段;
        /// </summary>
        /// <param name="url">域名</param>
        /// <param name="key">关键字,如:web_session</param>
        /// <param name="t">延时(没用到)</param>
        /// <returns></returns>
        public string getCookieEx(string url, string key, int t)
        {
            getCookie(url);
            if (mCookies.ContainsKey(key))
            {
                string cookies = "";
                foreach (var cookie in mCookies)
                {
                    cookies += cookie.Key + "=" + cookie.Value + ";";
                }
                cookies = key + "=" + mCookies[key];
                //cookies = "web_session=040069b3b3f6625dade26f8d1d364b44f72187";
                return cookies;
            }


            return null;
        }
        /// <summary>
        /// WebView2异步获取cookie
        /// </summary>
        /// <param name="url">与cookie关联的域名</param>
        private async void getCookie(string url)
        {
            List<CoreWebView2Cookie> cookieList = await webView.CoreWebView2.CookieManager.GetCookiesAsync(url);
            mCookies.Clear();
            for (int i = 0; i < cookieList.Count; ++i)
            {
                CoreWebView2Cookie cookie = webView.CoreWebView2.CookieManager.CreateCookieWithSystemNetCookie(cookieList[i].ToSystemNetCookie());
                mCookies.Add(cookie.Name, cookie.Value);
            }

        }
        public string getCookie()
        {
            string cookies = "";
            foreach (var cookie in mCookies)
            {
                cookies += cookie.Key + "=" + cookie.Value + ";";
            }
            return cookies;
        }
        /// <summary>
        /// 记录由JavaScript返回的信息
        /// </summary>
        /// <param name="text"></param>
        public void getJsResult(string text)
        {
            fMainForm.GetFMainForm().syncContext.Send(fMainForm.GetFMainForm().SetTextSafePost, text);//Post 将信息发送到窗体显示


        }
        //---------------------------------------------------------socket--------------------------------------------------------------------------------
        /// <summary>
        /// 重定向链接,该方法由JavaScript调用;
        /// </summary>
        /// <param name="url"></param>
        /// <param name="domain"></param>
        /// <param name="ua"></param>
        /// <returns></returns>
        public string getRedirectedUrl(string url, string domain, string ua)
        {
            return NetHelper.getRedirectedUrl(url, domain, ua);
        }
        /// <summary>
        /// 提交post请求,该方法由JavaScript调用;
        /// </summary>
        /// <param name="url"></param>
        /// <param name="args"></param>
        /// <param name="headers"></param>
        /// <returns></returns>
        public string getPostResultEx(string url, string args, string headers)
        {
            log(url);
            log(args);
            log(headers);
            return NetHelper.getPostResult(url, args, headers);
        }
        //-----------------------------------------------------------事件---------------------------------------------------------------------------
        /// <summary>
        /// 加载完页面时,获取cookie
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void webView_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
        {

            string url = webView.Source.Host.ToString();
            url = "https://" + url;
            getCookie(url);
        }
        /// <summary>
        /// CoreWebView2初始化完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        private void webView_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
        {
            //注册winning,winasync脚本c#互操作
            webView.CoreWebView2.AddHostObjectToScript("scriptHost", scriptHost);
            //注册全局变量winning 同步操作;winasync:异步操作;
            webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("var winasync= window.chrome.webview.hostObjects.scriptHost;");
            webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("var winning= window.chrome.webview.hostObjects.sync.scriptHost;");
        }
    }
}


六、网络访问组件


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using System.Windows.Forms;

namespace XhsVideo
{
    /// <summary>
    /// 网络访问组件
    /// </summary>
    internal class NetHelper
    {

        #region 文件下载
        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="url">文件下载地址</param>
        /// <param name="savePath">本地保存路径+名称</param>
        /// <param name="downloadCallBack">下载回调(总长度,已下载,进度)</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static async Task DownloadFileAsync(string url, string savePath, Action<string> downloadCallBack = null)
        {
            try
            {
                downloadCallBack?.Invoke($"文件【{url}】开始下载!");
                HttpResponseMessage response = null;
                using (HttpClient client = new HttpClient())
                    response = await client.GetAsync(url);
                if (response == null)
                {
                    downloadCallBack?.Invoke("文件获取失败");
                    return;
                }
                var total = response.Content.Headers.ContentLength ?? 0;
                var stream = await response.Content.ReadAsStreamAsync();
                var file = new FileInfo(savePath);
                using (var fileStream = file.Create())
                using (stream)
                {
                    if (downloadCallBack == null)
                    {
                        await stream.CopyToAsync(fileStream);
                        downloadCallBack?.Invoke($"文件【{url}】下载完成!");
                    }
                    else
                    {
                        byte[] buffer = new byte[1024];
                        long readLength = 0;
                        int length;
                        double temp = 0;
                        string msg = "";
                        while ((length = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                        {
                            // 写入到文件
                            fileStream.Write(buffer, 0, length);

                            //更新进度
                            readLength += length;
                            double progress = Math.Round((double)readLength / total * 100, 2, MidpointRounding.AwayFromZero);//.ToZero

                            if ((progress % 1) == 0 && (progress % 1) != temp)
                            { 
                                msg = $"总大小:【{total}】,已下载:【{readLength}】,进度:【{progress}】";
                                downloadCallBack?.Invoke(msg);
                            }
                            temp = progress % 1;
                            //下载完毕立刻关闭释放文件流
                            if (total == readLength && progress == 100)
                            {
                                fileStream.Close();
                                fileStream.Dispose();
                                msg = $"总大小:【{total}】,已下载:【{readLength}】,进度:【{progress}】下载完成。";
                                downloadCallBack?.Invoke(msg);
                            }


                        }
                    }
                }
            }
            catch (Exception ex)
            {
                downloadCallBack?.Invoke($"下载文件失败:{ex.Message}!");
            }
        }
        #endregion




        /// <summary>
        /// 异步下载文件
        /// </summary>
        /// <param name="url"></param>
        /// <param name="filename"></param>
        public static async void downloadfileAsync(string url,string filename)
        {
            using (var web = new WebClient())
            {
                await web.DownloadFileTaskAsync(url, filename);
            }
        }
      
        /// <summary>
        /// 地址重定向
        /// </summary>
        /// <param name="url">原地址</param>
        /// <param name="domain">域名</param>
        /// <param name="ua">userAgent</param>
        /// <returns>重定向后的地址</returns>
        public static string getRedirectedUrl(string url, string domain, string ua)
        {
            var str = getRedirectedUrl_T(url);
            return str.Result;
        }
        /// <summary>
        /// 异步地址重定向
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private static async Task<string> getRedirectedUrl_T(string url)
        {
            try
            {
                var handler = new HttpClientHandler()
                {
                    AllowAutoRedirect = false
                };
                var client = new HttpClient(handler);

                var response = await client.GetAsync(url).ConfigureAwait(continueOnCapturedContext: false);
                int statuscode = (int)response.StatusCode;
                if (statuscode == 307)
                {
                    string location = response.Headers.Location.ToString();
                    return location;
                }
                if (statuscode == 302)
                {
                    string location = response.Headers.Location.ToString();
                    return location;
                }
                else
                {
                    return "";
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine("getRedirectedUrl_T:" + e.ToString());
                return "";
            }
        }
        //---------------------------------------------------------------post---------------------------------------------------------------------
        /// <summary>
        /// Post访问
        /// </summary>
        /// <param name="url">访问地址</param>
        /// <param name="args">数据</param>
        /// <param name="headers">HTTP头</param>
        /// <returns>返回服务器JSON数据</returns>
        public static string getPostResult(string url, string args, string headers)
        {
            var str = getPostResult_T(url, args, headers);
            return str.Result;
        }
        /// <summary>
        /// 异步post调用
        /// </summary>
        /// <param name="url"></param>
        /// <param name="args"></param>
        /// <param name="headers"></param>
        /// <returns></returns>
        private static async Task<string> getPostResult_T(string url, string args, string headers)
        {
            try
            {
                var handler = new HttpClientHandler() { UseCookies = false };
                var client = new HttpClient(handler);// { BaseAddress = baseAddress };
                client.Timeout = TimeSpan.FromSeconds(20);
                var message = new HttpRequestMessage(HttpMethod.Post, url);
                message.Content = new StringContent(args);
                message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                //message.Headers;
                Dictionary<string, string> dictionary = ParseToDictionary(headers);
                foreach (var pair in dictionary)
                {
                    if (pair.Key.Equals("content-type")) continue;
                    if (pair.Key.Equals("Content-Type")) continue;
                    message.Headers.Add(pair.Key, pair.Value);
                }

                var result = await client.SendAsync(message).ConfigureAwait(continueOnCapturedContext: false);
                result.EnsureSuccessStatusCode();
                return await result.Content.ReadAsStringAsync();
            }
            catch (Exception e)
            {
                //EventLog.GetEventLogs(e.ToString);
                System.Diagnostics.Debug.WriteLine("getPostResultEx_T:" + e.ToString());
                ///console.write(e.ToString());
                return "";
            }
        }
        
        /// <summary>
        /// 字符串解析为字典数据
        /// </summary>
        /// <param name="str">以回车换行符分割的字符串</param>
        /// <returns>字典数据</returns>
        private static Dictionary<string, string> ParseToDictionary(string str)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            string str1 = str;
            int i = str1.IndexOf("\r\n");
            int j = 0;
            while (i > 0)
            {
                string str2 = str1.Substring(0, i);
                j = str2.IndexOf(":");
                if (j > 0)
                {
                    string str21 = str2.Substring(0, j);
                    string str22 = str2.Substring(j + 1);
                    result.Add(str21, str22);
                }
                str1 = str1.Substring(i + 2);
                i = str1.IndexOf("\r\n");
            }
            j = str1.IndexOf(":");
            if (j > 0)
            {
                string str21 = str1.Substring(0, j);
                string str22 = str1.Substring(j + 1);
                result.Add(str21, str22);
            }
            return result;
        }
        //-------------------------------------------------------------------GET -------------------------------------------------------------------
        /// <summary>
        /// 获取服务器端的HTML代码
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string getHtmlCode(string url)
        {
            var str = getHtmlCode_T(url);
            return str.Result;
        }
        /// <summary>
        /// 异步获取服务器端的HTML代码
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static async Task<string> getHtmlCode_T(string url)
        {
            try
            {
                var http = new HttpClient();
                http.Timeout = TimeSpan.FromSeconds(10);
                var result = await http.GetStringAsync(url).ConfigureAwait(continueOnCapturedContext: false);
                return result;
            }
            catch (Exception e)
            {
                //EventLog.GetEventLogs(e.ToString);
                System.Diagnostics.Debug.WriteLine("错误信息在这儿:" + e.ToString());
                ///console.write(e.ToString());
                Log.i(e.ToString());
                return "";
            }
        }

    }
}

七、使用方法:

    private void btnSearch_Click(object sender, EventArgs e)
        {
          
            //string keyword = "风景视频";   搜索关键字
            string keyword = tbUrl.Text.Trim();
            //string cookie = scriptHost.getCookie();   
            string jscode =  Config.jscode;     //javaScript代码

            webView.CoreWebView2.ExecuteScriptAsync(jscode);   //运行js代码
        }

        //js返回信息(该方法由js调用,需要js文件的+v:metabycf
        public void getJsResult(string text)
        {
            fMainForm.GetFMainForm().syncContext.Send(fMainForm.GetFMainForm().SetTextSafePost, text);//Post 将信息发送到窗体显示


        }

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值