인생은 짧고, 파이썬을 사용
앞서 언급 한 포털 :
화이트 파이썬 크롤러 과학 (2) : 사전 준비 (a)는 실질적으로 도서관 장착
리눅스 시작하기하는베이스 (B)-준비를 사전 : 흰색 파충류 파이썬을 배울 수 (3)
사전 엔트리 준비의 도커 기준 (III) : 흰색 파충류 파이썬을 배우기 (4)
화이트 학교 파이썬 파충류 (5) : 사전 준비 (4 개) 데이터베이스 인프라
화이트 학교 파이썬 파충류 (6) : 사전 준비 (E) 크롤러 프레임 설치
(가) URLLIB 기본 : 화이트 학교 파이썬 파충류 (11)
화이트 학교 파이썬 파충류 (12) : URLLIB 기본 (B)
화이트 학교 파이썬 파충류 (13) : URLLIB 기본 (C)
구문 분석 모듈
공식 문서 : https://docs.python.org/zh-cn/3.7/library/urllib.parse.html
우리 앞에에서 요청 모듈 및 오류 모듈 URLLIB,이 글, 우리는 구문 분석 모듈을 소개합니다.
그것을 설명하는 공식 문서를 봐 :
This module defines a standard interface to break Uniform Resource Locator (URL) strings up in components (addressing scheme, network location, path etc.), to combine the components back into a URL string, and to convert a "relative URL" to an absolute URL given a "base URL."
The module has been designed to match the Internet RFC on Relative Uniform Resource Locators. It supports the following URL schemes: 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.
The urllib.parse module defines functions that fall into two broad categories: URL parsing and URL quoting.
大意是该模块定义了一个处理 URL 的标准接口,主要用于分解 URL 字符串为组件和将组件组合回 URL 字符串,并且可以将相对 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。
urllib.parse 模块定义的函数分为两大类:URL 解析和 URL 引用。
URL 解析
urlparse()
先看最常用的一个方法 urlparse()
,该方法主要用于识别 URL 和将其分段,再来看下 urlparse 对于一个 URL 的定义:
scheme://netloc/path;parameters?query#fragment
可以看到, urlparse() 将一个 URL 拆解成为 6 个部分,分别是 scheme
、 netloc
、 path
、 params
、 query
和 fragment
。
大体上可以看出来,解析时是会有特定的分隔符的。
- scheme:位于
://
前面,代表了当前的协议。 - netloc:位于第一个
/
之前,代表了域名。 - path:位于第一个
/
和;
之间,代表了访问路径。 - parameters:位于
;
和?
之间,代表了路径元素的参数。 - query:位于
?
和#
之间,代表了查询组件。 - fragment:位于
#
之后,代表了片段识别。
一个标准的 URL 应该是由以上部分组成,但是实际上,我们现在看到的大部分的 URL 并不会在链接中包含 ;
。
看个小例子吧,随便在官方文档上找了个连接,我们使用 urlparse()
将它解析一下看下结果:
from urllib.parse import urlparse
result = urlparse('https://docs.python.org/zh-cn/3.7/library/urllib.parse.html#module-urllib.parse')
print(type(result))
print(result)
执行结果:
<class 'urllib.parse.ParseResult'>
ParseResult(scheme='https', netloc='docs.python.org', path='/zh-cn/3.7/library/urllib.parse.html', params='', query='', fragment='module-urllib.parse')
可以看到, urlparse()
返回的数据类型是 urllib.parse.ParseResult
对象,它实际上是一个元组数据类型,而这个元组则包含了我们上面介绍的那些组件元素。
如果我们想要获取这个链接中的某个值怎么获取呢?比如说我们想获取当前这个链接的域名信息:
print(result.netloc)
结果如下:
docs.python.org
我们可以直接通过这个 urllib.parse.ParseResult
对应的属性将它取出来,而 urlparse()
甚至为我们提供了索引的方式来进行取值,对应关系如下表(以下内容来自官方文档):
属性 | 索引 | 描述 | 值(如果不存在) |
---|---|---|---|
scheme | 0 | URL 方案说明符 | scheme parameter |
netloc | 1 | 域名 | 空字符串 |
path | 2 | 分层路径 | 空字符串 |
params | 3 | 最后路径元素的参数 | 空字符串 |
query | 4 | 查询组件 | 空字符串 |
fragment | 5 | 片段识别 | 空字符串 |
我们再通过索引的方式获取一下刚才我们想获取的域名信息:
print(result[1])
结果如下:
docs.python.org
获取成功。
我们再来看一下 urlparse()
的语法:
urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)
- urlstring:这是必填项,即待解析的URL。
- scheme:它是默认的协议(比如 http 或 https 等)。假如这个链接没有带协议信息,会将这个作为默认的协议。
- allow_fragments:即是否忽略 fragment 。如果它被设置为 False , fragment 部分就会被忽略,它会被解析为 path 、 parameters 或者 query 的一部分,而 fragment 部分为空。
我们再来写一个示例,连接还是使用刚才上面的链接,我们在这个示例中去掉链接中的 scheme 信息,在参数中指定 scheme 信息,并且将 allow_fragments 设置为 False ,看下片段识别将会被解析成哪一部分:
from urllib.parse import urlparse
result1 = urlparse('docs.python.org/zh-cn/3.7/library/urllib.parse.html#module-urllib.parse', scheme = "https", allow_fragments = False)
print(result1)
运行结果如下:
ParseResult(scheme='https', netloc='', path='docs.python.org/zh-cn/3.7/library/urllib.parse.html#module-urllib.parse', params='', query='', fragment='')
可以从结果中看出, scheme 信息是可以正常识别出来的,而片段识别信息则被解析成为了 path 的一部分。
urlunparse()
有了解析链接的 urlparse()
,那么一定会存在构建链接的 urlunparse()
。
这个方法它接受的参数是一个可迭代对象,但是它的长度必须是 6 ,否则会抛出参数数量不足或者过多的问题。
我们还是通过一个示例了解一下:
from urllib.parse import urlparse, urlunparse
params = ('https', 'www.geekdigging.com', 'index.html', 'people', 'a=1', 'geekdigging')
print(urlunparse(params))
这里的参数使用的数据类型是元组,当然也可以选择其他的可迭代的数据类型,例如列表或者特定的数据结构等等。
运行结果如下:
https://www.geekdigging.com/index.html;people?a=1#geekdigging
这样我们就简单的实现了 URL 的构造,如果其中某些参数在构建的时候不存在,切记不可直接不写,留空字符串即可,如下:
params = ('https', 'www.geekdigging.com', 'index.html', '', '', 'geekdigging')
urlsplit()
urlsplit()
这个方法和 urlparse()
非常像,唯一的区别就是 urlsplit()
不再单独解析 params
这个组件,只会返回 5 个组件的结果,它将 params
合并到了 path
中。还是看一个示例:
from urllib.parse import urlsplit
result_urlsplit = urlsplit("https://www.geekdigging.com/index.html;people?a=1#geekdigging")
print(type(result_urlsplit))
print(result_urlsplit)
结果如下:
<class 'urllib.parse.SplitResult'>
SplitResult(scheme='https', netloc='www.geekdigging.com', path='/index.html;people', query='a=1', fragment='geekdigging')
可以发现,返回结果是 urllib.parse.SplitResult
,它其实也是一个元组类型,既可以用属性获取值,也可以用索引来获取。示例如下:
print(result_urlsplit.netloc)
print(result_urlsplit[1])
结果如下:
www.geekdigging.com
www.geekdigging.com
urlunsplit()
看到这个命名各位同学应该大致上已经可以猜出来 urlunsplit()
它的作用以及用法了。
对的,木有错,它和上面介绍的 urlunparse()
是非常相似的,唯一的区别就是它的参数长度必须为 5 。示例如下:
from urllib.parse import urlunsplit
params_urlunsplit = ('https', 'www.geekdigging.com', 'index.html;people', 'a=1', 'geekdigging')
print(urlunsplit(params_urlunsplit))
结果如下:
https://www.geekdigging.com/index.html;people?a=1#geekdigging
其余的不多做介绍,完全参考上面的 urlunparse()
。
urljoin()
上面我们介绍了 urlunparse()
和 urlunsplit()
两个方法可以合成链接,前提是我们需要有定长的参数可迭代对象,链接的每一个组件都要对应的分开。
而 urllib.parase 中提供的 urljoin()
这个方法就比较有意思了。我们先看下官方的解释:
Construct a full ("absolute") URL by combining a "base URL" (base) with another URL (url). Informally, this uses components of the base URL, in particular the addressing scheme, the network location and (part of) the path, to provide missing components in the relative URL.
其中的大意是:通过基础的 URL 和另一个 URL 来完成组合完成最终的 URL ,它会分析基础的 URL 的 scheme
、 netloc
和 path
这三个内容,并对新连接缺失的部分进行补充,完成最终的组合。
有点没看懂?没关系,我们写几个简单的示例就清楚了:
print(urljoin("https://www.geekdigging.com/", "index.html"))
print(urljoin("https://www.geekdigging.com/", "https://www.geekdigging.com/index.html"))
print(urljoin("https://www.geekdigging.com/", "?a=aa"))
print(urljoin("https://www.geekdigging.com/#geekdigging", "https://docs.python.org/zh-cn/3.7/library/urllib.parse.html"))
结果如下:
https://www.geekdigging.com/index.html
https://www.geekdigging.com/index.html
https://www.geekdigging.com/?a=aa
https://docs.python.org/zh-cn/3.7/library/urllib.parse.html
不知各位同学看懂了没?
只有在第二个参数链接缺失 scheme
、 netloc
和 path
这三个内容的时候,才会从第一个参数中获取对应的内容进行组合。
parse_qs()
这个方法可以让我们将一串 GET 请求中的参数转换成为字典。
我们在百度搜索的时候,像百度发送的请求其实是 GET 请求,例如我在百度搜索 Python ,这时浏览器上的链接显示为:https://www.baidu.com/s?ie=UTF-8&wd=python 。这里的 GET请求参数实际上是 ?
后面这部分 ie=UTF-8&wd=python
。我们还是用 parse_qs()
来写个示例看一下:
from urllib.parse import parse_qs
print(parse_qs("ie=UTF-8&wd=python"))
执行结果如下:
{'ie': ['UTF-8'], 'wd': ['python']}
可以看到,参数成功的转换成了字典。
parse_qsl()
还有一个parse_qsl()方法,它用于将参数转化为元组组成的列表,示例如下:
from urllib.parse import parse_qsl
# parse_qsl 示例
print(parse_qsl("ie=UTF-8&wd=python"))
执行结果如下:
[('ie', 'UTF-8'), ('wd', 'python')]
可以看到,参数成功的转换成了元组组成的列表。
URL 引用
urlencode()
我们接着介绍一个比较常用的方法: urlencode()
。
这个方法是用来构造 GET 请求参数的,例如我们上面示例中所使用到的百度搜索的参数,它可以将一个字典转换成为 GET 请求参数。示例如下:
from urllib.parse import urlencode
# urlencode 示例
dict = {
"name": "极客挖掘机",
"age": 18
}
print("https://www.geekdigging.com/" + urlencode(dict))
结果如下:
https://www.geekdigging.com/name=%E6%9E%81%E5%AE%A2%E6%8C%96%E6%8E%98%E6%9C%BA&age=18
执行结果中有一串看不懂是什么的字符串,这个是 URL 编码,因为参数中含有中文参数时,可能会导致乱码,可以看到 urlencode()
这个方法,自动的帮我们对中文进行了 URL 编码。
quote()
这个方法是 urllib.parse 中专门为我们提供的 URL 转码的方法,我们还是拿上面一个示例的中文进行转码,各位同学可以看下结果是否相同:
from urllib.parse import quote
# quote 示例
print(quote("极客挖掘机"))
结果如下:
%E6%9E%81%E5%AE%A2%E6%8C%96%E6%8E%98%E6%9C%BA
好像是一样的对吧,说明我们测试成功。
unquote()
有了 quote()
的方法,当然会搞一个专门逆向的方法咯~~~
我们把刚才经过 URL 编码后的字符串拿出来做测试,看看能不能转回去:
from urllib.parse import unquote
# unquote 示例
print(unquote("%E6%9E%81%E5%AE%A2%E6%8C%96%E6%8E%98%E6%9C%BA"))
结果如下:
极客挖掘机
可以看到,经过我们 URL 编码后的字符串逆向成功。
本篇的内容就到这里了,内容有些又臭又长,不过还是希望各位同学可以亲自动手练习一下。
毕竟,自己不敲代码是永远学不会代码的。
示例代码
本系列的所有代码小编都会放在代码管理仓库 Github 和 Gitee 上,方便大家取用。