温馨提示:
爬虫玩得好,监狱进得早。数据玩得溜,牢饭吃个够。
《刑法》第 285 条,非法获取计算机信息系统数据罪。
违反国家规定,侵入前款规定以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,或者对该计算机信息系统实施非法控制,情节严重的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。
正文:
requests.post()
适用场景
【1】适用场景 : Post类型请求的网站
【2】参数 : data={}
2.1) Form表单数据: 字典
2.2) res = requests.post(url=url,data=data,headers=headers)
【3】POST请求特点 : Form表单提交数据
控制台抓包
打开方式及常用选项
【1】打开浏览器,F12打开控制台,找到Network选项卡
【2】控制台常用选项
2.1) Network: 抓取网络数据包
a> ALL: 抓取所有的网络数据包
b> XHR:抓取异步加载的网络数据包
c> JS : 抓取所有的JS文件
2.2) Sources: 格式化输出并打断点调试JavaScript代码,助于分析爬虫中一些参数
2.3) Console: 交互模式,可对JavaScript中的代码进行测试
【3】抓取具体网络数据包后
3.1) 单击左侧网络数据包地址,进入数据包详情,查看右侧
3.2) 右侧:
a> Headers: 整个请求信息
General、Response Headers、Request Headers、Query String、Form Data
b> Preview: 对响应内容进行预览
c> Response:响应内容
有道翻译破解案例(post)
最终的效果,就是运行完控制台的效果:
# 结果展示
请输入要翻译的词语: elephant
翻译结果: 大象
*************************
请输入要翻译的词语: 喵喵叫
翻译结果: mews
接下来我们就看一下抓包的步骤,我们这个有道翻译不仅是post 请求的方式,还是动态数据加载,是json数据类型
比如我们先翻译一个吧
这个你看我们要是想抓到翻译结果英文在源代码中显示,这是不可能的对吧,这个绝对是一万个没有的:
而且吧,你翻译完了,你说看IP地址的变化,那也没有变换啊,你说要是有点变化,我们还能捕捉一下它的规律是吧,但这也不会又变化,所以我们只能是F12抓包
如果要抓包,我们就得知道,我们在翻译这段话的时候,客户端和web服务器之间有那些网络数据包的交互,我发的什么请求,返回的又是什么响应,最终返回到页面上,所以我们就直接抓包,点击F12
刷新一下,点击Network选项卡,因为我们再刷新的时候,我们就可以抓到所有我们可web服务器之间所有交互的数据包
我们看这三个,解释一下:
ALL:所有的数据包,比如:png、css、js、ajax…
XHR:异步数据包
JS:js文件数据包
我们主要看这三个
接下来我们看ALL里面的这个,因为我们怀疑他就是响应翻译结果的数据包:
这个就是整个有道翻译的主页,我们点击它
看这个选项,这个就是post请求数据都在这里,在他的右面我们点开看到这是就是响应页面,中间的Preview就是预览,这个是没有翻译结果的,好了我们这个就是看一下这里面的各个选项的功能作用
接下来我们看看有没有异步的,就是XHR
我们看到有一条数据包,我们点开它,但是我们也是不知道是不是返回数据的异步包,我们先看Preview
可以看到啊,有我们翻译的这句话,我们确认了,这就是我们要找的,接下来需要分析一下,我们在Headers中分析,因为它是整个请求的信息啊,,所以我们转到Headers
接着我们往下看,这两个暂时都不用看呢
无非你想处理的和浏览器一样,就跟我们headers里面cookie什么的都弄成一样的,接着我们看这个查询参数:
这个无非就是把请求url里面的 ?后面的东西给你格式化输出了,就是让你好看,好找怎么加密的,就是格式化输出,这个也无所谓了,下一个,就要好好看看了Form Data表单数据:
这里是我们的翻译的那句话,下面就是发送了框里面的数据到上面url请求地址,最后返回到Preview里,把翻译的结果提出来最终显示在页面中,这就是整个过程,好的接下来就可以这样写代码:
html=requests.post(url=url,data=data,headers=headers).content.decode("utf-8",'ignore')
这个URL就是我们抓包抓到的url,就是上面的截图里面的url,而data就是我们Form Data里面的数据,其他的就是正常走了,其实我们还是没有弄完呢,我们看data数据:
我们最终是不是要把这个里面的数据定义到字典里面啊,前面还好说,但是我们看这个
这些应该是类似于一个时间戳的东西把,这每次翻译都是不一样的把,我们看sign这个是一个md5加密的字符串包括下面的bv也是加密的,我们也不知道它是通过什么加密的,你说它是给翻译单词加密?这是猜的,人家不可能给单词加密,因为太简单了,那像这样的东西,就得知道它是怎么生成的把,一般来说它是通过本地JS生成的,但是你要找到是那个js,js里面的哪段代码,再然后它的加密方式是什么样的,再想办法破解,所以我们以后在遇到这种,就要知道进行下一步的分析了
我们接着分析加密那个地方,我们在看翻译一次,对比Form Data里面的数据那些是一样的,那些又是不一样的:
通过对比,暂时不看加密的那四个,其他的都一样,那我们在看加密的那四个,出了bv其他的都不一样,那我就知道这个使用了一个固定的字符串,进行md5加密,我们怎么知道是md5加密,因为它是32位,根据固定字符串加密,所以每次都一样
接着我们点开JS的选项卡:找到js文件,因为我们在XHR里面有些东西不知道是怎么生成的,那就只能找js文件了:
那这个js文件太多了,我们也不知道哪个是怎么办,这时候我们可以直接==Ctrl + Alt + f==搜一下,这样就出现了一个搜索框,我们找那个关键字,就搜sign关键字吧,你搜别的关键字也行,这样就过滤掉一部分js文件,更简单找到包含关键字的js文件结果会显示在下面:
这个文件就是包含sign关键字的文件,那我们得找到这个文件里面包含关键字的代码段啊,所以我们单击找到的文件:
这样他就自动跳到Sources选项卡了我们在单击这个花括号,就格式化输出了,方便我们看了
它就会格式化输出代码方便我们看了:
我们这个搜到的代码有九千多行,我们只能再次搜关键字sign,直接 Ctrl + f,搜sign会看到有很多,所以我们要看差不多的关键字,有的关键字在路径里面,有的是在变量什么点什么,所以最终我们搜到这里
大概在8376左右行,这样,也看到了md5加密的,相比别的是会好一点的,起码它像,对吧,好我们接着分析,这样,我们看上面的ts:r,通过 r 找到在上面r = “” + (new Date).getTime()这个东西就是获取时间戳的,如果不知道,那你还是先去学习js吧,那接着我们复制这段代码到Console选项卡中输出一下:
是不是每个都不一样,这是一个13位的时间戳,当然python也可以生成时间戳,就是这样导入time模块后,输出 str(int(time.time() * 1000)) 这样就是一个13位的时间戳,好多网站其实都有时间戳
接着我们看bv:t 这个 t ,在上面是定义的一个加密定义的变量,后面是n.md5,这个 n 我们也不知道它是什么东西,在上面还有好多n,我们就不知道了,也没有必要了解了,我们就知道,这个n是加密后面的东西,赋值给了 t ,那我们就在Console选项卡中在打印一下这个
这个打印出来,我们看着很眼熟,这就是User-Agent的一部分吧,所以我们这个对应的bv 是不变的,这个就是对应User-Agent字符串加密的,所以不变,我们也不用管它了
我们接着看这个salt
它对应着 i , 这个 i 又被上面的 r 赋值,反正就是各种绕,那个 r 的话,我们刚才说了,就是一个时间戳 ,但是后面又给了这个东西:parseInt(10 * Math.random(), 10), 这个好像是随机数吧,因为它出现了一个random,又出现了数字,但是我们不知道,我们接着 Console:
这样我们就知道了,他就是一个随机生成0-9的数字,但这个应该是字符串的啊,因为,我们上面的 r 是一个字符串的时间戳,总不能让一个字符串去加数字吧,所以应该也是字符串
所以到现在我们可以发现一个规律,就是这个,图有点乱,但是没办法,下面我在解释:
我们看 r 是一个13 位的时间戳,也就是ts,是一个13位的时间戳;而这个 salt ,也就是ts 13位的时间戳加上一个0-9的随机数,也就是说,ts和salt前13位是一样的也就是最后一位不相同,那我们看一下:
确实如我们发现的,前13位是相同的,最后一位是不同的
接着我们看sign:
我们看这个图中标注的箭头,这都是固定的字符串不用管,而中间的 i 我们刚才弄明白了,我们主要看这个 e,哎呀完了,好像这个 e 我们不太好去找它是干嘛的了吧,那这时候我们就直接打断点,就在它这行打一个断点,因为这个程序运行到断点的这行就会停,我们好分析它下一步是怎么走的,那次是我们需要再次翻译一个单词,才可以:
可以看到翻译停止了,通过这样我们就知道,这个 e 就是我们翻译的单词,把鼠标放到e 上:
能看到这就是我们要翻译的单词,这时候我们就知道了sign 就是它前面的字符串加上e(要翻译的单词) 在加上i(时间戳) ,最后在加一个固定的字符串,对这几个东西进行md5加密,好了我们分析完了,接下来就是写代码了,但是写代码之前我们得对Form Data 里面的数据处理一下,处理成字典,因为里面的东西都是我们写代码的必需品,还有headers里面的参数,也得用浏览器的,所以这两个,我们批量处理一下:
我们复制里面的东西到pycharm里创建一个文件,放进去:
框里面所有的数据,我们处理成这样:
这个是Request Headers里面的,里面有很多东西我们用不到,因为需要严谨一点,嘿嘿,我就全整上了,如果不想整,就只整:Cookie、Referer、User-Agent 就好了
这个是 Form Data里面的
注意:
这两个处理成字典的数据保存起来,写代码会用到
1、这两个是用引号引起来的,我这里用的双引号,如果引完,有出红色下划线的,就说明有的数据使用双引号引起来的,那就用三引号
2、引号的后面,不能有空格!!!
接下来我们写代码:
导入模块
import requests
import time
import random
from hashlib import md5 # 加密
import json # 里面会有用到json串的
定义函数
class YdSpider(object):
def __init__(self):
# url一定为F12抓到的 headers -> General -> Request URL
self.url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
# 用到了我们刚刚转的headers字典
self.headers = {
# 检查频率最高 - 3个
"Cookie": "OUTFOX_SEARCH_USER_ID=-441685778@103.102.194.226; OUTFOX_SEARCH_USER_ID_NCOO=1311232133.5140777; _ntes_nnid=a6a6cfcf2f13779cd11ad8e2ca44836d,1595664981237; _ga=GA1.2.1866744817.1598613276; JSESSIONID=aaavr_8IVn9yc9P3to-wx; SESSION_FROM_COOKIE=unknown; ___rl__test__cookies=1605238339474",
# 这个Referer是指,从哪个网站跳转过来的
"Referer": "http://fanyi.youdao.com/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36",
}
md5加密函数
不懂得去自行解决一下喽~~
def md5_word(self, string):
s = md5()
s.update(string.encode())
return s.hexdigest()
定义ts、salt、sign
如果忘了这三个是什么了,再去看看上面啊,写这个就是用Python代码,代替js代码
# word就是要翻译的单词,给它传入进去
def get_ts_salt_sign(self, word):
ts = str(int(time.time() * 1000))
salt = ts + str(random.randint(0, 9))
string = "fanyideskweb" + word + salt + "]BjuETDhU)zqSxf-=B#7m"
sign = self.md5_word(string)
# 要把这是三个参数都要return出去,因为都得用到
return ts, salt, sign
最后一个函数:
获取我们定义的ts、salt、sign
def attack_yd(self, word):
# 获取ts,salt,sign
ts, salt, sign = self.get_ts_salt_sign(word)
# 这就是我们刚才的data字典,这里用到了
data = {
# "i": 翻译的单词
"i": word,
"from": "AUTO",
"to": "AUTO",
"smartresult": "dict",
"client": "fanyideskweb",
# 下面三个,换成我们之前定义的
# salt sign : 一定需要处理
"salt": salt,
"sign": sign,
"lts": ts,
"bv": "a85763c72b3babd7639e5d3df56ae30c",
"doctype": "json",
"version": "2.1",
"keyfrom": "fanyi.web",
"action": "FY_BY_REALTlME",
}
html = requests.post(url=self.url,
data=data,
headers=self.headers).text
# json.loads():json格式字符串转为pyhton数据类型
# 发起请求
html = json.loads(html)
result = html['translateResult'][0][0]['tgt']
return result
最后程序入口函数,用来控制整体逻辑:
def run(self):
word = input('请输入翻译的单词:')
print(self.attack_yd(word))
最后奉上全部代码:
import requests
import time
import random
from hashlib import md5
import json
class YdSpider:
def __init__(self):
# url: F12抓包抓到的POST的地址
self.url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
self.headers = {
# 检查频率最高的三个字段:Cookie Referer User-Agent
"Cookie": "OUTFOX_SEARCH_USER_ID=-441685778@103.102.194.226; OUTFOX_SEARCH_USER_ID_NCOO=1311232133.5140777; _ntes_nnid=a6a6cfcf2f13779cd11ad8e2ca44836d,1595664981237; _ga=GA1.2.1866744817.1598613276; JSESSIONID=aaavr_8IVn9yc9P3to-wx; SESSION_FROM_COOKIE=unknown; ___rl__test__cookies=1605238339474",
"Referer": "http://fanyi.youdao.com/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36",
}
def md5_word(self, string):
"""md5加密函数"""
s = md5()
s.update(string.encode())
return s.hexdigest()
def get_ts_salt_sign(self, word):
"""获取ts salt sign"""
ts = str(int(time.time() * 1000))
salt = ts + str(random.randint(0, 9))
string = "fanyideskweb" + word + salt + "]BjuETDhU)zqSxf-=B#7m"
sign = self.md5_word(string)
return ts, salt, sign
def attack_yd(self, word):
# 获取ts,salt,sign
ts, salt, sign = self.get_ts_salt_sign(word)
data = {
"i": word,
"from": "AUTO",
"to": "AUTO",
"smartresult": "dict",
"client": "fanyideskweb",
# salt sign : 一定需要处理
"salt": salt,
"sign": sign,
"lts": ts,
"bv": "a85763c72b3babd7639e5d3df56ae30c",
"doctype": "json",
"version": "2.1",
"keyfrom": "fanyi.web",
"action": "FY_BY_REALTlME",
}
html = requests.post(url=self.url,
data=data,
headers=self.headers).text
# json.loads():json格式字符串转为pyhton数据类型
html = json.loads(html)
result = html['translateResult'][0][0]['tgt']
return result
def run(self):
word = input('请输入翻译的单词:')
print(self.attack_yd(word))
if __name__ == '__main__':
spider = YdSpider()
spider.run()
好了,自己试试吧!