目录
一、urllib库介绍
二、request模块使用
(1)urlopen
①data参数
②timeout参数
(2)request
(3)高级用法
①验证
②代理
③Cookie
三、处理异常
①URLError
②HTTPError
四、解析链接
①urlparse
②urlunparse
③urlsplit
④urlunsplit
⑤urljoin
⑥urlencode
⑦parse_qs
⑧parse_qsl
⑨quote
⑩unquote
五、分析 Robots协议
①Robots 协议
一、urllib库介绍
urllib是Python的内置的HTTP请求库,不需要另外下载,可直接使用。urllib库包含以下四个模块:
①request:最基本的HTTP请求模块。可以模拟请求的发送。就像在浏览器李输入网址然后按下回车一样,只需要给库方法传入URL以及额外的参数,就可以发送请求的过程了。
②error:异常处理模块。如果出先请求异常,那么我们可以捕获这些异常,然后进行重试或其他操作以保证程序运行不会意外停止。
③parse:工具模块。提供了许多URL的处理方法,例如拆分、解析、合并等。
④robotparser:主要用于识别网站的robots.txt文件,然后判断哪些网站可以爬取,哪些网站不可以,但是实际使用很少。
二、request模块使用
(1)urlopen
urllib.request模块提供了最基本的构造HTTP请求的方法,利用这个模块可以模拟浏览器的请求发起过程,同时它还具有处理授权验证(Authentication)、重定向(Redirection)、浏览器Cookie以及其他一些功能。下面以Python官网为例,把这个网页抓取下来。
import urllib.requestif __name__ == '__main__':response = urllib.request.urlopen('https://www.python.org')print(type(response)) # 查看response类型print(response.read().decode('utf-8')) # 输入response内容,设置编码格式为utf-8
可以看出,响应是一个HTTPResponse类型的对象,主要包含read、redinto、getheader、getheaders、fileno等方法,以及msg、version、status、reason、debuglevel、closed等属性。
得到响应后,我们把它赋值给response变量,然后就可以调用上述方法和属性,得到返回的一系列信息了。
例如调用read方法可以得到响应的网页内容、调用status可以得到响应结果的状态码(200表示请求成功,404表示网页未找到等)
例如以下代码:
import urllib.request
if __name__ == '__main__':response = urllib.request.urlopen('https://www.python.org')print(response.status) # 返回状态码print(response.getheaders()) # 响应头信息print(response.getheader('Server')) # 获取只当参数的值
其中前两个输出分别是响应的状态码和响应的头信息;最后一个输出是调用getheader方法,并传入参数Server,获取响应头中Server的值,结果是nginx,意思是服务器是Nginx搭建的。
利用最基本的urlopen方法,可以完成对简单网页的GET请求抓取,但是如果想给链接传递一些参数,又该如何实现呢?首先看一下urlopen方法的API:
urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,capath=None,cadefault=False,context=None)
可以发现,除了第一个参数用于传递URL之外,我们还可以传递其他内容,例如Data(附加数据)、timeout(超时时间)等。
接下来对urlopen的参数进行详细说明:
①data参数
data参数是可选的。在添加该参数时,需要使用bytes方法将参数转化为字节流编码格式的内容,即bytes类型。另外,如果传递了这个参数,那么它的请求就不再是GET请求,而是POST请求。下面用示例来看一下。
import urllib.request
import urllib.parse
if __name__ == '__main__':data = bytes(urllib.parse.urlencode({'name': 'germey'}),encoding='utf-8')response = urllib.request.urlopen('https://www.httpbin.org/post', data=data)print(response.read().decode('utf-8'))
这里我们传递了一个参数name,值是germey,需要将它转码为bytes类型。转码时采用了bytes方法,该方法的第一个参数得是str(字符串)类型因此用urllib.parse模块里的urlencode方法将字典参数转化为字符串;第二个参数用于指定编码格式,这里指定为utf-8。
此处我们请求的站点www.httpbin.org,它可以提供HTTP请求测试。本次我们请求的URL为https://www.httpbin.org/post,这个链接可以用来测试POST请求,能够输出请求的一些信息,其中就包含我们传递的data参数。
上面示例运行结果如下:
②timeout参数
timeout参数用一个设置超时时间,单位为秒,意思是如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常。如果不指定参数,则会使用全局默认时间。这个参数支持HTTP、HTTPS、FTP请求。
下面通过示例来看一下:
import urllib.request
if __name__ == '__main__':response = urllib.request.urlopen('https://www.httpbin.org/get', timeout=0.1)print(response.read())
这里我们设置超时时间为0.1秒,程序在运行了0.1秒后,服务器依然没有响应,于是抛出了URLError异常。该异常属于urllib.error模块,错误原因是超时。
因此可以通过设置一个超时时间,实现当一个网页长时间未响应时,就跳过对它的抓取。此外利用try except 语句也可以实现,相关代码如下:
import socket
import urllib.request
import urllib.error
if __name__ == '__main__':try:response = urllib.request.urlopen('https://www.httpbin.org/get', timeout=0.1)except urllib.error.URLError as e:if isinstance(e.reason, socket.timeout):print('TIME OUT')
③其他参数
除了data参数和timeout参数,urlopen还要context参数,该参数必须是ssl.SSLContext类型,用来指定SSL的设置。
此外,cafile和capath这两个参数分别用来指定CA证书和其路径,这两个在请求HTTPS链接时会有用,cadefault参数现在已经弃用,其默认值为False。
(2)request
利用urlopen方法可以发起最基本的请求,但它那几个简单的参数并不足以构建一个完整的请求。如果需要往请求中加入Headers等信息,就得利用更强大的Request类来构建请求了。
首先,先看一下Request类的实际用法:
import urllib.request
if __name__ == '__main__':request = urllib.request.Request('https://python.org')response = urllib.request.urlopen(request)print(response.read().decode('utf-8'))
可以发现,我们一九时用urlopen方法发送请求,只不过这次该方法的参数不再是URL,而是一个Request类型的对象。通过构造这个数据结构,一方面可以将请求独立成一个对象,另一方面可以更加丰富和灵活的配置参数。
下面我们看一下可以通过怎样的参数来构造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,我们可以通过修改这个值来伪装浏览器。例如要伪装火狐浏览器,就可以把User-Agent设置为: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等。
下面我们传入多个参数尝试构建Request类:
from urllib import request,parse
if __name__ == '__main__':url = 'https://www.httpbin.org/post'headers = {'User-Agent':'Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 119.0.0.0Safari / 537.36','Host':'www.httpbin.org'}dict = {'name':'germey'}data = bytes(parse.urlencode(dict), encoding='utf-8')req = request.Request(url=url, data=data, headers=headers, method='POST')response = request.urlopen(req)print(response.read().decode('utf-8'))
这里我们通过4个参数构建了一个Request类,其中的url即请求URL,headers中指定了User-Agent和Host,data用urlencode方法和bytes方法把字典数据转成字节流格式。另外,指定了请求方式为POST。
运行结果如下:
(3)高级用法
我们已经可以构建请求了,那么对于一些高级的操作(例如cookie的处理、代理设置等),又该如何实现呢?此时就需要更强大的高级,Handler登场了。简而言之,Handler可以理解为各种处理器,由专门处理登录验证的,处理Cookie的、处理代理设置的。利用这些Handler,我们几乎可以实现HTTP请求中所有的功能。
首先介绍一下urllib.request模块里的BaseHandler类,这是其他所有Handler类的父类。它提供了最基本的方法,例如default_open、protocol_request等。
会由各种Handler子类继承BaseHandler类,接下来举几个子类例子如下:
①HTTPDefaultErrorHandler用于处理HTTP响应错误,所有错误都会抛出HTTPError类型的异常。
②HTTPRedirectHandler用于处理重定向。
③HTTPCookieProcessor用于处理Cookie。
④ProxyHandler用于设置代理,代理默认为空。
⑤HTTPPasswordMgr用于管理密码,它维护着用户名和密码的对照表。
⑥HTTPBasicAuthHandler用于管理认证,如果一个链接在打开时需要验证,那么可以使用这个类来解决认证问题。
另外一个比较重要的类是OpenerDirector,我们可以称为Opener。我们用过的urlopen方法,实际上就是urllib库为我们提供的一个opener。
那么,为什么要引入Opener呢?因为需要实现更高级的功能。之前使用的 Request类和 urlopen类相当于类库已经封装好的极其常用的请求方法,利用这两个类可以完成基本的请求, 但是现在我们需要实现更高级的功能,就需要深入一层进行配置,使用更底层的实例来完成操作,所以这里就用到了Opener。
Opener类可以提供open方法,该方法返回的响应类型和 urlopen方法如出一辙。那么, Opener类和Handler类有什么关系呢?简而言之就是, 利用Handler类来构建Opener类。
下面用几个实例来看看 Handler 类和Opener类的用法。
①验证
在访问某些网站是,例如https://ssr3.scrape.center,可能会弹出这样的认证窗口,如图所示。
遇到这种情况,就表示这个网站启用了基本身份验证,英文名叫做HTTPbasic Access Authentication,这是一种登录验证方式,允许网页浏览器或其他客户端程序在请求网站是提供用户名和口令形式的身份凭证。
那么爬虫如何请求这样的页面呢?借助HTTPBasicAuthHandler模块就可以完成,相关代码如下:
from urllib.request import HTTPBasicAuthHandler, HTTPPasswordMgrWithDefaultRealm, build_opener
from urllib.error import URLError
if __name__ == '__main__':username = 'admin'password = 'admin'url = 'https://ssr3.scrape.center/'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对象auth_handler,其参数是HTTPPasswordMgr-WithDefaultRealm对象,它利用add_password方法添加用户名和密码,这样就建立了一个用来处理验证的Handler类。 然后将刚建立的auth_handler类当作参数传入build——opener方法,构建一个Opener,这个Opener在发送请求时就相当于已经验证成功了。最后利用Opener类中的open方法打开链接,即可完成验证。这里获取的结果就是验证成功后的页面源码内容。
②代理
做爬虫的时候,免不了要使用代理,如果要添加代理,可以这样做。
from urllib.error import URLError
from urllib.request import ProxyHandler, build_opener
if __name__ == '__main__':proxy_handler = ProxyHandler({'http':'http://127.0.0.1:8080','https':'https://127.0.0.1:8080'})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)
这里我们需要事先在本地搭建一个HTTP代理,并让其允许在8080端口上。上面使用了ProxyHandler,其参数是一个字典,键名是协议类型(例如HTTP或者HTTPS等),键值是代理链接,可以添加多个代理。
③Cookie
处理Cookie需要用到相应的handler。
先看实例如何获取Cookie,相关代码如下:
import http.cookiejar, urllib.request
if __name__ == '__main__': cookie = http.cookiejar.CookieJar()handler = urllib.request.HTTPCookieProcessor(cookie)opener = urllib.request.build_opener(handler)response = opener.open('https://www.baidu.com')for item in cookie:print(item.name + "=" + item.value)
首先,必须声明一个CookieJar对象。然后需要利用HTTPCookieProcessor构建一个Handler,最后利用build_opener方法构建Opener,执行Open函数即可。
运行结果如下:
这里可以看到,分别输出了每个Cookie条目的名称和值。既然能输出,那么可不可以输出文件格式的内容呢?我们指定Cookie实际也是以文本形式保存到。因此答案是肯定的,这里通过下面的实例来看看。
import urllib.request, http.cookiejar
if __name__ == '__main__':filename = 'cookie.txt'cookie = http.cookiejar.MozillaCookieJar(filename)handler = urllib.request.HTTPCookieProcessor(cookie)opener = urllib.request.build_opener(handler)response = opener.open('https://www.baidu.com')cookie.save(ignore_discard=True, ignore_expires=True)
这时需要将CookieJar缓存MozillaCookieJar,他会在生成文件时用到,是CookieJar的子类,可以用来处理跟Cookie和文件有关的事件,例如读取和保存Cookie,可以将Cookie保存为Mozilla型浏览器的Cookie格式。
上面代码运行结果如下:
另外,LWPCookieJar统一可以读取和保存COokie,知识Cookie文件的保存格式和MozillaCookieJar不一样,他会保存LWP(libwww-perl)格式
import urllib.request, http.cookiejar
if __name__ == '__main__':filename = 'cookie.txt'# cookie = http.cookiejar.MozillaCookieJar(filename)cookie = http.cookiejar.LWPCookieJar(filename)handler = urllib.request.HTTPCookieProcessor(cookie)opener = urllib.request.build_opener(handler)response = opener.open('https://www.baidu.com')cookie.save(ignore_discard=True, ignore_expires=True)
运行结果如下:
由此看来,不同格式cookie文件差异还是比较大的。那么生成Cookie文件后,怎样从其中读取呢?以下面代码为例,对LWPCookieJar格式Cookie文件读取。
import urllib.request, http.cookiejar
if __name__ == '__main__':cookie = http.cookiejar.LWPCookieJar()cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)handler = urllib.request.HTTPCookieProcessor(cookie)opener = urllib.request.build_opener(handler)response = opener.open('https://www.baidu.com')print(response.read().decode('utf-8'))
三、处理异常
urllib库中的error模块定义了有request模块产生的异常。当出现问题时,request模块就会抛出error模块定义的异常。
①URLError
URLError类来之urllib库的error模块,继承自OSError类,时error异常模块的基类,由request模块产生的异常都可以通过捕获这个类来处理。
它有一个属性reason,即返回错误的原因。
下面用一个实例来看一下:
from urllib import request, error
if __name__ == '__main__':try:response = request.urlopen('https://cuiqingcai.com/404')except error.HTTPError as e:print(e.reason, e.code, e.headers, sep='\n')# reason:返回错误的原因# code:返回HTTP状态码# headers:返回请求头
我们打开了一个不存在的页面,按理说应该会报错,但是我们捕获了URLError这个异常,运行结果如下:
程序没有报错,而是输出了错误原因,这样就可以避免程序异常终止,同时异常得到了有效的处理。
②HTTPError
HTTPError是URLError类的子类,专门用来处理HTTP请求错误,例如认证请求失败等。它有如下3个属性。
code:返回HTTP状态码,例如404表示网页不存在,500表示服务器内部错误等。
reason:同父类一样,用于返回错误的原因。
headers:返回请求头。
下面通过实例看看:
from urllib import request, error
if __name__ == '__main__':try:response = request.urlopen('https://cuiqingcai.com/404')except error.HTTPError as e:print(e.reason, e.code, e.headers, sep='\n')# reason:返回错误的原因# code:返回HTTP状态码# headers:返回请求头
运行结果入下:
依旧是打开相同的网址,这里捕获了HTTPError异常,输出了reason、code和headers属性。因为URLError是HTTPError弗雷,所以可以先选择捕获子类的错误,再捕获父类的错误,对上述代码可以做以下修改:
from urllib import request, error
try:response = request.urlopen('https://cuiqingcai.com/404')
except error.HTTPError as e:print(e.reason, e.code, sep= '\n')
except error.URLError as e:print(e.reason)
else:print('request successful')
这样就可以做到先捕获HTTPError,获取它的错误原因、状态码、请求头等信息。如果不是HTTPError异常,就会捕获URLError异常,输出错误原因。最后else语句来处理正常的逻辑。这是一个较好的异常处理写法。
有时候,reason属性返回的不一定是字符串,也可能是一个对象。再看下面的实例:
import socket
import urllib.request
import urllib.error
if __name__ == '__main__':try: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')
这里我们直接设置超时时间来强制抛出timeout异常。
运行结果如下:
可以发现,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。
下面介绍parse模块的常用方法,看一下它的特别之处。
①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:
https://www.baidu.com/index.html;user?id=5 #comment
可以发现, urlparse方法在解析URL时有特定的分隔符。例如:// 前面的内容就是 scheme,代表协议。第一个/符号前面便是 netloc,即域名;后面是 path,即访问路径。分号;后面是 parans,代表参数。问号?后面是查询条件 query,一般用作 GET类型的 URL。井号#后面是锚点fragment,用于直接定位页面内部的下拉位置。
于是可以得出一个标准的链接格式, 具体如下:
scheme://netloc/path;params? query#fragment
一个标准的URL都会符合这个规则,利用urlparse 方法就可以将它拆分开来。除了这种最基本的解析方式外, urlparse 方法还有其他配置吗? 接下来, 看一下它的 API用法:
urllib. parse. urlparse(urlstring, scheme='', allow_fragments=True)
可以看到, urlparse方法有3个参数。
□urlstring: 这是必填项, 即待解析的 URL。
□scheme:这是默认的协议(例如 http 或https等)。如果待解析的 URL 没有带协议信息,就会将这个作为默认协议。我们用实例来看一下:
from urllib. parse import urlparseresult = urlparse(' www.baidu.com/index.html;user?id=5 #comment',scheme='https')print(result)
运行结果如下:
可以发现,这里提供的URL不包含最前面的协议信息,但是通过默认的scheme 参数,返回了结果 https。
假设带上协议信息:
from urllib. parse import urlparseresult = urlparse(' http://www.baidu.com/index.html;user?id=5 #comment',scheme='https')
print(result)
可见,scheme参数只有在URL中不包含协议信息的时候才生效。如果URL中有,就会返回解析出的 scheme。
□allow_fragments:是否忽略 fragment。如果此项被设置为 False,那么 fragment 部分就会被忽略,它会被解析为 path、params 或者 query的一部分,而 fragment 部分为空。
下面我们用实例来看一下:
from urllib. parse import urlparseresult= urlparse(' https://www.baidu.com/index.html;user?id=5 #comment',allow_fragments=False)print(result)
运行结果如下:
假设URL中不包含 params 和query,我们再通过实例看一下:
from urllib. parse import urlparseresult = urlparse(' https://www.baidu.com/index.html #comment',allow_fragments=False)print(result)
运行结果如下:
可以发现,此时 fragment 会被解析为 path的一部分。
返回结果 ParseResult实际上是一个元组,既可以用属性名获取其内容,也可以用索引来顺序获取。实例如下:
from urllib. parse import urlparseresult = urlparse(' https://www.baidu.com/index.html #comment',allow_fragments=False)print(result. scheme, result[0], result. netloc, result[1], sep='\n')
这里我们分别用属性名和索引获取了 scheme 和 netloc,运行结果如下:
可以发现,两种获取方式都可以成功获取,且结果是一致的。
②urlunparse
有了 urlparse 方法,相应就会有它的对立方法 urlunparse, 用于构造 URL。这个方法接收的参数是一个可迭代对象,其长度必须是 6,否则会抛出参数数量不足或者过多的问题。先用一个实例看一下:
from urllib. parse import urlunparsedata = ['https',' www.baidu.com','index.html','user','a=6','comment']
print(urlunparse(data))
这里参数 data用了列表类型。当然,也可以用其他类型,例如元组或者特定的数据结构。
运行结果如下:
这样我们就成功实现了 URL的构造。
③urlsplit
这个方法和 urlparse 方法非常相似,只不过它不再单独解析 params 这一部分(params会合并到path中), 只返回5个结果。实例如下:
from urllib. parse import urlsplitresult = urlsplit(' https://www.baidu.com/index.html;user?id=5 #comment')print(result)
运行结果如下:
可以发现,返回结果是 SplitResult,这其实也是一个元组,既可以用属性名获取其值,也可以用索引获取。实例如下:
from urllib. parse import urlsplitresult = urlsplit(' https://www.baidu.com/index.html;user?id=5 #comment')print(result. scheme, result[0])
运行结果如下:
④urlunsplit
与urlunparse方法类似,这也是将链接各个部分组合成完整链接的方法,传入的参数也是一个可迭代对象,例如列表、元组等, 唯一区别是这里参数的长度必须为5。实例如下:
from urllib. parse import urlunsplitdata = ['https',' www.baidu.com','index.html','a=6','comment']print(urlunsplit(data))
运行结果如下:
⑤urljoin
urlunparse 和urlunsplit 方法都可以完成链接的合并,不过前提都是必须有特定长度的对象,链接的每一部分都要清晰分开。除了这两种方法,还有一种生成链接的方法,是 urljoin。我们可以提供一个 base_url(基础链接)作为该方法的第一个参数,将新的链接作为第二个参数。urljoin方法会分析 base_url的scheme.netloc 和path这3个内容,并对新链接缺失的部分进行补充,最后返回结果。
下面通过几个实例看一下:
from urllib. parse import urljoinprint(urljoin(' https://www.baidu.com','FAQ.html'))print(urljoin(' https://www.baidu.com','https://cuiqingcai.com/FAQ.html'))print(urljoin(' https://www.baidu.com/about.html','https://cuiqingcai.com/FAQ.html'))print(urljoin(' https://www.baidu.com/about.html','https://cuiqingcai.com/FAQ.html?question=2'))print(urljoin(' https://www.baidu.com?wd=abc','https://cuiqingcai.com/index.php'))print(urljoin(' https://www.baidu.com','?category=2 #comment'))print(urljoin(' www.baidu.com','?category=2 #comment'))print(urljoin(' www.baidu.com #comment','?category=2'))
运行结果如下:
可以发现,base_url提供了三项内容:scheme、netloc和path。如果新的链接里不存在这三项,就予以补充;如果存在,就使用新的链接里面的, base_url中的是不起作用的。
通过urljoin方法,我们可以轻松实现链接的解析、拼合与生成。
⑥urlencode
这里我们再介绍一个常用的方法——urlencode,它在构造 GET 请求参数的时候非常有用,实例如下:
from urllib. parse import urlencodeparams ={'name': 'germey','age': 25
}base_url=' https://www.baidu.com?'url= base_url+ urlencode(params)print(url)
这里首先声明了一个字典 params,用于将参数表示出来,然后调用 urlencode方法将 params 序列化为 GET 请求的参数。
运行结果如下:
可以看到,参数已经成功地由字典类型转化为GET请求参数。
urlencode方法非常常用。有时为了更加方便地构造参数,我们会事先用字典将参数表示出来,然后将字典转化为URL的参数时,只需要调用该方法即可。
⑦parse_qs
有了序列化,必然会有反序列化。利用 parse_qs 方法,可以将一串 GET请求参数转回字典,实例如下:
from urllib. parse import parse_qsquery = 'name=germey&age=25'print(parse_qs(query))
运行结果如下:
可以看到, URL的参数成功转回为字典类型。
⑧parse_qsl
parse_qsl方法用于将参数转化为由元组组成的列表,实例如下:
from urllib. parse import parse_qslquery = 'name=germey&age=25'print(parse_qsl(query))
运行结果如下:
可以看到,运行结果是一个列表,该列表中的每一个元素都是一个元组,元组的第一个内容是参数名,第二个内容是参数值。
⑨quote
该方法可以将内容转化为URL 编码的格式。当URL 中带有中文参数时,有可能导致乱码问题,此时用quote 方法可以将中文字符转化为 URL 编码,实例如下:
from urllib. parse import quotekeyword = '壁纸'url= ' https://www.baidu.com/s?wd='+quote(keyword)print(url)
这里我们声明了一个中文的搜索文字,然后用quote 方法对其进行 URL 编码,最后得到的结果如下:
⑩unquote
有了 quote方法,当然就有 unquote方法,它可以进行URL 解码,实例如下:
from urllib. parse import unquoteurl= ' https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%88'print(unquote(url))
这里的url是上面得到的 URL 编码结果,利用 unquote方法将其还原,结果如下:
可以看到, 利用 unquote 方法可以方便地实现解码。
五、分析 Robots协议
利用 urllib库的 robotparser模块,可以分析网站的Robots协议。我们再来简单了解一下这个模块的用法。
①Robots 协议
Robots协议也称作爬虫协议、机器人协议,全名为网络爬虫排除标准(Robots Exclusion Protocol),用来告诉爬虫和搜索引擎哪些页面可以抓取、哪些不可以。它通常是一个叫作 robots. txt的文本文件,一般放在网站的根目录下。
搜索爬虫在访问一个站点时,首先会检查这个站点根目录下是否存在robots. txt文件,如果存在,就会根据其中定义的爬取范围来爬取。如果没有找到这个文件,搜索爬虫便会访问所有可直接访问的页面。
下面我们看一个 robots. txt的样例:
User-agent: *
Disallow:/
Allow:/public/
这限定了所有搜索爬虫只能爬取 public 目录。将上述内容保存成robots. txt文件,放在网站的根目录下,和网站的入口文件(例如 index. php、index. html和 index. jsp等)放在一起。
上面样例中的User-agent描述了搜索爬虫的名称,这里将其设置为*,代表 Robots协议对所有爬取爬虫都有效。例如, 我们可以这样设置:
User-agent: Baiduspider
这代表设置的规则对百度爬虫是有效的。如果有多条User-agent记录,则意味着有多个爬虫会受到爬取限制,但至少需要指定一条。
Disallow指定了不允许爬虫爬取的目录,上例设置为/,代表不允许爬取所有页面。
Allow一般不会单独使用,会和Disallow一起用,用来排除某些限制。上例中我们设置为/public/,结合Disallow的设置,表示所有页面都不允许爬取,但可以爬取 public 目录。
下面再来看几个例子。禁止所有爬虫访问所有目录的代码如下:
User-agent: *
Disallow:/
允许所有爬虫访问所有目录的代码如下:
User-agent: *
Disallow:
另外,直接把robots. txt文件留空也是可以的。禁止所有爬虫访问网站某些目录的代码如下:
User-agent: *
Disallow: /private/
Disallow: /tmp/
只允许某一个爬虫访问所有目录的代码如下:
User-agent: WebCrawler
Disallow:
User-agent: *
Disallow:/
以上是 robots. txt的一些常见写法。