有道翻译js加密
进入有道翻译页面,打开开发者工具,任意输入单词,会得到一个AJAX请求返回的响应文件,有我们想要的json数据:
我们要解决的问题是:程序中任意输入一个单词,获取单词的释义。那么我们必须发送一个请求来获取上述的响应文件,现在我们想要分析一下这个url如何得到,两种思路,一个是直接分析url,一个是分析参数,很显然这个案例分析参数更加方便:
这么多参数,我们难道都要分析吗?并不是,我们想要通过对比不同的响应文件中的参数取出发生变化的、有意义的参数:
因此我们需要研究三个参数,在文件栏ctrl+f查找参数名寻找js文件。注意需要重新刷新页面才能加载js文件。
接下来就要分析js加密,当然,不需要分析整个文件,只要查找到对应位置即可,同样是ctrl+f查找:
一共有12个匹配对象,如何确定位置?除了经验,可以找参数比较独立的,比如a.salt就不是,salt:a.i一般也不是。本人现在也是小白到入门状态,没啥经验,凭感觉,解密不了就用selenium。不过这个案例还得分析下去。
很明显,参数的值对应r,t,i,而这三个变量的值正好在上面。语法好像看不懂,影响不大,复制出来放到console中测试,注意只拿没有变量的部分。bv是一样的就不要分析了:
第一个式子可以大胆猜测是时间戳变化的,可以自己使用time模块打印一个对照下。第二个式子应该是0-9的随机整数,多输出几遍就能观察出规律。
e是单词,找到文件开头,发现并没有定义e就使用了,很明显是从其他地方传入的参数,只有单词是跟这些参数绑定的。
不放心或者没看懂可以进入调试模式,文件右键选择第一个:
ts = str(int(time.time()*1000))
salt = ts + str(random.randint(0, 10))
sign = hashlib.md5(bytes("fanyideskweb" + word + salt + "mmbP%A-r6U3Nw(n]BjuEU", encoding='utf-8')).hexdigest()
js解密可以看出不是件简单的事,要不精通算法,要不经验丰富。我们可以勇于尝试,但不要钻牛角尖,可以试试去掉某些参数,会发现其中有一些是干扰项,对请求结果没有影响。因此拿到一个url不要先想js解密,下面的css和base64、其他页面的数据传递。
完整代码如下,可参考:
import time, random
import hashlib
import requests
def main(word):
"""加密破解"""
base_url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
headers = {
'Origin': 'http://fanyi.youdao.com',
'Referer': 'http://fanyi.youdao.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': 'OUTFOX_SEARCH_USER_ID=1900023740@10.108.160.18; OUTFOX_SEARCH_USER_ID_NCOO=1880372795.73266; JSESSIONID=aaan4xJWxbJPbgdOOiqnx; ___rl__test__cookies=1594777517602',
}
ts = str(int(time.time()*1000))
salt = ts + str(random.randint(0, 10))
sign = hashlib.md5(bytes("fanyideskweb" + word + salt + "mmbP%A-r6U3Nw(n]BjuEU", encoding='utf-8')).hexdigest()
data = {
'i': word,
'from': 'AUTO',
'to': 'AUTO',
'smartresult': 'dict',
'client': 'fanyideskweb',
'salt': salt,
'sign': sign,
'ts': ts,
'bv': 'd6ea15a844a294021d16ba0a44b1d2a1',
'doctype': 'json',
'version': '2.1',
'keyfrom': 'fanyi.web',
'action': 'FY_BY_REALTlME',
}
response = requests.post(base_url, headers=headers, data=data)
print(response.json())
result = ''
for data in response.json()['smartResult']['entries']:
result += data
return result
if __name__ == '__main__':
word = input('请输入单词')
print(main(word))
需要注意一下一些请求头也是必须的,有时少了或者多了都请求不到数据。
大众点评CSS加密
获取整个页面很容易,但是会发现页面内容和页面显示的不一样,而且是下述图片中的格式,那么就要考虑是css加密:
第一步,进入到每个css文件查到字体文件:
点开,其中有一个css文件中开头是font-face@的字眼,找到了4个woff字体文件,那么程序下载字体文件需要先请求这个css,在截出woff文件的完整地址。
下载后可以使用网上的资源,这里使用下述软件打开:
使用fontTools模块的方法能取到对应的键,但是值只能手动输入了。。。
font = TTFont(css_dir+filename)
keys = font.getGlyphOrder()[2:]
完整代码如下:
import requests, os, re
from lxml import etree
from fontTools.ttLib import TTFont
def get_xpath(url):
"""获取页面的xpath对象"""
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
'Cookie': 'fspop=test; cy=4459; cye=nansha; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; _lxsdk_cuid=173508f8b33c8-02c70d039a1b2a-4353760-100200-173508f8b3346; _lxsdk=173508f8b33c8-02c70d039a1b2a-4353760-100200-173508f8b3346; _hc.v=941e9f6d-3616-a16a-9120-b3cc89d45a1d.1594784451; Hm_lvt_602b80cf8079ae6591966cc70a3940e7=1594784452; s_ViewType=10; Hm_lpvt_602b80cf8079ae6591966cc70a3940e7=1594784554; _lxsdk_s=173508f8b35-124-c94-297%7C%7C77',
}
response = requests.get(url, headers=headers)
# print(response.text)
# 获取css文件
tree = css_parse(response.text)
return etree.HTML(tree)
def css_parse(tree):
'''
对页面内容进行css解密
:param tree:
:return: 正确的页面内容
'''
# 字体文件的存放目录、
css_dir = './css_woff/'
# 判断css_woff有没有字体文件。
# listdir:获取一个文件夹中的所有文件名
filenames = os.listdir(css_dir)
# print(filenames)
if not filenames:
# 1、下载woof文件
filenames = download_woff(tree)
#4、读取字体文件,生成data字典。
data = dict()
for filename in filenames:
font = TTFont(css_dir+filename)
keys = font.getGlyphOrder()[2:]
# print(keys)
character = list(
'1234567890店中美家馆小车大市公酒行国品发电金心业商司超生装园场食有新限天面工'
'服海华水房饰城乐汽香部利子老艺花专东肉菜学福饭人百餐茶务通味所山区门药银农龙停尚安'
'广鑫一容动南具源兴鲜记时机烤文康信果阳理锅宝达地儿衣特产西批坊州牛佳化五米修爱北养'
'卖建材三会鸡室红站德王光名丽油院堂烧江社合星货型村自科快便日民营和活童明器烟育宾精'
'屋经居庄石顺林尔县手厅销用好客火雅盛体旅之鞋辣作粉包楼校鱼平彩上吧保永万物教吃设医'
'正造丰健点汤网庆技斯洗料配汇木缘加麻联卫川泰色世方寓风幼羊烫来高厂兰阿贝皮全女拉成'
'云维贸道术运都口博河瑞宏京际路祥青镇厨培力惠连马鸿钢训影甲助窗布富牌头四多妆吉苑沙'
'恒隆春干饼氏里二管诚制售嘉长轩杂副清计黄讯太鸭号街交与叉附近层旁对巷栋环省桥湖段乡'
'厦府铺内侧元购前幢滨处向座下県凤港开关景泉塘放昌线湾政步宁解白田町溪十八古双胜本'
'单同九迎第台玉锦底后七斜期武岭松角纪朝峰六振珠局岗洲横边济井办汉代临弄团外塔杨铁浦'
'字年岛陵原梅进荣友虹央桂沿事津凯莲丁秀柳集紫旗张谷的是不了很还个也这我就在以可到错'
'没去过感次要比觉看得说常真们但最喜哈么别位能较境非为欢然他挺着价那意种想出员两推做'
'排实分间甜度起满给热完格荐喝等其再几只现朋候样直而买于般豆量选奶打每评少算又因情找'
'些份置适什蛋师气你姐棒试总定啊足级整带虾如态且尝主话强当更板知己无酸让入啦式笑赞'
'片酱差像提队走嫩才刚午接重串回晚微周值费性桌拍跟块调糕')
keys = [k[-4:] for k in keys]
# print(keys)
for i, k in enumerate(keys):
if k not in data:
data[k] = character[i]
# 5、利用data字典,进行内容替换
for k, v in data.items():
tree = tree.replace(f'&#x{k};', v)
# print(tree)
return tree
def download_woff(tree):
"""下载woff文件"""
# 1、先取找到页面中css连接
css_url = re.search(r'href="(//s3plus\.meituan\..*?\.css)"', tree, re.S).group(1)
# 2、请求连接,从响应中获取字体文件的url
css_content = requests.get('http:' + css_url).text
# print(css_content)
# 3、下载字体文件
woff_urls = re.findall(r',url\("(//s3plus.*?\.woff)"\);\}', css_content, re.S)
filenames = []
for url in woff_urls:
filename = re.split('/', url)[-1]
# print(filename)
content = requests.get('http:'+url).content
with open(f'./css_woff/{filename}', 'wb') as fp:
fp.write(content)
if filename not in filenames:
filenames.append(filename)
return filenames
def content_none(s):
"""内容为空设为无"""
if not s:
s = ['无']
return s[0]
def check_save(tree):
"""使用xpath截取内容,并保存到字典中"""
store_list = tree.xpath('//*[@id="shop-all-list"]/ul/li')
for li in store_list:
store_dict = dict()
name = content_none(li.xpath('.//h4/text()'))
score = content_none(li.xpath('./div[2]/div[2]/div/div[2]/text()'))
type = ''.join(content_none(li.xpath('.//*[@class="tagName"]/text()')))
print(name, score, type)
def main():
"""主任务"""
url = 'https://www.dianping.com/nansha/ch10/g1783'
tree = get_xpath(url)
# print(tree)
# 截取并保存商店信息
check_save(tree)
if __name__ == '__main__':
infos = list()
main()
base64加密
爬取的案例是安居客的租房价格,跟上述案例一样的状况,但是在css文件中并没有想要的字体文件,仔细观察,font-face在源代码中存在;因为安居客只对10个数字加密,文件不大就放在了源代码中。但是不能直接使用上述案例的代码,因为源代码中是base64字节数据,并不是woff字体文件,想要解码后保存到字体文件再用css解码。
# 获取字体文件内容
font_str = re.search(r'base64,(.*?)\'\)', content, re.S).group(1)
print(font_str)
# 使用base64将字符串转为二进制
font_byte = base64.b64decode(font_str)
# print(font_byte)
with open('base.woff', 'wb') as fp:
fp.write(font_byte)
与上述案例不同的是数据保存的位置也是不一样的,想要使用内存流保存到写命令文件进行查看:
# 将二进制转换为内存流保存到xml文件中
font = TTFont(BytesIO(font_byte))
font.saveXML('font.xml')
因此取键时用的方法是:
keys = font.getBestCmap()
数字和name后的数字可以发现差1。还需要将二进制转换为十六进制才是页面内容的数据。使用字典推导式完成创建字典。
keys = {hex(k)[-4:]:str(int(v[-2:])-1) for k, v in keys.items()}
完整代码如下:
import requests, re
from lxml import etree
from fontTools.ttLib import TTFont
import base64
from io import BytesIO
def base64_parse(content):
"""使用base64解密"""
# 获取字体文件内容
font_str = re.search(r'base64,(.*?)\'\)', content, re.S).group(1)
print(font_str)
# 使用base64将字符串转为二进制
font_byte = base64.b64decode(font_str)
# print(font_byte)
with open('base.woff', 'wb') as fp:
fp.write(font_byte)
# 将二进制转换为内存流保存到xml文件中
font = TTFont(BytesIO(font_byte))
font.saveXML('font.xml')
keys = font.getBestCmap()
keys = {hex(k)[-4:]:str(int(v[-2:])-1) for k, v in keys.items()}
print(keys)
# 替换
for k, v in keys.items():
content = content.replace(f'&#x{k};', v)
return content
def get_xpath(url):
"""请求获取内容,解密,返回xpath对象"""
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
'cookie': 'sessid=3848F726-A5B6-1FEB-0C8B-B2600C01A781; aQQ_ajkguid=41A6562E-9ADD-9975-3120-8FDF0370D208; lps=http%3A%2F%2Fguangzhou.anjuke.com%2Fsale%2F%3Fpi%3Dbaidu-cpc-gz-ty3%26kwid%3D2375552275%26bd_vid%3D10424599839857223901%7Chttps%3A%2F%2Fwww.baidu.com%2Fbaidu.php%3Fsc.K60000avpXkFvm720QFa41bDvZ9FQdt7DZBtg52wy-gIxlaXI7zLXGgTAUYQ4YE9b2jr9q83qbUaXx1SgOQ_deAKUZXrRUYZQuoS3iCBcsbtp-mtR5XkOO1kw83s4fqIHVB2m8K3JuIIdNPPW3jS3CcOb7y8MJJZkT1701UTNqI-k1q7jHZbNzULvtH-ysSF3B9ck6DIvBDauNCglgIkTEdUDHC1.DD_NR2Ar5Od66dmATLjReK4nkuTZp-DDUP7nZxAqBqM761s33TSo91YPZZovtAFWFktIveIbzXr4i_nYQ7xWELe-.U1Yk0ZDq_Q2SYeOPVeORVPj71lc0TA-W5HD0Ijv43qUIVeORV0KGUHYznWR0u1dEugK1nfKdpHdBmy-bIfKspyfqP0KWpyfqrjf0UgfqnH0krNtknjDLg1DsPjwxn1msnfKopHYs0ZFY5HmLn0KBpHYkPH9xnW0Yg1ckPsKVm1Yknj0kg1D3PHR3rHTdPWPxnHc3nWm4PWn3ndts0Z7spyfqn0Kkmv-b5H00ThIYmyTqn0K9mWYsg100ugFM5H00TZ0qnW6Lnjf4rHDvn0K8IM0qna3snj0snj0sn0KVIZ0qn0KbuAqs5H00ThCqn0KbugmqTAn0uMfqn0KspjYs0Aq15H00mMTqnH00UMfqn0K1XWY0mgPxpywW5gK1QyPV0A-bm1dcfbD0IA7zuvNY5Hnkg1nkPNtkPWb0IZN15HD4PW6dnjnvnHTsPj0vPj6zPHc0ThNkIjYkPHbYP1bLPH0LP1Dk0ZPGujdhnjT4ujcLPj0snjRLnj-b0AP1UHd7PbNDrHT1PRRYP1c3fHFj0A7W5HD0TA3qn0KkUgfqn0KkUgnqn0KbugwxmLK95H00XMfqnfKVmdqhThqV5HKxn7tknjfvg100uA78IyF-gLK_my4GuZnqn7tsg1Kxn0Ksmgwxuhk9u1Ys0AwWpyfqnH0Ln1TYnH6zP0K-IA-b5iYk0A71TAPW5H00IgKGUhPW5H00Tydh5H00uhPdIjYs0A-1mvsqn0KlTAkdT1Ys0A7buhk9u1Yk0Akhm1Ys0AwWmvfq0Zwzmyw-5HR4njckPsKBuA-b5HcYrHm1fWIKwbRdrHN7nD7jnbfdnRPjrRNaPjDLnHKa0AqW5HD0mMfqn0KEmgwL5H00ULfqnfKETMKY5HcWnanznanzc1R1nHb1n1fvc1nYnan1Pj08nj0snj0sc1DWnBnsczYWna31nW01nHcWni3znH0knj00TNqv5H08rjFxna3sn7tsQW0sg108P1uxna3Ln7tsQWn10AF1gLKzUvwGujYs0APzm1Y1P16krf%26word%3D%26ck%3D3825.1.127.221.200.208.197.413%26shh%3Dwww.baidu.com%26sht%3Dbaidu%26us%3D1.0.1.0.1.348.0%26bc%3D110101; ctid=12; twe=2; id58=e87rkF8OrckXaXGSCxq+Ag==; 58tj_uuid=a27439c5-1bfc-42b4-927c-f7ac0d5de97e; _ga=GA1.2.748456254.1594797515; _gid=GA1.2.1497413756.1594797515; als=0; xxzl_cid=f81e3c5395c144499edb69b71876e869; xzuid=1183fe44-9b38-4638-a025-a161337cc6af; wmda_uuid=66087cead6b942869460861a6faf2f16; wmda_new_uuid=1; wmda_visited_projects=%3B6289197098934; xzfzqtoken=tW3hEazrpz8eBNHNkYvbCd2VaS%2B45jP3Pbgc3qf4LeaeSznlSNpgDEAwXlGa3SMCin35brBb%2F%2FeSODvMgkQULA%3D%3D; __xsptplusUT_8=1; _gat=1; new_session=1; init_refer=https%253A%252F%252Fwww.baidu.com%252Fbaidu.php%253Fsc.K60000avpXkFvm720QFa41bDvZ9FQdt7DZBtg52wy-gIxlaXI7zLXGgTAUYQ4YE9b2jr9q83qbUaXx1SgOQ_deAKUZXrRUYZQuoS3iCBcsbtp-mtR5XkOO1kw83s4fqIHVB2m8K3JuIIdNPPW3jS3CcOb7y8MJJZkT1701UTNqI-k1q7jHZbNzULvtH-ysSF3B9ck6DIvBDauNCglgIkTEdUDHC1.DD_NR2Ar5Od66dmATLjReK4nkuTZp-DDUP7nZxAqBqM761s33TSo91YPZZovtAFWFktIveIbzXr4i_nYQ7xWELe-.U1Yk0ZDq_Q2SYeOPVeORVPj71lc0TA-W5HD0Ijv43qUIVeORV0KGUHYznWR0u1dEugK1nfKdpHdBmy-bIfKspyfqP0KWpyfqrjf0UgfqnH0krNtknjDLg1DsPjwxn1msnfKopHYs0ZFY5HmLn0KBpHYkPH9xnW0Yg1ckPsKVm1Yknj0kg1D3PHR3rHTdPWPxnHc3nWm4PWn3ndts0Z7spyfqn0Kkmv-b5H00ThIYmyTqn0K9mWYsg100ugFM5H00TZ0qnW6Lnjf4rHDvn0K8IM0qna3snj0snj0sn0KVIZ0qn0KbuAqs5H00ThCqn0KbugmqTAn0uMfqn0KspjYs0Aq15H00mMTqnH00UMfqn0K1XWY0mgPxpywW5gK1QyPV0A-bm1dcfbD0IA7zuvNY5Hnkg1nkPNtkPWb0IZN15HD4PW6dnjnvnHTsPj0vPj6zPHc0ThNkIjYkPHbYP1bLPH0LP1Dk0ZPGujdhnjT4ujcLPj0snjRLnj-b0AP1UHd7PbNDrHT1PRRYP1c3fHFj0A7W5HD0TA3qn0KkUgfqn0KkUgnqn0KbugwxmLK95H00XMfqnfKVmdqhThqV5HKxn7tknjfvg100uA78IyF-gLK_my4GuZnqn7tsg1Kxn0Ksmgwxuhk9u1Ys0AwWpyfqnH0Ln1TYnH6zP0K-IA-b5iYk0A71TAPW5H00IgKGUhPW5H00Tydh5H00uhPdIjYs0A-1mvsqn0KlTAkdT1Ys0A7buhk9u1Yk0Akhm1Ys0AwWmvfq0Zwzmyw-5HR4njckPsKBuA-b5HcYrHm1fWIKwbRdrHN7nD7jnbfdnRPjrRNaPjDLnHKa0AqW5HD0mMfqn0KEmgwL5H00ULfqnfKETMKY5HcWnanznanzc1R1nHb1n1fvc1nYnan1Pj08nj0snj0sc1DWnBnsczYWna31nW01nHcWni3znH0knj00TNqv5H08rjFxna3sn7tsQW0sg108P1uxna3Ln7tsQWn10AF1gLKzUvwGujYs0APzm1Y1P16krf%2526word%253D%2526ck%253D9405.5.88.278.213.208.197.176%2526shh%253Dwww.baidu.com%2526sht%253Dbaidu%2526bc%253D110101%2526us%253D1.1953.3.0.1.348.0.0; new_uv=2; __xsptplus8=8.2.1594800613.1594800613.1%232%7Cwww.baidu.com%7C%7C%7C%7C%23%23_jrkSlogPk4zsxqsvVkWV24QlL_ja8li%23; wmda_session_id_6289197098934=1594800616402-3e54aece-959f-1a24',
}
response = requests.get(url, headers=headers)
# print(response.text)
# base64解密
tree = base64_parse(response.text)
# print(tree)
return etree.HTML(tree)
def main():
"""主线任务"""
base_url = 'https://gz.zu.anjuke.com/?from=navigation'
# 得到解密后的xpath对象
tree = get_xpath(base_url)
div_list = tree.xpath('//div[@class="zu-itemmod"]')
# print(div_list)
for div in div_list:
title = div.xpath('.//div[@class="zu-info"]/h3/a/b/text()')[0]
price = div.xpath('.//div[@class="zu-side"]/p/strong/b/text()')[0]
print(title, price)
if __name__ == '__main__':
main()
解密的过程就跟密室逃脱似的,想要顺着主线寻找线索,最后串起来组成开锁密码,过程痛并快乐着,加油!!