第一次写技术文章,有点小紧张,小兴奋。这两天想着好好运用一下刚学到的requests和bs4来爬虫,网上找了个小爬虫项目,就是爬虫实现有道翻译。一开始以为很简单,结果断断续续用了挺长时间,主要是有道的反爬虫机制在提升,所以参考的那篇博客写的时候可能有道还没有用什么反爬虫吧,所以经过自己的捣鼓以及参考了第二个博客现在终于实现了,还是很开心,谢谢自己坚持下来,学到了不少东西,和大家分享一下吧。
首先说明一下用到的工具:火狐浏览器的Web开发者的network工具,用它我们可以清楚看到我们在有道上查一个单词或者词语的时候发出和收到的HTTP/HTTPS请求和响应内容,这个是我们实现爬虫的基础,用来我们构造请求头以及确定我们用get方法还是post方法,在这里呢通过看请求头发现查询用的是post方法,那么呢,我们还需要这个工具来构造我们的提交内容,这里是整个爬虫能不能实现,也是破解有道反爬虫的关键,会在下面详细说!!!!!
我们先看一下我查一个单词用火狐的network工具得到的内容吧
箭头1处,是我们构建爬虫的URL,2处我们得到使用的方法是Post,再通过3确定要提交的是表单,我们根据请求头的格式构造我们自己的请求头,4这里我们构建请求头时要特别注意,每次查询时Cookie的值都不同,下面细讲
上图是我们构造表单内容需要的格式,箭头5,6这里就是反爬虫的机制,也是我们代码能否实现的关键,也下面讲如何确定salt、sign的值。
现在开始介绍如何确定构造cookie,salt,以及sign的值吧,使用的工具依然是network,它的堆栈跟踪,如下图
我们点箭头指的链接跳到下图:
然后为了更好的研究把箭头指的内容我们用站长工具http://tool.chinaz.com/tools/jsformat.aspx来更好的显示。
搜索到salt,可以看到下面一段js代码
t.translate = function(e, t) {
T = u("#language").val();
var n = b.val(),
r = "" + ((new Date).getTime() + parseInt(10 * Math.random(), 10)),
o = u.md5(S + n + r + D),
a = n.length;
if (L(), w.text(a), a > 5e3) {
var l = n;
n = l.substr(0, 5e3),
o = u.md5(S + n + r + D);
var c = l.substr(5e3);
c = (c = c.trim()).substr(0, 3),
u("#inputTargetError").text("有道翻译字数限制为5000字,“" + c + "”及其后面没有被翻译!").show(),
w.addClass("fonts__overed")
} else w.removeClass("fonts__overed"),
u("#inputTargetError").hide();
f.isWeb(n) ? i() : s({
i: n,
from: _,
to: C,
smartresult: "dict",
client: S,
salt: r,
sign: o,
doctype: "json",
version: "2.1",
keyfrom: "fanyi.web",
action: e || "FY_BY_DEFAULT",
typoResult: !1
},
t)
},
t.showResult = a
}),
可以看出salt,sign的值分别为r,o,在上面我已经加粗显示,r就是系统当前时间戳加上一个1到10之间的随机整数,o是一个获取S + n + r + D字符串的md5值(这个可以百度一下),所以python代码中我们需要用到time,random以及hashlib(获取S + n + r + D字符串的md5值)这些库。S的值可以看到对应的是请求报文中的client值fanyideskweb,n是查询的内容,r就是salt,D我们也可以通过查看js脚本找到它对应的值为ebSeFb%=XZ%T[KZ)c(sy!
这样之后我们的表单就可以构建好了,但我们在查询不同内容的时候,请求头里的cookie也是不同的,所以我们还需要找到cookie值的构造规律,这个是在v1.js这个脚本中找到的如下图
同样的,我们用站长工具搜索Cookie找到如下代码:
可以看到cookie最后一串数字其实就是系统当前的时间戳。
好喽,找到salt、sign、Cookie后我们就已经要成功了,可以附上我的代码喽,不知道怎么把代码很好的展示出来,我就直接粘贴过来吧。。。对了,还有一件事情要说,就是URL的值我是就直接用的请求头里的URL的,只要把salt、sign和Cookie成功构造好,就不会出现网上的那些错误,也不需要去掉_o
import requests
from bs4 import BeautifulSoup
import json
import time
import random
import hashlib
def crawl_youdao():
while True:
content =raw_input('请输入要查询的单词/词语(输入0退出翻译):')
d = content
if content == '0':
print('欢迎下次使用!')
break
url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule '
ctime=int(time.time()*1000)#!!!
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding':'gzip, deflate',
'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',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
#!!!
'Cookie':'YOUDAO_MOBILE_ACCESS_TYPE=1; OUTFOX_SEARCH_USER_ID=-1289760786@10.168.8.76; OUTFOX_SEARCH_USER_ID_NCOO=939708194.3484184; _ntes_nnid=3261dca1448d041f16596bf4942976dd,1524295653112; JSESSIONID=aaaWj8T4yP2lIcfjqeSlw; ___rl__test__cookies='+str(ctime),
'Host': 'fanyi.youdao.com',
'Pragma': 'no-cache',
'Referer': 'http://fanyi.youdao.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0',
'X-Requested-With': 'XMLHttpRequest'
}
salt=str(ctime+random.randint(1,10))#!!!
sign = hashlib.md5("fanyideskweb" + content + salt + "ebSeFb%=XZ%T[KZ)c(sy!").hexdigest()#!!!!
data={
'from':'AUTO',
'to':'AUTO',
'smartresult':'dict',
'client':'fanyideskweb',
'doctype':'json',
'version':'2.1',
'keyfrom':'fanyi.web',
'action':'FY_BY_REALTIME',
'typoResult':'true',
'i':content,
'salt': salt,
'sign':sign
}
res = requests.post(url,params =data, headers=headers)
soup = BeautifulSoup(res.text,'lxml')
jd = json.loads(soup.text)
#print soup.text
#print jd
print('翻译结果:')
for translate in jd['smartResult']['entries']:
print(translate)
print('\n')
if __name__=='__main__':
crawl_youdao()
然后然后就是看看我的程序运行结果吧,终于实现了,好开心啊,开心到飞起~~~,写出来和大家分享一下我内心的喜悦~~~~~
特别感谢:https://blog.csdn.net/shadkit/article/details/79174948
网友评论