需求: 将这个网页中的代理ip和端口号进行爬取
分析:
爬取的数据是动态加载
并且我们进行了抓包工具的全局搜索,没有查找到结果
意味着:爬取的数据从服务端请求到的是加密的密文数据
页面每10s刷新一次,刷新后发现数据更新,但是浏览器地址栏的url没有变,说明加载出的数据是由ajax请求到的
动态加载出来的数据是由ajax请求到的,并且请求到的数据为加密数据
定位到ajax数据包,从中可以看到url和动态变化的请求参数和加密的相应数据
将ajax请求到的密文数据捕获
动态的获取动态变化的请求参数
基于抓包工具进行了动态变化请求参数taken的全局搜索,定位到了taken产生的源头,就是如下js代码:
var token = md5(String(page) + String(num) + String(timestamp));
对密文数据进行解密
通过分析找到了解密的js函数:decode_str(encode_str),encode_str就是密文数据
查找encode_str的实现:
js逆向:将js代码转换成python代码。开发环境只能执行python代码
js加密/js逆向源码相关代码:
function get_proxy_ip(page, num, click_btn) {
var timestamp = Date.parse(new Date());
timestamp = timestamp / 1000;
var token = md5(String(page) + String(num) + String(timestamp));
$.get('../proxy?page=' + page + '&num=' + num + '&token=' + token + '&t=' + timestamp, function (result) {
if (result.status === 'true') {
var setHtml = "";
$("#ip-list").html(setHtml);
var encode_str = result.list;
var items = str_to_json(decode_str(encode_str));
for (var index = 0; index < items.length; ++index) {
item = items[index];
setHtml += "<tr>\n<td>" + (index + 1) + "</td>\n";
setHtml += "<td>" + item.ip.toString() + "</td>\n";
setHtml += "<td>" + item.port.toString() + "</td>\n";
setHtml += "<td>" + item.time.toString() + "</td>\n</tr>\n";
}
$("#ip-list").html(setHtml);
if (click_btn === 'next') {
document.getElementById("last-page").disabled = false;
if (items.length < 15) {
document.getElementById("next-page").disabled = true;
}
} else {
document.getElementById("next-page").disabled = false;
if (page === 1) {
document.getElementById("last-page").disabled = true;
}
}
}
});
}
js解密代码:
function decode_str(scHZjLUh1) {
// Base64.decode(scHZjLUh1)
scHZjLUh1 = Base64["\x64\x65\x63\x6f\x64\x65"](scHZjLUh1);
key = '\x6e\x79\x6c\x6f\x6e\x65\x72';#key = 'b'nyloner'
len = key["\x6c\x65\x6e\x67\x74\x68"];
code = '';
for (i = 0; i < scHZjLUh1["\x6c\x65\x6e\x67\x74\x68"]; i++) {
var coeFYlqUm2 = i % len;
code += window["\x53\x74\x72\x69\x6e\x67"]["\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65"](scHZjLUh1["\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74"](i) ^ key["\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74"](coeFYlqUm2))
}
return Base64["\x64\x65\x63\x6f\x64\x65"](code)
}
对以上代码分析结果如下:
function decode_str(scHZjLUh1) {
// Base64.decode(scHZjLUh1)
scHZjLUh1 = Base64.decode(scHZjLUh1);
key = 'nyloner';
len = key.length;
code = '';
for (i = 0; i < scHZjLUh1.length; i++) {
var coeFYlqUm2 = i % len;
code += window.String.fromCharCode(scHZjLUh1.charCodeAt(i) ^ key.charCodeAt(coeFYlqUm2))
}
return Base64.decode(code)
}
// '\x64\x65\x63\x6f\x64\x65'.encode('utf-8')
// 注意:遇到类似字节的代码,一般不是加密数据,通过encode编码尝试是否可以得到有效信息
代码实现:
导入所需模块:
import time
import hashlib
import requests
import base64
js逆向/将js代码转换成python代码:
# js逆向之后的结果
def decode_str(scHZjLUh1):
# 解密成字符串
scHZjLUh1 = base64.decodestring(scHZjLUh1.encode())
key = 'nyloner'
lenth = len(key)
code = ''
sch_lenth = len(scHZjLUh1)
for i in range(sch_lenth):
coeFYlqUm2 = i % lenth
# chr(0-255)返回对应编码的字符
# ord(a-z)返回编码数值
code += chr(scHZjLUh1[i] ^ ord(key[coeFYlqUm2]))
code = base64.decodestring(code.encode())
code = code.decode('utf-8')
return code
生成请求所需的token值:
def getToken():
page = str(1)
num = str(15)
t = str(int(time.time()))
md5 = hashlib.md5()
md5.update((page+num+t).encode('utf-8'))
token = md5.hexdigest()
return token
对响应数据解密:
token = getToken()
url = 'https://nyloner.cn/proxy'
param = {
'num':'15',
'page':'1',
't':str(int(time.time())),
'token':token
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
'Cookie':'sessionid=20ryihg87smnkko2kx6634jbcf4umhfp'
}
code = requests.get(url,headers=headers,params=param).json().get('list')
str_code = decode_str(code)
str_code
需求: url:https://www.aqistudy.cn/html/city_detail.html ,对其气象数据进行爬取
分析:
处理动态变化且加密的请求参数d
对请求参数d进行全局搜索(不可行)
点击页面中的搜索按钮后,在抓包工具中就捕获到了那两个ajax请求的数据包
分析getData这个js函数的实现:目的就是为了找到ajax请求对应的js代码
type=="HOUR":按照小时为时间单位进行查询
并没有在该函数的实现中发现ajax请求对应的代码,但是发现了另外的两个函数调用getAQIData();getWeatherData();
分析getWeatherData();和getAQIData()这两个函数的定义,想要去找到ajax请求对应的代码:
这两个函数实现的区别: method变量赋值的字符串不一样 GETDETAIL / GETCITYWEATHER
相同: 都没有出现ajax请求对应的代码,但是发现了另一个函数的调用: getServerData(method,param,匿名函数,0.5)
对getServerData(method,param,匿名函数,0.5) 参数进行分析
method:GETDETAIL或者GETCITYWEATHER
param:字典,有四组键值对
对getServerData(method,param,匿名函数,0.5)函数进行分析
getServerData(method,param,匿名函数,0.5)这个函数的实现,还是为了找到ajax请求对应的代码:
基于抓包工具的全局搜索才可以找到
发现这个函数的实现代码看不懂,函数的实现好像是一组密文 / 网站对js函数的实现加密
插入两个概念/上面密文用到了js混淆:
JS混淆:对js函数的实现代码进行加密
JS反混淆:将加密的js代码解密成原文
分析getServerData函数的实现:
终于找到了ajax请求对应的代码
参数d是由getParam(method, object)函数返回的
method:method
object:param字典,四个键值分别是城市名称。type,起始时间,结束时间
请求到的密文数据的解密方式:
decodeData(data):data参数就是响应的密文数据,返回值就是解密后的原文数据
JS逆向:python可以使用 PyExecJS 库来实现模拟JavaScript代码执行。
环境的安装:
python pip install PyExecJS
必须还要安装nodeJs的开发环境 /PyExecJS是依赖nodeJs环境运行的
请求到加密的响应数据
将加密的响应数据进行解密
代码实现:
import execjs
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}
node = execjs.get()
file = 'test.js'
ctx = node.compile(open(file,encoding='utf-8').read())
# 如何五个变量会作为getPostParamCode的参数
method = 'GETDETAIL' # GETCITYWEATHER
city = '北京'
type = 'HOUR'
start_time = '2018-01-25 00:00:00'
end_time = '2018-01-25 23:00:00'
# 模拟执行getPostParamCode函数
js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
params = ctx.eval(js)
# print(params) # 请求参数d
url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
data = {
'd':params
}
# 获取了加密的响应数据
response_code = requests.post(url=url,headers=headers,data=data).text
# response_code
# 模拟执行decodeData函数对密文数据进行解密
js = 'decodeData("{0}")'.format(response_code)
page_text = ctx.eval(js)
print(page_text)
抓包工具概述:
本质就是一款代理服务器,适用于实现请求和相应的拦截
miteproxy
爬虫05 /js加密/js逆向、常用抓包工具、移动端数据爬取
原文:https://www.cnblogs.com/liubing8/p/11998380.html