以下内容仅用于技术交换和提高 如有侵权 私信我 我会立即删除
最近在找工作,写写文章记载点能力吧,要否则就真是菜鸟了,我劝以后的萌新也 没事儿研究时写写记载一下 都是吹牛的资源.
前些年在抖音做短视频小说 SD AI绘画 语音合成了 虽然全系列做成了一个统一软件可以比现在市场很多类似的优秀,但是无奈举债多,没有能力再创业了,所以分享一下内容给后来朋友们,后续可能会分享一下抖音剪映语音辨认的接口流程,和抖音豆包AI 全链路API,白嫖算力,看各人热情吧,原业务是抓取知乎的盐故事及时上新内容, 来为做小说创作的朋友们提供一个小说热度帅选的功能.主要维度有两个一个是点赞 一个是评论量,很简单抓取汗青和秒更上新便是.
写了个GUI 界面貌寝勿喷 但是功能实用,这里主要是为了先容知乎的算法的.下面进入正题
抓取之前先先容下大概思路和用到的工具操作流程
知乎的app 很多内容照旧直接使用的web 只是知乎在app中自界说了浏览器头,辨认浏览器让一些内容显示和屏蔽
知乎的web 隐蔽了很多app 的内容但是却暴漏了他们的一些算法缺陷,让我们可以借助web加密来和通过app的api
下面我们将在手机端抓包知乎 使用的工具 有HttpCanary ,VMOS Pro 和 知乎APP
HttpCanary 俗称小黄鸟是手机端的抓包工具,有的时间比pc上的软件好用,情况证书安装 何使用啥的自己去搜吧,
VMOS Pro 是虚拟机可以模仿一个完备的手机系统 这里模仿我们需要留意选择模仿安卓7.1的 64位 这个虚拟机还支持root 一键安装xposed 再加上Frida 什么的爽的不要
为什么要这么做呢就是高版本安卓系统修改加强了ssl 体系认证 导致三方证书无法方便正常使用 ,
如果直接在原机器安装后抓包 抓包工具是无发抓取到知乎任何数据的,
上面这套下来基本市场9成的app都可以抓到了(之前我测试微信竟然可以),但是某些大厂照旧抓不到的 比如抖音 他自己重写了底层网络内容在so里 包罗一些ssl证书认证的内容,可以绕开虚拟机直接和外部通讯.纵然你外部有抓包工具也抓不到,
所以说知乎技术有待提高. 另外再次声明一下 我抓取的知乎数据访问不高哈,不会对服务器造成影响,只是模仿人类正常访问 然后搜集信息用于热度分析,别给我发律师函
看我标题为是那么要借助浏览器插件hook呢,原因很简单,
代码成本低和逆向成本相对低,
操作成本低(有时间测试和测试逆向过成 我们可以动态修改代码 刷新一下就直接进入浏览器情况了,如果用其他方式替换js可能需要借助抓包工具,或者自己写转发抓包拦截工具,比力麻烦)
只需要一个焦点插装就把问题解决了,插件自己 其实也可以直接举行数据处理和网络请求 但是为了节流资源就只有一个浏览器了.不过确实也可以这么做,根据具体场景自己选吧
还有就是为了测试这个方案的可行性,原因是知乎核默算法中包罗很多浏览器情况的信息,所以这是综合选择的结果,所以提取hook他的算法做成服务器,
这样也还可以使加密解耦合 为更大范围的抓取做服务支持如果算法有变 我们都不用改爬虫源代码只要改加密服务器就可以了,不过知乎更新并不是很勤快,用了1年多了这个方法,而且这个方法貌似防爬机制都触发的概率比力低,
来上两章手机截图吧 不一步步演示了
下边这张图就是 抓包到的数据了 我圈出的api 就是我们目标的api 我筛选的是一些点赞 评论和简洁的信息 其他自己
分析吧,我只是做信息集会,
点进去 切换到请求那一栏 所有请求信息都能看到了 多抓几次你会发现真正需要的和变化参数没几个
第一眼肯定是懵逼,多试几次会,然后再结合分享去的web端网页比如 https://www.zhihu.com/xen/market/remix/paid_column/1824095654672261120
1824095654672261120末了一串是小说集 的id
你会发现请求头直接 6个参数就可以了
- baseurl = "https://api.zhihu.com/pluton/products/" + ids + "/paid_column/section?offset=" + str( offset) + "&limit=100&order_by=global_idx"
- headers = {
- "X-Requested-With": "Fetch",
- # "X-Zse-96": "2.0_MbKocVal+L2oCq1+4AgxlJU7kBqZd==1/ES8UyXtgHtxbdF1=iCTvBPo3cXLwsEc",
- "x-zse-96": "2.0_" + black_code,
- "x-zse-93": os_vr, # 不可修改
- # user-agent 不重要 可以随意发送命令
- "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
- "accept": "*/*",
-
- "cookie": cookie
- }
复制代码 x-zse-96 其中 black_code 是加密值 前边2.0固定版本 我上图是版的显示是1.0 而网页时2.0
x-zse-93 的 os_vr 是情况的版本 这里由于跟的web版本不一样我们选择从浏览器那边动态获取
cookie 是web情况初始化后的辨认信息,不是重点 也从浏览器情况获取就可以了
醒醒啦 主要任务来了
第一步 分析知乎的页面找到算法验证的关键点
在浏览器器调试源码里 找到 vendors.2d292cce3f0653fa0e5e.js
然后搜索 __g._encrypt 结果会出来两个 至于怎么找 不在本次教程范围内 新手可以看我一上篇
能搜索出来两个结果 这里我们只用到第一个 断点刷新一下你们就知道了,这是对上边代码中 baseurl 举行加密的算法
知乎此处比力弱了的点是把这种 关键点api 的值输入和输出 直接暴露了,虽然也是闭包存在,但是我们直接改这里代码就可以修改玉成局可用的算法,其实可以学学抖音 再加密肴杂一次 就不好找了 他这种应该vue编译出来直接上线了那种 毫无防护手段 作为大厂 照旧很不测的写个小工具源代码都能逆向出来
我们修改方案
- function(d){window._encrypt=__g._encrypt;return __g._encrypt(encodeURIComponent(d))})
复制代码 这样 就把加密算法 提取出来了 时间成本极低 然后 我们就可以用 window._encrypt全局方法做文章了
第二步 写一个浏览器插件 在知乎提取出关键js 放进去 修改指定关键行 然后替换
新建文件夹 zhihuchajian 这个文件夹地址 一会儿 浏览器 需要用到
然后按照上述目次创建文件布局
下面我给出文件内容
hoook_manager.js
- chrome.webRequest.onBeforeRequest.addListener(
- // onBeforeRequest 可见 是再浏览器请求前替换
- function(details) {
- const url = details.url;
- if (url === 'https://static.zhihu.com/xen/productDetailsApps~albumColumnAndMagazine/vendors.2d292cce3f0653fa0e5e.js') {
- console.log("vendors.2d292cce3f0653fa0e5e.js")
- return {redirectUrl: chrome.extension.getURL("hook_js/vendors.2d292cce3f0653fa0e5e.js")}; //hook.js是你要替换的js脚本
- }
- return {cancel: false}
- },
- {urls: ["<all_urls>"], types: ["script"]},
- ["blocking"] //类型blocking为拦截,
- );
复制代码 manifest.json
- {
- "name": "知乎HooK",
- "version": "1.1",
- "manifest_version": 2,
- "description": "插件描述.",
- "permissions": [
- "activeTab",
- "storage",
- "storage",
- "contextMenus",
- "notifications",
- "tabs",
- "webRequest",
- "webRequestBlocking",
- "https://static.zhihu.com/*",
- "https://www.zhihu.com/*"
- ],
- "host_permissions": [
- "http://127.0.0.1/*"
- ],
- "browser_action": {
- "default_title": "Get Page HTML"
- },
- "background": {
- "scripts": [
- "hoook_manager.js"
- ],
- "persistent": true
- },
- "web_accessible_resources": [
- "hook_js/vendors.2d292cce3f0653fa0e5e.js"
- ]
- }
复制代码 vendors.2d292cce3f0653fa0e5e.js 这个太大 不粘帖了,自己去改吧 .这个方法把我们插件放在编辑器里 修改js 到达了动态修改页面js 的目的 爽的不要不要的 比追栈服从高不少
第三步 启动 web 启动浏览器远程 debuger 模式 并且加载插件
这个浏览器地址是默认安装的位置 留意修改你名字 这是一个 命令行 在桌面新建一个bat 复制进去修改 生存 再启动
- C:\Users\你得主机名字\AppData\Local\Google\Chrome\Application\chrome.exe --remote-debugging-port=9222 --user-data-dir="C:selenumAutomationProfile" --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" --load-extension=D:/txt_to_video/zhihu/zhihu_app_api/zhihuchajian
复制代码 –remote-debugging-port 指定浏览器开发端口 9222
–user-data-dir 指定浏览器情况存储的路径 设置这个可以把一个浏览器当作多个浏览器来用
–user-agent 自界说浏览器头
–load-extension 指定自己的浏览器插件路径
上述操作完成基本就你可以看到 自己的插件了
再去访问 知乎网址 https://www.zhihu.com/xen/market/remix/paid_column/1824095654672261120
调试期内 查察源码 就看到
我们的代码见效了,舒畅的玩耍吧, 记得我们的bat 要一直启动不能关闭
第四步 利用python web框 架 暴露一个简单服务,买通浏览器 实验js 完成我们算法认证
其实到这步已经没啥意思了 不过该写还要写 烂作者凑字儿 哈哈 我就贴代码了 看解释吧
主要使用 selenium ,如果selenium 情况和驱动缺失 自行下载安装 学习不做教程
- import os
-
- from selenium.webdriver import ChromeOptions
-
- from selenium import webdriver
-
- from flask import Flask
- app = Flask(__name__)
- chrome_options = ChromeOptions()
- chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
- browser = webdriver.Chrome(options=chrome_options)
- browser.get("https://www.zhihu.com")
- script = """
- console.log("#防止爬虫代码 ,最好在 浏览器环境中 使用油猴插件方法 全局在加载前加载此段代码")
- Object.defineProperty(navigator,"webdriver",{get:() => false,});
- """
- #运行Javascript
- browser.execute_script(script)
- browser.get("https://www.zhihu.com/xen/market/remix/paid_column/1518918494300667904")
- browser.execute_script(script)
- monidianji="""
- // 选择具有特定class的div元素
- var element = document.querySelector('.CatalogModule-allSection-ctqBJ');
- // 检查元素是否存在
- if (element) {
- // 创建一个新的点击事件
- var clickEvent = new MouseEvent('click', {
- 'view': window,
- 'bubbles': true,
- 'cancelable': true
- });
-
- // 触发点击事件
- element.dispatchEvent(clickEvent);
- } else {
- console.log('没有找到具有指定class的元素');
- }
- """
- browser.execute_script(monidianji)
- @app.route("/")
- def hello_world():
- """Example Hello World route."""
- api = """
- /get_code/<light_code> return black_code
- /get_cookie return current cookie
- /get_header return headers
- """
- return api
- @app.route('/get_cookie', methods=['GET'])
- def get_cookie():
- javascript_code = """
- return JSON.stringify({'cookie': document.cookie})
- """
- try:
- result = browser.execute_script(javascript_code)
- if result:
- tata = {
- 'data': result
- }
- return tata
- else:
- return "{'error':'浏览器端出现异常或者环境没有初始化'}"
- except Exception as e:
- print(e)
- return f"{'error':'浏览器端出现异常或者环境没有初始化'}"
- @app.route('/get_header', methods=['GET'])
- def get_header():
- javascript_code = """
- var ttsa=new RegExp("d_c0=([^;]+)").exec(document.cookie)
- return JSON.stringify({'xUDId':JSON.parse(document.getElementById("resolved").value).appContext.xUDId,'authId':JSON.parse(document.getElementById("resolved").value).appContext.authId,'otherId':ttsa[1],'cookie':document.cookie,'userAgent':navigator.userAgent})
- """
- try:
- result = browser.execute_script(javascript_code)
- if result:
- tata={
- 'data': result
- }
- return tata
- else:
- return "{'error':'浏览器端出现异常或者环境没有初始化'}"
- except Exception as e:
- print(e)
- return "{'error':'浏览器端出现异常或者环境没有初始化'}"
- @app.route('/get_code/<light_code>', methods=['GET'])
- def get_data(light_code):
- javascript_code = """
- // JavaScript代码示例
- //alert('Hello World!');
- if(undefined!=window._encrypt){
- var tt=window._encrypt(encodeURIComponent('"""
- ttff = """'))
- console.log(tt)
- return tt
- }else{
- console.log('环境window._encrypt没有设置')
- console.log('定义为代码 return __g._encrypt(encodeURIComponent(d))')
- console.log('return __g._encrypt(encodeURIComponent(d))')
- console.log('window._encrypt=__g._encrypt')
- // 选择具有特定class的div元素
- var element = document.querySelector('.CatalogModule-allSection-ctqBJ');
- // 检查元素是否存在
- if (element) {
- // 创建一个新的点击事件
- var clickEvent = new MouseEvent('click', {
- 'view': window,
- 'bubbles': true,
- 'cancelable': true
- });
-
- // 触发点击事件
- element.dispatchEvent(clickEvent);
- } else {
- console.log('没有找到具有指定class的元素');
- }
- }
- """
- # zhihu = ZhiHu()
- # code = zhihu.get_pinglun("1716821153832767488")
- if light_code==None or light_code=='':
- return "{'error':'light_code参数异常浏览器端出现异常或者环境没有初始化'}"
- javascript_code = javascript_code + light_code + ttff
- try:
- result = browser.execute_script(javascript_code)
- if result:
- tata={
- 'data': result
- }
- return tata
- else:
- return "{'error':'浏览器端出现异常或者环境没有初始化'}"
- except Exception as e:
- print(e)
- return "{'error':'浏览器端出现异常或者环境没有初始化'}"
- if __name__ == "__main__":
- print( 'return __g._encrypt(encodeURIComponent(d))')
- print('window._encrypt=__g._encrypt')
- # app.run(port=8080)
- app.run(debug=False, host="0.0.0.0", port=int(os.environ.get("PORT", 7355)))
- # app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
- # web框架 使用请参考 https://blog.csdn.net/weixin_38037405/article/details/127149032
- #app.run(host='0.0.0.0', port=80, debug=True)
- # browser.quit()
复制代码 以上代码弄完还不能正常运行 不过大的操作情况 已经就位 为了给知乎留点面子不写了
但是给各人一点提示
def get_data(light_code):
这个方法我们接收的 light_code 是有固定算法需要计算的不需要浏览器情况 ,不过我提供的关键点 你们向上追栈是可以找到的
流程是 大概就是这几个参数 说到这里自己去搜这几个关键参数就应该找到了 这些一起计算一个明码值 再传给浏览器加密依赖一些浏览器动态信息加密给出暗码 ,再通通过浏览器头发送get 请求,这里就完成了逆向 可以舒畅爬了
# os_vr = “101_3_3.1”
# story_id = “1716821153832767488”
# url_api = f"/pluton/products/{story_id}/paid_column/section"
# authId = “7481a31d25d73bfc1edf9240df687cf9”
这样我们就可以自己动态修改请求 也可以 还不去关注浏览器内登录信息了 暴露的服务还可以解耦给其他 分布式服务使用 就是一个快乐
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |