1. urllib.parse分解URL
urllib.parse模塊提供了一些函數,可以管理URL及其組成部分,這包括將URL分解為組成部分以及由組成部分構成URL。
1.1 解析
urlparse()函數的返回值是一個ParseResult對象,其相當於一個包含6個元素的tuple。
from urllib.parse import urlparse url = 'http://netloc/path;param?query=arg#frag' parsed = urlparse(url) print(parsed)
通過元組接口得到的URL各部分分別是機制、網絡位置、路徑、路徑段參數(由一個分號將路徑分開)、查詢以及片段。
盡管返回值相當於一個元組,但實際上它基於一個namedtuple,這是tuple的一個子類,除了可以通過索引訪問,它還支持通過命名屬性訪問URL的各部分。屬性API不僅更易於程序員使用,還允許訪問tupleAPI中未提供的很多值。
from urllib.parse import urlparse url = 'http://user:pwd@NetLoc:80/path;param?query=arg#frag' parsed = urlparse(url) print('scheme :', parsed.scheme) print('netloc :', parsed.netloc) print('path :', parsed.path) print('params :', parsed.params) print('query :', parsed.query) print('fragment:', parsed.fragment) print('username:', parsed.username) print('password:', parsed.password) print('hostname:', parsed.hostname) print('port :', parsed.port)
輸入URL中可能提供用戶名(username)和密碼(password),如果沒有提供就設置為None。主機名(hostname)與netloc值相同,全為小寫並且去除端口值。如果有端口(port),則轉換為一個整數,如果沒有則設置為None。
urlsplit()函數可以替換urlparse(),但行為稍有不同,因為它不會從URL分解參數。
from urllib.parse import urlsplit url = 'http://user:pwd@NetLoc:80/p1;para/p2;para?query=arg#frag' parsed = urlsplit(url) print(parsed) print('scheme :', parsed.scheme) print('netloc :', parsed.netloc) print('path :', parsed.path) print('query :', parsed.query) print('fragment:', parsed.fragment) print('username:', parsed.username) print('password:', parsed.password) print('hostname:', parsed.hostname) print('port :', parsed.port)
由於沒有分解參數,tuple API胡顯示五個元素而不是6個,並且這里沒有params屬性。
要想從一個URL剝離出片段標識符,如從一個URL查找基頁面名,可以使用urldefrag()。
from urllib.parse import urldefrag original = 'http://netloc/path;param?query=arg#frag' print('original:', original) d = urldefrag(original) print('url :', d.url) print('fragment:', d.fragment)
返回值是一個基於namedtuple的DefragResult,其中包含基URL和片段。
1.2 反解析
還可以利用一些方法把分解的URL的各個部分重新組裝在一起,形成一個串。解析的URL對象有一個geturl()方法。
from urllib.parse import urlparse original = 'http://netloc/path;param?query=arg#frag' print('ORIG :', original) parsed = urlparse(original) print('PARSED:', parsed.geturl())
geturl()只適用於urlparse()或urlsplit()返回的對象。
利用urlunparse()可以將包含串的普通元組重新組合為一個URL。
from urllib.parse import urlparse, urlunparse original = 'http://netloc/path;param?query=arg#frag' print('ORIG :', original) parsed = urlparse(original) print('PARSED:', type(parsed), parsed) t = parsed[:] print('TUPLE :', type(t), t) print('NEW :', urlunparse(t))
盡管urlparse()返回的ParseResult可以作為一個元組,但這個例子卻顯式地創建了一個新元組,來展示urlunparse()也適用於普通元組。
如果輸入URL包含多余的部分,那么重新構造的URL可能會將其去除。
from urllib.parse import urlparse, urlunparse original = 'http://netloc/path;?#' print('ORIG :', original) parsed = urlparse(original) print('PARSED:', type(parsed), parsed) t = parsed[:] print('TUPLE :', type(t), t) print('NEW :', urlunparse(t))
在這里,原URL中沒有參數、查詢和片段。新URL看起來與原URL並不相同,不過按照標准它們是等價的。
1.3 連接
除了解析URL,urlparse還包括一個urljoin()方法,可以由相對片段構造絕對URL。
from urllib.parse import urljoin print(urljoin('http://www.example.com/path/file.html', 'anotherfile.html')) print(urljoin('http://www.example.com/path/file.html', '../anotherfile.html'))
在這個例子中,計算第二個URL時要考慮路徑的相對部分("../")。
非相對路徑的處理與os.path.join()的處理方式相同。
from urllib.parse import urljoin print(urljoin('http://www.example.com/path/', '/subpath/file.html')) print(urljoin('http://www.example.com/path/', 'subpath/file.html'))
如果連接到URL的路徑以一個斜線開頭(/),那么urljoin()會把URL的路徑重置為頂級路徑。如果不是以一個斜線開頭,那么新路徑值則追加到URL當前路徑的末尾。
1.4 解碼查詢參數
參數在被增加到一個URL之前,需要先編碼。
from urllib.parse import urlencode query_args = { 'q': 'query string', 'foo': 'bar', } encoded_args = urlencode(query_args) print('Encoded:', encoded_args)
編碼會替換諸如空格之類的特殊字符,以確保采用一種符合標准的格式將它們傳遞到服務器。
如果要利用查詢串中的變量傳遞一個值序列,那么需要在調用urlencode()時將doseq設置為True。
from urllib.parse import urlencode query_args = { 'foo': ['foo1', 'foo2'], } print('Single :', urlencode(query_args)) print('Sequence:', urlencode(query_args, doseq=True))
結果是一個查詢串,包含與一個名關聯的多個值。
要解碼這個查詢串,可以使用parse_qs()或parse_qsl()。
from urllib.parse import parse_qs, parse_qsl encoded = 'foo=foo1&foo=foo2' print('parse_qs :', parse_qs(encoded)) print('parse_qsl:', parse_qsl(encoded))
parse_qs()的返回值是一個將名映射到值的字典,而parse_qsl()返回一個元組列表,每個元組包含一個名和一個值。
查詢參數中可能有一些特殊字符,會導致服務器端在解析URL時出問題,所以在傳遞到urlencode()時要對這些特殊字符“加引號”。要在本地對它們加引號以建立這些串的安全版本,可以直接使用quote()或quote_plus()函數。
from urllib.parse import quote, quote_plus, urlencode url = 'http://localhost:8080/~hellmann/' print('urlencode() :', urlencode({'url': url})) print('quote() :', quote(url)) print('quote_plus():', quote_plus(url))
quote_plus()中的加引號實現會更大程度的替換字符。
要完成加引號操作的逆過程,可以在適當的時候使用unquote()或unquote_plus()。
from urllib.parse import unquote, unquote_plus print(unquote('http%3A//localhost%3A8080/%7Ehellmann/')) print(unquote_plus( 'http%3A%2F%2Flocalhost%3A8080%2F%7Ehellmann%2F' ))
編碼的值會轉換回一個普通的URL串。