100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 有道翻译 爬虫 分析反爬 附代码

有道翻译 爬虫 分析反爬 附代码

时间:2020-03-20 11:40:06

相关推荐

有道翻译 爬虫 分析反爬 附代码

有道翻译 爬虫,破解反爬加密

0引言1准备工作开始第一部分开始第二部分准备工作,下载并安装requests模块确定反爬用的变量确定变量的值requests模块的使用

0引言

我在这里会很详细地说明一些常见的错误,虽然第一部分很基础,但依然有不少人经常会出现这些问题。这些经验是我不断搜索,总结,实验得来的,能让小白少走很多弯路。

小白可以先看第一部分,再看第二部分;嫌篇幅长的可以直接看第二部分。有疑问可以联系邮箱或留言:1131202803@

1、第一部分:使用python语言,对有道翻译进行爬虫,对固定词条爬取翻译结果,并显示。第一部分的代码块全部复制粘贴到py文件,即为完整代码

2、第二部分:破解反爬,使得应用可以爬取任意词条的翻译结果。第二部分的最后有完整代码

1准备工作

我们首先打开有道翻译的网址/,并输入翻译内容”I love China“。点击翻译,就出现了翻译内容。

现在的很多浏览器都有”检查“功能(我使用的是Microsoft Edge浏览器),在有道翻译网页空白处右击,出现快捷菜单,快捷菜单的最下面是”检查“。然后在跳出的页面点击”网络“按钮,没有网络按钮,可以点+号添加,之后点击”翻译按钮“,就能出现记录,我们找到一个"请求方法"为post的记录

我们滑动这条记录的”标头“页,可以看到有很多的数据,那它们都是干嘛的呢?

常规里面有我们需要访问的服务器的URl,也就是请求URL请求表头是我们客户端的一些信息,比如Accept _Encoding是客户端支持的压缩方式、User-Agent是我们电脑的操作系统以及浏览器的版本等等…它的作用就是协调服务器与客户端,还有就是有些网站会设置反爬,通过查取这部分内容来看这个请求是来自于浏览器还是爬虫软件,所以我们需要它来伪装我们。不过这个反爬比较容易被破解表单数据是我们向服务器发送的内容,以及一些密钥,json的版本号等等

其中还有一些是加密所需的,会再第二部分讲解

开始第一部分

现在让我们打开python编程软件吧。先引入模块,很多人会使用requests模块,这个模块也确实很方便,不过这个是需要用户自己下载安装的模块,关于安卓使用requests模块做爬虫会在第二部分讲解。第一部分的模块都是内置模块,不需要另外安装

import urllib.request#访问链接import urllib.parse#数据格式import gzip#数据解压import json#数据提取

然后将我们需要的数据初始化。

#这个是常规中”请求URL“的内容,#虽然我们浏览器打开的是/#但其实真正进行翻译,并返回数据的就是这个链接url = "/translate_o?smartresult=dict&smartresult=rule"

创建一个字典data,用来存放表单数据,代码如下

注意

1.无论是索引还是值,都是字符串,缺单引号的记得补上

2、请不要复制我的data字典,你应该去表单数据复制

3、有些小白可能很聪明,一看,"i"的值就是要翻译的内容,所以就去改了’i‘的值,但是data字典的内容在第一部分不可以去更改,因为’sign’就是根据"i"进行简单的算法得到的。

(如何更改在第二部分讲解,所以第一部分是固定词条翻译)

另外,有些人会说,只需要写一部分必要的数据就够了,说的没错。

但是如果缺少了必要数据就会失败。有些网页很狡猾,时不时地添上一些数据用作加密反爬。例如的时候,有道翻译 的表单数据并没有”lts“,'bv’之类的数据。

所以现在如果缺了这些数据,你可能就得像我当初一样,不断调试,才能找到问题所在。

因此,我的做法是全部写上

data={'i': 'I Love China','from': 'AUTO','to': 'AUTO','smartresult': 'dict','client': 'fanyideskweb',#salt、sign、lts、bv用作加密'salt': '16277104659667','sign': '610df756a6ea61141498b88f51400cdd','lts': '1627710465966','bv': '0107a2ffb088ff47b6a1f153475a951f',#通信的文档种类'doctype': 'json',#js版本号'version': '2.1','keyfrom': 'fanyi.web',#事件触发方式:按键触发'action': 'FY_BY_CLICKBUTTION'}

head字典用来存放请求标头的内容,注意事项和data字典一样

此外,head字典是客户端的标识内容,如果没有head字典,很有可能会被服务器认为是爬虫软件。另外head字典不涉及加密反爬,不过它里面有规定双方的一些通信方式、压缩方式、数据格式、最大数据长度

head = {'Accept': 'application/json, text/javascript, */*; q=0.01','Accept-Encoding': 'gzip, deflate, br','Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6','Connection': 'keep-alive',#连接方式'Content-Length': '250',#最大数据长度'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8','Cookie':'OUTFOX_SEARCH_USER_ID=-1490794583@10.169.0.81;JSESSIONID=aaaywmjXuC2RK_LEqg7Rx; OUTFOX_SEARCH_USER_ID_NCOO=1016804751.1602943; fanyi-ad-id=113723;fanyi-ad-closed=1; ___rl__test__cookies=1627710465963','Host': '','Origin': '','Referer': '/','sec-ch-ua':' "Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"','sec-ch-ua-mobile': '?0','Sec-Fetch-Dest': 'empty','Sec-Fetch-Mode': 'cors','Sec-Fetch-Site': 'same-origin',#用户操作系统及浏览器信息'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62','X-Requested-With': 'XMLHttpRequest'}

#因为 有道翻译 要求,浏览器上传给服务器的数据,需要以

#application/x-www-form-urlencoded形式,utf-8编码

#上传数据的要求在head字典的Content-Type中

data = urllib.parse.urlencode(data).encode('utf-8')

#将 请求url、上传服务器的数据、请求表头 传给req对象,req是Request类的对象req = urllib.request.Request(url,data,head)

在这插一句,如果没有data,urlopen方法就只是访问网页

有了data,才能对网页执行post操作,然后网页才能返回对应的数据

#将 req对象传给 response 对象#访问网页,上传数据,得到服务器返回的数据response = urllib.request.urlopen(req)#使用read()方法,读取数据html = response.read()

#因为网页返回的数据经过了压缩,所以需要解压,

#并且将其解压后的数据以"utf-8"的方式解码成二进制数据,后才能正确显示

#压缩方法的说明是在head字典的Accept-Encoding里面,总共有三种压缩格式

#你可以三种解压都写,看看哪个出来不是乱码,我用了第一个就直接对了,没试后面两个

#或者你也可以在head字典中删除Accept-Encoding这样就不需要解压了

#因为这个元素是向服务器说明客户端所支持的压缩格式,你写什么,它就以什么方式压缩

html = gzip.decompress(html)

因为数据是utf-8编码的,我们需要解码成二进制数据

errors = 'ignore’是解决报错的,也有可能你删掉这个不会报错

html = html.decode('utf-8',errors='ignore')

这样就结束了吗?一开始我也以为结束了,到这我们print(html),屏幕会显示什么呢?

如果是显示像下面,有输入内容,有翻译结果,那么说明你目前的操作正确了。

如果是下面这两种结果,就说明你的data字典有错误,或者head字典有错误

如果是上面的这种,errorCode:50是因为你的数据没有通过验证,服务器没有给你返回翻译结果。看看你是不是更改了’i’的值,仔细校对。

再如果你的是乱码,有0xfe类似的数据,说明没有进行utf-8编码再显示,如果有看不懂的汉字,说明解压操作不对。

到这里,还没有结束。是不是这个返回结果有点复杂,里面有很多我们不需要的东西。而且如果我们以这么一个结果返回给使用这个应用的用户来说,就显得我们很不专业。所以我们要继续筛选数据。

看着这个结果,是不是很像字典?但它不是字典。不信?你可以print(type(html))试试。

它是一个字符串。其实服务器是想给我们发送字典的,可是如果直接发送字典,数据很容易读取错误,所以js就给这个数据加上了引号。

我们用json.load方法,得到真正的数据

你再看看target的类型,就已经是一个字典了

target = json.loads(html)

我们可以打印出来,看看怎么索引它,

我们可以看到,搜索结果在字典的“translateResult”索引对应的值里面,而那个值又是个元组,元组里面还是元组,最里面是个字典然后“tgt”是翻译结果,“src”是输入内容。

#翻译结果print(target['translateResult'][0][0]['tgt'])#需要翻译的内容print(target['translateResult'][0][0]['src'])#近似结果print(target['smartResult']['entries'])

在这个过程中,我遇到了一个问题,一直没有解决

我想将html(字符串类型)target(字典类型)保存到.txt文件。前面一个虽然不报错,但是txt文件里并没有写入内容,后一个因为是字典类型,所以我使用的write()方法会报错。

代码如下:

f = open(r"D:\\2_python练习\\爬虫数据.txt","w")f.write(html)f.close

第一部分完结,第二部分待续

开始第二部分

准备工作,下载并安装requests模块

一开始我自己做第二部分的时候还是想使用内置模块urllib.request来着,但是程序写完,发现连接不上服务器,报这种错误

去参考了别人的代码反复实验,才确定了错误原因。requests毕竟是大神们为爬虫量身定制的,肯定比内置模块要符合爬虫的应用场景吧。

1、下载链接:requests模块的下载地址

一般解压到python的这个路径,其实放在哪都是一样的,只不过把东西放的规整的方便以后查找。

打开这个文件夹,你会找到一个名为setup,后缀名是.py的文件。

2、此时同时按下 win+r 键,输入cmd,打开命令行。

3、先将当前目录更改为setup.py所在目录

(当前目录与目标目录的盘符不同时,先输入“盘符:”命令,回车后更改盘符)

4、在使用cd命令,更改路径

cd D:\Python\Python39\Lib\requests-2.8.1

5、输入如下命令

进行安装,等待安装完成即可

python setup.py install

安装完成后,去import requests看看是否成功

确定反爬用的变量

在第一部分提到,salt、sign、lts、bv是用作反爬的变量,怎么看出来的呢?这个就需要经验了,我们发送给服务器三个数据:url,data,head。url肯定不涉及,head是一些格式类的、客户端标识类的,而data中这四个之外的数据都能猜出是什么意思,只有这四个里面是一串数据加字母,而且你在有道翻译更改翻译内容翻译,你会发现,这四个中的三个,它们的值是在变化的,其他的内容不变。以此就确定下来了。

确定变量的值

那么怎么知道这几个值的算法呢?原来,浏览器可以看看网站的js代码。

“源”→找到main→点击“优质打印”

然后就会跳出这个代码文件了(没有跳出这个网页可能因为你没有点击“优质打印”,网页关掉重新操作)

然后点击…,点击搜索

搜索四个中的其中一个试试会搜到什么

有很多结果,然而我们需要找到类似这样的结果

从上图,我们可以很清楚的看到,salt、sign、lts、bv的值都来自i对象的salt、sign、ts、bv,i对象是r类的实例。r就是ts、t就是bv、i就是salt、sign比较复杂最后再说。

t可能一眼看不明白是啥,不过也不用知道它是啥,因为它就是那个一直不会变的值。不信你可以反复翻译一样看看。所以bv不需要变

***r(lts)**其实是生成了一个时间戳,然后+一对单引号,把时间戳数字变成了字符串。

时间戳其实就是python里面的time.ime(),但是python的时间戳是一个整数部分为10位的浮点数,所以我们需要把python的时间戳1000再强制类型转换成int类型,最后变成字符串类型就是lts了。时间戳可以理解为从1970年1月1日0点0分0秒,到现在(执行创建时间戳操作)的秒数。不过js的是13位整数,以毫秒为单位,python的是10位,以秒为单位。时间戳就是每个消息的唯一标识,因为它精确到了毫秒,不容易出现相同时间戳的消息

import timer = str(int(time.time()*1000)) #时间戳

i,也就是salt,其实就是随机生成一个0-9的数字,把它变成字符类型,连接在r的尾部

import randomrandom_num = random.randint(0,9)salt = r+str(random_num)

sign比较麻烦,他是"fanyideskweb" + e + salt+ "Y2FYu%TNSbMCxc3t2u^XT"相加后的字符串,又经过md5加密后的字符串。e和salt都是字符串,你的式子前后的两个字符串可能跟我的不一样,以你浏览器显示的源码为准。

那我们先来看看e是什么东西。

行号的左边点一下,就会出现一个红点,这个就是程序的断点,当程序执行后,会在断点处暂停,方便我们调试。出现红点后,输入要翻译的内容,点击页面的翻译按钮,就可以执行程序了。这时我们发现,e其实就是我们要翻译的内容

我们可以给e赋一个字符串变量,这个变量可以通过键盘输入得到,然后赋值给e,把e再赋值给data字典的‘i’

md5加密我就直接附上代码了,不多做解释。

import hashlib#sign字符串str_sign = "fanyideskweb" + e + salt+ "Y2FYu%TNSbMCxc3t2u^XT"#创建md5对象md = hashlib.md5()#将str_sign编码后加密更新md.update(str_sign.encode())#将数据变成16进制形式sign = md.hexdigest()

然后我们将 e,sign,r,salt赋值给data字典中对应的数据即可。

requests模块的使用

到这里,我们已经解决了反爬问题,可是如果你继续使用第一部分的urllib.request模块,就会发生远程关闭连接的错误,所以需要使用request模块

import requestsresponse = requests.post(url, headers=head, data=data)target = response.json()

最后附上第二部分的完整代码。有些数据,你需要照你自己的改,

import jsonimport timeimport randomimport hashlibimport requests#网址url = "/translate_o?smartresult=dict&smartresult=rule"#一个创建data字典的方法,e为形参def data_new(e):r = str(int(time.time()*1000)) #时间戳random_num = random.randint(0,9)salt = r+str(random_num)str_sign = "fanyideskweb" + e + salt+ "Y2FYu%TNSbMCxc3t2u^XT"md = hashlib.md5()md.update(str_sign.encode())sign = md.hexdigest()data={'i': e,'from': 'AUTO','to': 'AUTO','smartresult': 'dict','client': 'fanyideskweb','salt': salt,'sign': sign,'lts': r,'bv': '0107a2ffb088ff47b6a1f153475a951f','doctype': 'json','version': '2.1','keyfrom': 'fanyi.web','action': 'FY_BY_REAlTIME'}return datahead = {'Accept': 'application/json, text/javascript, */*; q=0.01','Accept-Encoding': 'gzip, deflate, br','Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6','Connection': 'keep-alive','Content-Length': '250','Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8','Cookie': 'OUTFOX_SEARCH_USER_ID=-1490794583@10.169.0.81; JSESSIONID=aaaywmjXuC2RK_LEqg7Rx;OUTFOX_SEARCH_USER_ID_NCOO=1016804751.1602943; fanyi-ad-id=113723;fanyi-ad-closed=1; ___rl__test__cookies=1627710465963','Host': '','Origin': '','Referer': '/','sec-ch-ua':' "Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"','sec-ch-ua-mobile': '?0','Sec-Fetch-Dest': 'empty','Sec-Fetch-Mode': 'cors','Sec-Fetch-Site': 'same-origin','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62','X-Requested-With': 'XMLHttpRequest'}while True :content = str(input("\n请输入需要翻译的内容(输入‘e’退出程序):\n"))if content == 'e':#内容为eexit(0)#结束程序else:#获取data字典data = data_new(content)response = requests.post(url, headers=head, data=data)target = response.json()print("翻译结果:",target['translateResult'][0][0]['tgt'])

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。