之前Info函数的封装中使用了一个POST的爬取过程得到了英文翻译的结果,这里针对POST的过程做进一步的分析和讲解,供大家进一步了解POST在企业实战中的使用和爬虫破解的细节。
大家对于一个那个诡异的URL链接 "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null" 可能不知从何而来,而如果直接使用POST数据包中的链接"http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule", 发现并不能得到正确的结果,原因不具体分析了。
从POST的数据包中,我们可以看到:
# 把form数据转规范化,然后post给服务端
formdata = {
"i":key,
"from":"AUTO",
"to":"AUTO",
"smartresult":"dict",
"client":"fanyideskweb",
"salt":"1523933959290",
"sign":"248f5d216c45a64c38a3dccac0f4600d",
"doctype":"json",
"version":"2.1",
"keyfrom":"fanyi.web",
"action":"FY_BY_REALTIME",
"typoResult":"false"
}
这里的salt和sign明显是数字加密技术的点(salt是加密中的加盐操作,sign是一个身份验证),所以这个网站之后没法正常抓取,关键就是这里的salt和sign被加密了。
我原来就讲过,其实爬虫本质上是一种黑客技术,这里毫无疑问,有道采用了一些加密的处理。而根据我们之前的原理分析可知,这个加密的逻辑事实上肯定隐藏在客户端的浏览器中了。
那我们这里就模拟JS的过程来分析一下(这个过程比较自然,大部分人都能掌握),当然这需要一些前端的HTML和JavaScript的知识作为准备。
我们来看一下有道翻译的主页:http://fanyi.youdao.com/
打开主页,查看源码,通过搜索,我们可以看到:
这里有3个js的脚本,我们稍加分析,可以知道核心的代码隐藏在fanyi.min.js这个js文件中。
具体代码如下:
var r = function(e) {
var t = "" + ((new Date).getTime() + parseInt(10 * Math.random(), 10));
return {
salt: t,
sign: n.md5("fanyideskweb" + e + t + "6x(ZHw]mwzX#u0V7@yfwK")
}
};
当然,我们需要一定的JS知识来阅读这个代码,很容易读懂这段代码,这个salt,有当前时间戳,一个random随机值的和产生;
而这个sign则是一段信息的md5值。
这里忽略细节,我们给出Python对此的模拟过程:
def getSalt(): # 得到加盐信息
return str(int(time.time()*1000)+random.randint(0,11))
def getSign(key,salt):# 得到sign信息
sign = ("fanyideskweb" + key + salt + "6x(ZHw]mwzX#u0V7@yfwK")
hashObj = hashlib.md5()
hashObj.update(sign.encode("utf-8"))
return hashObj.hexdigest()
有了这两个破解的关键点,我们就可以使用正常的抓包模拟POST的过程来完成有道翻译的过程(这个翻译对中英文都支持)
,从而来完成一个在线词典的功能:
# -*- coding: utf-8 -*-
"""
Created on Tue Apr 17 11:01:28 2018
@author: Administrator
"""
import urllib
import json
import time
import random
import hashlib
def getSalt():
return str(int(time.time()*1000)+random.randint(0,11))
def getSign(key,salt):
#print(key)
#print(salt)
sign = ("fanyideskweb" + key + salt + "6x(ZHw]mwzX#u0V7@yfwK")
hashObj = hashlib.md5()
hashObj.update(sign.encode("utf-8"))
return hashObj.hexdigest()
# 设置一个退出程序的出口
isOut = False
# 不断调用爬取翻译页面的功能
#直到isOut被设置为True,退出程序
while True:
if isOut == True:
break
#假定用户输入“CloseMe”,则退出
key = input("请输入需要翻译的文字,\
输入CloseMe表示退出:\n")
if key == "CloseMe":
isOut = True
continue #回到循环开始处,然后结果条件满足退出
# 做真正的查询操作
url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
# http://tool.chinaz.com/js.aspx
# 把form数据转规范化,然后post给服务端
saltInfo = getSalt()
formdata = {
"i":key,
"from":"AUTO",
"to":"AUTO",
"smartresult":"dict",
"client":"fanyideskweb",
"salt":saltInfo,
"sign":getSign(key,saltInfo),
"doctype":"json",
"version":"2.1",
"keyfrom":"fanyi.web",
"action":"FY_BY_REALTIME",
"typoResult":"false"
}
data = urllib.parse.urlencode(formdata).encode()
# 给服务器发送post请求
# 构造headers
headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
"X-Requested-With": "XMLHttpRequest",
"Accept-Language":"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Content-Length":len(data),
"Cookie":"YOUDAO_MOBILE_ACCESS_TYPE=1; OUTFOX_SEARCH_USER_ID=1547024714@106.38.155.125; OUTFOX_SEARCH_USER_ID_NCOO=305129980.33",
"Connection":"keep-alive",
"Accept":"application/json, text/javascript, */*; q=0.01",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Host":"fanyi.youdao.com",
"Referer":"http://fanyi.youdao.com/"
}
req = urllib.request.Request(url,
data,
headers,
method="POST")
response = urllib.request.urlopen(req)
info = response.read().decode("utf-8")
# json decode: json str --> dict
jsonLoads = json.loads(info)
print(jsonLoads['translateResult'][0][0]["src"])
print(jsonLoads['translateResult'][0][0]["tgt"])