当前位置:网站首页>urllib库的使用
urllib库的使用
2022-07-17 05:22:00 【W_chuanqi】
个人简介
作者简介:大家好,我是W_chuanqi,一个编程爱好者
个人主页:W_chaunqi
支持我:点赞+收藏️+留言
愿你我共勉:“若身在泥潭,心也在泥潭,则满眼望去均是泥潭;若身在泥潭,而心系鲲鹏,则能见九万里天地。”

文章目录
urllib的使用
首先介绍一个 Python 库,叫作 urllib,利用它就可以实现 HTTP请求的发送,而且不需要关心 HTTP协议本身甚至更底层的实现,我们要做的是指定请求的 URL、请求头、请求体等信息。此外 urllib 还可以把服务器返回的响应转化为 Python 对象,我们通过该对象便可以方便地获取响应的相关信息,如响应状态码、响应头、响应体等。
首先,我们了解一下 urllib 库的使用方法,它是 Python 内置的 HTTP 请求库,也就是说不需要额外安装,可直接使用。urllib 库包含如下 4 个模块。
- request:这是最基本的 HTTP 请求模块,可以模拟请求的发送。就像在浏览器里输入网址然后按下回车一样,只需要给库方法传人 URL 以及额外的参数,就可以模拟实现发送请求的过程了。
- error:异常处理模块。如果出现请求异常,那么我们可以捕获这些异常,然后进行重试或其他操作以保证程序运行不会意外终止。
- parse:一个工具模块。提供了许多 URL 的处理方法,例如拆分、解析、合并等。
- robotparser:主要用来识别网站的 robots.txt文件,然后判断哪些网站可以爬,哪些网站不可以,它其实用得比较少。
1.发送请求
使用 urllib 库的 request 模块,可以方便地发送请求并得到响应。我们先来看下它的具体用法。
urlopen
urllib.request 模块提供了最基本的构造 HTTP 请求的方法,利用这个模块可以模拟浏览器的请求发起过程,同时它还具有处理授权验证(Authentication )、重定向(Redirection)、浏览器 Cookie以及其他一些功能。
下面我们体会一下 request模块的强大之处。这里以 Python官网为例,我们把这个网页抓取下来;
import urllib.request
response = urllib.request.urlopen('https://www.python.org')
print(response.read().decode('utf-8'))
运行结果如下图所示:

这里我们只用了两行代码,便完成了 Python 官网的抓取,输出了其网页的源代码。得到源代码之后,我们想要的链接、图片地址、文本信息不就都可以提取出来了吗?
接下来,看看返回的响应到底是什么。利用 type 方法输出响应的类型:
import urllib.request
response = urllib.request.urlopen('https://www.python.org')
print(type(response))
输出结果如下:

可以看出,响应是一个 HTTPResposne 类型的对象,主要包含 read、readinto、getheader、getheaders、fileno 等方法,以及 msg、version、status、reason、debuglevel、closed 等属性。
得到响应之后,我们把它赋值给 response 变量,然后就可以调用上述那些方法和属性,得到返回结果的一系列信息了。
例如,调用 read 方法可以得到响应的网页内容、调用 status 属性可以得到响应结果的状态码( 200 代表请求成功,404 代表网页未找到等)。
下面再通过一个实例来看看:
import urllib.request
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.parse
import urllib.request
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 参数。
上面实例的运行结果如下:

可以发现我们传递的参数出现在了form字段中,这表明表明表单模拟提交,以POST方式传递数据。
timeout 参数
timeout 参数用于设置超时时间,单位为秒,意思是如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常。如果不指定该参数,则会使用全局默认时间。这个参数支持 HTTP、HTTPS,FTP 请求。
下面用实例来看一下:
import urllib.request
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
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')
这里我们请求了 https://www.httpbin.org/get 这个测试链接,设置超时时间为 0.1 秒,然后捕获到URLError 这个异常,并判断异常类型是 socket.timeout,意思是超时异常,因此得出确实是因为超时而报错的结论,最后打印输出了 TIME OUT。
运行结果如下:

按照常理来说,0.1 秒几乎不可能得到服务器响应,因此输出了 TIME OUT 的提示。
通过设置 timeout 参数实现超时处理,有时还是很有用的。
其他参数
除了data参数和 timeout 参数,urlopen方法还有 context参数,该参数必须是ssl.SSLContext类型,用来指定SSL的设置。
此外,cafile和capath这两个参数分别用来指定CA证书和其路径,这两个在请求HTTPS链接时会有用。
cadefault 参数现在已经弃用了,其默认值为 False。
Request
利用urlopen方法可以发起最基本的请求,但它那几个简单的参数并不足以构建一个完整的请求。如果需要往请求中加入 Headers 等信息,就得利用更强大的 Request 类来构建请求了。
首先,我们用实例感受一下 Request 类的用法:
import urllib.request
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
url = 'https://www.httpbin.org/post'
headers = {
'User-Agent': 'Mozilla/4.0 (compatible;MSIE 5.5;Windows NT)',
'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。
运行结果如下:
观察结果可以发现,我们成功设置了 data、headers 和 method。
通过 add_header 方法添加 headers 的方式如下:
req= request.Request(url=url, data=data, method='posT')
req.add_header('User-Agent','Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
有了 Request 类,我们就可以更加方便地构建请求,并实现请求的发送啦。
高级用法
我们已经可以构建请求了,那么对于一些更高级的操作(例如 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用于管理认证,如果一个镜接在打开时需要认证,那么可以用这个类来解决认证问题。
另一个比较重要的类是CpenerDirector,我们可以称之为Qpener。我们之前用过的 uhopen方法,实际上就是 urllib 库为我们提供的一个 Opener。
那么,为什么要引人 Opener 呢?因为需要实现更高级的功能。之前使用的 Request类和 urlopen类相当于类库已经封装好的极其常用的请求方法,利用这两个类可以完成基本的请求,但是现在我们需要实现更高级的功能,就需要深人一层进行配置,使用更底层的实例来完成操作。所以这里就用到了 Opener。
Qpener 类可以提供 open 方法,该方法返回的响应类型和ulopen方法如出一辙。那么,Opener类和 Handler 类有什么关系呢?简而言之就是,利用 Handler 类来构建 Qpener 类。
下面用几个实例来看看 Handler 类和 Opener 类的用法。
验证
在访问某些网站时,例如 htps/ssr3 .serape.center,可能会弹出这样的认证窗口,如图下所示。

遇到这种情况,就表示这个网站启用了基本身份认证,英文叫作 HTTP Basic Access Authentication ,这是一种登录验证方式,允许网页浏览器或其他客户端程序在请求网站时提供用户名和口令形式的身份凭证。
那么爬虫如何请求这样的页面呢?借助 HTTPBasicAuthHandler 模块就可以完成,相关代码如下:
from distutils.log import error
from urllib.error import URLError
from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener
from urllib.error import URLError
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,其参数是 HTTPPasswordMgrwithDefaultRealm对象,它利用 add_ password方法添加用户名和密码,这样就建立了一个用来处理验证的 Handler 类。
然后将刚建立的 auth_handler 类当作参数传人 build_opener 方法,构建一个Opener,这个 Opener在发送请求时就相当于已经验证成功了。
最后利用 Opener 类中的 open 方法打开链接,即可完成验证。这里获取的结果就是验证成功后的页面源码内容。
代理
做爬虫的时候,免不了要使用代理,如果要添加代理,可以这样做:
from urllib.error import URLError
from urllib.request import ProxyHandler, build_opener
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 端口上。
上面使用了 Proxy_Handler,其参数是一个字典,键名是协议类型(例如 HTTP 或者 HTTPS 等)键值是代理链接,可以添加多个代理。
然后利用这个 Handler 和 build_opener 方法构建了一个 Opener,之后发送请求即可。
Cookie
处理 Cookie 需要用到相关的 Handler。
我们先用实例来看看怎样获取网站的 Cookie,相关代码如下:
import http.cookiejar
import urllib.request
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
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 格式。
运行上面的实例之后,会发现生成了一个 cookie.txt 文件,该文件内容如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CGkZeRID-1657119789554)(https://s2.loli.net/2022/07/03/weEGvNAlMKLCrP6.png)]
另外,LWPCookieJar 同样可以读取和保存 Cookie,只是 Cookie 文件的保存格式和MozillaCookieJar不一样,它会保存成LWP(libwww-perl)格式。
要保存 LWP 格式的 Cookie 文件,可以在声明时就进行修改:
cookie = http.cookiejar.LWPCookieJar(filename)
此时生成的内容如下:

由此看来,不同格式的 Cookie 文件差异还是比较大的。
那么,生成 Cookie 文件后,怎样从其中读取内容并加以利用呢?
下面我们以 LWPCookieJar 格式为例来看一下:
import urllib.request, http.cookiejar
cookie = http.cookiejar.LwPCookieJar()
cookie.load(`cookie.txt', ignore_discard-True, ignore_expires-True)
handler = urllib.request.HTiPCookieprocessor(cookie)
opener - urllib.request.build_opener(handler)
response = opener.open('https://wmw.baidu.com')
print(response.read().decode('utf-8'))
可以看到,这里调用 load方法来读取本地的 Cookie 文件,获取了 Cookie的内容。这样做的前提是我们首先生成了 LWPCookieJar 格式的 Cookie,并保存成了文件。读取 Cookie 之后,使用同样的方法构建 Handler 类和 Opener 类即可完成操作。
运行结果正常的话,会输出百度网页的源代码。
通过上面的方法,我们就可以设置绝大多数请求的功能。
2.处理异常
我们已经了解了如何发送请求,但是在网络不好的情况下,如果出现了异常,该怎么办呢?这时要是不处理这些异常,程序很可能会因为报错而终止运行,所以异常处理还是十分有必要的。
urllib 库中的 error 模块定义了由 request模块产生的异常。当出现问题时,request 模块便会抛出error 模块中定义的异常。
URLError
URLError 类来自 urllib库的 error 模块,继承自OSError类,是error 异常模块的基类,由 request模块产生的异常都可以通过捕获这个类来处理。
它具有一个属性 reason,即返回错误的原因。
下面用一个实例来看一下:
from urllib import request, error
try:
response = request.urlopen('https://cuiqingcai.com/404')
except error.URLError as e:
print(e.reason)
我们打开了一个不存在的页面,照理来说应该会报错,但是我们捕获了 URLError 这个异常,运行结果如下:

程序没有直接报错,而是输出了错误原因,这样可以避免程序异常终止,同时异常得到了有效处理。
HTTPError
HTTPError 是 URLError 的子类,专门用来处理 HTTP 请求错误,例如认证请求失败等。它有如下3个属性。
- code:返回 HTTP 状态码,例如 404 表示网页不存在,500 表示服务器内部错误等。
- reason:同父类一样,用于返回错误的原因。
- headers:返回请求头。
下面我们用几个实例来看看:
from urllib import request, error
try:
response = request.urlopen('https://cuiqingcai.com/404')
except error.HTTPError as e:
print(e.reason, e.code, e.headers, sep='\n')
运行结果如下:

依然是打开同样的网址,这里捕获了 HTTPError 异常,输出了 Teason、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, e.headers, sep='\n')
except error.URLError as e:
print(e.reason)
else:
print('Request Successfully')
这样就可以做到先捕获 HTTPError,获取它的错误原因、状态码、请求头等信息。如果不是HTTPError 异常,就会捕获 URLError 异常,输出错误原因。最后,用 else 语句来处理正常的逻辑。这是一个较好的异常处理写法。
有时候,reason 属性返回的不一定是字符串,也可能是一个对象。再看下面的实例:
import socket
import urllib.request
import urllib.error
try:
response = urllib.request.urlopen('https://ww.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 方法来判断它的类型,做出更详细的异常判断。
3.解析链接
前面说过,urllib 库里还提供了 parse模块,这个模块定义了处理URL的标准接口,例如实现URL各部分的抽取,合并以及链接转换。它支持如下协议的URL处理:file、fip、gopher、hdl、http、https、imap、mailtio、mms、news、nntp、prospero、rsync、rtsp、rtspu、 sftp、sip、sips、snews、svn、svn+ssh、telnet 和 wais。
下面我们将介绍 parse 模块中的常用方法,看一下它的便捷之处。
urlparse
该方法可以实现 URL 的识别和分段,这里先用一个实例来看一下:
from urllib.parse import urlparse
result = 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,即访问路径。分号;后面是 params,代表参数。问号?后面是查询条件 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或 htps 等)。如果待解析的 URL 没有带协议信息,就会将这个作为默认协议。我们用实例来看一下:
from urllib.parse import urlparse result = urlparse('www.baidu.com/index.html;user?id=5#comment', scheme='https') print(result)运行结果如下:
可以发现,这里提供的URL 不包含最前面的协议信息,但是通过默认的 scheme参数,返回了结果 https。
假设带上协议信息:
result = urlparse( 'https://www.baidu.com/index.html;user?id=5#comment', scheme='https')则结果如下:
可见,scheme参数只有在URL 中不包含协议信息的时候才生效。如果 URL中有,就会返回解析出的 scheme。
allow_fragments:是否忽略 fragment。如果此项被设置为 False,那么 fragment 探分就会被忽略,它会被解析为 path、params 或者 query 的一部分,而 fragment 部分为空。
下面我们用实例来看一下:
from urllib.parse import urlparse
result = urlparse(
'https://www.baidu.com/index.html;user?id=5#comment', allow_fragments=False)
print(result)
运行结果如下:

假设 URL 中不包含 params 和 query,我们再通过实例看一下:
from urllib.parse import urlparse
result = urlparse(
'https://www.baidu.com/index.html#comment', allow_fragments=False)
print(result)
运行结果如下:

可以发现,此时 fragment 会被解析为 path 的一部分。
返回结果 ParseResult 实际上是一个元组,既可以用属性名获取其内容,也可以用索引来顺序获取。实例如下:
from urllib.parse import urlparse
result = 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 urlunparse
data = ['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 urlsplit
result = urlsplit('https://www.baidu.com/index.html;user?id=5#comment')
print(result)
运行结果如下:

可以发现,返回结果是 SplitResult,这其实也是一个元组,既可以用属性名获取其值,也可以用索引获取。实例如下:
from urllib.parse import urlsplit
result = urlsplit('https://www.baidu.com/index.html;user?id=5#comment')
print(result.scheme, result[0])
运行结果如下:

urlunsplit
与urlunparse方法类似,这也是将链接各个部分组合成完整链接的方法,传人的参数也是一个可迭代对象,例如列表、元组等,唯一区别是这里参数的长度必须为 5。实例如下:
from urllib.parse import urlunsplit
data = ['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 urljoin
print(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 urlencode
params = {
'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_qs
query = 'name=germey&age=25'
print(parse_qs(query))
运行结果如下:

可以看到,URL 的参数成功转回为字典类型。
parse_qsl
parse_qsl 方法用于将参数转化为由元组组成的列表,实例如下:
from urllib.parse import parse_qsl
query = 'name=germey&age=25'
print(parse_qsl(query))
运行结果如下:

可以看到,运行结果是一个列表,该列表中的每一个元素都是一个元组,元组的第一个内容是参数名,第二个内容是参数值。
quote
该方法可以将内容转化为URL编码的格式。当 URL中带有中文参数时,有可能导致乱码问题,此时用 quote 方法可以将中文字符转化为 URL 编码,实例如下:
from urllib.parse import quote
keyword = '壁纸'
url = 'https://www.baidu.com/s?wd='+quote(keyword)
print(url)
这里我们声明了一个中文的搜索文字,然后用 quote 方法对其进行 URL 编码,最后得到的结果如下:

unquote
有了 quote 方法,当然就有 unquote 方法,它可以进行 URL解码,实例如下:
from urllib.parse import unquote
url = 'https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8'
print(unquote(url))
这里的 url 是上面得到的 URL 编码结果,利用 unquote 方法将其还原,结果如下:

可以看到,利用 unquote 方法可以方便地实现解码。
本节我们介绍了 parse模块的一些常用URL处理方法。有了这些方法,我们可以方便地实现 URL的解析和构造,建议熟练掌握。
4.分析 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的一些常见写法。
爬虫名称
大家可能会疑惑,爬虫名是从哪儿来的?为什么叫这个名?其实爬虫是有固定名字的,例如百度的爬虫就叫作 Baiduspider。下表列出了一些常见搜索爬虫的名称及对应的网站。
| 爬虫名称 | 网站名称 |
|---|---|
| Baiduspider | 百度 |
| Googlebot | 谷歌 |
| 360Spider | 360搜索 |
| YodaoBot | 有道 |
| ia_archiver | Alexa |
| Scooter | altavista |
| Bingbot | 必应 |
robotparser
了解 Robots 协议之后,就可以使用 robotparser 模块来解析 robots.txt 文件了。该模块提供了一个类RobotFileParser,它可以根据某网站的 robots.txt 文件判断一个爬取爬虫是否有权限爬取这个网页。
该类用起来非常简单,只需要在构造方法里传人 robots.txt 文件的链接即可。首先看一下它的声明:
urllib.robotparser.RobotFileParser(url='')
当然,也可以不在声明时传人robots.txt 文件的链接,就让其默认为空,最后再使用set_url()方法设置一下也可以。
下面列出了 RobotFileParser 类的几个常用方法。
- set_url:用来设置 robots.txt 文件的链接。如果在创建 RobotFileParser 对象时传入了链接,就不需要使用这个方法设置了。
- read:读取 robots.txt 文件并进行分析。注意,这个方法执行读取和分析操作,如果不调用这个方法,接下来的判断都会为False,所以一定记得调用这个方法。这个方法虽不会返回任何内容,但是执行了读取操作。
- parse:用来解析 robots.txt 文件,传人其中的参数是robots.txt 文件中某些行的内容,它会按照robots.txt 的语法规则来分析这些内容。
- can_fetch:该方法有两个参数,第一个是User-Agent,第二个是要抓取的 URL。返回结果是True或False,表示 User-Agent 指示的搜索引擎是否可以抓取这个 URL。
- mtime:返回上次抓取和分析 robots.txt 文件的时间,这对于长时间分析和抓取 robots.txt 文件的搜索爬虫很有必要,你可能需要定期检查以抓取最新的 robots.txt 文件。
- modified:它同样对长时间分析和抓取的搜索爬虫很有帮助,可以将当前时间设置为上次抓取和分析 robots.txt 文件的时间。
下面我们用实例来看一下:
from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.set_url('https://www.baidu.com/robots.txt')
rp.read()
print(rp.can_fetch('Baiduspider', 'https://www.baidu.com'))
print(rp.can_fetch('Baiduspider', 'https://www.baidu.com/homepage/'))
print(rp.can_fetch('Cooglebot', 'https://www.baidu.com/homepage/'))
这里以百度为例,首先创建了一个 RobotFileParser 对象 rp,然后通过 set_url 方法设置了robots.txt 文件的链接。当然,要是不用 set_url方法,可以在声明对象时直接用如下方法设置:
rp=RobotFileParser('https://www.baidu.com/robots.txt')
接着利用 can_fetch方法判断了网页是否可以被抓取。运行结果如下:

可以看到,这里我们利用 Baiduspider可以抓取百度的首页以及 homepage页面,但是 Googlebot不能抓取 homepage 页面。
打开百度的 robots.txt 文件,可以看到如下信息:
User-agent: Baiduspider
Disallow: /baidu
Disallow: /s?
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: Googlebot
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: MSNBot
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: Baiduspider-image
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: YoudaoBot
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: Sogou web spider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: Sogou inst spider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: Sogou spider2
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: Sogou blog
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: Sogou News Spider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: Sogou Orion spider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: ChinasoSpider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: Sosospider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: yisouspider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: EasouSpider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: *
Disallow: /
不难看出,百度的 robots.txt 文件没有限制 Baiduspider 对百度 homepage 页面的抓取,限制了Googlebot 对 homepage 页面的抓取。
这里同样可以使用 parse 方法执行对 robots.txt 文件的读取和分析,实例如下:
from urllib.request import urlopen
from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.parse(urlopen('https://www.baidu.com/robots.txt').read().decode('utf-8').split('\n'))
print(rp.can_fetch('Baiduspider', 'https://www.baidu.com'))
print(rp.can_fetch('Baiduspider', 'https://www.baidu.com/homepage/'))
print(rp.can_fetch('Googlebot', 'https://www.baidu.com/homepage/'))
运行结果是一样的:

本节介绍了 robotparser 模块的基本用法和实例,利用此模块,我们可以方便地判断哪些页面能抓取、哪些页面不能。
llow: /bh
User-agent: yisouspider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: EasouSpider
Disallow: /baidu
Disallow: /s?
Disallow: /shifen/
Disallow: /homepage/
Disallow: /cpro
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh
User-agent: *
Disallow: /
不难看出,百度的 robots.txt 文件没有限制 Baiduspider 对百度 homepage 页面的抓取,限制了Googlebot 对 homepage 页面的抓取。
这里同样可以使用 parse 方法执行对 robots.txt 文件的读取和分析,实例如下:
```python
from urllib.request import urlopen
from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.parse(urlopen('https://www.baidu.com/robots.txt').read().decode('utf-8').split('\n'))
print(rp.can_fetch('Baiduspider', 'https://www.baidu.com'))
print(rp.can_fetch('Baiduspider', 'https://www.baidu.com/homepage/'))
print(rp.can_fetch('Googlebot', 'https://www.baidu.com/homepage/'))
运行结果是一样的:
[外链图片转存中…(img-zEmOU1c3-1657119789561)]
本节介绍了 robotparser 模块的基本用法和实例,利用此模块,我们可以方便地判断哪些页面能抓取、哪些页面不能。
边栏推荐
- 关于文件上传下载问题
- FreeBSD 12 国内源提速 pkg 和 ports 做个笔记
- Quickly understand redirection
- 正则表达式
- 高防服务器是如何确认哪些是恶意IP/流量?ip:103.88.32.XXX
- Manual string comparison (pointer question)
- ArraysList方法
- 剑指Offer刷题记录——Offer 03. 数组中重复的数字
- Sed command of text three swordsman -- text replacement; Grep command - text filtering
- Xiaodi network security - Notes (2)
猜你喜欢
随机推荐
103.53.124. What is the difference between X IP BGP line and ordinary dedicated line
PyTorch学习笔记(一)
Minecraft Paper 1.18.1 版开服教程,我的世界开服教程,MCSManager9面板使用教程
What do you need to build a website
[automated testing] - robotframework practice (I) building environment
Quickly learn to use cut command and uniq command
快速理解重定向
文本三剑客之awk命令--截取
快速掌握sort命令,tr命令
Utilisation et différenciation des dictionnaires, des tuples et des listes,
字典、元組和列錶的使用及區別,
Yuanzi racehorse.
University
文本三剑客之sed命令--文本替换;grep命令---文本过滤
类与super、继承
Dictionary, use of sets, conversion of data types
Freebsd 12 安装RPM包
FreeBSD 12 domestic source speed up PKG and ports take a note
Performance evaluation and comparison of Huawei cloud Kunpeng arm ECs and x86 ECS
Galaxy Kirin server system building local and LAN Yum source










