##论一只爬虫的自我修养2
(这个实战分别举两个例子进行,第一个例子使用Python下载一只猫,第二个例子是我们用Python来模拟浏览器通过在线有道翻译进行文本的翻译)
##使用Python下载一只猫
http://placekitten.com/,这个网站是为猫农量身定制的一个站点,网站后面你只需要加上 /宽度/高度,就可以得到一只相应宽度和高度的猫的图片。这些图片都是JPG格式的,你可以通过右键将其简单保存到你想保存的地方。
点进连接后加上 /宽度/高度
下载到桌面
(下面就是要使用来Python实现刚才的从那个网站加载猫猫图片的操作,如下代码:)
(解析代码:首先先是导入了urllib包下的request模块,然后调用他的urlopen函数, url 参数可以是字符串,也可以是 Request object,其实,在上面的程序中,传入的是地址字符串,但他会自动将地址字符串转换为 Request 对象,然后再将对象传入 urlopen() ,执行完这行就获取了整个页面的内容,但是个对象,需要用read()函数去读取,返回的是一个二进制形式的字符串,所在在进行文件操作的时候,第一个参数是文件名,也就是图片的名字,第二参数因为cat_img是二进制的形式,所以写入也用二进制的方式写入文件,然后运行成功后,会在你这个.py源程序的文件夹,也就是这个程序当前文件夹下载好刚刚那张图片)
(刚刚提到url 参数可以是字符串,也可以是 Request object,如果传入字符串地址,他会自动给你转化为request对象在传入的,上面的代码等价于:)
(另外,urlopen() 函数返回的 response 其实是一个对象(object),这个对象是类文件,就是跟文件对象是很相似的,因此你可以用read()方法来读取内容)
(从文档上看,除了可以使用 read() 方法之外,还可以使用 geturl() 、info() 和 getcode() 方法,运行刚刚那个程序后如下:)
(主要讲下第二个info(),这个方法返回得到的是一个 HTTPMessage 的对象,打印出来可以看出来就是服务器返回的head头信息,200代表成功的状态码)
##用Python来模拟浏览器通过在线有道翻译进行文本的翻译
(如下是在浏览器上操作)
(先按F12打开审查元素功能,然后点击Network,当点击翻译时,下面就会显示很多向服务器发出的请求,分析一下Headers的内容)
※Request URL:http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule:
(有人会认为 urlopen()函数打开的应该是 http://fanyi.youdao.com/ 这个地址,事实上不是,其实在内部嵌入的是前面的这个地址,实现翻译的机制是在这。里面这个地址才是可以实现翻译的)
※Request Method:POST
(请求的方法是 Post 的形式。HTTP协议有7种请求方式,常用的有2种:get和post,想要了解他们的区别或者更多请到–>HTTP请求消息)
※Status Code:200 OK
(状态码 200 表示正常响应。如果是 404 就是页面资源找不到,可能是url打错了,也可能是没有这个资源。想了解更多状态码可以到—>HTTP响应消息,或者可以直接去百度状态码,会有各种状态码对应的不同情况)
※Remote Address:103.72.47.248:80
(这个是服务器端ip地址,加上他打开的端口号)
※Resquest Headers:
(Resquest Headers:是客服端 (现在这里是浏览器,用 Python代码的时候就是我们的代码)发送请求的请求头,这个常常服务端来判断是否非人类访问,这么说,有些人是比较坏的,写一个 Python 代码,然后用这个代码批量的,不断地访问网站的数据,这样子,服务器的压力就很大了,所以呢,服务器一般是不欢迎非人类的访问的。
一般我们就是使用Resquest Headers里面的User-Agent这个请求头来识别是浏览器访问还是代码访问,可以看到其实这里显示的就是浏览器告诉服务器,我访问你使用的浏览器版本信息,如果使用Python 访问的话,这个User-Agent默认就是 Python URL 3.8,这样用防火墙一识别就知道这个访问其实是来自代码,而不是来自浏览器,就有可能把你屏蔽掉,但是这个User-Agent是可以进行自定义的,下一篇会介绍如何隐藏,
然后想要了解除了User-Agent这个请求头的其他常用请用头,请call—>HTTP请求头)
※Form Data:
(表单数据,其实就是我们这个Post提交的主要内容,在 i 这里看到了提交的待翻译的内容。那么用python如何提交post表单呢,那就去看看文档)
(data参数必须是基于application/x-www-form-urlencoded的格式,然后我们可以使用urllib.parse.urlencode()函数将字符串转换为所需要的形式。)
import urllib.request
import urllib.parse
# 从Request URL:拷贝过来。把_o删了
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
# data就是表单数据,把Form Data 中的内容拷贝过来
data = {}
data['i'] = 'hello world'
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '15803439446390'
data['sign'] = '8e349204c5d1140741ffe43284595085'
data['ts'] = '1580343944639'
data['bv'] = 'bbb3ed55971873051bc2ff740579bb49'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_CLICKBUTTION'
#使用urllib.parse.urlencode()函数将字符串转换为所需要的形式
#把Unicode的文件格式转换为uf-8的编码形式
data = urllib.parse.urlencode(data).encode('utf-8')
response = urllib.request.urlopen(url,data)
#解码的时候也要用uf-8来解码
html = response.read().decode("utf-8")
print(html)
(结果倒是可以了,只是这样的结果是给程序员看的,如果是给用户看,用户可能会看不动,还以为是报错了,另外,如果大家对于编码还有什么困惑的,可以查看:Python编码问题的解决方案总结,可以看到,到事实上打印出来的是转码后字符串,可以用字符串查找的方式把结果查找出来,打印出来,但是这样太被动了
其实,这是一个 json 结构,数据结构也是键值对的形式, 是一种轻量级的数据交换结构,说白了,这里就是用字符串的形式把 Python 的输出结果给封装起来,这个字符串里面包含的事实是一个字典,字典里边"translateResult" 里面的值是一个列表的列表的字典,事实上他就是一个json结构,字符串里边包含的就是python里边可以识别的正常结构,只需要把这些字符串去掉就可以了,如下:)
(解析:首先导入json,用json的loads方法把刚刚那个字符串给载入,可以看到得到的确实就是一个字典,那就好办了,要访问字典里边的键,就用他的关键词来访问,因为有多层,就一层一层地访问就好了,最后下面在美化一下就好了,后边也可以结合easyGui做出图形界面,以后就可以用自己的来翻译了)
import urllib.request
import urllib.parse
import json
content = input("请输入需要翻译的内容:")
# 从Request URL:拷贝过来。把_o删了
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
# data就是表单数据,把Form Data 中的内容拷贝过来
data = {}
data['i'] = content
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '15803439446390'
data['sign'] = '8e349204c5d1140741ffe43284595085'
data['ts'] = '1580343944639'
data['bv'] = 'bbb3ed55971873051bc2ff740579bb49'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_CLICKBUTTION'
#使用urllib.parse.urlencode()函数将字符串转换为所需要的形式
#把Unicode的文件格式转换为uf-8的编码形式
data = urllib.parse.urlencode(data).encode('utf-8')
response = urllib.request.urlopen(url,data)
#解码的时候也要用uf-8来解码
html = response.read().decode("utf-8")
target = json.loads(html)
print("翻译结果:%s"%(target["translateResult"][0][0]["tgt"]))
(这样的代码还不能应用到我们的生产实践中,因为你这样搞多了,服务器就会发现非人类的 User Agent 频繁访问,就会把你屏蔽掉了。还有就是发现这个IP怎么访问的这么频繁,就把你拉黑了。其实这些问题,Python都是有解决方法的,下一章会提到。)
##温故知新之习题
0. urlopen() 方法的 timeout 参数用于设置什么?
答:timeout 参数用于设置连接的超时时间,单位是秒。
1. 如何从 urlopen() 返回的对象中获取 HTTP 状态码?
答:
…
response = urllib.request.urlopen(url)
code = response.getcode()
…
2. 在客户端和服务器之间进行请求-响应时,最常用的是哪两种方法?
答:GET 和 POST。
3. HTTP 是基于请求-响应的模式,那是客户端发出请求,服务端做出响应;还是服务端发出请求,客户端做出响应呢?
答:发出请求的永远是客户端,做出响应的永远是服务端。
4. User-Agent 属性通常是记录什么信息?
答:普通浏览器会通过该内容向访问网站提供你所使用的浏览器类型、操作系统、浏览器内核等信息的标识。
5. 如何通过 urlopen() 使用 POST 方法向服务端发出请求?
答:urlopen 函数有一个 data 参数,如果给这个参数赋值,那么 HTTP 的请求就是使用 POST 方式;如果 data 的值是 None,也就是默认值,那么 HTTP 的请求就是使用 GET 方式。
6. 使用字符串的什么方法将其它编码转换为 Unicode 编码?
答:decode。decode 的作用是将其他编码的字符串转换成 unicode 编码,相反,encode 的作用是将 unicode 编码转换成其他编码的字符串。
7. JSON 是什么鬼?
答:JSON 是一种轻量级的数据交换格式,说白了这里就是用字符串把 Python 的数据结构封装起来,便与存储和使用。
##动动手
0. 配合 EasyGui,给“下载一只猫“的代码增加互动:
※让用户输入尺寸;
※如果用户不输入尺寸,那么按默认宽400,高600下载喵;
※让用户指定保存位置。
程序实现如下图:
答:
import easygui as g
import urllib.request
def main():
msg = "请填写喵的尺寸"
title = "下载一只喵"
fieldNames = ["宽:","高:"]
fieldValues = []
size = width,height = 400,600
fieldValues = g.multenterbox(msg,title,fieldNames,size)
while 1:
if fieldValues == None:
break
errmsg = ""
try:
width = int(fieldValues[0].strip())
except:
errmsg +="宽必须为整数!"
try:
height = int(fieldValues[1].strip())
except:
errmsg += "高度必须为整数!"
if errmsg == "":
break
fieldValues = g.multenterbox(errmsg, title, fieldNames, fieldValues)
url = "http://placekitten.com/g/%d/%d" % (width, height)
response = urllib.request.urlopen(url)
cat_img = response.read()
filepath = g.diropenbox("请选择存放喵的文件夹")
if filepath:
filename = '%s/cat_%d_%d.jpg' % (filepath, width, height)
else:
filename = 'cat_%d_%d.jpg' % (width, height)
with open(filename, 'wb') as f:
f.write(cat_img)
if __name__ == "__main__":
main()
1. 写一个登录豆瓣的客户端。
这道题可能要难为大家了,因为需要 N 多你没学过的知识!
不过我也不打算让你断送希望,下边是一个可行的 Python 2 的代码片段,请修改为 Python 3 版本。其中一些库和知识点你可能还没学过,但凭借着过人的自学能力,你可以在不看答案的情况下完成任务的,对吗?
程序实现如下图:
Python2 实现的代码:
# -- coding:gbk --
import re
import urllib, urllib2, cookielib
loginurl = 'https://www.douban.com/accounts/login'
cookie = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
params = {
"form_email":"your email",
"form_password":"your password",
"source":"index_nav" #没有的话登录不成功
}
#从首页提交登录
response=opener.open(loginurl, urllib.urlencode(params))
#验证成功跳转至登录页
if response.geturl() == "https://www.douban.com/accounts/login":
html=response.read()
#验证码图片地址
imgurl=re.search('<img id="captcha_image" src="(.+?)" alt="captcha" class="captcha_image"/>', html)
if imgurl:
url=imgurl.group(1)
#将图片保存至同目录下
res=urllib.urlretrieve(url, 'v.jpg')
#获取captcha-id参数
captcha=re.search('<input type="hidden" name="captcha-id" value="(.+?)"/>' ,html)
if captcha:
vcode=raw_input('请输入图片上的验证码:')
params["captcha-solution"] = vcode
params["captcha-id"] = captcha.group(1)
params["user_login"] = "登录"
#提交验证码验证
response=opener.open(loginurl, urllib.urlencode(params))
''' 登录成功跳转至首页 '''
if response.geturl() == "http://www.douban.com/":
print 'login success ! '
答:Python 3 对比 Python 2 有不少的改变。
在本题中:
※urllib 和 urllib2 合并,大多数功能放入了 urllib.request 模块;
※原来的 urllib.urlencode() 变为 urllib.parse.urlencode().encode(),由于编码的关系,你还需要在后边加上 encode(‘utf-8’);
※cookielib 被改名为 http.cookiejar;
课堂中我们还没讲,所以这里借机会给大家简单科普一下 cookie 是什么东西:
我们说 HTTP 协议是基于请求响应模式,就是客户端发一个请求,服务端回复一个响应酱紫……
但 HTTP 协议是无状态的,也就是说客户端这会儿给服务端提交了账号密码,服务端回复验证通过,但下一秒客户端说我要访问 XXOO 资源,服务端回复:“啊??你是谁?!”
为了解决这个尴尬的困境,有人就发明出了 cookie。cookie 相当于服务端(网站)用于验证你的身份的密文。于是客户端每次提交请求的时候,服务端通过验证 cookie 即可知道你的身份信息。想要了解更多cook可以到—>会话技术概述_Cookie,那么正如你所猜测的,CookieJar 是 Python 用于存放 cookie 的对象。
当然,这里已经给你提供了 Python 2 的代码,你不懂上边这些,也不影响完成作业。
import re
import urllib.request
from http.cookiejar import CookieJar
# 豆瓣的登录url
loginurl = 'https://www.douban.com/accounts/login'
cookie = CookieJar()
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor)
data = {
"form_email":"your email",
"form_password":"your password",
"source":"index_nav"
}
data = {}
data['form_email'] = '你的账号'
data['form_password'] = '你的密码'
data['source'] = 'index_nav'
response = opener.open(loginurl, urllib.parse.urlencode(data).encode('utf-8'))
#验证成功跳转至登录页
if response.geturl() == "https://www.douban.com/accounts/login":
html = response.read().decode()
#验证码图片地址
imgurl = re.search('<img id="captcha_image" src="(.+?)" alt="captcha" class="captcha_image"/>', html)
if imgurl:
url = imgurl.group(1)
# 将验证码图片保存至同目录下
res = urllib.request.urlretrieve(url, 'v.jpg')
# 获取captcha-id参数
captcha = re.search('<input type="hidden" name="captcha-id" value="(.+?)"/>' ,html)
if captcha:
vcode = input('请输入图片上的验证码:')
data["captcha-solution"] = vcode
data["captcha-id"] = captcha.group(1)
data["user_login"] = "登录"
# 提交验证码验证
response = opener.open(loginurl, urllib.parse.urlencode(data).encode('utf-8'))
# 登录成功跳转至首页 '''
if response.geturl() == "http://www.douban.com/":
print('登录成功!')