本文主要讲解Python3中的urllib库的用法。urllib是Python标准库中用于网络请求的库。该库有4个模块,分别是:urllib.request、urllib.error、urllib.parse和urllib.robotparser。其中urllib.request和urllib.error两个库在爬虫程序中使用比较频繁。
本文目录如下:
- urlopen()
- 简单抓取网页
- 设置请求超时
- 使用data参数提交数据
- Request
- 简单的使用Request
- Request高级用法
- 使用代理
- 认证登录
- Cookie设置
- HTTPResponse
- 错误解析
1. urlopen()
模拟浏览器发起一个HTTP请求,需要用到urllib.request模块。urllib.request的作用不仅是发起请求,还能获取请求返回结果。下面先看一下urlopen()的API。
urllib.request.urlopen(url , data=None , [timeout ,]*, cafile=None , capath=None , cadefault= False , context=None);
参数和说明如下:
参数 | 说明 |
url | string类型的地址,也就是要访问的url,例如:http://www.baidu.com。 |
data | bytes类型的内容,可通过bytes()函数转换为字节流,它也是可选参数。使用data参数,请求方式变成以post方式提交表单。使用标准格式是:application/x-www-form-urlencoded。 |
timeout | 参数用于设置请求超时时间,单位是秒。 |
cafile/capath | 参数代表CA证书和CA证书路径,如果适用HTTPS则需要用到。 |
context | 参数必须是ssl.SSLContext类型,用来指定SSL设置。 |
cadefault | 参数已弃用,可以略去。 |
- urlopen()可以单独传入urllib.request.Request对象。
- urlopen()返回的结果是一个http.client.HTTPResponse对象。
- 实际使用过程中, 用得最多的参数是url和data。
2. 简单抓取网页
下面来看一个简单的示例,使用urlib.request.urlopen()去请求百度贴吧,并获取它的页面源代码。运行代码如下:
1 import urllib.request 2 3 url = 'https://tieba.baidu.com/' 4 response = urllib.request.urlopen(url) 5 html = response.read() # 获取页码的源码 6 print(html.decode('gbk')) # 转化为GBK编码
运行结果如下:
通过以上示例可以知道,使用 urlib.request.urlopen() 方法,传入网址,就可以成功获取到网页的页面源码。
3. 设置请求超时
有时,在访问网页是常常会遇到这样的情况——由于计算机网络或者对方服务器崩溃等原因,导致请求迟迟无法得到响应。同样地,程序去请求的时候,也会遇到这样的问题,因此,可以手动设置超时。当请求超时的时候,可以采取进一步的措施,或再次请求。因此,urlopen()中可以通过timeout设置超时响应时间。
以下代码在url参数后添加了一个timeout设置,如果超过1s就舍弃它或重新获取网页。
1 import urllib.request 2 3 url = 'https://tieba.baidu.com/' 4 response = urllib.request.urlopen(url , timeout=1) 5 html = response.read() # 获取页码的源码 6 print(html.decode('gbk')) # 转化为GBK编码
4. 使用data参数提交数据
有的页面可以通过data传递一些其他的内容。data参数是可选的,如果添加data,需要它是字节流编码格式的内容,即bytes类型,通过bytes()函数可以进行转换,另外,如果传递了data参数,那么它的请求方式就不再是GET方式,而是通过POST方式。那么,他们是如何传递参数的呢?
data需要被转换为字节流。而data是一个字典,需要使用 urllib.request.urlencode() 将字典转化为字符串,再使用bytes()函数转换为字节流。最后使用urlopen()发起请求,请求时模拟用POST方式提交数据。
1 import urllib.parse 2 import urllib.request 3 4 url = 'http://httpbin.org/post' 5 data =bytes(urllib.parse.urlencode({'word':'hello'}).encode('utf-8')) 6 response = urllib.request.urlopen(url , data = data) 7 print(response.read())
运行后控制台会输出:
5. Request
通过urlopen()方法可以发起简单的请求,但它的几个简单的参数并不足以构建一个完整的请求。如果请求中需要加入headers(请求头)、指定请求方式等信息,那么久可以利用更强大的Request类来构建一个请求。下面来看一下Request请求的构造方法。
urllib.request.Request( url, data=None, headers={}, origin_req_host= None, unverifiable=False, method=None )
Request请求的参数和描述如下:
参数 | 数据类型 | 必填 | 描述 |
url | string | 是 | 请求链接 |
data | bytes | 否 | 与urlopen()的data的参数相同,请求表单的数据 |
hearders | 否 | 指定发起的HTTP请求的头部信息。headers是一个字典,它除了在Request中添加外,还可以通过调用Request实例的add_header()方法来添加请求头 | |
origin_req_host | string | 否 | 请求方法host的名称或IP地址 |
unverifiable
|
boolean | 否 | 这个请求是否无法验证的,默认值False。意思是说用户没有足够的权限来选择接收这个请求的结果。例如,我们请求一个HTML文档中的图片,但是我们没有自动抓取图像的权限,我们就要将unverifiable的值设置成True。 |
method | string | 否 | 发起请求的方式,有GET、POST、DELETE和PUT等。 |
6. 简单使用Request
了解Request参数后,下面就来简单地使用他来请求百度贴吧这个网址。
需要注意的是,使用Request伪装成浏览器发起HTTP请求,如果不设置headers中的User-Agent,默认的User-Agent是Python-urlib/3.5。因为可能一些网站会将该请求拦截,所以需要伪装成浏览器发起的请求。例如,使用User-Agent为Chrome浏览器。
运行代码如下:
1 import urllib.request 2 3 url = 'https://tieba.baidu.com/' 4 hearders = { 5 "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36' 6 } 7 request = urllib.request.Request(url=url , headers= hearders) 8 response = urllib.request.urlopen(request) 9 print(response.read().decode('gbk'))
运行结果如下:
这里涉及到 User-Agent 头部信息的获取,可以使用谷歌浏览器随便打开一个网站,然后按【F12】进行调试界面,切换到【Network】选项卡刷新页面,随意选择一个请求,如图所示,即可找到需要的“User-Agent”,将其复制过来就可以了。
7. Request高级用法
如果需要在请求中添加代理、处理请求的Cookie,那么就需要用到Handler 和OpenerDirector两个知识点。
7.1 Handler
Handler即处理者、处理器,能处理请求(HTTP、HTTPS、FTP等)中的各种事情。Handler的具体实现是 urlib.request.BaseHandler 类。 urlib.request.BaseHandler 类是所有其他Handler的基类,其提供了最基本的Handler的方法,如default_open()、protocol_request()等。继承BaseHandler类的Handler子类很多,这里列举了几个比较常见的类。
- ProxyHandler:为请求设置代理。
- HTTPCookieProcessor: 处理HTTP请求的Cookie。
- HTTPDefaultErrorHandler: 处理HTTP响应错误。
- HTTPRedirectHandler : 处理HTTP响应重定向。
- HTTPPasswordMgr:用于密码管理,它维护了用户名密码的表。
- HTTPBasicAuthHandler : 用于登录认证,一般和HTTPPasswordMgr结合使用。
7.2 OpenerDirector
OpenerDirector ,也可以成为Opener。之前用过的urlopen()方法,实际上就是urlib提供一个Opener。那么Opener和Handler又有什么关系呢?Opener对象有 build_opener(handler) 方法创建出来的。创建自定义的Opener,就需要使用install_opener(opener)方法。值得注意的是,install_opener实例化会得到一个全局的OpenerDirector对象。
8. 使用代理
为什么需要使用代理?有些网站做了浏览器频率限制,如果请求频率过高,该网站就会封IP,禁止我们访问,所以就需要使用代理来突破这个“枷锁”。
1 # 使用代理 2 import urllib.request 3 4 url = 'https://tieba.baidu.com/' 5 hearders = { 6 "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36' 7 } 8 proxy_header = urllib.request.ProxyHandler({ 9 'http':'212.200.246.24:80', 10 'https':'116.63.93.172:8081' 11 }); 12 opener = urllib.request.build_opener(proxy_header) 13 urllib.request.install_opener(opener) 14 15 request = urllib.request.Request(url=url , headers= hearders) 16 response = urllib.request.urlopen(request) 17 print(response.read().decode('gbk'))
通过以上代码可知,可以调用 ProxyHandler 方法设置代理,模拟成多个不同的客户端,成功“欺骗”网站,获取不同数据。
在实际项目中,如果需要大量使用代理IP,可到专门做代理的IP供应商处购买,虽然晚上有大量免费的,但是大都不稳定。
9. 认证登录
有些网站需要携带账号和密码进行登录之后才能继续浏览网页。遇到这样的网站,就需要用到认证登录。认证登录步骤思路如下:
- 使用HTTPPasswordMgrWithDefaultRealm()实例化一个账号密码管理对象;
- 使用add_password()函数添加账号和密码;
- 使用HTTPBasicAuthHandler()得到Handler;
- 使用build_opener()获取Opener对象;
- 使用Opener的open函数发起请求。
下面以携带账号和密码登录的百度贴吧为例,代码如下:
1 # 认证登录 2 import urllib.request 3 4 url = 'https://tieba.baidu.com/' 5 user = 'test_user' # 运行时用了真实账号,这里指代一下 6 password = 'test_password' # 运行时用了真实账号,这里指代一下 7 pwdmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() # 实例化账号密码管理对象 8 pwdmgr.add_password(None ,url, user , password) # 添加账号密码 9 auth_handler = urllib.request.HTTPBasicAuthHandler(pwdmgr) # 获取handler 10 opener = urllib.request.build_opener((auth_handler)) # 得到Opener对象 11 12 response = opener.open(url) 13 print(response.read().decode('gbk'))
控制台的运行结果与上文一致,这里省略了它。
10. Cookie设置
如果请求页面每次都需要身份验证,那么就可以使用Cookie来自动登录,免去重复验证的操作。获取Cookie需要 http.cookiejar.CookieJar() 实例化一个Cookie对象,再用 urlib.request.HTTPCookieProcessor 构建出Handler对象,最后使用Opener的open()函数即可。下面以获取请求百度贴吧的Cookie为例,代码如下:
1 # Cookie设置 2 import http.cookiejar 3 import urllib.request 4 5 url = 'https://tieba.baidu.com/' 6 fileName = 'cookie.txt' 7 8 cookie = http.cookiejar.CookieJar() 9 handler = urllib.request.HTTPCookieProcessor(cookie) 10 opener = urllib.request.build_opener(handler) 11 response = opener.open(url) 12 13 f = open(fileName ,'a') 14 for item in cookie: 15 f.write(item.name +"=" + item.value + ' ') 16 f.close()
运行完成后,文件的同级目录即可生成“cookie.txt”文件,记录保存的cookie。
11. HTTPResponse
从前面的例子可知,使用urlib.request.urlopen()或opener.open(url)返回结果是一个HTTPResponse对象。http.client.HTTPResponse对象包含msg、version、status、reasson、debuglevel、closed等属性及read()、readinto()、getheader(name)、getheaders()、fileno()等函数。
12. 错误解析
发起请求难免会出现各种异常,因此需要对异常进行处理,异常处理主要由两个类:urlib.error.URLError和urlib.error.HTTPError。
12.1 URLError
URLError是urlib.error异常类的基类,可以用于捕获有urlib.request产生的异常。它具有一个属性reason,即返回错误的原因。捕获URL异常的代码示例如下:
1 import urllib.request 2 import urllib.error 3 4 url = 'http://www.google.com' # 正常国内网络下,谷歌无法连接 5 6 try: 7 response = urllib.request.urlopen(url) 8 except urllib.error.URLError as e: 9 print(e.reason)
这里执行了捕获异常的代码,控制台运行结果如下:
12.2 HTTPError
HTTPError是UEKRoor的子类,专门处理HTTP和HTTPS请求的结果。它具有以下3个属性:
- code: HTTP请求返回的状态码:
- renson:与基类用法一样,表示返回错误的原因:
- headers:HTTP请求返回的响应信息。
获取HTTP异常的示例代码(输出类错误状态码、错误原因、服务器响应头)如下:
1 # HTTPError 2 import urllib.request 3 import urllib.error 4 5 url = 'http://www.google.com' # 正常国内网络下,谷歌无法连接 6 7 try : 8 response = urllib.request.urlopen(url) 9 except urllib.error.HTTPError as e: 10 print('code:' + e.code + ' ') 11 print('reason' + e.reason + ' ') 12 print('headers' + e.headers + ' ')