本笔记仅个人认知和见解,水平有限,还请见谅。
内容大多来自Python文档和学习材料,作相应的扩充或压缩后的笔记。
没有很多实例,大多是理论知识
urllib库是python内置库,利用它就可以实现HTTP请求发送,而不需要关心HTTP链接是如何实现的,我们只需要给定指定的URL、请求头和请求体等信息即可。
urllib库包含四个模块:
request:基本的HTTP请求模块,可以模拟浏览器发送请求。
error:异常处理模块,可以被我们用于捕获异常。
parse:工具模块,提供了众多URL处理方法。
robotparser:用于识别网站的robots.txt文件,并判断哪些网站可以爬,哪些网站不能爬。
request库不仅可以模拟浏览器的请求发起过程,同时还具有处理授权验证(Authentication)、重定向(Redirection)、浏览器Cookie等功能。
首先是urlopen方法的API:urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,capath=None,cadefault=False,context=None)
首先是基础用法:response = urllib.request.urlopen('https://www.python.org')
其中urlopen只由第一个参数URL,即网址,这是唯一必须指定的内容,是请求的目标站点。urlopen返回一个HTTPResponse类型的对象,储存在response里。这个用法完成了对Python官网的GET请求。
urlopen的其他参数:
data参数:传递该参数则表示进行POST请求,传递的内容是POST请求中的表单信息。在传递前要用bytes方法将参数转化为字节流编码格式的内容(bytes类型)。传递字典类型的数据时,应先用urllib.parse.urlencode方法进行编码。
timeout参数:用于设置超时时间,单位为秒。如果超过设置的时间,则发生异常。如果不指定则实用全局默认时间。超时的异常是urllib.error.URLError,其错误原因是超时,查看异常的属性reason,得到socket.timeout,意思是超时异常。
context参数:指定SSL的设置,参数必须是ssl.SSLContext类型。
cafile参数和capath参数:用于指定CA证书和其路径,用于HTTPS链接请求。
cadefault参数:已经弃用,可以不管,默认值为False。
Request类的构造方法:class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
参数说明:
url:唯一必须参数,指定请求URL。
data:同urlopen,传输bytes类型的数据。
headers:headers是个字典,即请求头。既可以直接在写Request类时写这个选项的内容,也可以通过调用实例的方法add_header方法添加。通常User-Agent来伪装浏览器(默认的User-Agent为Python-urllib)。
origin_req_host:请求方(己方)的host名或IP地址。
unverifiable:表示请求是否是无法验证的。True表示确实无法验证。
method:请求使用的方法,类型是一个字符串,比如GET、POST。
学习小笔记:
其实这里我是完全不理解unverifiable这个参数到底是什么意思,到底是无法验证是True还是可以验证是True。所以在网上搜的时候,我发现大家的说法和书上都是一样的,“指请求无法验证,默认为 False。用户并没有足够的权限来选择接收这个请求结果,例如请求一个 HTML 文档中的图片,但没有自动抓取图像的权限,这时 unverifiable 为 True。”我觉得很离谱,原来大家都喜欢抄书,后来去python的documentation看到了原文,原来书上的东西很多都是文档里翻译来的,这句话疑似是机翻的,有点奇奇怪怪的,但是话倒是说的比较明白,“所谓无法验证的请求,是指用户没有机会对请求的 URL 做验证。”结合图片那个例子,大概就是True无法验证。
urllib是urllib.request的一个方法,用于获得网站响应,响应是HTTPResponse类型的对象;Request是一个类,用于构建简单的Request对象,即请求内容。Request对象可以作为参数传入urlopen方法,如此可以使用比较完整的请求获得网站响应。
对于urlopen和Request的data参数,都需要byte类型数据(字节流编码格式),于是需要byte()方法将参数转为字节流编码格式,如果是字典类型,需要先用urllib.parse.urlencode方法将字典转为字符串。
Request类的headers参数,是字典类型的数据,既可以在构建Request类时候就指定,也可以使用add_header()方法添加。
Handler可以当作Request的升级版,可以管理网站验证,代理,Cookie等Request类难以管理的请求。Handler类有一个大爹:BaseHandler类,是所有Handler类的父类。其子类有如下几类比较常用:[(源自Python文档)](urllib.request — 用于打开 URL 的可扩展库 — Python 3.9.14 文档)
HTTPDefaultErrorHandler:为 HTTP 错误响应定义的默认 handler,所有出错响应都会转为 HTTPError 异常。
HTTPRedirectHandler:一个用于处理重定向的类。
HTTPCookieProcessor(cookiejar=None):一个用于处理 HTTP Cookies 的类。
ProxyHandler(proxies=None):用于设置代理,默认代理为空。
HTTPPasswordMgr:维护 (realm, uri) -> (user, password) 映射数据库。即用于管理密码,维护用户名密码对照表。
HTTPPasswordMgrWithDefaultRealm:维护 (realm, uri) -> (user, password) 映射数据库。realm 为 None 视作全匹配,若没有其他合适的安全区域就会检索它。
HTTPPasswordMgrWithPriorAuth:HTTPPasswordMgrWithDefaultRealm 的一个变体,也带有 uri -> is_authenticated 映射数据库。可被 BasicAuth 处理函数用于确定立即发送身份认证凭据的时机,而不是先等待 401 响应。
HTTPBasicAuthHandler(password_mgr=None):处理远程主机的身份认证。 password_mgr 应与 HTTPPasswordMgr 兼容;如果给出错误的身份认证方式, HTTPBasicAuthHandler 将会触发 ValueError 。
OpenerDirector对象封装了请求方法,比如urlopen就是比较简单的请求方法。构建更高级的请求,需要用到更底层的实例完成,即OpenerDirector。OpenerDicrector有以下常用方法:[(源自Python文档)](urllib.request — 用于打开 URL 的可扩展库 — Python 3.9.14 文档)
OpenerDirector.add_handler(handler)
:handler 应为 BaseHandler 的实例。将检索以下类型的方法,并将其添加到对应的处理链中(注意 HTTP 错误是特殊情况)。
— 表明该 handler 知道如何打开 protocol 协议的URL
http_error_
— 表明该 handler 知道如何处理代码为 type 的 HTTP 错误。
— 表明该 handler 知道如何处理来自协议为 protocol (非http)的错误。
— 表明该 handler 知道如何预处理协议为 protocol 的请求。
— 表明该 handler 知道如何后处理协议为 protocol 的响应。
*OpenerDirector*.open(url, data=None[, timeout])
:用于打开URL,其返回值和错误类型与urlopen()方法相同。超时选项仅适用于HTTP,HTTPS和FTP。
还有构建OpenerDirector的方法:
urllib.request.build_opener([handler, ...])
:返回一个 OpenerDirector 实例,以给定顺序把处理函数串联起来。处理函数可以是 BaseHandler 的实例,也可以是 BaseHandler 的子类(这时构造函数必须允许不带任何参数的调用)。
使用build_opener方法构建OpenerDirector实例,然后利用OpenerDirector实例的方法open()打开URL,得到网站响应。
HTTPPasswordMgr 对象与HTTPPasswordMgrWithDefaultRealm 对象都有以下方法:
HTTPPasswordMgr.add_password(realm, uri, user, passwd)
uri 可以是单个 URI,也可以是 URI 列表。realm、user 和 passwd 必须是字符串。这使得在为 realm 和超级 URI 进行身份认证时,(user, passwd) 可用作认证令牌。
HTTPPasswordMgr.find_user_password(realm, authuri)
为给定 realm 和 URI 获取用户名和密码。如果没有匹配的用户名和密码,本方法将会返回 (None, None) 。对于 HTTPPasswordMgrWithDefaultRealm 对象,如果给定 realm 没有匹配的用户名和密码,将realm设为None。
这两个对象主要用于解决请求网站时,网站开启了基本身份认证(HTTP Basic Access Authentication)。可以在实例化对象时就给定(realm, uri, user, passwd)的内容。
ProxyHandler({'协议类型':'代理链接','协议类型':'代理链接',...})
即可构建一个ProxyHandler实例。然后用上图的方法可以发送请求。
CookieJar对象包含在http.cookiejar内,需要先import。创建实例的方法:cookie = http.cookiejar.CookirJar()
。这样创建出来的是一个空Cookie,可以用于Handler构建并用于请求网站,以此获得Cookie。
用于构建handler,用法:handler = urllib.request.HTTPCookieProcessor(CookieJar)
。
MozillaCookieJar对象是CookieJar的子类,用于处理与Cookie和文件相关的事件如读取和保存。对于的文件格式是Mozilla型浏览器Cookie格式(cookies.txt文件格式)。用法:
cookie = http.cookiejar.MozillaCookieJar(filename)
与MozillaCookieJar对象类似,LWPCookieJar对象用于处理LWP(libwww-perl)格式(Set-Cookie3文件格式)的Cookie的读取和保存。
cookie = http.cookiejar.LWPCookieJar(filename)
加载:cookie.load(filename=None, ignore_discard=False, ignore_expires=False)
ignore_discard: 若为True,即使设定了丢弃 cookie 仍然保存它们,意思是保存会话Cookie。 ignore_expires: 若为True,即使 cookie 已超期仍然保存它们。旧的 cookie 将被保留,除非是被新加载的 cookie 所覆盖。
保存:cookie.save(filename=None, ignore_discard=False, ignore_expires=False)
ignore_discard: 若为True,即使设定了丢弃 cookie 仍然保存它们,意思是保存会话Cookie。 ignore_expires: 若为True,即使 cookie 已超期仍然保存它们。文件如果已存在则会被覆盖,这将清除其所包含的全部 cookie。 已保存的 cookie 可以使用 load() 或 revert() 方法来恢复。
其他方法见Python文档
URLError类来自urllib库的error模块,其父类是OSError类,由request模块产生的异常都可以由通过捕获这个处理。
用法:exception urllib.error.URLError as objectname
其属性只有一个:reason,即返回错误的原因(如Not Found)。
HTTPError是URLError的子类之一,专门处理HTTP请求错误(如认证请求等特殊HTTP错误)。
用法:exception urllib.error.HTTPError as objectname
属性:
code:返回HTTP状态码,如404等,
reason:返回错误原因(如Not Found)。
headers:返回请求头
需要注意,reason的返回值有可能不是字符串类,有可能是一个对象,如超时错误的返回值
try:
response = urllib.request.urlopen('https://emorepitg.top', timeout=0.01)
except urllib.error.URLError as e:
print(type(e.reason))
if isinstance(e.reason,socket.timeout):
print('TIME OUT')
<class 'socket.timeout'>
TIME OUT
如上,超时错误的reason属性是一个socket.timeout类,可以用isinstance方法检查来判单是否是超时错误。也可以直接打印reason属性,因为这个类的附带值是一个字符串,其值总是 “timed out”。
urllib内的parse模块提供了若干解析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解析,然后输出解析结果的类型(urllib.parse.ParseResult)以及结果本身(字符串)。
用法:urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)
传入字符型URL,在URL没有指定协议的时候用scheme指定协议(如果URL没有协议的时候用scheme指定协议,则解析后的netloc会被放在path内,是个很奇怪的问题,由于没有实战,并不知道其作用,留疑)。
print(urllib.parse.urlparse('https://docs.python.org/zh-cn/3.9/library/urllib.parse.html?highlight=urllib%20parse#module-urllib.parse'))
ParseResult(scheme='https', netloc='docs.python.org', path='/zh-cn/3.9/library/urllib.parse.html', params='', query='highlight=urllib%20parse', fragment='module-urllib.parse')
print(urllib.parse.urlparse('docs.python.org/zh-cn/3.9/library/urllib.parse.html?highlight=urllib%20parse#module-urllib.parse',scheme='https'))
ParseResult(scheme='https', netloc='', path='docs.python.org/zh-cn/3.9/library/urllib.parse.html', params='', query='highlight=urllib%20parse', fragment='module-urllib.parse')
解析出的结果是ParseResult类型对象,包含六个部分:
scheme |
0 | URL协议 | scheme 参数 |
---|---|---|---|
netloc |
1 | 网络位置部分 | 空字符串 |
path |
2 | 分层路径 | 空字符串 |
params |
3 | No longer used | always an empty string |
query |
4 | 查询组件 | 空字符串 |
fragment |
5 | 片段识别 | 空字符串 |
urlparse的对立方法,将上述六个参数构造成URL。参数可以是列表类型,也可以是元组等,但必须是长度为6的可迭代对象,其返回值是一个字符串。
用法:urllib.parse.urlunparse(parts)
与urlparse方法和urlunparse方法类似,urlsplit方法可以解析URL,但是这一方法不再单独解析params这一个部分,而是合并到path中去。其返回值是一个urllib.parse.SplitResult类。同样地,urlunsplit只需要五个参数即可。
通过合并一个 “基准 URL” (base) 和另一个 URL (url) 来构造一个完整 (“absolute”) URL。
用法:urllib.parse.urljoin(base, url, allow_fragments=True)
>>> urllib.parse.urljoin('https://www.emorepitg.top/index.html','?category=2')
'https://www.emorepitg.top/index.html?category=2'
>>> urllib.parse.urljoin('https://www.emorepitg.top/index.html','https://www.csdn.net/index.php?category=2')
'https://www.csdn.net/index.php?category=2'
“基准URL”提供scheme、netloc和path三个参数(即https://emorepitg.top/index.html),如果“另一个URL“不存在这三项,就用”基准URL“去补,如果”另一个URL“存在这三个参数,”基准URL”的内容就不起作用
将字典类型转化为URL适用的字符串类型,即将字典转为key=value
对,并用是&
连接的字符串。
用法:urllib.parse.urlencode(query, doseq=False, safe='', encoding=None, errors=None, quote_via=quote_plus)
当使用二元组序列作为 query 参数时,每个元组的第一个元素为键而第二个元素为值。 值元素本身也可以为一个序列,在那种情况下,如果可选的形参 doseq 的值为 True,则每个键的值序列元素生成单个 key=value 对(以 ‘&’ 分隔)。 被编码的字符串中的参数顺序将与序列中的形参元素顺序相匹配。
safe, encoding 和 errors 形参会被传递给 quote_via (encoding 和 errors 形参仅在查询元素为 str 时会被传递)。
将链接中用&
连接的key=value
对转化为字典类型,但是需要注意的是,字典的值是一个列表。
>>> urllib.parse.parse_qs('name=Emore&age=20')
{'name': ['Emore'], 'age': ['20']}
>>> type(urllib.parse.parse_qs('name=Emore&age=20')['name'])
list
同上parse_qs方法。将链接中用&
连接的key=value
对转化为元组类型的列表。
>>> urllib.parse.parse_qsl('name=Emore&age=20')
[('name', 'Emore'), ('age', '20')]
quote方法将URL中的中文转化为URL编码的格式。
>>> urllib.parse.quote('甲寅')
'%E7%94%B2%E5%AF%85'
quote方法的对立方法,将URL编码格式转化为中文(解码)
urllib.robotparser是robots.txt语法分析模块。Robots协议(也称为爬虫协议、机器人协议等)的全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。
urllib.robotparser.RobotFileParser(url='')
这个类提供了一些可以读取、解析和回答关于 url 上的 robots.txt 文件的问题的方法。
set_url(url)
设置指向 robots.txt 文件的 URL。RobotFileParser对象创建时没有给url时调用来设置url。
read()
读取 robots.txt URL 并将其输入解析器。需要注意,如果获得了RobotFileParser对象但是没有调用过read方法,则别的所有解析方法返回值都是False,所以一定要记得调用。
parse(lines)
解析行参数。
can_fetch(useragent, url)
判断useragent是否可以抓取url,返回True或False。
mtime()
返回最近一次获取 robots.txt 文件的时间。 这适用于需要定期检查 robots.txt 文件更新情况的长时间运行的网页爬虫。
modified()
将最近一次获取 robots.txt 文件的时间设置为当前时间。
至此,urllib库的四个模块大致学习完毕,其中有一些小地方需要注意的。
urllib.request模块中,data参数需要用byte类型
urllib.request.Request模块中,headers参数是字典类型
搞清楚request和urlopen的关系,Handler与OpenerDirector的关系
urlparse方法在URL没有带scheme时,将scheme以参数导入,netloc会跑到path内
urlunparse方法必须是六个参数的可迭代对象
urlunsplit方法必须是五个参数的可迭代对象
parse_qs解析出的字典的值是一个列表
quote方法和unquote方法对字符串和URL编码处理
robotparser对象处理前需要用read方法
如果URL没有协议的时候用scheme指定协议,则解析后的netloc会被放在path内,是个很奇怪的问题,由于没有实战,并不知道其作用。
print(urllib.parse.urlparse('https://docs.python.org/zh-cn/3.9/library/urllib.parse.html?highlight=urllib%20parse#module-urllib.parse'))
ParseResult(scheme='https', netloc='docs.python.org', path='/zh-cn/3.9/library/urllib.parse.html', params='', query='highlight=urllib%20parse', fragment='module-urllib.parse')
print(urllib.parse.urlparse('docs.python.org/zh-cn/3.9/library/urllib.parse.html?highlight=urllib%20parse#module-urllib.parse',scheme='https'))
ParseResult(scheme='https', netloc='', path='docs.python.org/zh-cn/3.9/library/urllib.parse.html', params='', query='highlight=urllib%20parse', fragment='module-urllib.parse')