网页抓取解密(基于-redis框架的浏览器)
优采云 发布时间: 2021-10-18 12:16网页抓取解密(基于-redis框架的浏览器)
1 简介
几个月前写了一个网站(请原谅我是小偷)的爬虫。这两天我需要重新采集一次。我使用了scrapy-redis 框架。我认为第二次爬行可以轻松完成。是的,但是没想到在爬虫启动的几秒内,出现了很多重试提示,心里顿时一震。闲暇时间大概结束了。
经过仔细分析,发现是请求获取店铺列表有问题。通过浏览器抓包,发现请求头参数比之前多了一个X-Shard和x-uab参数,如下图所示:
X-Shard 不是问题。乍一看是兴趣点的经纬度,但是用x-uab看了之后,让人心酸。JS加密只能反向解密。
2 js反向解决
最直接的思路是根据“x-uab”关键字搜索所有key(chrome浏览器-source中按ctrl+shift+F快捷键),结果如下:
接下来做断点调试:点击数字,数字位置出现一个蓝点,表示断点添加成功,然后刷新获取店铺列表页面,程序会在断点处停止. 如下:
在控制台中调试 o.getUA() 函数并查看输出:
果然,事实证明猜测是正确的,正是 o.getUA() 函数负责生成请求头中的 x-uab 参数。
继续向下查看getUA()函数的引用(将光标放在要查看的函数上,可以查看该函数的引用),就是下图中的函数:
图中的s就是我们想要的x-uab参数,在控制台输出中可以证明下图:
因此,这里的u-xab是由e生成的,在函数e传入的参数中,第一个参数为常数2,第二个参数a未定义。哦,好像没有传其他参数。继续向下找到这个 e(2,a) 函数:
这就是函数e(r, i, n, h, p)方法,可以直接运行得到加密参数。把这个函数e(r, i, n, h, p)方法的代码全部取出来保存成js文件。
回到顶部
3 代码
3.1 选项一
你以为找到上面生成x-uab的js代码就大功告成了?青春,你太年轻太单纯!
如何运行这个js脚本是off(nan)键(dian)。
这个函数 e(r, i, n, h, p) 有近 40,000 行代码。在 Python 中重新实现 (jiu) (shi) 是 (bu) 有点 (ke) 大 (neng) 是困难的。所以,我选择直接用Python执行这个js脚本。
如何用python执行js脚本,杜娘给你一堆资料,自己查查。我这里选择了execjs。
因为在上面复制的脚本中,只定义了一个e(r, i, n, h, p)方法,并且没有调用这个方法,所以只好在js文件的最后加上一些代码来调用:
function getParam() {
var a;
var param = e(2,a);
return param
};
那么,我们开始推Python代码:
import execjs
node = execjs.get()
file = 'eleme.js'
ctx = node.compile(open(file).read())
js_encode = 'getParam()'
params = ctx.eval(js_encode)
print(params)
尝试执行,心凉,代码异常:
execjs._exceptions.ProgramError: TypeError: 'window' 未定义
window对象估计是在浏览器打开的时候创建的,里面收录了浏览器的信息,所以当你用Python来执行这段代码的时候,就没有xixiang这样的东西了。本来想尝试伪造window对象,但是搜了一下,发现js脚本里有上百处用windows。这还没有结束。代码乱码,级别不够追根究底(这个地方困扰我很久了,知道方法请指教)。
后来,从一个学长那里(感谢学长),我学会了一种四处走动的方法。这个前辈的做法是用无头浏览器PhantomJS(之前的引擎是node.js)替换execjs引擎。换句话说,PhantomJS 用于执行 js 脚本。PhantomJS 是一个浏览器,自然会创建一个窗口。目的。
在使用 PhantomJS 之前,需要先下载它的驱动,然后将 Python 代码放到统一目录下。之前的Python代码也有修改:
import execjs
import os
os.environ["EXECJS_RUNTIME"] = "PhantomJS"
node = execjs.get()
file = 'eleme.js'
ctx = node.compile(open(file).read())
js_encode = 'getParam()'
params = ctx.eval(js_encode)
print(params)
果然,按照这个方法,成功获取到了加密字符串。
3.2 选项二
其实第二个选项是我在未定义的窗口对象异常发生后尝试的第一种方法。但是因为在js代码中添加的js脚本有问题,以为不行,所以咨询了前辈,得到了第一种方案。
方案二的思路与方案一类似,但更粗暴。是不是因为浏览器中没有执行所以没有window对象?然后我会模拟浏览器执行。
执行前还必须修改js脚本,调用js文件末尾的e方法,添加如下代码:
var a;
var param = e(2,a);
return param;
记住:不要把它放在任何函数中。我曾经把这段代码放在函数中来强制执行。结果是在浏览器中可以获取到加密后的字符串,但是在Python中获取的是None。
用来模拟浏览器的selenium和chrome webDriver,代码如下:
from selenium import webdriver
browser = webdriver.Chrome(executable_path='chromedriver.exe')
with open('eleme.js', 'r') as f:
js = f.read()
print(browser.execute_script(js))
该方法也可以获取加密后的字符串。
最后要说的是,如果你需要获取大量的x-uab,使用选项二的效率会更高,因为如果使用选项二,你可以自己打开浏览器(都调用一个webdriver对象),然后快速执行 js ,返回加密后的字符串。
4 总结
一个js反向解密就完成了。但也有一些疑问:
(1) 使用chrome断点调试时,js脚本被压缩混淆,可以通过chrome的漂亮打印功能(即一对大括号)美化格式,但有时会失败。就像下图,格式化之后,还是一团糟:
这个问题耽误了我很久,调试不了!
(2) 在js基础不好的情况下,我很困惑为什么在运行的时候,先通过o.getUA()调用e函数中的嵌套函数,然后在嵌套函数中调用e方法本身e 函数. 是什么操作? ? 函数调用不应该总是先调用外层函数,然后再调用嵌套函数吗?
(3)如果浏览器中执行js的方法不适用,只能替换window对象。如何操作?
(4)这个e函数有近4万行,一个加密函数代码这么多,我不信。肯定有很多东西混淆视听。但是我试过调试和跟踪。我可以只能说迷茫之后,想不通了。跟踪,晕,怎么简化这个脚本?
如果哪位前辈能解疑解惑,请告诉我,非常感谢!谢谢!