爬取动态加载的数据(js加密eval,jsfuck )
[外链图片转存失败(img-sFgp9WYn-1566049211688)(E:\CSDN 博客\裁判文书网\首页.png)]
从各个标签入手,当点击其中一个分类,刑事案件的时候,会跳转到下一个页面,那么可以知道,在这个页面我们要获取到的信息就是各级标签的url
start_urls = ['http://wenshu.court.gov.cn/']
def parse(self, response):
'''解析出案件类型的url'''
str1 = response.body.decode('utf-8')
case_urls = re.findall(r'''首页 (.*?)''',case_urls, re.S)[0:1]
for urls in case_url:
url = 'http://wenshu.court.gov.cn' + urls
yield scrapy.Request(url=url,callback=self.case_links,)
,分析后发现,该请求是POST,并且里面的数据都是加密的,需要去获取js信息破解出需要的信息[外链图片转存失败(img-po5Xr6dr-1566049211690)(E:\CSDN 博客\裁判文书网\动态加载的数据信息.png)]
这里我们需要的参数为 guid ,vl5x ,param
直接在浏览器中搜索对应的参数名,筛选查找出来的js文件,定位到有效信息
vl5x很直观的可以从cookies里看出来和cookies里的vjkl5很像
[外链图片转存失败(img-v0sPjRfd-1566049211690)(E:\CSDN 博客\裁判文书网\获取cookies.png)]
[外链图片转存失败(img-7s6b0i62-1566049211690)(E:\CSDN 博客\裁判文书网\vl5x.png)]
[外链图片转存失败(img-AEa4zuWx-1566049211691)(E:\CSDN 博客\裁判文书网\guid vl5x.png)]
[外链图片转存失败(img-Zgr2LHIE-1566049211691)(E:\CSDN 博客\裁判文书网\get_key()].png)
在Lawyee.CPWSW.ListExtend.js文件中,发现了getKey函数的定义
在getKey函数的定义中,发现eval()函数,该函数是对js代码进行加密的函数,所以需要对eval()函数进行还原
百度一下就能找到eval()解密的网址https://wangye.org/tools/scripts/eval/
解密的步骤:
a.发现有些eval函数里面还有一层,所以有些需要解密两次,从外向里进行替换,例如eval(de("eval(_fxxx( 先替换de,然后解密,在替换_fxxx,再解密;这里的de和_fxxx我们可以从Lawyee.CPWSW.ListExtend.js文件中找到,_fxxx
b.将以上所有解码的函数存放在一个自己建的js文件中,
c.除此以外还发现在解码得到的的函数中还用到了其他的js(md5.js、base64、sha1.js),我们直接在浏览器中搜索既可以直接找到,并将它们一起放到自己建的js文件中
在解码的最后果然发现了[外链图片转存失败(img-LpgSUboq-1566049211691)(E:\CSDN 博客\裁判文书网\vj联系.png)]
所以我们我们就明白了vl5x的来源了,其实就是gek_key(vjkl5)只不过get_key被加密了
获取vjkl5代码
def case_links(self,response):
str1 = response.body.decode('utf-8')
param = re.findall(r'\+AJLX\+\+(.*)', response.url)[0]
param = urllib.parse.unquote(param)
# 获取到cookies里的vjkl5
vjkl5 = response.headers.getlist('Set-Cookie')[0].decode().split(';')[0].split('=')[1]
求出vl5x的值(这里需要使用execjs执行js代码,wenshu.js就是我们存放get_key()解码得到的js代码及其他js文件的文件)
js_content = open('D:\python练习\wenshu\wenshu\wenshu.js', encoding='utf8').read()
# js_obj = execjs.compile #(js_content)返回一个js对象,该对象包含了js运行的环境。
# 再使用js对象调用call()函数。
# call('getKey', vjkl5) #参数1-要执行的js文件中对应的函数,参数2-给调用函数传递的参数。
vl5x = execjs.compile(js_content).call('getKey', vjkl5)
guid 的js代码并没有与加密,直接copy执行就可以了
def get_guid(self):
guid = self.creat_guid() + self.creat_guid() + "-" + self.creat_guid() + "-" + self.creat_guid() + self.creat_guid() + "-" + self.creat_guid() + self.creat_guid() + self.creat_guid();
return guid
def creat_guid(self):
return execjs.eval('(((1 + Math.random()) * 65536) | 0).toString(16).substring(1)')
[外链图片转存失败(img-Vgy3NYkz-1566049211692)(E:\CSDN 博客\裁判文书网\docid4.png)]
[外链图片转存失败(img-uEiQBnhb-1566049211693)(E:\CSDN 博客\裁判文书网\docid1.png)]
这里直接发现了Navi函数,以及他需要的参数key就是获取到的文书列表里面的 “文书ID”信息
搜索Navi函数[外链图片转存失败(img-lhoERS8Y-1566049211693)(E:\CSDN 博客\裁判文书网\navi.png)]
[外链图片转存失败(img-VsSWqny0-1566049211693)(E:\CSDN 博客\裁判文书网\unzip.png)]
本以为直接执行js既可以的但是,发现没有那么简单,在分析后发现其实他是需要一个秘钥来解密的,在unzip的函数里发现 这里用的是AES的加密方式 ,
encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
})
所以我们需要获得他的秘钥,及_KEY这里的key是默认的,但是尝试了以后发现并不可行,那么我们就需要去找这个秘钥的来源,这个秘钥一定是要和加密的秘钥一样那么要想在浏览器中解密,服务器一定会返回一个秘钥回来,在得到的信息在里发现除了文书ID外还有一个参数RunEval并且一直在变,所以就大胆猜测,这个就是
a.把RunEval带入unzip函数后得到
# 构建js的执行对象 执行js文件中的decode函数
runeval = execjs.compile(js_content).call('decode',runEval)
# 正则筛选出被JSFUCK加密的获取变量值得代码
[外链图片转存失败(img-iufgQ7wJ-1566049211694)(E:\CSDN 博客\裁判文书网\jsfuck.png)]
这是一段被jsfuck加密的js代码,可以自行百度获取更多关于jsfuck的知识
分析这段代码
$hidescript=string,fromCharCode,
这是一个赋值语句,并且后面有大量的$hidescript出现,直接运行这段代码也获得了对应的值,
在jsfuck的代码中还有_=“constructor”; _ [ _ ] [ _ ]()这个函数,也正是这个函数里面需要$hidescript
所以把前面获得的值带入到函数中我就获得了str_keyde 值,这也就是我们需要的秘钥
runeval = execjs.compile(js_content).call('decode',runEval)
# 正则筛选出被JSFUCK加密的获取变量值得代码
js_key = '$hidescript='+re.findall(r'\$hidescript=(.*?)_="constructor"',runeval)[0]
# 构建js执行对象
ctx = execjs.compile('''function js_KEY(){return %s}''' % js_key)
# 执行js代码 获取$hidescript的值
ctx_key = ctx.call("js_KEY")
print(ctx_key)
#这里需要将"进行转义
ctx_key = '"'+ctx_key.replace('._KEY=','._KEY=\\')+'"'
# 正则筛选出被JSFUCK加密的获取str_key函数的js代码,其中需要先获取到$hidescript的值
js_str_key = re.findall(r'''_="constructor";_\[_\]\[_\](.*?)\(\);''',runeval)[0]
# 将$hidescript替换成他的真实值
js_str_key = js_str_key.replace('$hidescript',ctx_key)
# 构建js执行对象
str_key = execjs.compile('''function js_KEY(){return %s}''' % js_str_key)
# 执行 js 获取str_key
str_key = str_key.call("js_KEY")
print(str_key)
Tm('._KEY="452318096;,*Mh)
setTimeout('com.str._KEY="44cb5235f18c40d2a9896101d0169d1d";',8000*Math.random());
接下来就可以直接带入js获取doc_id了
for doc_id in doc_ids:
# 构建js的执行对象 执行js文件中的Navi函数
real_id = execjs.compile(js_content).call('Navi', doc_id, real_str_key)
print(real_id)
结果
029bb843-b458-4d1c-8928-fe80da403cfe
f08d44ee-b647-11e3-84e9-5cf3fc0c2c18
eff7f53c-b647-11e3-84e9-5cf3fc0c2c18
f074f302-b647-11e3-84e9-5cf3fc0c2c18
f08d1d40-b647-11e3-84e9-5cf3fc0c2c18
508508aa-f317-4845-b560-a178bc4245d4
e05ad449-e7cf-4f5c-8b21-711351599674
cb564602-23aa-46e3-abe3-841f59727e0f
2aae74eb-3c59-42ad-95d7-85d683249c6b
75542beb-5da3-4926-9330-a5948f2b629f
spider.py
# -*- coding: utf-8 -*-
# @Time : 2019/8/8 11:19
# @Author : lsc
# @Site ;
# @File : wenshu_spider.py
# @Software : PyCharm
import scrapy
import urllib.parse
import re
import execjs
import os
import time
class WenshuSpider(scrapy.Spider):
name = 'wenshu'
# 要注意这个地方
# allowed_domains = ['wenshu.court.gov.cn/']
start_urls = ['http://wenshu.court.gov.cn/']
def parse(self, response):
'''解析出案件类型的url'''
str1 = response.body.decode('utf-8')
# print(str1)
print('==========================================================')
case_urls = re.findall(r'''首页 (.*?)''',case_urls, re.S)[0:1]
for urls in case_url:
url = 'http://wenshu.court.gov.cn' + urls
yield scrapy.Request(url=url,callback=self.case_links,)
def case_links(self,response):
print('+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
str1 = response.body.decode('utf-8')
param = re.findall(r'\+AJLX\+\+(.*)', response.url)[0]
param = urllib.parse.unquote(param)
# 获取到cookies里的vjkl5
vjkl5 = response.headers.getlist('Set-Cookie')[0].decode().split(';')[0].split('=')[1]
print(vjkl5)
js_content = open('D:\python练习\wenshu\wenshu\wenshu.js', encoding='utf8').read()
# js_obj = execjs.compile #(js_content)返回一个js对象,该对象包含了js运行的环境。
# 再使用js对象调用call()函数。
# call('getKey', vjkl5) #参数1-要执行的js文件中对应的函数,参数2-给调用函数传递的参数。
vl5x = execjs.compile(js_content).call('getKey', vjkl5)
# print(vl5x)
meta = self.get_metas(index='1', page='10', param=param, vl5x=vl5x,)
# print(meta)
url = 'http://wenshu.court.gov.cn/List/ListContent'
# FormRequest: 发送POST请求的类。
# Request: 默认发送GET请求,method设置请求方法。
# 由于在请求列表页接口时,cookie中vjkl5的值每次都是变化的,所以在请求时,需要将从response的Set-Cookie中获取的vjkl5的值更换一下,否则使用同一个vjkl5的值,会出现 'remind key' 错误。
# urllib/requests: Cookie的自动化管理(cookiejar)?为什么要自动化管理,而不手动去解析?
# 1. 复杂,每一个请求的响应都要去提取并存储;
# 2. 容易遗漏;
# scrapy框架是如何管理Cookie的?
# 默认启用了一个Cookie的中间件,实现了Cookie的自动化管理。将所有响应的Set-Cookie中的cookie信息保存下来,在后续的请求中,将这些cookie携带上。
# 为什么单独添加Cookies?
# 因为每一次请求列表页,都会返回一个新的Set-Cookie: vjkl5=,但是scrapy请求时自动携带的cookie,可能还是之前的旧的vjkl5=,就会导致这个POST请求携带的Cookie无法和服务器保存的Cookie不一致。
# 设置上cookies,就意味着每次请求都携带最新返回的vjkl5的值。
yield scrapy.FormRequest(
url=url,
callback=self.case_links_detail,
formdata=meta,
cookies={
'vjkl5': vjkl5
}
)
# 在使用cookie的时候,观察cookie的值的变化。
def case_links_detail(self,response):
print('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
json_string = re.search(re.compile(r'\[(.*?)\]', re.S), response.text)
# 设置js代码 的执行环境
os.environ["EXECJS_RUNTIME"] = "PhantomJS"
js_content = open('D:\python练习\wenshu\wenshu\docid.js', encoding='utf8').read()
# js_obj = execjs.compile #(js_content)返回一个js对象,该对象包含了js运行的环境。
# 再使用js对象调用call()函数。
# call('getKey', vjkl5) #参数1-要执行的js文件中对应的函数,参数2-给调用函数传递的参数。
if json_string:
# 将字符串中 '\' 都替换为空。
# \是特殊字符,需要表达普通字符\,需要使用\进行转义。
result = json_string.group(1).replace("\\", '').replace('“', '(').replace('”', ')')
# result是一个字符串 "{'RunEval':''},{'裁判要旨':'', '文书ID':''},{'裁判要旨':'', '文书ID':''},{'裁判要旨':'', '文书ID':''}"
# 获取RunEval的值,加密字符串。
runEval = re.search(re.compile(r'"RunEval":"(.*?)","', re.S), result).group(1)
print(runEval)
# 获取文书的ID
doc_ids = re.findall(re.compile(r'"文书ID":"(.*?)","', re.S), result)
print(doc_ids)
# 构建js的执行对象 执行js文件中的decode函数
runeval = execjs.compile(js_content).call('decode',runEval)
# 正则筛选出被JSFUCK加密的获取变量值得代码
js_key = '$hidescript='+re.findall(r'\$hidescript=(.*?)_="constructor"',runeval)[0]
# 构建js执行对象
ctx = execjs.compile('''function js_KEY(){return %s}''' % js_key)
# 执行js代码 获取$hidescript的值
ctx_key = ctx.call("js_KEY")
print(ctx_key)
#这里需要将"进行转义
ctx_key = '"'+ctx_key.replace('._KEY=','._KEY=\\')+'"'
# 正则筛选出被JSFUCK加密的获取str_key函数的js代码,其中需要先获取到$hidescript的值
js_str_key = re.findall(r'''_="constructor";_\[_\]\[_\](.*?)\(\);''',runeval)[0]
# 将$hidescript替换成他的真实值
js_str_key = js_str_key.replace('$hidescript',ctx_key)
# 构建js执行对象
str_key = execjs.compile('''function js_KEY(){return %s}''' % js_str_key)
# 执行 js 获取str_key
str_key = str_key.call("js_KEY")
print(str_key)
real_str_key = re.findall(r'''_KEY="(.*?)";''',str_key)[0]
print(real_str_key)
for doc_id in doc_ids:
# 构建js的执行对象 执行js文件中的Navi函数
real_id = execjs.compile(js_content).call('Navi', doc_id, real_str_key)
print(real_id)
url = 'http://wenshu.court.gov.cn/content/content?DocID='+real_id+"&KeyWord="
# yield scrapy.Request(url=url, callback=self.case_doc )
def case_doc(self,response):
print('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@**********************************')
print(response.url)
def get_metas(self,param,index,page,vl5x):
''' 需要构造 POST 参数
Param: 案件类型:刑事案件
Index: 1
Page: 10
Order: 法院层级
Direction: asc
vl5x: 19df1bfbf20eb3ea82cba533
number: wens
guid: d39aa838-d6ad-2792bcbd-f975ac094318
'''
guid = self.get_guid()
meta = {
'Param': param,
'Index': index,
'Page': page,
'Order': '法院层级',
'Direction': 'asc',
'vl5x': vl5x,
'number': 'wens',
'guid': guid,
}
return meta
def get_guid(self):
guid = self.creat_guid() + self.creat_guid() + "-" + self.creat_guid() + "-" + self.creat_guid() + self.creat_guid() + "-" + self.creat_guid() + self.creat_guid() + self.creat_guid();
return guid
def creat_guid(self):
return execjs.eval('(((1 + Math.random()) * 65536) | 0).toString(16).substring(1)')
guid(self):
guid = self.creat_guid() + self.creat_guid() + “-” + self.creat_guid() + “-” + self.creat_guid() + self.creat_guid() + “-” + self.creat_guid() + self.creat_guid() + self.creat_guid();
return guid
def creat_guid(self):
return execjs.eval('(((1 + Math.random()) * 65536) | 0).toString(16).substring(1)')