《Python 3网络爬虫开发实战 》崔庆才著 第二章笔记
使用urllib
它是 Python内置的HTTP请求库,也就是说不需要额外安装即可使用。
包含以下4个模块:
名字 | 说明 |
---|---|
request | 它是最基本的 HTTP请求模块,可以用来模拟发送请求。就像在浏览器里输入网址然后回车一样,只需要给库方法传入URL以及额外的参数,就可以模拟实现这个过程了。 |
error | 异常处理模块,如果出现请求错误,我们可以捕获这些异常,然后进行重试或其他操作以保证程序不会意外终止。 |
parse | 一个工具模块,提供了许多URL处理方法,比如拆分、解析、合并等。 |
robotparser | 主要是用来识别网站的 robots.txt文件,然后判断哪些网站可以爬,哪些网站不可以爬,它其实用得比较少。 |
重点讲解下前3个模块。
发送请求
使用urllib 的request模块,我们可以方便地实现请求的发送并得到响应。
1.urlopen()
urllib.request模块提供了最基本的构造HTTP请求的方法,利用它可以模拟浏览器的一个请求发起过程,同时它还带有处理授权验证(authenticaton)、重定向( redirection)、浏览器Cookies以及其他内容。
import urllib.request
response=urllib.request.urlopen('https://www.python.org')
print(response.read().decode('utf-8'))
查看返回的类型
import urllib.request
response=urllib.request.urlopen('https://www.python.org')
print(type(response))
发现是HTTPResponse类型
该对象包含了以下方法:
- read():返回网页的内容。
- readinto()
- getheader(name)
- getheaders()
- fileno()
以及以下属性:
- msg
- version
- status:请求页面的状态码。
- reason
- debuglevel
- closed
得到这个对象之后,我们把它赋值为response变量,然后就可以调用这些方法和属性,得到返回结果的一系列信息了。
urlopen API:
urllib.request.urlopen(url, data=None, [timeout, ]* , cafile=None , capath=None, cadefault=False, context=None)
data参数
data参数是可选的。如果要添加该参数,并且如果它是字节流编码格式的内容,即 bytes类型,则需要通过bytes()方法转化。另外,如果传递了这个参数,则它的请求方式就不再是GET方式,而是POST方式。
import urllib.parse
import urllib.request
data=bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf-8')
response = urllib.request.urlopen('http://httpbin.org/post', data=data)
print(response.read())
这里我们传递了一个参数 word,值是 hello。它需要被转码成 bytes(字节流)类型。其中转字节流采用了bytes()方法,该方法的第一个参数需要是str(字符串)类型,需要用urllib.parse模块里的urlencode()方法来将参数字典转化为字符串;第二个参数指定编码格式,这里指定为utf8。
timeout参数
timeout参数用于设置超时时间,单位为秒,意思就是如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常。如果不指定该参数,就会使用全局默认时间。它支持 HTTP、HTTPS、FTP请求。
import urllib.request
response = urllib.request.urlopen('http://httpbin.org/get', timeout=1)
print(response.read())
这里我们设置超时时间是1秒。程序1秒过后,服务器依然没有响应,于是抛出了URLError 异常该异常属于urllib.error模块,错误原因是超时。
因此,可以通过设置这个超时时间来控制一个网页如果长时间未响应,就跳过它的抓取。这可以利用try except语句来实现,相关代码如下:
import socket
import urllib.request
import urllib.error
try:response = urllib.request.urlopen( 'http://httpbin.org/get', timeout=0.1)
except urllib.error.URLError as e:if isinstance(e.reason,socket.timeout):print( 'TIME OUT')
这里我们请求了http:/httpbin.org/get测试链接,设置超时时间是0.1秒,然后捕获了URLError异常,接着判断异常是socket.timeout类型(意思就是超时异常),从而得出它确实是因为超时而报错,打印输出了 TIME OUT。
其他参数
除了data参数和timeout参数外,还有context参数,它必须是ssl.SsLContext类型,用来指定SSL设置。
此外,cafile和 capath这两个参数分别指定CA证书和它的路径,这个在请求HTTPS链接时会有用。
cadefault参数现在已经弃用了,其默认值为False。
2.Request
我们知道利用urlopen()方法可以实现最基本请求的发起,但这几个简单的参数并不足以构建一个完整的请求。如果请求中需要加入Headers等信息,就可以利用更强大的Request类来构建。
import urllib.request
request = urllib.request.Request( 'https://python.org')
response = urllib.request.urlopen(request)
print(response.read().decode( 'utf-8 '))
下面我们看一下Request可以通过怎样的参数来构造,它的构造方法如下:
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None,unverifiable=False,method=None)
第一个参数url用于请求URL,这是必传参数,其他都是可选参数。
第二个参数 data如果要传,必须传 bytes(字节流)类型的。如果它是字典,可以先用urllib.parse模块里的urlencode()编码。
第三个参数 headers是一个字典,它就是请求头,我们可以在构造请求时通过headers参数直接构造,也可以通过调用请求实例的add_header()方法添加。添加请求头最常用的用法就是通过修改 User-Agent来伪装浏览器,默认的User-Agent是Python-urllib,我们可以通过修改它来伪装浏览器。比如要伪装火狐浏览器,你可以把它设置为:
Mozilla/5.0 (X11; U;Linux i686)Gecko/20071127 Firefox/2.0.0.11
第四个参数origin_req_host指的是请求方的host名称或者IP地址。
第五个参数unverifiable表示这个请求是否是无法验证的,默认是False,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,我们请求一个 HTML文档中的图片,但是我们没有自动抓取图像的权限,这时unverifiable的值就是True。
第六个参数method是一个字符串,用来指示请求使用的方法,比如 GET、POST和 PUT等。
下面我们传入多个参数构建请求来看一下:
from urllib import request, parse
url = 'http://httpbin.org/post'
headers = {'User-Agent':'Mozilla/4.o (compatible;MSIE 5.5; windows NT)' ,'Host':'httpbin.org'
}
dict = {'name':'Germey'
}data = bytes(parse.urlencode(dict),encoding='utf8')
req = request.Request(url=url, data=data,headers=headers,method='POST')
response = request.urlopen(req)
print(response.read().decode('utf-8'))
headers也可以用add_header()方法来添加:
req = request.Request(url=url, data=data,method='POST')
req.add_header( 'User-Agent', 'Mozilla/4.o (compatible; MSIE 5.5; Windows NT)')
3.高级用法
在上面的过程中,我们虽然可以构造请求,但是对于一些更高级的操作(比如 Cookies处理、代理设置等),就需要更强大的工具 Handler 登场了。简而言之,我们可以把它理解为各种处理器,有专门处理登录验证的,有处理Cookies的,有处理代理设置的。利用它们,我们几乎可以做到 HTTP请求中所有的事情。
首先,介绍一下urllib.request模块里的BaseHandler类,它是所有其他Handler的父类,它提供了最基本的方法,例如 default_open()、 protocol_request()等。有各种 Handler子类继承这个BaseHandler类,举例如下。
类 | 说明 |
---|---|
HTTPDefaultErrorHandler | 用于处理HTTP响应错误,错误都会抛出 HTTPError类型的异常。 |
HTTPRedirectHandler | 用于处理重定向。 |
HTTPCookieProcessor | 用于处理Cookies。 |
ProxyHandler | 用于设置代理,默认代理为空。 |
HTTPPasswordMgr | 用于管理密码,它维护了用户名和密码的表。 |
HTTPBasicAuthHandler | 用于管理认证,如果一个链接打开时需要认证,那么可以用它来解决认证问题。 |
另一个比较重要的类就是OpenerDirector,我们可以称为Opener。Opener可以使用open()方法,返回的类型和urlopen()如出一辙。它就是利用Handler来构建Opener。下面用几个实例来看看它们的用法。
验证
有些网站在打开时就会弹出提示框,直接提示你输入用户名和密码,验证成功后才能查看页面。
借助HTTPBasicAuthHandler就可以完成
from urllib.request import HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_opener from urllib.error import URLErrorusername = 'username' password = 'password' url = 'http://localhost:5000/' p= HTTPPasswordMgrWithDefaultRealm() p.add_password(None,url,username,password) auth_handler = HTTPBasicAuthHandler(p) opener = build_opener(auth_handler)try:result = opener.open(url)html = result.read().decode('utf-8')print(html) except URLError as e:print(e.reason)
这里首先实例化HTTPBasicAuthHandler对象,其参数是HTTPPasswordMgrWithDefaultRealm对象,它利用add_password()添加进去用户名和密码,这样就建立了一个处理验证的 Handler。
接下来,利用这个Handler并使用build_opener()方法构建一个Opener,这个Opener在发送请求时就相当于已经验证成功了。
接下来,利用Opener的open()方法打开链接,就可以完成验证了。这里获取到的结果就是验证后的页面源码内容。
代理
在做爬虫的时候,免不了要使用代理,如果要添加代理,可以这样做:
from urllib.error import URLError from urllib.request import ProxyHandler,build_openerproxy_handler = ProxyHandler({'http': 'http://127.0.0.1:9743','https ': 'https://127.0.0.1:9743' })opener = build_opener(proxy_handler)try:response = opener.open( 'https://www.baidu.com')print(response.read().decode( 'utf-8')) except URLError as e:print(e.reason)
这里我们在本地搭建了一个代理,它运行在9743端口上。
这里使用了ProxyHandler,其参数是一个字典,键名是协议类型(比如HTTP或者HTTPS等).键值是代理链接,可以添加多个代理。
然后,利用这个Handler 及 build opener()方法构造一个Opener,之后发送请求即可。
Cookies
Cookies的处理就需要相关的Handler 了。我们先用实例来看看怎样将网站的Cookies获取下来,相关代码如下:
import http.cookiejar,urllib.requestcookie = http.cookiejar.CookieJar() handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open( ' http://wwwl.baidu.com')for item in cookie:print(item.name+"="+item.value)
首先,我们必须声明一个CookieJar对象。接下来,就需要利用HTTPCookieProcessor来构建一个Handler,最后利用build_opener()方法构建出Opener,执行open()函数即可。运行结果如下:
我们还可以输出为文件格式:
import http.cookiejar,urllib.requestfilename = "cookies.txt" cookie = http.cookiejar.MozillaCookieJar(filename) handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener. open('http://www.baidu.com' )cookie.save(ignore_discard=True,ignore_expires=True)
这时CookieJar就需要换成MozillaCookieJar,它在生成文件时会用到,是CookieJar的子类,可以用来处理Cookies 和文件相关的事件,比如读取和保存Cookies,可以将Cookies保存成Mozilla型浏览器的 Cookies格式。运行之后,可以发现生成了一个cookies.txt文件.其内容如下:
另外,LWPCookieJar同样可以读取和保存Cookies,但是保存的格式和MozillaCookieJar不一样,它会保存成libwww-perl(LWP)格式的Cookies文件。要保存成LWP格式的Cookies文件,可以在声明时就改为:
cookie = http.cookiejar.LWPCookieJar(filename)
可以发现他们之间的差别有点大。
下面我们以LWPCookieJar格式来看看如何使用cookie文件
import http.cookiejar,urllib.request cookie = http.cookiejar.LWPCookieJar() cookie.load('cookies.txt',ignore_discard=True,ignore_expires=True) handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response = opener.open('http://www.baidu.com') print(response.read().decode('utf-8'))
可以看到,这里调用load()方法来读取本地的Cookies文件,获取到了Cookies的内容。不过前提是我们首先生成了LWPCookieJar格式的Cookies,并保存成文件,然后读取 Cookies之后使用同样的方法构建Handler和 Opener即可完成操作。
运行结果正常的话,会输出百度网页的源代码。
通过上面的方法,我们可以实现绝大多数请求功能的设置了。
处理异常
urllib的error模块定义了由request模块产生的异常。如果出现了问题,request模块便会抛出error模块中定义的异常。
1.URLError
URLError类来自urllib库的error模块,它继承自OSError类,是error异常模块的基类,由request模块生的异常都可以通过捕获这个类来处理。
它具有一个属性reason,即返回错误的原因。
from urllib import request,error
try:response=request.urlopen('https://cuiqingcai.com/index.htm')
except error.URLError as e:print(e.reason)
程序没有直接报错,而是输出了如上内容,这样通过如上操作,我们就可以避免程序异常终止,同时异常得到了有效处理。
2.HTTPError
它是URLError的子类,专门用来处理IITTP请求错误,比如认证请求失败等。它有如下3个属性。
名字 | 说明 |
---|---|
code | 返回HTTP状态码,比如404表示网页不存在,500表示服务器内部错误等。 |
reason | 同父类一样,用于返回错误的原因。 |
headers | 返回请求头。 |
from urllib import request,error
try:response=request.urlopen('https://cuiqingcai.com/index.htm')
except error.HTTPError as e:print(e.reason,e.code,e.headers,sep='\n')
依然是同样的网址,这里捕获了HTTPError异常,输出了reason、code和 headers属性。
因为URLError是 HTTPError的父类,所以可以先选择捕获子类的错误,再去捕获父类的错误
有时候,reason属性返回的不一定是字符串,也可能是一个对象。
import socket
import urllib.request
import urllib.errortry:response=urllib.request.urlopen('https://www.baidu.com',timeout=0.01)
except urllib.error.URLError as e:print(type(e.reason))if isinstance(e.reason,socket.timeout):print('TIME OUT')
可以发现,reason属性的结果是socket.timeout类。所以,这里我们可以用isinstance()方法来判断它的类型,作出更详细的异常判断。
解析链接
urllib 库里还提供了 parse 模块,它定义了处理 URL 的标准接口,例如实现 URL 各部分的抽取、合并以及链接转换。它支持如下协议的 URL 处理:file、ftp、gopher、hdl、http、https、imap、mailto、mms、news、nntp、prospero、rsync、rtsp、rtspu、sftp、sip、sips、snews、svn、svn+ssh、telnet 和 wais。
1.urlparse()
该方法可以实现 URL 的识别和分段
from urllib.parse import urlparseresult = urlparse('https://www.baidu.com/index.html;user?id=5#comment')
print(type(result))
print(result)
这里我们利用urlparse()方法进行了一个URL 的解析。首先,输出了解析结果的类型,然后将结果也输出出来。
可以看到,返回结果是一个ParseResult类型的对象,它包含6个部分,分别是scheme,netloc,path,params,query和 fragment。一般的URL标准格式如下:
scheme://netloc/path;params?query#fragment
名字 | 说明 | 输出 |
---|---|---|
scheme |
《Python 3网络爬虫开发实战 》崔庆才著 第二章笔记相关推荐
最新文章
热门文章 |