Python爬虫-请求模块Urllib
Python3中的Urllib 模块中包含多个功能的子模块,具体内容如下:
urllib.request:用于实现基本HTTP请求的模块。
urllib.error:异常处理模块,如果在发送网络请求时出现了错误,可以捕获异常进行异常的有效处理。
urllib.parse:用于解析URL的模块。
urllib.robotparser:用于解析robots.txt文件,判断网站是否可以爬取信息。
使用 urlopen()方法发送请求
urllib.request模块提供了urlopen()方法,用于实现最基本的HTTP请求,然后接收服务端所返回的响应数据。urlopen()方法的语法格式如下:
urllib.request.urlopen(url,data-None,[timeout,]*,cafile-None,capath-None,cadefault-False, context=None)
url:需要访问网站的URL完整地址。
data:该参数默认为None,通过该参数确认请求方式,如果是None,表示请求方式为GET,否则请求方式为POST,在发送POST请求时,参数data 需要以字典形式的数据作为参数值,并且需要将字典类型的参数值转换为字节类型的数据才可以实现POST请求。
timcout:以秒为单位,设置超时。
cafile、capath:指定一组 HTTPS请求受信任的CA证书,cafile指定包含CA证书的单个文件,capath指定证书文件的目录。
cadefault:CA证书默认值。
context:描述SSL选项的实例。
HTTPResponse 常用的方法与属性获取信息
import urllib.request # 导入request子模块
url = 'https://www.python.org/'
response = urllib.request.urlopen(url=url) # 发送网络请求
print('响应状态码为:',response.status)
print('响应头所有信息为:',response.getheaders())
print('响应头指定信息为:',response.getheader('Accept-Ranges'))
# 读取HTML代码并进行utf-8解码
print('Python官网HTML代码如下:\n',response.read().decode('utf-8'))
运行:
响应状态码为: 200
响应头所有信息为: [('Connection', 'close'), ('Content-Length', '50771'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'SAMEORIGIN'), ('Via', '1.1 varnish, 1.1 varnish'), ('Accept-Ranges', 'bytes'), ('Date', 'Sat, 17 Aug 2024 04:05:29 GMT'), ('Age', '883'), ('X-Served-By', 'cache-iad-kiad7000025-IAD, cache-nrt-rjtf7700020-NRT'), ('X-Cache', 'HIT, HIT'), ('X-Cache-Hits', '461, 64'), ('X-Timer', 'S1723867530.760647,VS0,VE0'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload')]
响应头指定信息为: bytes
使用urlopen()方法发送POST请求
import urllib.request # 导入urllib.request模块
import urllib.parse # 导入urllib.parse模块
url = 'https://www.httpbin.org/post' # post请求测试地址
# 将表单数据转换为bytes类型,并设置编码方式为utf-8
data = bytes(urllib.parse.urlencode({'hello':'python'}),encoding='utf-8')
response = urllib.request.urlopen(url=url,data=data) # 发送网络请求
print(response.read().decode('utf-8')) # 读取HTML代码并进行
运行:
{
"args": {},
"data": "",
"files": {},
"form": {
"hello": "python"
},
"headers": {
"Accept-Encoding": "identity",
"Content-Length": "12",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "www.httpbin.org",
"User-Agent": "Python-urllib/3.7",
"X-Amzn-Trace-Id": "Root=1-66c02297-6c016ea61ad2e00e078c48d7"
},
"json": null,
"origin": "61.136.223.36",
"url": "https://www.httpbin.org/post"
}
设置网络超时
捕获超时异常处理网络超时
import urllib.request # 导入urllib.request模块
import urllib.error # 导入urllib.error模块
import socket # 导入socket模块
url = 'https://www.python.org/' # 请求地址
try:
# 发送网络请求,设置超时时间为0.1秒
response = urllib.request.urlopen(url=url, timeout=0.1)
print(response.read().decode('utf-8')) # 读取HTML代码并进行utf-8解码
except urllib.error.URLError as error: # 处理异常
if isinstance(error.reason, socket.timeout): # 判断异常是否为超时异常
print('当前任务已超时,即将执行下一任务!')
运行:
当前任务已超时,即将执行下一任务!
复杂的网络请求
通过上一节的学习可以得知,urlopen()方法能够发送一个最基本的网络请求,但这并不是一个完整的网络请求。如果要构建一个完整的网络请求,还需要在请求中添加 Headers、Cookies以及代理IP等内容,这样才能更好的模拟一个浏览器所发送的网络请求。Request类则可以构建一个多种功能的请求对象,其语法格式如下:
urllib. request. Request(url, data-None, headers-{}, origin_req_host-None, unverifiable=False, method=None)
url:需要访问网站的URL完整地址。
data:该参数默认为None,通过该参数确认请求方式,如果是None,表示请求方式为GET,否则请求方式为POST。在发送POST请求时,参数data需要以字典形式的数据作为参数值,并且需要将字典类型的参数值转换为字节类型的数据才可以实现 POST请求。
headers:设置请求头部信息,该参数为字典类型。添加请求头信息最常见的用法就是修改User-Agent来伪装成浏览器。例如,headers ={‘User-Agent’:‘Mozilla/5.0(Windows NT 10.0;
WOW64)AppleWebKit/537.36(KHTML,like Gecko)Chrome/83.0.4103.61 Safari/537.36’},表示伪装谷歌浏览器进行网络请求。
origin_req_host:用于设置请求方的host名称或者是IP。
unverifiable:用于设置网页是否需要验证,默认是False。
method:用于设置请求方式,如GET、POST等,默认为GET请求。
设置请求头信息
如果需要设置请求头信息,首先通过 Request类构造一个带有 headers 请求头信息的 Request 对象,然后为urlopen()方法传入 Request对象,再进行网络请求的发送。示例代码如下:
import urllib.request # 导入urllib.request模块
import urllib.parse # 导入urllib.parse模块
url = 'https://www.httpbin.org/post' # 请求地址
# 定义请求头信息
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'}
# 将表单数据转换为bytes类型,并设置编码方式为utf-8
data = bytes(urllib.parse.urlencode({'hello':'python'}),encoding='utf-8')
# 创建Request对象
r = urllib.request.Request(url=url,data=data,headers=headers,method='POST')
response = urllib.request.urlopen(r) # 发送网络请求
print(response.read().decode('utf-8')) # 读取HTML代码并进行utf-8解码
运行:
{
"args": {},
"data": "",
"files": {},
"form": {
"hello": "python"
},
"headers": {
"Accept-Encoding": "identity",
"Content-Length": "12",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "www.httpbin.org",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-66c024ad-1d1900532e58a6e548b8eacc"
},
"json": null,
"origin": "61.136.223.36",
"url": "https://www.httpbin.org/post"
}
异常处理
在实现网络请求时,可能会出现很多异常错误,Urllib模块中的urllib.error 子模块,包含了URLError与 HTTPError两个比较重要的异常类。
调用reason,属性处理URLError异常
import urllib.request # 导入urllib.request模块
import urllib.error # 导入urllib.error模块
try:
# 向不存在的网络地址发送请求
response = urllib.request.urlopen('http://site2.rjkflm.com:666/123index.html')
except urllib.error.URLError as error: # 捕获异常信息
print(error.reason) # 打印异常原因
运行:
[Errno 11001] getaddrinfo failed
HTTPError类是URLError类的子类,主要用于处理HTTP请求所出现的异常,该类有以下三个属性:
code:返回HTTP状态码。
reason:返回错误原因。
headers:返回请求头。
双重异常的捕获
由于 HTTPError 是 URLError的子类,有时 HTTPError类会有捕获不到的异常,所以可以先捕获子类HTTPError的异常,然后再去捕获父类URLError的异常,这样可以起到双重保险的作用。示例代码如下:
import urllib.request # 导入urllib.request模块
import urllib.error # 导入urllib.error模块
try:
# 向不存在的网络地址发送请求
response = urllib.request.urlopen('https://www.python.org/',timeout=0.1)
except urllib.error.HTTPError as error: # HTTPError捕获异常信息
print('状态码为:',error.code) # 打印状态码
print('HTTPError异常信息为:',error.reason) # 打印异常原因
print('请求头信息如下:\n',error.headers) # 打印请求头
except urllib.error.URLError as error: # URLError捕获异常信息
print('URLError异常信息为:',error.reason)
运行:
URLError异常信息为: timed out
解析链接
Urllib 模块中提供了parse 子模块,主要用于解析URL,可以实现 URL 的拆分或者是组合。它支持多种协议的URL处理,如 file、ftp、gopher、hdl、http、https、imap、mailto、mms、news、nntp、prospero、rsync、rtsp、rtspu、sftp、shttp、sip、sips、snews、SVn、svn+ssh、telnet、wais、ws、wss。
拆分 URL
使用urlparse()方法拆分URL
import urllib.parse #导入urllib.parse模块
parse_result = urllib.parse.urlparse('https://docs.python.org/3/library/urllib.parse.html')
print(type(parse_result)) # 打印类型
print(parse_result) # 打印拆分后的结果
运行:
<class 'urllib.parse.ParseResult'>
ParseResult(scheme='https', netloc='docs.python.org', path='/3/library/urllib.parse.html', params='', query='', fragment='')
从以上的运行结果中可以看出,调用urlparse()方法将返回一个ParseResult对象此对象,由6部分组成:scheme表示协议,netloc表示域名,path表示访问的路径,params表示参数,query表示查询条件,fragment表示片段标识符。
使用urlsplit()方法拆分URL
urlsplit()方法与urlparse()方法类似,都可以实现URL的拆分。只是urlsplit()方法不再单独拆分params这部分内容,而是将 params合并到path当中,所以返回的结果中只有5部分内容,并且返回的数据类型为 SplitResult。示例代码如下:
import urllib.parse #导入urllib.parse模块
# 需要拆分的URL
url = 'https://docs.python.org/3/library/urllib.parse.html'
print(urllib.parse.urlsplit(url)) # 使用urlsplit()方法拆分URL
print(urllib.parse.urlparse(url)) # 使用urlparse()方法拆分URL
运行:
SplitResult(scheme='https', netloc='docs.python.org', path='/3/library/urllib.parse.html', query='', fragment='')
ParseResult(scheme='https', netloc='docs.python.org', path='/3/library/urllib.parse.html', params='', query='', fragment='')
组合URL
使用urlunparse()方法组合URL
import urllib.parse #导入urllib.parse模块
list_url = ['https','docs.python.org','/3/library/urllib.parse.html','','','']
tuple_url = ('https','docs.python.org','/3/library/urllib.parse.html','','','')
dict_url = {'scheme':'https','netloc':'docs.python.org','path':'/3/library/urllib.parse.html','params':'','query':'','fragment':''}
print('组合列表类型的URL:',urllib.parse.urlunparse(list_url))
print('组合元组类型的URL:',urllib.parse.urlunparse(tuple_url))
print('组合字典类型的URL:',urllib.parse.urlunparse(dict_url.values()))
运行:
组合列表类型的URL: https://docs.python.org/3/library/urllib.parse.html
组合元组类型的URL: https://docs.python.org/3/library/urllib.parse.html
组合字典类型的URL: https://docs.python.org/3/library/urllib.parse.html
使用urlunparse()方法组合URL时,需要注意可迭代参数中的元素必须是6个。
使用urlunsplit()方法组合URL
import urllib.parse #导入urllib.parse模块
list_url = ['https','docs.python.org','/3/library/urllib.parse.html','','']
tuple_url = ('https','docs.python.org','/3/library/urllib.parse.html','','')
dict_url = {'scheme':'https','netloc':'docs.python.org','path':'/3/library/urllib.parse.html','query':'','fragment':''}
print('组合列表类型的URL:',urllib.parse.urlunsplit(list_url))
print('组合元组类型的URL:',urllib.parse.urlunsplit(tuple_url))
print('组合字典类型的URL:',urllib.parse.urlunsplit(dict_url.values()))
运行:
组合列表类型的URL: https://docs.python.org/3/library/urllib.parse.html
组合元组类型的URL: https://docs.python.org/3/library/urllib.parse.html
组合字典类型的URL: https://docs.python.org/3/library/urllib.parse.html
连接URL
使用urljoin()方法连接URL
import urllib.parse #导入urllib.parse模块
base_url = 'https://docs.python.org' # 定义基础链接
# 第二参数不完整时
print(urllib.parse.urljoin(base_url,'3/library/urllib.parse.html'))
# 第二参数完成时,直接返回第二参数的链接
print(urllib.parse.urljoin(base_url,'https://docs.python.org/3/library/urllib.parse.html#url-parsing'))
运行:
https://docs.python.org/3/library/urllib.parse.html
https://docs.python.org/3/library/urllib.parse.html#url-parsing
URL的编码与解码
URL编码是GET请求中比较常见的,是将请求地址中的参数进行编码,尤其是对于中文参数。parse子模提供了urlencode()方法与 quote()方法用于实现URL的编码,而unquote()方法可以实现对加密后的URL 进行解码操作。
使用urlencode()方法编码请求参数
urlencode()方法接收一个字典类型的值,所以要想将URL 进行编码需要先将请求参数定义为字典类型,然后再调用urlencode()方法进行请求参数的编码。示例代码如下:
import urllib.parse #导入urllib.parse模块
base_url = 'http://httpbin.org/get?' # 定义基础链接
params = {'name':'Jack','country':'中国','age':30} # 定义字典类型的请求参数
url = base_url+urllib.parse.urlencode(params) # 连接请求地址
print('编码后的请求地址为:',url)
运行:
编码后的请求地址为: http://httpbin.org/get?name=Jack&country=%E4%B8%AD%E5%9B%BD&age=30
地址中“%E4%B8%AD%E5%9B%BD&”内容为中文(中国)转码后的效果。
使用quote()方法编码字符串参数
quote()方法与urlencode()方法所实现的功能类似,但是urlencode()方法中只接收字典类型的参数,而quote()方法则可以将一个字符串进行编码。示例代码如下:
import urllib.parse #导入urllib.parse模块
base_url = 'http://httpbin.org/get?country=' # 定义基础链接
url = base_url+urllib.parse.quote('中国') # 字符串编码
print('编码后的请求地址为:',url)
运行:
编码后的请求地址为: http://httpbin.org/get?country=%E4%B8%AD%E5%9B%BD
使用unquote()方法解码请求参数
unquote()方法可以将编码后的URL 字符串逆向解码,无论是通过urlencode()方法或者是 quote()
方法所编码的URL字符串都可以使用 unquote()方法进行解码。示例代码如下:
import urllib.parse #导入urllib.parse模块
u = urllib.parse.urlencode({'country':'中国'}) # 使用urlencode编码
q=urllib.parse.quote('country=中国') # 使用quote编码
print('urlencode编码后结果为:',u)
print('quote编码后结果为:',q)
print('对urlencode解码:',urllib.parse.unquote(u))
print('对quote解码:',urllib.parse.unquote(q))
运行:
urlencode编码后结果为: country=%E4%B8%AD%E5%9B%BD
quote编码后结果为: country%3D%E4%B8%AD%E5%9B%BD
对urlencode解码: country=中国
对quote解码: country=中国
URL 参数的转换
使用parse_qs()方法将参数转换为字典类型
请求地址的URL是一个字符串,如果需要将其中的参数转换为字典类型,可以先使用urlsplit()方
法拆分 URL,然后再调用 query属性获取URL 中的参数,最后使用 parse_qs()方法将参数转换为字典类型的数据。示例代码如下:
import urllib.parse #导入urllib.parse模块
# 定义一个请求地址
url = 'http://httpbin.org/get?name=Jack&country=%E4%B8%AD%E5%9B%BD&age=30'
q = urllib.parse.urlsplit(url).query # 获取需要的参数
q_dict = urllib.parse.parse_qs(q) # 将参数转换为字典类型的数据
print('数据类型为:',type(q_dict))
print('转换后的数据:',q_dict)
运行:
数据类型为: <class 'dict'>
转换后的数据: {'name': ['Jack'], 'country': ['中国'], 'age': ['30']}