先上代码为敬
#!/usr/bin/env python# -*- coding:utf-8 -*-# 裁判文书网爬虫(requests版)import requestsimport execjsfrom selenium import webdriverfrom time import sleepimport randomclass wenshu: def __init__(self): self.headers = { 'user-agent': 'mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) ' 'chrome/89.0.4389.128 safari/537.36 ', 'host': 'wenshu.court.gov.cn', 'origin': 'https://wenshu.court.gov.cn', 'sec-ch-ua': 'google chrome";v="89", "chromium";v="89", ";not a brand";v="99', 'referer': 'https://wenshu.court.gov.cn/website/wenshu/181217bmtkhnt2w0/index.html?pageid=27cc21' '6a1f2f1f52fb1f145752d59ee2&s21=%e7%bb%8f%e6%b5%8e%e7%8a%af%e7%bd%aa', 'accept': '*/*', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-cn,zh;q=0.9', 'connection': 'keep-alive', 'content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'cookie': '' } self.url = 'https://wenshu.court.gov.cn/website/parse/rest.q4w' self.request = requests.session() # 初始化execjs node = execjs.get() # 加载需要执行的js文件 self.ctx = node.compile(open('decrypt_content.js', encoding='utf-8').read()) chrome_options = webdriver.chromeoptions() # 让浏览器不显示自动化测试 chrome_options.add_argument('disable-infobars') self.chrome = webdriver.chrome(executable_path='../../driver/chromedriver.exe', options=chrome_options) def send_login(self): # 模拟登陆获取到登陆的cookie self.chrome.get(url='https://wenshu.court.gov.cn/website/wenshu/181010carhs5bs3c/index.html?open=login') self.chrome.implicitly_wait(10) # 最大化浏览器 self.chrome.maximize_window() # 因为登录框在iframe框中,需要先切换到iframe中 self.chrome.switch_to.frame('contentiframe') self.chrome.find_element_by_xpath('//*[@id="root"]/div/form/div[1]/div[1]/div/div/div/input').send_keys('username') self.chrome.find_element_by_xpath('//*[@id="root"]/div/form/div[1]/div[2]/div/div/div/input').send_keys('password') sleep(random.randint(1, 5)) self.chrome.find_element_by_xpath('//*[@id="root"]/div/form/div[3]/span').click() sleep(5) xpath_search_base = '//div[@class="header"]/div[@class="item_table"]//div[@class="search-con clearfix"]/div[1]' self.chrome.find_element_by_xpath(xpath_search_base + '/div[2]/input').send_keys('经济犯罪') sleep(random.randint(1, 5)) self.chrome.find_element_by_xpath(xpath_search_base + '/div[3]').click() sleep(random.randint(1, 5)) self.chrome.refresh() self.chrome.implicitly_wait(10) return self.chrome.get_cookies() def send_request(self, ws_params): return self.request.post(url=self.url, headers=self.headers, data=ws_params).json() def decrypt_response(self, ws_content): # 解密数据接口返回的加密内容 # 解密的key secret_key = ws_content['secretkey'] # 加密的数据内容 result = ws_content['result'] # 需要执行的方法名,第一个参数加密内容,第二个参数key func_name = 'des3.decrypt("{0}", "{1}")'.format(result, secret_key) # 获取解密后的数据内容 # 此处在windows下执行有报编码错误问题,需要将源码下的subprocess.py文件里的encoding改成utf-8 return self.ctx.eval(func_name)if __name__ == '__main__': wenshu = wenshu() # 获取登陆后的cookie cookies = wenshu.send_login() # 将cookie转换为字符串 json_cookie = '' for cookie in cookies: name = cookie['name'] value = cookie['value'] json_cookie += name+'='+value+'; ' # 退出selenium浏览器自动化 # wenshu.chrome.quit() print(json_cookie) wenshu.headers['cookie'] = json_cookie # 通过加载js生成ciphertext参数 ciphertext = wenshu.ctx.eval("cipher()") # 通过加载js生成__requestverificationtoken参数 verification_token = wenshu.ctx.eval("random(24)") params = { 'pageid': '2f48cc03c6e38865bcbdaf2e81a09f7d', 's21': '经济犯罪', 'sortfields': 's50:desc', 'ciphertext': ciphertext, 'pagenum': '1', 'querycondition': '[{"key":"s21","value":"经济犯罪"}]', 'cfg': 'com.lawyee.judge.dc.parse.dto.searchdatadsodto@querydoc', '__requestverificationtoken': verification_token } # 发送请求获取返回结果 response = wenshu.send_request(params) print(response) # 获取解密后的内容 content = wenshu.decrypt_response(response) print(content)
? python
裁判文书网自从去年更新之后,所有内容页都需要登陆后才能访问,这里采用的爬虫语言是python,采用requet请求方式爬取数据内容,共引用requests, execjs两个模块,这里以“经济犯罪”文书为例子
打开裁判文书网,首先因为所有模块都要登录才能查看,所以这里先登录一下,然后在搜索框中输入“经济犯罪”点击搜索
这种数据列表类型的网站,当然是先从异步请求开始分析,可以看到总共发送了三个路径一样的异步请求,一个一个先点开看看里面长啥样
第一个请求result里面只有username,应该是身份校验相关的请求,排除了
第二个请求非常可疑,result里面虽然是乱码,但是居然有99kb,正常的加密过后的其他内容都不可能有这么大,所有result里面的乱码极有可能就是加密后的秘文数据。
第三个请求内容应该是标题或者候选项信息等,排除。
三个异步请求看下来,第二个异步请求疑似度最高,那么就选择第二个异步请求作为突破点开始分析,既然传过来的是密文,那解密文件肯定能在响应里面找到,一般这种解密密文操作,都是属于js的工作,先把js都来看一下
这么多js请求,一般就是慢慢排查呗,果不其然,在website.js中,找到了玄妙之处
当看到这个des3的时候,基本上可以会心一笑了,这个变量命名我爱了。特别是对应还有encrypt和decrypt这两个函数,我只觉得内心一暖。des3对称秘钥加密,把这段js搞下来单独跑应该是ok的
果断开始拿起我的js调试工具,我这里用的是发条js,各位自己用的什么js调试工具都可以,我的作法比较粗暴,整体copy。
此处加载会出现一个缺少formatedate函数,很简单,也在这段js文件里面,不过是在闭包函数里面,拿出来单独做一个函数就可以了,在这里用发条js去调试的时候,内容太大了导致发条js调试工具崩溃了,这里只好手写代码调试。
# 初始化execjs node = execjs.get() # 加载需要执行的js文件 self.ctx = node.compile(open('decrypt_content.js', encoding='utf-8').read()) def decrypt_response(self, ws_content): # 解密数据接口返回的加密内容 # 解密的key secret_key = ws_content['secretkey'] # 加密的数据内容 result = ws_content['result'] # 需要执行的方法名,第一个参数加密内容,第二个参数key func_name = 'des3.decrypt("{0}", "{1}")'.format(result, secret_key) # 获取解密后的数据内容 # 此处在windows下执行有报编码错误问题,需要将源码下的subprocess.py文件里的encoding改成utf-8 return self.ctx.eval(func_name)
很简单,通过数据接口取到数据后,拿到内容和秘钥,直接用execjs去执行,就会得到如下结果
哇喔,拿到数据,ok了
这个时候当我们回头再去访问接口的时候,接口会报一个“没有权限访问的问题”,通过对比请求参数,可以发现是ciphertext,__requestverificationtoken这两个参数的问题,接下来就是解决这两个参数了
ciphertext参数是通过如下js生成的,直接代码调用cipher()方法就可以了
function cipher() { var date = new date(); var timestamp = date.gettime().tostring(); var salt = random(24); var year = date.getfullyear().tostring(); var month = (date.getmonth() + 1 < 10 ? "0" + (date.getmonth() + 1) : date.getmonth()).tostring(); var day = (date.getdate() < 10 ? "0" + date.getdate() : date.getdate()).tostring(); var iv = year + month + day; var enc = des3.encrypt(timestamp, salt, iv).tostring(); var str = salt + iv + enc; var ciphertext = strtobinary(str); return ciphertext;}function strtobinary(str) { var result = []; var list = str.split(""); for (var i = 0; i < list.length; i++) { if (i != 0) { result.push(" "); } var item = list[i]; var binarystr = item.charcodeat().tostring(2); result.push(binarystr); } ; return result.join("");}
__requestverificationtoken参数是通过一个random函数生成的,以下是js,代码直接调用random(24)就可以了
function random(size) { var str = "", arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; for (var i = 0; i < size; i++) { str += arr[math.round(math.random() * (arr.length - 1))]; } return str;}
2022,中国加油