爬蟲系列-urllib基本庫的使用

學習爬蟲,最基礎便是模擬瀏覽器向服務器發出請求,請求需要來我們自己來構造?以及如何構造?具體怎麼實現?服務器的響應和應答原理是什麼樣的?
可能我們對這些一無所知,但python提供了功能齊全的類庫來幫助我們完成這些請求。常用的HTTP庫有urlllib.requests.trep等。
而我們今天主要介紹的就是urllib庫,這個庫只需要我們傳入相應的參數和相應的鏈接。首先我給出官方文檔的鏈接,所有的學習都是基於官方文檔來的。

urllib庫基本介紹

python內置的的HTTP請求庫,不需要額外安裝,主要分爲下面四個模塊:
1.urllib.request 用於打開和請求URL
2.urllib.error 異常處理模塊,如果出現請求錯誤,我們可以捕獲這些異常,進行重試防止意外終止
3.urllib.parse 用於解析URL,合併,拆分
4.urllib.robotparser用於識別robots.txt文件,判斷哪些網站可以爬,哪些不可以爬,它其實用的比較少。

1.發送請求

使用urllib中的request模塊,我們可以方便的實現請求的發送並得到響應。

urlopen()函數

urllib.request除了提供最基本的構造HTTP請求方式,它還可以模擬瀏覽器的一個請求發起過程,同時它還帶有處理授權、重定向.瀏覽器Cookies以及其他內容。

import urllib.request
response =urllib.request.urlopen('https://docs.python.org/3/library/urllib.html')#請求urllib官方文檔爲例子
print(response.read().decode('utf-8'))#打印出返回的結果

這裏我截取部分結果,這個就是返回的網頁源代碼。
在這裏插入圖片描述這裏我們用了兩行代碼就獲取了網頁的源代碼,那麼我們想要的信息就可以直接從裏面提取出來。我們也可以看一下返回的到底是什麼?我們可以用type()查看響應的類型。

print(type(response))
#<class 'http.client.HTTPResponse'>

可以發現,它是一個HTTPResponse類型的對象,主要包含read()、readinto()、getheader(name)、getheaders()、fileno()等方法,以及msg、version、status、reason、debuglevel、closed等屬性。當我們請得到這個對象,然後賦值就可以調用這些屬性和方法,得到返回的結果的一系列信息了。
例如利用status、getheaders方法可以查看輸出了響應的狀態和響應的頭信息。

print(response.status)#查看返回的狀態碼,比如200表示請求正常,404表示網頁未找到
print(response.getheaders())#查看響應的信息
print(response.getheader('Sever'))#查看服務器是用什麼搭建的(一般不常用)

這是我返回的結果,狀態碼返回200表示正常請求到了。其他就是返回的頭信息。
在這裏插入圖片描述
利用最基本的urlopen()函數,可以完成最基本的GET請求抓取。但urlopen()函數的功能其實不止這樣還有很多,比如post請求等以及一些相關參數。
urlopen()常用的參數也是下面三個,其他基本很少涉及,有需要的話可以看官網詳解。

urlopen(url, data, timeout)

第一個參數url即爲URL,第二個參數data是訪問URL時要傳送的數據,第三個timeout是設置超時時間。
第二三個參數是可以不傳送的,data默認爲空None,timeout默認爲 socket._GLOBAL_DEFAULT_TIMEOUT
第一個參數URL是必須要傳送的,在這個例子裏面我們傳送了百度的URL,執行urlopen方法之後,返回一個response對象,返回信息便保存在這裏面。
下面我舉一個例子需要post請求的,大家可能會問post請求是什麼,通俗的來說就是發送表單信息,相當於發送一些你個人信息什麼,比如登錄的時候。但現在post請求一般也很少見,我找一個簡單 的例子給大家看一下。

import urllib.request
import urllib.parse
url='http://www.iqianyue.com/mypost'
postdata =urllib.parse.urlencode({'name':"zf","pass":123456}).encode('utf-8')#將表單以j字典形式發哦送
r =urllib.request.Request(url,postdata)
data =urllib.request.urlopen(r).read()#此時的r是一個request對象
print(data)

在這裏插入圖片描述
這上面的request.Request和parse.urlencode我後面會一一講解。大家主要就是了解post請求的形式。
urlopen()函數中的第三個參數timeout,是用於設置超時時間,意思就是在請求超出這個時間會拋出異常,如果不設定的話採用默認時間,這個參數針對一些請求需要時間比較長的網站是很有效的。
下面這個是我做的一個實驗,和之前那個例子相比,我就改了一下timeout參數就報錯了,說請求超時。
在這裏插入圖片描述在urlopen()函數中除了這幾個參數外,還有context參數,它必須是ssl.SSLContext類型用來指定SSL設置。此外還有cafile和capath兩個參數指定CA證書和它的路徑,這個在請求HTTPS鏈接會很有用。想了解更多可以看看官方文檔

2.Request

我們知道利用urlopen()方法可以實現最基本請求的發起,只不過這次參數不再是URL,而是一個Request類型的對象,通過構造這個數據結構,一方面我們可以將請求獨立成一個對象,另外一方面也可以更加豐富和靈活配置參數。
函數參數大致如下:

class urllib.request.Request(url,data = None,headers = {},origin_req_host = None,unverifiable = False,method = None )

Request這個類可以設置請求頭等參數,可以將請求構造更加完整。

import urllib.request
request =urllib.request.Request('https://docs.python.org/3/library/urllib.html')
response =urllib.request.urlopen(request)
print(response.read().decode('utf-8'))

Request中的第一個參數url用於請求URL,這是必傳參數,其他都是可選參數。第二個參數data如果要傳,必須傳bytes(字節流)類型。如果它是字典,先用urllib.parse模塊裏的urlencode()編碼。第三個參數headers是一個字典,它就是請求頭。可以直接構造或者通過實例的add_header()方法添加。第四個參數origin_req_host 指的是請求方的host名稱或者IP地址。第五個第五個參數unverifiable表示這個請求是否是無法驗證的,默認是False,意思就是說用戶沒有足夠權限來選擇接收這個請求的結果。例如,我們請求一個HTML文檔中的圖片,但是我們沒有自動抓取圖像的權限,這時unverifiable的值就是True。第六個參數method是一個字符串,用來指示請求使用的方法,比如GET、POST和PUT等。
下面我們看一個例子:

from urllib import request, parse

url = 'http://httpbin.org/post'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0',
    'Host': 'httpbin.org'
}
dict = {
    'name': 'Germey'
}
data = bytes(parse.urlencode(dict), encoding='utf8')
req = request.Request(url=url, data=data, headers=headers, method='POST')
response = request.urlopen(req)
print(response.read().decode('utf-8'))

在這裏插入圖片描述
這個例子中我們定義了headers,data,method,url這些參數,指定了請求方式爲post。
另外,headers也可以用add_header()方法來添加:

req = request.Request(url=url, data=data, method='POST')
req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
Cookies

Cookies指某些網站爲了辨別用戶身份、進行會話跟蹤而存儲在用戶本地終端上的數據。通俗的來說也就是一個用戶身份的ID一樣,保存着我們的信息。比如登錄之後,瀏覽器可能下次就直接利用這個cookies,下次就不需要登錄。
那麼cookies的獲取怎麼做,舉個例子:

import http.cookiejar, urllib.request
cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
for item in cookie:
    print(item.name+"="+item.value)

HTTPCookieProcessor這個類用於處理Cookies。首先,我們必須聲明一個CookieJar對象。接下來,就需要利用HTTPCookieProcessor來構建一個Handler,最後利用build_opener()方法構建出Opener,執行open()函數即可。

BAIDUID=852B733A72A7E6DA5CEC9ED6C4B7A9B6:FG=1
BIDUPSID=852B733A72A7E6DA5CEC9ED6C4B7A9B6
H_PS_PSSID=1434_21102_29523_29521_29720_29567_29220_28701
PSTM=1568728617
delPer=0
BDSVRTM=0
BD_HOME=0

這裏輸出了每條cookies的名稱和值。但有的時候我們希望能夠將上面的cookies保存下來。

filename = 'cookies.txt'
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)

這時CookieJar就需要換成MozillaCookieJar,它在生成文件時會用到,是CookieJar的子類,可以用來處理Cookies和文件相關的事件,比如讀取和保存Cookies,可以將Cookies保存成Mozilla型瀏覽器的Cookies格式。

運行之後,可以發現生成了一個cookies.txt文件,其內容如下:
在這裏插入圖片描述
通過保存的cookies我們下次可以直接從文件中讀取。但具體操作我就不過多的介紹了。大家可以看官方文檔,urllib.request的功能還是很多,想具體瞭解可以去官網學習。

處理異常

1.URLError
上面我們大致瞭解了請求的過程,但是有的時候網絡問題或者其他問題導致出現了異常,該怎麼辦呢?這時如果不處理這些異常,程序很可能因報錯而終止運行,所以異常處理還是十分有必要的。
urllib中的error模塊定義了由request模塊產生的異常。如果出現問題,request模塊便會拋出error模塊中定義的異常。
下面舉個例子,比如我把網址寫錯了:

from urllib import request, error
try:
    response = request.urlopen('https://docs.python.org/3/library/urllib.htm')
except error.URLError as e:
    print(e.reason)

返回的是:

Not Found

程序沒有報錯,這樣我們避免程序異常終止。
2.HTTPError
它是URLError的子類,專門用來處理HTTP請求錯誤,比如認證請求失敗等。它有如下3個屬性。
code:返回HTTP狀態碼,比如404表示網頁不存在,500表示服務器內部錯誤等。
reason:同父類一樣,用於返回錯誤的原因。
headers:返回請求頭。
我們還是看一個例子:

from urllib import request,error
try:
    response = request.urlopen('https://docs.python.org/3/library/urllib.htm')
except error.HTTPError as e:
    print(e.reason, e.code, e.headers, sep='\n')

返回的是:

Not Found
404
Server: nginx
Content-Type: text/html
X-Clacks-Overhead: GNU Terry Pratchett
Strict-Transport-Security: max-age=315360000; includeSubDomains; preload
Via: 1.1 varnish
Content-Length: 162
Accept-Ranges: bytes
Date: Tue, 17 Sep 2019 14:28:24 GMT
Via: 1.1 varnish
Age: 200
Connection: close
X-Served-By: cache-jfk8123-JFK, cache-hnd18738-HND
X-Cache: MISS, HIT
X-Cache-Hits: 0, 1
X-Timer: S1568730505.660194,VS0,VE7
Vary: Accept-Encoding

依然是同樣的網址,這裏捕獲了HTTPError異常,輸出了reason、code和headers屬性。有很多出現錯誤的可能,比如timeout超時等,其他更多具體的還是官網瞭解,這裏我只是大致介紹一下。
這一塊我還舉一個超時的例子:

import socket
import urllib.request
import urllib.error

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')

返回的是:

<class 'socket.timeout'>
TIME OUT

解析鏈接

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。
1.urlparse()
這個方法主要實現對url的識別和分段,這裏還是看一個例子:

from urllib.parse import urlparse
result = urlparse('https://docs.python.org/3/library/urllib.html')
print(type(result), result)

返回包括六個部分,對url進行了分段:

<class 'urllib.parse.ParseResult'> ParseResult(scheme='https', netloc='docs.python.org', path='/3/library/urllib.html', params='', query='', fragment='')

可以發現,urlparse()方法將其拆分成了6部分。大體觀察可以發現,解析時有特定的分隔符。比如,: //前面的就是scheme,代表協議;第一個/前面便是netloc,即域名;分號;前面是params,代表參數。所以我們可以得出標準的鏈接格式:

scheme://netloc/path;parameters?query#fragment

一個標準的url都會符合這個規則,利用urlparse()方法可以將它拆分開來。
除了這種最基本的解析方式外,urlparse()方法還有其他配置

urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)

urlstring:這是必填項,即待解析的URL。
scheme:它是默認的協議(比如http或https等)。假如這個鏈接沒有帶協議信息,會將這個作爲默認的協議。
allow_fragments:即是否忽略fragment。如果它被設置爲False,fragment部分就會被忽略,它會被解析爲path、parameters或者query的一部分,而fragment部分爲空。
舉一個例子:

urlparse('http://www.baidu.com/index.html;user?id=5#comment', scheme='https')

返回的結果是這樣的:

ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')

如果我們沒有帶協議

from urllib.parse import urlparse
result = urlparse('www.baidu.com/index.html;user?id=5#comment', scheme='https')
print(result)

則返回的是這樣的

ParseResult(scheme='https', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment')

可見,scheme參數只有在URL中不包含scheme信息時才生效。如果URL中有scheme信息,就會返回解析出的scheme。
接下來我們來看看第三個參數:

from urllib.parse import urlparse
result = urlparse('http://www.baidu.com/index.html;user?id=5#comment', allow_fragments=False)
print(result)
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5#comment', fragment='')

然而當URL中不包含params和query,我們再通過實例看一下:

from urllib.parse import urlparse
result = urlparse('http://www.baidu.com/index.html#comment', allow_fragments=False)
print(result)

返回的:

ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html#comment', params='', query='', fragment='')

可以發現,當URL中不包含params和query時,fragment便會被解析爲path的一部分。
2.urlunparse()
有了urlparse(),相應地就有了它的對立方法urlunparse()。它接受的參數是一個可迭代對象,但是它的長度必須是6,否則會拋出參數數量不足或者過多的問題。先用一個實例看一下:

from urllib.parse import urlunparse
data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
print(urlunparse(data))

返回的就是我們希望能夠拼接成的url

http://www.baidu.com/index.html;user?a=6#comment

3.urlunsplit()
與urlunparse()類似,它也是將鏈接各個部分組合成完整鏈接的方法,傳入的參數也是一個可迭代對象,例如列表、元組等,唯一的區別是長度必須爲5。示例如下:

from urllib.parse import urlunsplit
data = ['http', 'www.baidu.com', 'index.html', 'a=6', 'comment']
print(urlunsplit(data))

拼接後的url如下:

http://www.baidu.com/index.html?a=6#comment

4.urlsplit()
這個方法和urlparse()方法非常相似,只不過它不再單獨解析params這一部分,只返回5個結果。上面例子中的params會合併到path中。示例如下:

from urllib.parse import urlsplit
result = urlsplit('http://www.baidu.com/index.html;user?id=5#comment')
print(result)

運行的結果是:

SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html;user', query='id=5', fragment='comment')

5.urlencode()
這裏我們再介紹一個常用的方法——urlencode(),它在構造GET請求參數的時候非常有用,示例如下:

from urllib.parse import urlencode
params = {
    'name': 'germey',
    'age': 22
}
base_url = 'http://www.baidu.com?'
url = base_url + urlencode(params)
print(url)

運行結果:

http://www.baidu.com?name=germey&age=22

這個方法非常常用。有時爲了更加方便地構造參數,我們會事先用字典來表示。要轉化爲URL的參數時,只需要調用該方法即可。
6.parse_qs()
既然有了構造,當然也可以分解,這個函數就是將其分解成字典:

from urllib.parse import parse_qs
query = 'name=germey&age=22'
print(parse_qs(query))

分解之後就是這樣

{'name': ['germey'], 'age': ['22']}

除此之外我們還以將它轉成元組組成的列表。parse_qsl()方法,它用於將參數轉化爲元組組成的列表。
7.quote()
該方法可以將內容轉化爲URL編碼的格式。URL中帶有中文參數時,有時可能會導致亂碼的問題,此時用這個方法可以將中文字符轉化爲URL編碼,示例如下:

from urllib.parse import quote
keyword = '我愛中國'
url = 'https://www.baidu.com/s?wd=' + quote(keyword)
print(url)
https://www.baidu.com/s?wd=%E6%88%91%E7%88%B1%E4%B8%AD%E5%9B%BD

這個在我們構建關鍵詞,然後希望它可以一一的形成url然後去爬取是很有用的。當然我們可以對中文進行編碼當然也可以對其進行解碼。
8.unquote()
它可以進行URL解碼,示例如下:

from urllib.parse import unquote
url = 'https://www.baidu.com/s?wd=%E6%88%91%E7%88%B1%E4%B8%AD%E5%9B%BD'
print(unquote(url))

解碼之後:

https://www.baidu.com/s?wd=我愛中國

這一塊我們主要了解了一些url處理方法,方便我們對url進行解析和構造。

Robots協議

Robots協議也稱作爬蟲協議、機器人協議,它的全名叫作網絡爬蟲排除標準(Robots Exclusion Protocol),用來告訴爬蟲和搜索引擎哪些頁面可以抓取,哪些不可以抓取。它通常是一個叫作robots.txt的文本文件,一般放在網站的根目錄下。

當搜索爬蟲訪問一個站點時,它首先會檢查這個站點根目錄下是否存在robots.txt文件,如果存在,搜索爬蟲會根據其中定義的爬取範圍來爬取。如果沒有找到這個文件,搜索爬蟲便會訪問所有可直接訪問的頁面。
這一塊也是很重要的,大家自己看看了解,以防在法律邊緣試探。
urllib基本庫大部分常用的也是這些了,我是參考了一個網絡爬蟲大佬崔慶才書籍總結,大家有興趣可以去看看,然後想了解更多可以去官網看看。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章