web
端的有道翻译,在之前是直接可以爬的。也就是说只要获取到了他的接口,你就可以肆无忌惮的使用他的接口进行翻译而不需要支付任何费用。那么自从有道翻译推出他的API
服务的时候,就对这个接口做一个反爬虫机制(如果大家都能免费使用到他的翻译接口,那他的 API 服务怎么赚钱)。这个反爬虫机制在爬虫领域算是一个非常经典的技术手段。那么他的反爬虫机制原理是什么?如何破解?接下来带大家一探究竟。
一、正常的爬虫流程:
如果你要爬取他的翻译接口,这个流程还是不能少的。首先我们打开有道翻译的链接:http://fanyi.youdao.com/
。然后在页面中右键->检查->Network 项
。这时候就来到了网络监听窗口,以后你在这个页面中发送的所有网络请求,都会在Network
这个地方显示出来。接着我们在翻译的窗口输入我们需要翻译的文字,比如输入hello
。然后点击自动翻译
按钮,那么接下来在下面就可以看到浏览器给有道发送的请求,这里截个图看看:
在上图,我们可以看到发送了很多的网络请求,这里我们点击第一个网络请求进行查看:
可以看到,我们在点击自动翻译的时候,发送的请求就是上图中Request URL
的那个URL
,然后我们再点击那个Response
,我们可以看到返回的结果:
并且,现在我们再回到Headers
的地方,然后滚动到最下面,可以看到有一个Form Data
的地方,这个下面展示了许多的数据,这些数据就是你在点击__翻译__的时候浏览器给服务器发送的数据:
对其中几个比较重要的数据进行解释:
i
:需要进行翻译的字符串,这个地方我们输入的是 hello。salt
:加密用到的盐。这个是我们破解有道反爬虫机制的关键点,后面会讲到。sign
:签名字符串。也是破解反爬虫机制的关键点。
其他的数据类型暂时就不怎么重要了,都是固定写法,我们后面写代码的时候直接鞋子就可以了。到现在为止,我们就可以写一个简单的爬虫,去调用有道翻译的接口了。这里我们使用的网络请求库是Python3
自带的urllib
,相关代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<span class="hljs-comment"># 导入需要的库</span> <span class="hljs-keyword">import</span> urllib.request <span class="hljs-keyword">import</span> urllib.parse <span class="hljs-keyword">import</span> json <span class="hljs-comment"># 等待用户输入需要翻译的单词</span> content = input(<span class="hljs-string">'请输入需要翻译的单词:'</span>) <span class="hljs-comment"># 有道翻译的 url 链接</span> url = <span class="hljs-string">'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule&sessionFrom=null'</span> <span class="hljs-comment"># 发送给有道服务器的数据</span> data = {} <span class="hljs-comment"># 需要翻译的文字</span> data[<span class="hljs-string">'i'</span>] = content <span class="hljs-comment"># 下面这些都先按照我们之前抓包获取到的数据</span> data[<span class="hljs-string">'from'</span>] = <span class="hljs-string">'AUTO'</span> data[<span class="hljs-string">'to'</span>] = <span class="hljs-string">'AUTO'</span> data[<span class="hljs-string">'smartresult'</span>] = <span class="hljs-string">'dict'</span> data[<span class="hljs-string">'client'</span>] = <span class="hljs-string">'fanyideskweb'</span> data[<span class="hljs-string">'salt'</span>] = <span class="hljs-string">'1500349255670'</span> data[<span class="hljs-string">'sign'</span>] = <span class="hljs-string">'997742c66698b25b43a3a5030e1c2ff2'</span> data[<span class="hljs-string">'doctype'</span>] = <span class="hljs-string">'json'</span> data[<span class="hljs-string">'version'</span>] = <span class="hljs-string">'2.1'</span> data[<span class="hljs-string">'keyfrom'</span>] = <span class="hljs-string">'fanyi.web'</span> data[<span class="hljs-string">'action'</span>] = <span class="hljs-string">'FY_BY_CL1CKBUTTON'</span> data[<span class="hljs-string">'typoResult'</span>] = <span class="hljs-string">'true'</span> <span class="hljs-comment"># 对数据进行编码处理</span> data = urllib.parse.urlencode(data).encode(<span class="hljs-string">'utf-8'</span>) <span class="hljs-comment"># 创建一个 Request 对象,把 url 和 data 传进去,并且需要注意的使用的是 POST 请求</span> request = urllib.request.Request(url=self.url, data=data, method=<span class="hljs-string">'POST'</span>) <span class="hljs-comment"># 打开这个请求</span> response = urllib.request.urlopen(request) <span class="hljs-comment"># 读取返回来的数据</span> result_str = response.read().decode(<span class="hljs-string">'utf-8'</span>) <span class="hljs-comment"># 把返回来的 json 字符串解析成字典</span> result_dict = json.loads(result_str) <span class="hljs-comment"># 获取翻译结果</span> print(<span class="hljs-string">'翻译的结果是:%s'</span> % result_dict) |
我们运行这个文件后,当我们输入的是hello
的时候,我们可以得到哈罗的这个正确的翻译结果。而当我们输入其他需要翻译的字符串的时候,比如输入i love you
,那么就会得到一个错误代码{"errorCode":50}
。这就奇怪了,有道词典不可能只能翻译一个英文单词吧。而这个,就是有道词典的反爬虫机制。接下来我们就来破解有道词典的反爬虫机制。
二、破解反爬虫机制:
我们可以多次的进行翻译,并且每次翻译后都去查看翻译的时候发送的这个网络请求,比较每次翻译时候发送的Form Data
的值。我们注意到,Form Data
在每次发送网络请求的时候,只有i
和salt
以及sign
这三个是不同的,其他的数据都是一样的,这里我用hello
和world
两个单词翻译时候Form Data
的数据进行比较:

图中的Form Data
也证实了我刚刚所说的,就是除了i
、salt
以及sign
是不一样的。其余都是一样的。而i
不一样是很正常的。因为i
代表的是要翻译的字符串,这个不同是很正常。而salt
和sign
这两个东西不一样,是怎么产生的呢?这里我们可以分析一下,这两个值在每次请求的时候都不一样,只有两种情况:第一是每次翻译的时候,浏览器会从有道服务器获取一下这两个值。这样可以达到每次翻译的时候值不同的需求。第二是在本地,用JS
代码按照一定的规则生成的。那么我们首先来看第一个情况,我们可以看到在每次发送翻译请求的时候,并没有一个请求是专门用来获取这两个值的:
所以就可以排除第一种情况。就只剩下一种可能,那就是在本地自己生成的,如果是在本地自己生成的,那么规则是什么呢?这里我们点击网页,查看网页源代码,查找所有的JS
文件,我们找到那个fanyi.js
:
然后点击这个文件,跳转到这个源文件中,然后全选所有的代码,复制下来,再打开站长工具:http://tool.chinaz.com/Tools/jsformat.aspx
。把代码复制进去后,点击格式化:
然后把格式化后的代码,复制下来,用sublime
或者pycharm
打开都可以,然后搜索salt
,可以找到相关的代码:
这里我们就可以发现所有的值的生成原理了。这里来做个简介:
d
:代表的是需要翻译的字符串。f
:当前时间的时间戳加上 0-10 的随机字符串。u
:一个常量——fanyideskweb
。c
:一个常量——rY0D^0'nM0}g5Mm1z%1G4
。salt
:就是f
变量,时间戳。sign
:使用的是u + d + f + c
的md5
的值。
知道salt
和sign
的生成原理后,我们就可以写Python
代码,来对接他的接口了,以下是相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<span class="hljs-keyword">import</span> urllib.request <span class="hljs-keyword">import</span> urllib.parse <span class="hljs-keyword">import</span> json <span class="hljs-keyword">import</span> time <span class="hljs-keyword">import</span> <span class="hljs-built_in">random</span> <span class="hljs-keyword">import</span> hashlib content = input(<span class="hljs-string">'请输入需要翻译的句子:'</span>) url = <span class="hljs-string">'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule&sessionFrom=https://www.google.com/'</span> data = {} u = <span class="hljs-string">'fanyideskweb'</span> d = content f = <span class="hljs-built_in">str</span>(<span class="hljs-built_in">int</span>(time.time()*<span class="hljs-number">1000</span>) + <span class="hljs-built_in">random</span>.randint(<span class="hljs-number">1</span>,<span class="hljs-number">10</span>)) c = <span class="hljs-string">'rY0D^0\'nM0}g5Mm1z%1G4'</span> sign = hashlib.md5((u + d + f + c).encode(<span class="hljs-string">'utf-8'</span>)).hexdigest() data[<span class="hljs-string">'i'</span>] = content data[<span class="hljs-string">'from'</span>] = <span class="hljs-string">'AUTO'</span> data[<span class="hljs-string">'to'</span>] = <span class="hljs-string">'AUTO'</span> data[<span class="hljs-string">'smartresult'</span>] = <span class="hljs-string">'dict'</span> data[<span class="hljs-string">'client'</span>] = <span class="hljs-string">'fanyideskweb'</span> data[<span class="hljs-string">'salt'</span>] = f data[<span class="hljs-string">'sign'</span>] = sign data[<span class="hljs-string">'doctype'</span>] = <span class="hljs-string">'json'</span> data[<span class="hljs-string">'version'</span>] = <span class="hljs-string">'2.1'</span> data[<span class="hljs-string">'keyfrom'</span>] = <span class="hljs-string">'fanyi.web'</span> data[<span class="hljs-string">'action'</span>] = <span class="hljs-string">'FY_BY_CL1CKBUTTON'</span> data[<span class="hljs-string">'typoResult'</span>] = <span class="hljs-string">'true'</span> data = urllib.parse.urlencode(data).encode(<span class="hljs-string">'utf-8'</span>) request = urllib.request.Request(url=url,data=data,method=<span class="hljs-string">'POST'</span>) response = urllib.request.urlopen(request) <span class="hljs-built_in">print</span>(response.read().decode(<span class="hljs-string">'utf-8'</span>)) |
写在最后:
像以上这种,通过用JS
在本地生成随机字符串的反爬虫机制,在爬虫的时候是经常会遇到的一个问题。希望通过以上的讲解,能为大家提供一种思路。以后再碰到这种问题的时候知道该如何解决。这样本篇文章的目的也就达到了。
