小小千想和您聊一聊

当前位置: 首页> 技术分享> 第3章 Python中的HTTP操作库

第3章 Python中的HTTP操作库

  本章学习目标

  l 掌握urllib库的使用

  l 掌握URLError库的使用

  l 掌握Requests库的使用

  读取URL与下载网页是每个爬虫必备且关键的功能,要实现这些功能就需要与HTTP请求打交道。Python中实现HTTP请求主要通过使用urllib库与Requests库两种方式,在实际开发中还需要考虑程序对网站访问失败时的情况,因此还需要对爬取过程中的异常情况进行处理,此时就需要用到URLError模块。

  3.1 urllib库简介

  3.1.1 urllib库的概念

  urllib库是Python编写爬虫程序操作URL的常用内置库。在不同的Python解释器版本下,使用方法也稍有不同,本书采用Python3.X来讲解urllib库,具体版本是Python 3.6.1。

  需要说明的是,在Python 2.X中urllib库包含urllib2和urllib两个版本,而在Python 3.X中urllib2合并到了urllib中。在此总结了一些urllib模块在Python2.X和Python3.X中使用的变动,方便大家快速掌握该库的用法,具体如下所示:

# Python2.X 到 Python3.X的变化
import urllib2  -----  import urllib.request,urllib.error
import urllib	-----  import urllib.request,urllib.error,urllib.parse
import urlparse	 -----  import urllib.parse
import urllib2.urlopen	-----  import urllib.request.urlopen
import urllib.quote  -----  import urllib.request.quote
urllib.request			# 请求模块
urllib.error			# 异常处理模块
urllib.parse			# url解析模块
urllib.robotparser 		# robots.txt解析模块

  3.1.2 urllib库的使用

  上节对urllib库作了简单的介绍,接下来讲解如何使用urllib库快速爬取一个网页。具体步骤如下:

  · 导入相应的模块。

  · 使用urllib.request.urlopen()方法打开并爬取一个网页。

  · 使用response.read()方法读取网页内容,并以“utf-8”格式进行解码。

  具体示例代码如下:

# 导入Python3.X中urllib.request库
import urllib.request 
# 创建爬取千锋官网对象
response = urllib.request.urlopen('http://www.1000phone.com')
# 打印爬取所有结果以中文方式
print(response.read().decode('utf-8'))

  上述示例中使用到了urlopen方法,该方法常用的三个参数,具体示例如下:

  urllib.requeset.urlopen(url,data,timeout)

  其中url表示需要打开的网址;data表示访问网址时需要传送的数据,一般在使用post请求时使用;timeout是设置网站的访问超时时间。

  response是爬取到的网页对象,若想读取该网页内容则可用response.read()方法,并使用“utf-8”解码即可。如果不使用read()而直接对response解码,则上述示例返回结果如下:

  上述示例中的千锋教育官网是通过get请求获得的,下面演示使用urllib库中的post方法获取网页内容。这里使用“http://httpbin.org/”网站演示,具体示例代码如下:

# urllib分析模块
import urllib.parse
# 用于打开url的库
import urllib.request
# urlencode的参数是词典,它可以将key-value这样的键值对转换成想要的格式。
data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf-8')
print(data)
response = urllib.request.urlopen('http://httpbin.org/post', data=data)
print(response.read().decode("utf-8"))

  注意使用该网站的post方法时需要在网址后面加上“/post”,如上述代码中所示。上面代码中客户端对网站服务器发送了请求数据{'word': 'hello'},并使用urllib.parse库中bytes()方法将请求数据进行转换后,放入urllib.request.urlopen()方法的data参数中,这样就完成了一次post请求。运行该程序,结果如图3.1所示。

  [A1] 图3.1 返回结果

  此时可以看到客户端通过post请求向网站服务器传递了表单数据“word”:“hello”,程序也返回了相应的结果。

  通过上面的学习,相信大家已经可以使用urllib库对网页进行简单爬取,如果爬取的网页结果想要保存到本地,可通过如下代码实现:

import urllib.request 
response = urllib.request.urlopen("http://www.1000phone.com") 
data = response.read()	
filehandle = open('D:/file/1000phone.html',"wb")
filehandle.write(data)
filehandle.close()

  执行完上面代码后,即可在“D:/file/”目录中找到文件“1000phone.html”。代码中首先通过open()函数以“wb”二进制写入的方式打开文件,打开后再将其赋值给变量filehandle,然后再用write()方法将爬取的data数据写入打开的文件中,写入完成后使用close()方法关闭该文件,使其不能再进行读写操作,程序到此结束。运行结果如图3.2所示。

  图3.2 文件1000phone.html

  用浏览器打开本地“1000phone.html”文件,结果如图3.3所示。

  图3.3 1000phone.html打开效果

  除此方法外,还可以使用urllib.request中的urlretrieve()方法直接将对应信息写入本地文件,具体代码如下所示:

  import urllib.request

  filename = urllib.request.urlretrieve("http://www.1000phone.com",

  filename="D:/file/1000phone1.html")

  执行上述程序后,目录中增加了“1000phone1.html”文件,如图3.4所示。

  图3.4 1000phone1.html

  打开该文件,将会看到与图3.3一样的内容。

  urllib库中还有一些常用方法,如例3-1所示。

  例3-1 获取网页信息、状态码、地址等


 1	import urllib.request
 2	file = urllib.request.urlopen("http://www.1000phone.com")
 3	print(file.info())      	# 网页信息
 4	print(file.getcode())	# 返回状态码
 5	print(file.geturl())	# 返回URL


  运行程序,结果如图3.5所示。

  图3.5 输出结果

  从图3.5中可以看出file.info()输出了对应的网页信息;file.getcode()获得了爬取网页的状态码,返回200表示响应正确;方法geturl()返回了当前所爬取网页的源URL地址。

  在浏览网页时,如果此网页长时间没有响应,系统就会提示该网页超时无法打开。根据实际需要,可以设置超时的时间值。比如,有的网站响应快,若以10秒作为判断是否超时的标准,timeout值就是10,有的网站响应缓慢,可以设置timeout值为80秒。如例3-2所示。

  例3-2 设置超时时间


 1	import urllib.request
 2	for i in range(1,100):
 3		try:
 4			file = urllib.request.urlopen("http://www.codingke.com",
 5	                    timeout=0.3)    		# 打开网页超时设置为0.3秒
 6			data = file.read()
 7			print(len(data)) 		 		# 打印爬取内容的长度
 8		except Exception as e:       		# 捕捉异常
 9			print("异常了……"+str(e))


  上面代码中执行循环99次,每次循环都爬取网址“http://www.codingke.com”,并将超时设置0.3秒,即0.3秒未响应则判定超时,如果捕捉到异常就输出“异常了……”与异常原因。运行该程序,结果如图3.6所示。

  图3.6 timeout为0.3时捕捉到超时异常

  结果显示在这99次循环爬取中,有时能正确爬取内容并返回内容长度,有时却引发了超时异常。这个结果是因为程序中设置timeout的值是0.3,在短时间内向服务器发送大量访问请求,服务器在0.3秒内无法响应,报错超时异常。

  通常情况下,timeout默认值为1秒,通过浏览器请求网站信息时不会发生超时异常。将例3-2中timeout值修改了1,运行程序,结果如图3.7所示。

  图3.7 timeout为1时没有超时异常

  可以知道爬取网页时正确设置timeout的值,可以避免超时异常。其格式代码如下:

  urllib.request.urlopen("url",timeout=default)

  3.2 设置HTTP请求方法

  在上节内容中简单介绍了使用urllib库中的get与post方法获取网页内容。其实HTTP的请求方式除了GET与POST外,还包括如下几种:PUT、HEAD、DELETE、OPTIONS、TRACE。其中最常用请求方式是GET与POST,各类型主要作用如表3.1所示。

  表3.1 常见请求方式用法

  3.2.1 GET请求实战

  本书以在扣丁学堂(www.codingke.com)中查询Python课程为例讲解GET请求。首先打开扣丁学堂首页,然后检索关键词“python”,查看查询结果,并观察URL变化,如图3.8所示。

  图3.8 扣丁学堂首页

  可以发现,检索关键词“python”后URL变为“http://www.codingke.com/search/course?

  keywords=python”,这里keywords=python刚好是需要查询的信息,因此字段keywords对应的值就是用户检索的关键词。由此可见,在扣丁学堂查询一个关键词时,会用GET请求进行,其中关键性字段是keywords,查询格式就是“http://www.codingke.com/search/course?

  keywords=关键词”。

  若要实现用爬虫自动地在扣丁学堂上查询关键词是php的结果,示例代码如例3-3所示。

  例3-3 爬取扣丁学堂中php课程的网页内容并保存在本地

  1 import urllib.request
  2 keywd = 'php'
  3 url = 'http://www.codingke.com/search/course?keywords='+keywd
  4 req = urllib.request.Request(url)
  5 data = urllib.request.urlopen(req).read()
  6 fhandle = open("D:/file/php.html",'wb')
  7 fhandle.write(data)
  8 fhandle.close()

  上述代码首先定义了关键词给keywd变量,然后按照分析好的URL格式,构建所需爬取的URL地址赋值给变量url,再使用urllib.request.Request()构建一个Request对象赋值给变量req,再用urllib.request.urlopen()打开对应的Request对象,此时网页中包含了GET请求信息,读取页面内容后赋值给data变量,最后保存内容到D:/file/php.html文件中。

  执行以上代码之后,在对应目录下已经出现php.html网页,结果如图3.9所示。

  图3.9 扣丁学堂PHP课程

  上述示例中存在一个问题,当要检索的关键词是中文时,例如keywd = '开发' ,继续执行代码则会出现如下错误:

  UnicodeEncodeError: 'ascii' codec can't encode characters in position 28-29:

  ordinal not in range(128) # 编码出错

  可以看出,上述代码由于编码问题出错,修改代码如例3-4所示。

  例3-4 解决关键词是中文的编码问题

 1 import urllib.request
  2 url = 'http://www.codingke.com/search/course?keywords='
  3 keywd = '开发' # 使用中文查询
  4 key_code = urllib.request.quote(keywd) # 对关键字编码
  5 url_all = url+key_code # 字符串拼接
  6 req = urllib.request.Request(url_all)
  7 data = urllib.request.urlopen(req).read()
  8 fhandle = open('D:/file/dev.html','wb')
  9 fhandle.write(data)
  10 fhandle.close()

  例3-4使用urllib.request.quote()对关键词部分进行编码,编码后重新构造完整URL。 关于GET请求的实例就介绍到这里,希望大家多多练习并可在其他网站上尝试操作。

  3.2.2 设置代理服务

  前面讲解的都是如何爬取一个网页内容,前提是客户端使用的IP地址没有被网站服务器屏蔽。如果该IP地址被网站服务器屏蔽掉,该如何解决呢?

  当使用同一个IP地址频繁爬取网页时,网站服务器极有可能将这个IP地址屏蔽掉。解决办法就是让网站服务器上看到的IP地址并非本机IP,这样即使这个IP地址被屏蔽了,也可以更换另一个IP地址继续爬取。这就是代理服务的思路。

  获取代理IP主要有如下几种方式:

  l IP代理池:部分厂商将很多IP做成代理池,提供API接口,允许用户使用程序调用。

  l VPN:国内外都有很多厂商提供VPN服务,可以分配不同的网络路线,并可以自动更换IP,实时性高,速度快。

  l ADSL宽带拨号:ADSL宽带拨号的特点就是断开再重新连接后,分配的IP会变化,爬虫利用这个原理更换IP。

  网址“http://www.xicidaili.com/”中有很多代理服务器地址,如图3.10所示。

  图3.10 代理服务器列表

  从图3.11中可看出,上面网址提供了大量的代理IP地址,大家尽量选用验证时间较短的IP用作代理服务器。接下来通过一个示例示范使用代理IP进行爬取网页,具体代码如例3-5所示。

  例3-5 使用代理IP爬取网页

 1 import urllib
  2 import urllib.request
  3 # 创建代理函数
  4 def use_proxy(proxy_addr, url):
  5 # 代理服务器信息
  6 proxy = urllib.request.ProxyHandler({'http':proxy_addr})
  7 # 创建opener对象
  8 opener = urllib.request.build_opener(proxy,urllib.request.HTTPHandler)
  9 urllib.request.install_opener(opener)
  10 data = urllib.request.urlopen(url).read().decode('utf-8')
  11 return data
  12 proxy_addr = '118.114.77.47:8080'
  13 data = use_proxy(proxy_addr, "http://www.1000phone.com")
  14 print('网页数据长度是:',len(data))

  运行上面程序,结果如图3.11所示。

  图3.11 网页数据结果

  例3-5中首先创建函数use_proxy(proxy_addr, url),该函数的功能是实现使用代理服务器爬取URL网页。在该函数中,本书设置两个形参,第一个形参proxy_addr填写代理服务器的IP地址及端口,第二个参数url填写待爬取的网页地址。通过urllib.request.ProxyHandler()来设置对应的代理服务器信息,接着使用urllib.request.build_opener()方法创建一个自定义的opener对象,该方法中第一个参数是代理服务器信息,第二个参数是类。

  接下来使用urllib.request.install_opener()创建全局默认的opener对象,那么在使用urlopen()时亦会使用本文安装的全局opener对象,所以本文下面可以直接使用urllib.request.urlopen()打开对应网址爬取网页并读取,紧接着赋值给变量data,最后返回data的值给函数。

  此时可以看到已经成功使用代理服务器爬取到了千锋教育首页,并返回了内容大小。如果代理服务器失效则会发生异常错误,将会看到运行结果如图3.13所示。

  图3.12 连接失败异常

  从图3.13中可以看出,如果使用代理服务器爬网站时出现异常,需考虑是否为代理IP失效,若失效则应自动更换为其他代理IP再次进行爬取。

  3.3 URLError应用

  在程序运行中难免发生异常,对于异常的处理是编写程序时经常要考虑的问题。本节内容就来学习如何处理Python程序中遇到的异常。

  首先需要导入异常处理的模块——urllib.error模块,该模块中包含了URLError类以及它的子类HTTPError类。

  Python代码中处理异常需要使用try-except语句,在try中执行主要代码,在except中捕获异常,并进行相应的异常处理。产生异常的原因一般包括网络无连接、连接不到指定服务器、服务器失效以及触发了HTTPError等。

  在确保使用的设备正常联网的情况下,下面通过将千锋教育官网网址后拼接一个“/1”的错误网址,来演示如何处理异常。具体代码如例3-6中所示。

  例3-6 使用URLError处理url错误的异常

1 import urllib.request
  2 import urllib.error
  3 try:
  4 urllib.request.urlopen("http://1000phone.com/1") # 爬取不存在的url
  5 except urllib.error.URLError as e: # 主动捕捉异常
  6 # print(e) # 打印异常信息
  7 # print(dir(e)) # 查看e的属性以方法
  8 print(e.code)
  9 print(e.reason)

  运行程序,结果如图3.13所示。

  图3.13 页面找不到异常

  例3-6中因为请求了一个错误的url地址,所以必然会出现404的错误,该错误会引发except程序块执行,并通过urllib.error.URLError as e捕获异常信息e,然后进行相应的异常处理。这里输出了异常状态和异常原因。

  之前提到,产生URLError的原因有如下几种:

  l 服务器URL不存在

  l 网络无连接

  l 服务器无响应

  l 触发了HTTPError

  在例3-6中,404异常显然不属于前三者,而是由于触发了HTTPError。所以,在例3-6中使用HTTPError代替URLError可以直接处理这种异常,具体如例3-7所示。

  例3-7 使用HTTPError类处理HTTPError异常

 1 import urllib.request
  2 import urllib.error
  3 try:
  4 urllib.request.urlopen("http://1000phone.com/1") # 爬取不存在的url
  5 except urllib.error.HTTPError as e: # 主动捕捉异常
  6 print(e.code)
  7 print(e.reason)

  运行程序,结果如图3.14所示。

  图3.14 页面找不到异常

  例3-7中程序返回的状态码(e.code)为404,是服务器返还给客户端的编码,不同的编码有不同的含义。常见的状态码含义如下:

  l 200: OK 正常

  l 301: Moved Permanently 永久性重定向

  l 302: Found 临时重定向

  l 304: Not Modified 请求资源未更新

  l 305: Use Proxy 必须使用代理访问资源

  l 400: Bad Request 客户端请求语法错误,服务器无法解析

  l 401: Unauthorized 请求要求用户的身份认证

  l 403: Forbidden 服务器理解客户端请求,但拒绝执行

  l 404: Not Found 服务器找不到资源

  l 500: Internal Server Error 服务器内部错误

  l 502: Bad Gateway 充当网管或代理的服务器,从远端服务器接收到无效的请求。

  HTTPError子类无法处理连接不上服务器、URL不存在、无网络引发的异常,这些异常只能通过URLError处理。若使用HTTPError处理这几种异常,则程序会报错。具体代码如例3-8所示。

  例3-8 使用HTTPError类处理非HTTPError异常

 1 import urllib.request
  2 import urllib.error
  3 try:
  4 urllib.request.urlopen("http://www.1000phone.cc")
  5 except urllib.error.HTTPError as e: # HTTPError抓取异常
  6 print(e.reason) # reason属性是个元组(错误号,错误信息)

  运行程序,结果如图3.15所示:

  图3.15 无法异常处理

  从以上结果可看出,HTTPError无法处理不存在的网址异常,现在更换为URLError进行异常处理,如例3-9所示。

  例3-9 使用URLError处理不存在的网址异常

 1 import urllib.request
  2 import urllib.error
  3 try:
  4 urllib.request.urlopen("http://www.1000phone.cc")# 抓取不存在的网址
  5 except urllib.error.URLError as e: # 使用URLError抓取异常
  6 print(e.reason)

  运行结果如图3.16所示:

  图3.16 成功抓取异常输出

  可以看出,如果仅有HTTPError子类,则无法处理连接不上服务器、远程URL不存在、无网络等异常。此时可先使用HTTPError类进行异常处理,若无法处理,再让程序用URLError进行处理,具体示例代码如例3-10所示。

  例3-10 使用HTTPError类与URLError类处理异常

1 import urllib.error
  2 import urllib.request
  3 try:
  4 urllib.request.urlopen("http://www.1000phone.cc")
  5 except urllib.error.HTTPError as e : # 先用子类异常处理
  6 print(e.code)
  7 print(e.reason)
  8 except urllib.error.URLError as e : # 再用父类异常处理
  9 print(e.reason)

  运行程序,结果如图3.17所示。

  图3.17 抓取异常处理

  总之,不管发生哪种异常,都可以先用HTTPError子类处理,无法处理时再用URLError类处理异常。需要注意的是,有时不能直接用URLError代替HTTPError,比如此时确保设备无网络连接(拔掉网线或者关闭WIFI连接),再次验证示例3-6,会发现程序报错。还有当改变示例3-6中的url为一个不存在的url,程序也会报错,具体代码如例3-11所示。

  例3-11 有时不能直接使用URLError代替HTTPError

1 import urllib.request
  2 import urllib.error
  3 try:
  4 urllib.request.urlopen("http://www.1000phone.cc")
  5 except urllib.error.URLError as e:
  6 print(e.code)
  7 print(e.reason)

  运行程序,结果如图3.18所示。

  图3.18 URLError没有属性code

  例3-11中打开的网址是不存在的,此时触发报错的原因是远程URL不存在,即不存在错误码,没有e.code,只有e.reason,因此程序报错 “object has no attribute ‘code’”,出现此类错误的情况还包括连接不上服务器、无网络等。

  虽然去掉e.code部分即可解决此类问题,但如果触发的是HTTPError异常,需要获取对应的状态码时就必须使用e.code。因此,在使用URLError进行异常处理的时候,需要做出一个判断,如果含有e.code就输出对应信息,否则就忽略。有了这个判断,无论哪种URL异常,都可以用URLError处理,如例3-12所示。

  例3-12 使用URLError处理HTTPError异常

 1 import urllib.request
  2 import urllib.error
  3 try:
  4 urllib.request.urlopen("http://www.1000phone.cc")
  5 except urllib.error.URLError as e :
  6 if hasattr(e,'code'): # 使用hasattr判断e中是否有code属性
  7 print(e.code) # 有就打印出状态码
  8 if hasattr(e,'reason'): # 使用hasattr判断e中是否有reason属性
  9 print(e.reason)

  上述代码中,如果触发了HTTPError异常,则判断出有e.code,输出结果中包含状态码和异常原因;若发生异常的原因是连接不上服务器、远程URL不存在、无网络等异常中的一个,则只会输出e.reason。

  3.4 Requests库

  使用Requests库进行HTTP请求是Python爬虫开发中最为常用的方式,由于Requests库简洁易用的特性,已成为Python开发社区中最受欢迎的库。目前众多大公司都在使用Requests库,如Nike、Twitter、Spotify、Amazon、NSA、Google等。

  3.4.1 安装Requests库

  在Windows环境下安装Requests库只需要在终端运行简单命令即可,代码如下所示:

  pip install requests

  在Mac OS系统下安装Requests库也很简单,由于在前面的讲解中已经说明使用Python 3版本,故Mac系统在安装好Python 3后,需要在终端运行如下命令:

  pip3 install requests

  安装完成后需要验证Requests库是否安装成功,验证方式是在Python的shell中输入import requests,如果不报错则表示安装成功。

  3.4.2 发送请求

  Requests库提供了几乎所有的HTTP动词的功能:GET、OPTIONS、HEAD、POST、PUT、DELETE,另外它还提供了headers参数便于定制请求头。

  使用Requests发送请求方式示例如下所示:

import requests
  r = requests.get("http://httpbin.org/get")
  r = requests.post("http://httpbin.org/post", data={‘key’:’value’})
  r = requests.put("http://httpbin.org/put", data={‘key’:’value’})
  r = requests.delete("http://httpbin.org/delete")
  r = requests.head("http://httpbin.org/get")
  r = requests.options("http://httpbin.org/get")

  下面重点介绍GET请求和POST请求,以及如何添加请求头。

  GET请求

  在有些情况下,GET请求的URL 会带参数,比如 https://segmentfault.com/blogs?page=2,该 URL 有一个值为2的参数page。为满足这种需求,Requests 库为GET请求提供了 params 关键字参数,且允许以一个字典来提供参数值,具体示例代码如下所示:

 import requests
  payload = {'page': '1', 'per_page': '10'}
  r = requests.get("http://httpbin.org/get", params=payload)
  print(r.url)

  运行该程序,可以看到打印出的URL已经被正确编码,结果如下所示:

  http://httpbin.org/get?page=1&per_page=10

  POST请求

  同样的,使用Requests发送POST请求如下代码所示:

  import requests

  r = requests.post("http://httpbin.org/post", data={‘key’:’value’})

  通常发送POST请求时还会附上数据,例如发送编码为表单形式的数据或编码为JSON形式的数据,这时可以使用Requests库提供的data参数。

  通过data参数传递一个字典数据,字典数据在发出请求时会被自动编码为表单形式,代码如下所示:

 import requests
  payload = {'page': 1, 'per_page': 10}
  r = requests.post("http://httpbin.org/post", data=payload)
  print(r.text)
  运行结果如以下代码所示(省略部分数据):
  {
  "args": {},
  "data": "",
  "files": {},
  "form": {
  "page": "1",
  "per_page": "10"
  },
  …
  }

  从上述结果可以看出网站已经接收到传递的字典数据。

  POST请求除了发送表单数据外,还可以发送JSON形式的数据。给requests.post()方法的data参数传递一个字典数据,代码如下所示:

 import json
  import requests
  payload = {'page': 1, 'per_page': 10}
  r = requests.post("http://httpbin.org/post", data=json.dumps(payload))
  print(r.text)
  运行结果如下所示。
  {
  "args": {},
  "data": "{\"page\": 1, \"per_page\": 10}",
  "files": {},
  "form": {},
  "headers": {
  "Accept": "*/*",
  "Accept-Encoding": "gzip, deflate",
  "Connection": "close",
  "Content-Length": "27",
  "Host": "httpbin.org",
  "User-Agent": "python-requests/2.18.4"
  },
  "json": {
  "page": 1,
  "per_page": 10
  },
  "origin": "",
  "url": "http://httpbin.org/post"
  }

  上述程序中使用json.dumps()方法将字典数据转换成JSON格式的字符串类型,运行程序,结果也接收到了page,per_page数据。

  添加请求头信息

  Requests库还可以为请求添加HTTP头部信息,通过传递一个字典类型数据给headers参数来实现。示例代码如下所示。

 import requests
  url = 'http://httpbin.org/post'
  payload = {'page': 1, 'per_page': 10}
  headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}
  r = requests.post("http://httpbin.org/post", json=payload, headers=headers)
  print(r.headers) # 查看服务器返回的响应头信息
  运行该程序,结果如下所示。
  {'Connection': 'keep-alive', 'Server': 'Cowboy',
  'Date': 'Sun, 25 Feb 2018 08:10:10 GMT',
  'Content-Length': '506', 'Content-Type': 'text/html; charset=utf-8',
  'Cache-Control': 'no-cache, no-store'}

  此时可以看到服务器返回的响应头信息。

  3.4.3 响应接收

  HTTP响应由三部分组成:状态行、响应头、响应正文。当使用requests.*发送请求时,Requests首先构建一个Requests对象,该对象会根据请求方法或相关参数发起HTTP请求。一旦服务器返回响应,就会产生一个Response对象,该响应对象包含服务器返回的所有信息,也包含原本创建的Request对象。

  对于响应状态码,可以访问响应对象的status_code属性,代码如下所示:

  import requests

  ret = requests.get("http://httpbin.org/get")

  print (ret.status_code) # 正常访问返回200状态码

  对于响应正文,可以通过多种方式读取,如下所示:

  l 普通响应,通过ret.text获取

  l JSON响应,通过ret.json获取

  l 二进制内容响应,通过ret.content获取

  首先查看如何读取unicode形式的响应,代码如下所示:

 import requests
  r = requests.get("https://github.com/timeline.json")
  print (r.text)
  print (r.encoding)
  运行该程序,结果如下所示。
  {"message":"Hello there, wayfaring stranger. If you’re reading this then you
  probably didn’t see our blog post a couple of years back announcing that this
  API would go away: http://git.io/17AROg Fear not, you should be able to get
  what you need from the shiny new Events API
  instead.","documentation_url":"https://developer.github.com/v3/activity/e
  vents/#list-public-events"}
  utf-8

  上述结果表明了Requests自动解码了服务器的内容。

  在默认情况下,除了HEAD请求,Requests会自动处理所有的重定向,使用响应对象的history属性可以追踪重定向。response.history是一个列表,该列表以从旧到新的请求顺序进行排列。使用示例代码如下所示:

 import requests
  headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}
  r = requests.get('https://toutiao.io/k/c32y51', headers=headers)
  ret = r.status_code
  print(ret) # 查看状态吗
  print(r.url) # 查看链接
  print(r.history) # 查看对象列表信息
  print(r.history[0].text) # 查看第一条具体信息
  运行结果如下所示。
  200
  https://www.jianshu.com/p/490441391db6?hmsr=toutiao.io&utm_medium=toutiao
  .io&utm_source=toutiao.io
  [, ]
  You are being
  href="http://www.jianshu.com/p/490441391db6?hmsr=toutiao.io&utm_mediu
  m=toutiao.io&utm_source=toutiao.io">redirected.
  Requests库还可以发送cookie到服务器,代码如下所示:
  import requests
  url = 'http://httpbin.org/cookies'
  cookies = dict(key1='value1')
  ret = requests.get(url,cookies=cookies)
  print(ret.text)
  运行结果如下所示。
  {
  "cookies": {
  "key1": "value1"
  }
  }

  结果表明服务器已经读取到了cookie信息。

  3.4.4 会话对象

  在前面讲解Cookie知识时已经知道,HTTP协议是无状态协议。为此,Requests提供了会话对象Session,该对象可以跨请求保持某些参数,也可以在同一个Session实例发出的所有请求之间保持Cookie。

  下面通过一个示例演示跨请求保持Cookie,具体代码如下。

 import requests
  s = requests.session()
  ret = s.get('http://httpbin.org/cookies/set/sessioncookie/this_is_cookie')
  print(ret)
  ret2 = s.get('http://httpbin.org/cookies')
  print(ret2.text)
  运行程序,结果如下所示。
  {
  "cookies": {
  "sessioncookie": "this_is_cookie"
  }
  }

  上述示例中有两次请求,第一次请求使用Session会话对象发送get请求,并设置好Cookie,第二次请求使用Session发出另一次get请求,用于获取Cookie。从运行结果可见Cookie信息得到了保持。

  3.5 本章小结

  本章较为详细的介绍了Python网络爬虫中实现HTTP请求的两种方式,以及请求出现异常的处理方式。本章知识点很重要,大家需掌握urllib库的概念以及使用方法,Requests库的安装及使用,以及请求的异常处理。学习完本章内容,大家一定要动手进行实践,为后面学习打好基础。

  3.6 习题

  1.填空题

  (1) 在Python3中,Python2的urllib2库更名为 。

  (2) 使用urllib.request模块的作用是 。

  (3) 使用timeout参数的作用是 。

  (4) 状态码301的含义是 。

  (5) 正则表达式“a|b”作用是 。

  2.选择题

  (1) 下列正则表达式,( )表示匹配换行符。

  A.\n B.\W

  C.\b D.\d

  (2) 下列正则表达式中,( )能匹配“linux”或者“ios”。

  A.\wlinux\dios B.linux\b\dios

  C.linux|ios D.linux.*ios

  (3) 下列选项中,( )可以用来检测网页找不到的状态码。

  A.200 B.404

  C.301 D.501

  (4) 在Python3中,( )用于导入处理url异常的模块。

  A.import urllib.error B.import urllib.parse

  C.import re D.import sys

  (5) 下列选项中,( )是产生URLError的原因是。(多选)

  A.服务器URL不存在 B.服务器无响应

  C.网络无连接 D.触发了HTTPError

  3.思考题

  (1) 登录数据提交可以用GET吗?

  (2) 使用urllib.request.urlopen抓取“www.1000phone.com/666” 结果是什么?

  4.编程题

  现有字符串“abcdefg_Pythonphp_Java”,请使用正则表达式匹配出Python,且输出一段文字“I use Python”。

  [A1]“User-agent”:“Python-urllib/3.6”

上一篇:HTML5工具初识之网页编辑器

下一篇:认识HTML

QQ技术交流群

千锋Python官方①群
790693323

加入群聊