一、搜索接口及原理
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 将信息发送到窗体显示
}