记一次反爬虫破解


写在前面

参考文献来自csdn梦想橡皮擦,本次尝试的是网易有道词典的接口,这类教程网上有很多,但大多过时了,也不敢确定本文的有效性能保持多久,所以记录代码的同时,一并记录下方法。有道的反爬原理是生成本地随机变量进行验证,如果不能摸清随机变量的生成规则,就只能收到{'errorcode':'50'},另外,有道对请求头也有一定要求。

用到的包

#发起网页请求
import requests
#对字符进行md5加密
import hashlib
#获取时间戳
import time
#生成随机数
import random

获取随机变量

#translate为要翻译的内容
def generate_salt_sign(translate):
    #在浏览器控制台(F12-Console)中输入"navigator.appVersion"
    app_version = "5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36"
    #对上面获取到的内容进行md5加密
    bv = hashlib.md5(app_version.encode(encoding='utf-8')).hexdigest()
    #获取当前时间戳,乘以1000,然后取整
    lts = str(int(1000 * time.time()))
    #lts加上0-10的一个随机字符
    salt = lts + str(random.randint(0,10))
    #sign为一个常量+要翻译的内容+salt+一个常量,然后进行md5加密
    sign = hashlib.md5(("fanyideskweb" + translate + salt + "]BjuETDhU)zqSxf-=B#7m").encode(encoding='utf-8')).hexdigest()
    return salt,sign,lts,bv

请求参数

def params():
    #空字典(Form Data)
    data = {}
    #要翻译的内容
    translate = 'hello'
    data['i'] = translate
    #默认值
    data['from'] = 'AUTO'
    data['to'] = 'AUTO'
    data['smartresult'] = 'dict'
    #"fanyideskweb"即为上面的第一个常量
    data['client'] = 'fanyideskweb'
    #获取变量并存入字典
    data['salt'],data['sign'],data['lts'],data['bv'] = generate_salt_sign(translate)
    #以下皆为默认值
    data['doctype'] = 'json'
    data['version'] = '2.1'
    data['keyfrom'] = 'fanyi.web'
    data['action'] = 'FY_BY_REALTlME'
    data['typoResult'] = 'false'
    #时间戳的整数类型,用于构造Cookie
    temp = int(data['lts'])

    return data,temp

主要函数

def tran():
    #有道的接口url地址
    url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
    #获取参数字典
    data,temp = params()
    #构造请求头,Cookie末尾的字符串为当前时间戳减5
    headers = {
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Connection': 'keep-alive',
        'Content-Length': str(len(data)),
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Cookie': 'OUTFOX_SEARCH_USER_ID=-263132750@10.108.160.101; JSESSIONID=aaaN1fFGaN7vmh3Msh_xx; OUTFOX_SEARCH_USER_ID_NCOO=914767987.7880249; ___rl__test__cookies={}'.format(temp - 5),
        'Host': 'fanyi.youdao.com',
        'Origin': 'http://fanyi.youdao.com',
        'Referer': 'http://fanyi.youdao.com/',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
        'X-Requested-With': 'XMLHttpRequest'
    }
    #发起post请求
    result = requests.post(url = url,headers = headers,data = data)
    #输出获取的内容,可以使用json模块将其格式化
    print(result.text)

查找对应参数

本站对于图片的支持不是很好,我在这里简单口述一下方法,首先打开有道,F12打开开发者工具,选择Network-All,然后输入一些内容进行翻译,Network会自动刷新,选择Name

translate_o?smartresult=dict&smartresult=rule

点击Preview,即可看到翻译的内容,点击Headers,往下翻,即可看到Request Headers和Form Data,这就是我们发起post请求时传递的内容,有一部分是定值,主要变化的是salt、sign、lts这三项。接下来找到上述文件的Initiator属性,鼠标悬停,选择t.translate,点进去,再点点击左下角的{}对文本格式化,可以看到函数变得有规律了,这里记录着Form Data里的内容,可有几项仍然隐藏着,按CTRL+F打开搜索,查询var r = function,应该会匹配到两项,其中一个就记录着salt,sign,lts以及bv的生成规则(要注意sign的最后一个参数,如果跟本文不一样则进行替换),这是一个JavaScript函数,解析后可以发现正是上面generate_salt_sign函数的内容,使用python复现规则也不难。

代码的优化

实际上并不需要完全传递headers和data,删减尝试后对代码进行简化:

import requests
import hashlib
import time
import random

def generate_salt_sign(translate):
    lts = str(int(1000 * time.time()))
    salt = lts + str(random.randint(0,10))
    sign = hashlib.md5(("fanyideskweb" + translate + salt + "]BjuETDhU)zqSxf-=B#7m").encode(encoding='utf-8')).hexdigest()

    return salt,sign

def params():
    data = {}
    translate = input('请输入要翻译的内容:')
    data['i'] = translate
    data['client'] = 'fanyideskweb'
    data['salt'],data['sign'] = generate_salt_sign(translate)
    data['keyfrom'] = 'fanyi.web'

    return data

def tran():
    url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule"
    data = params()
    headers = {
        'Cookie': 'OUTFOX_SEARCH_USER_ID=-263132750@10.108.160.101;',
        'Referer': 'http://fanyi.youdao.com/',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
    }
    result = requests.post(url = url,headers = headers,data = data)
    print(result.text)
if __name__ == '__main__':
    tran()

其他问题

如果把接口url里面的"_o"去掉,则很容易就能获得返回的内容,但翻译的效果似乎会差一些。