一、反爬措施
- Content-Type
- 防盗链
- Cookie验证
- post_data参数加密
二、调试过程
1、确定加密参数
抓包,找到数据接口:http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule, post请求
post_data参数说明:
- 必须参数
- ‘i’: 翻译原文
- ‘client’: ‘fanyideskweb’,
- ‘salt’: 盐,
- ‘sign’: 加密参数,
- ‘version’: ‘2.1’, # 不带该参数请求,返回的结果中,翻译的原文丢失,只有翻译后的数据
- ‘keyfrom’: ‘fanyi.web’,
- 可选参数
- ‘from’: ‘AUTO’,
- ‘to’: ‘AUTO’,
- ‘smartresult’: ‘dict’,
- ‘ts’: 13位时间戳,
- ‘bv’: User-Agent值MD5加密,但服务器不进行验证,
- ‘doctype’: ‘json’,
- ‘action’: ‘FY_BY_REALTlME’
复制url、post_data、headers进行请求时,失败,观察headers
- Content-Type’: ‘application/x-www-form-urlencoded; charset=UTF-8’,
- 说明:body提交的数据需要按照 k1=v1&k2=v2 的方式进行编码,然后进行提交
因此重构post_data参数后,请求成功
from urllib import parse
body = parse.urlencode(post_data) # post_data为字典类型的数据
对headers进行删减,发现Cookie中必须携带OUTFOX_SEARCH_USER_ID字段,通过不停的测试,最终确定headers中的必须参数:
- ‘Content-Type’: ‘application/x-www-form-urlencoded; charset=UTF-8’,
- ‘Cookie’: ‘[email protected]’,
- ‘Referer’: ‘http://fanyi.youdao.com/’
- ‘User-Agent’
2、调试过程
(1)Cookie
清空缓存后,全局搜索OUTFOX_SEARCH_USER_ID对应的值,在请求:http://fanyi.youdao.com/ 时,response中存在该值,且每次清空缓存后,该值都会发生变化
(2)sign
全局搜索sign参数,找到js文件:http://shared.ydstatic.com/fanyi/newweb/v1.0.17/scripts/newweb/fanyi.min.js
生成sign的js代码如下:
发现post_data中的参数salt, sign, ts, bv都在字段r中,r=g.generateSaltSign(n), 通过调试找到生成r的对应参数,js代码如下
可见各参数的生成:
- bv: User-Agent参数的值MD5
- ts: 时间戳
- salt: 时间戳+随机数(0-9)
- sign: “fanyideskweb” + 待翻译数据 + ts + “@6f#X3=cCuncYssPsuRUE”, 前后两个字符串是固定的
python实现加密过程:
import hashlib
import random
def MD5(data_str):
object = hashlib.md5()
object.update(data_str.encode('utf-8'))
return object.hexdigest()
def get_youdao_sign(word, ts, user_agent=''):
salt = str(ts)+str(random.randint(0,10))
bv = MD5(user_agent)
sign = MD5("fanyideskweb" + word + salt + "@6f#X3=cCuncYssPsuRUE")
return salt, bv, sign
三、总结
1、注意点
- Content-Type
post请求时,需要关注该参数,不同的值对应不同的post数据格式,目前遇到的post_data的数据类型包括:字典dicts、json.dumps(dicts), ''dicts", “key1=val1&key2=val2” - Cookie
Cookie中的OUTFOX_SEARCH_USER_ID值不需要频繁更换,有效期很长
2、知识点
四、代码实现
url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
ts = int(time.time()*1000)
word = 'We will be together forever'
salt, bv, sign = get_youdao_sign(word, ts, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36')
post_data = {
'i': word,
'client': 'fanyideskweb',
'salt': salt,
'sign': sign,
'version': '2.1', # 该参数注释后,返回的结果中,翻译的原文丢失,只有翻译后的数据
'keyfrom': 'fanyi.web',
# 'from': 'AUTO',
# 'to': 'AUTO',
# 'smartresult': 'dict',
# 'ts': ts,
# 'bv': bv,
# 'doctype': 'json',
# 'action': 'FY_BY_REALTlME'
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': '[email protected]',
'Referer': 'http://fanyi.youdao.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
}
post_data = parse.urlencode(post_data)
response = requests.post(url, headers=headers, data=post_data)
print(response.text)