1. 程式人生 > >小白學 Python 爬蟲(14):urllib 基礎使用(四)

小白學 Python 爬蟲(14):urllib 基礎使用(四)

人生苦短,我用 Python

前文傳送門:

小白學 Python 爬蟲(1):開篇

小白學 Python 爬蟲(2):前置準備(一)基本類庫的安裝

小白學 Python 爬蟲(3):前置準備(二)Linux基礎入門

小白學 Python 爬蟲(4):前置準備(三)Docker基礎入門

小白學 Python 爬蟲(5):前置準備(四)資料庫基礎

小白學 Python 爬蟲(6):前置準備(五)爬蟲框架的安裝

小白學 Python 爬蟲(7):HTTP 基礎

小白學 Python 爬蟲(8):網頁基礎

小白學 Python 爬蟲(9):爬蟲基礎

小白學 Python 爬蟲(10):Session 和 Cookies

小白學 Python 爬蟲(11):urllib 基礎使用(一)

小白學 Python 爬蟲(12):urllib 基礎使用(二)

小白學 Python 爬蟲(13):urllib 基礎使用(三)

Parse 模組

官方文件:https://docs.python.org/zh-cn/3.7/library/urllib.parse.html

前面我們介紹了 urllib 的 Request 模組和 Error 模組,本篇我們接著介紹 Parse 模組。

先看一下官方文件的解釋吧:

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 個部分,分別是 schemenetlocpathparamsqueryfragment

大體上可以看出來,解析時是會有特定的分隔符的。

  • 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 的 schemenetlocpath 這三個內容,並對新連線缺失的部分進行補充,完成最終的組合。

有點沒看懂?沒關係,我們寫幾個簡單的示例就清楚了:

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

不知各位同學看懂了沒?

只有在第二個引數連結缺失 schemenetlocpath 這三個內容的時候,才會從第一個引數中獲取對應的內容進行組合。

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 上,方便大家取用。

示例程式碼-Github

示例程式碼-Gitee

參考

https://www.cnblogs.com/zhangxinqi/p/9170312.h