Python3 爬蟲學(xué)習(xí)筆記02-Urllib庫

什么是Urllib?

Urllib是python內(nèi)置的HTTP請求庫,包括以下模塊:

  1. urllib.request 請求模塊
  2. urllib.error 異常處理模塊
  3. urllib.parse url解析模塊
  4. robots.txt解析模塊

1)urllib.request 請求模塊

模擬瀏覽器發(fā)起一個 HTTP 請求,我們需要用到 urllib.request 模塊。urllib.request 的作用不僅僅是發(fā)起請求, 還能獲取請求返回結(jié)果。發(fā)起請求,單靠 urlopen() 方法就可以叱咤風(fēng)云。我們先看下 urlopen() 的 API:

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
  • url:第一個參數(shù) String 類型的地址或者變量
  • data:是 bytes 類型的內(nèi)容,可通過 bytes()函數(shù)轉(zhuǎn)為化字節(jié)流。它也是可選參數(shù)。使用 data 參數(shù),請求方式變成以 POST 方式提交表單。使用標(biāo)準(zhǔn)格式是application/x-www-form-urlencoded
  • timeout:參數(shù)是用于設(shè)置請求超時時間。單位是秒。
  • cafilecapath:代表 CA 證書和 CA 證書的路徑。如果使用HTTPS則需要用到。
  • context:參數(shù)必須是ssl.SSLContext類型,用來指定SSL設(shè)置
  • cadefault:參數(shù)已經(jīng)被棄用,可以不用管了。
該方法也可以單獨(dú)傳入urllib.request.Request對象

該函數(shù)返回結(jié)果是一個http.client.HTTPResponse對象。

1.1 簡單抓取網(wǎng)頁

我們使用 urllib.request.urlopen() 去請求百度,并獲取到它頁面的源代碼。

import urllib.request

url = "http://www.baidu.com"
response = urllib.request.urlopen(url)
html = response.read()         # 獲取到頁面的源代碼
print(html.decode('utf-8'))    # 轉(zhuǎn)化為 utf-8 編碼

1.2 設(shè)置請求超時

有些請求可能因為網(wǎng)絡(luò)原因無法得到響應(yīng)。因此,我們可以手動設(shè)置超時時間。當(dāng)請求超時,我們可以采取進(jìn)一步措施,例如選擇直接丟棄該請求或者再請求一次。

import urllib.request

url = "http://www.baidu.com"
response = urllib.request.urlopen(url, timeout=1)
print(response.read().decode('utf-8'))

1.3 使用 data 參數(shù)提交數(shù)據(jù)

在請求某些網(wǎng)頁時需要攜帶一些數(shù)據(jù),我們就需要使用到 data 參數(shù)。

import urllib.parse
import urllib.request

url = "http://127.0.0.1:8000/book"
params = {
  'name':'浮生六記',
  'author':'沈復(fù)'
}

data = bytes(urllib.parse.urlencode(params), encoding='utf8')
response = urllib.request.urlopen(url, data=data)
print(response.read().decode('utf-8'))

params 需要被轉(zhuǎn)碼成字節(jié)流,而 params 是一個字典。我們需要使用urllib.parse.urlencode() 將字典轉(zhuǎn)化為字符串。再使用 bytes() 轉(zhuǎn)為字節(jié)流。最后使用 urlopen() 發(fā)起請求,請求是模擬用 POST 方式提交表單數(shù)據(jù)。

1.4 使用 Request

由上我們知道利用 urlopen() 方法可以發(fā)起簡單的請求。但這幾個簡單的參數(shù)并不足以構(gòu)建一個完整的請求,如果請求中需要加入headers(請求頭)、指定請求方式等信息,我們就可以利用更強(qiáng)大的Request類來構(gòu)建一個請求。
Request 的構(gòu)造方法:

urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
  • url參數(shù)是請求鏈接,這個是必傳參數(shù),其他的都是可選參數(shù)。
  • data參數(shù)跟 urlopen() 中的 data 參數(shù)用法相同。
  • headers參數(shù)是指定發(fā)起的 HTTP 請求的頭部信息。headers 是一個字典。它除了在 Request 中添加,還可以通過調(diào)用 Reques t實例的 add_header() 方法來添加請求頭。
  • origin_req_host參數(shù)指的是請求方的 host 名稱或者 IP 地址。
  • unverifiable參數(shù)表示這個請求是否是無法驗證的,默認(rèn)值是False。意思就是說用戶沒有足夠權(quán)限來選擇接收這個請求的結(jié)果。

例如我們請求一個HTML文檔中的圖片,但是我們沒有自動抓取圖像的權(quán)限,我們就要將 unverifiable 的值設(shè)置成 True。

  • method參數(shù)指的是發(fā)起的 HTTP 請求的方式,有 GET、POST、DELETE、PUT等

1.4.1 簡單使用 Request

使用 Request 偽裝成瀏覽器發(fā)起 HTTP 請求。如果不設(shè)置 headers 中的User-Agent,默認(rèn)的User-AgentPython-urllib/3.5。可能一些網(wǎng)站會將該請求攔截,所以需要偽裝成瀏覽器發(fā)起請求。
User-Agent 設(shè)置為 Chrome 瀏覽器:

import urllib.request

url = "http://tieba.baidu.com/"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))

1.4.2 Request 高級用法

如果我們需要在請求中添加代理、處理請求的 Cookies,我們需要用到HandlerOpenerDirector

Handler
中文意思是處理者、處理器。 Handler 能處理請求(HTTP、HTTPS、FTP等)中的各種事情。它的具體實現(xiàn)是這個類 urllib.request.BaseHandler。它是所有的 Handler 的基類,其提供了最基本的Handler的方法,例如default_open()、protocol_request()等。
繼承 BaseHandler 有很多個,列舉幾個比較常見的類:

  • ProxyHandler:為請求設(shè)置代理
  • HTTPCookieProcessor:處理 HTTP 請求中的 Cookies
  • HTTPDefaultErrorHandler:處理 HTTP 響應(yīng)錯誤。
  • HTTPRedirectHandler:處理 HTTP 重定向。
  • HTTPPasswordMgr:用于管理密碼,它維護(hù)了用戶名密碼的表。
  • HTTPBasicAuthHandler:用于登錄認(rèn)證,一般和 HTTPPasswordMgr 結(jié)合使用。

OpenerDirector
我們可以稱之為 Opener。我們之前用過 urlopen() 這個方法,實際上它就是 urllib 為我們提供的一個Opener。那 Opener 和 Handler 又有什么關(guān)系?opener 對象是由 build_opener(handler) 方法來創(chuàng)建出來 。我們需要創(chuàng)建自定義的 opener,就需要使用 install_opener(opener)方法。值得注意的是,install_opener 實例化會得到一個全局的 OpenerDirector 對象。

1.5 使用代理

有些網(wǎng)站做了瀏覽頻率限制。如果我們請求該網(wǎng)站頻率過高。該網(wǎng)站會被封 IP,禁止我們的訪問。所以我們需要使用代理來突破這“枷鎖”。
例:為 HTTP 請求設(shè)置代理

import urllib.request

url = "http://www.baidu.com/"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}

proxy_handler = urllib.request.ProxyHandler({
    'http': 'web-proxy.oa.com:8080',
    'https': 'web-proxy.oa.com:8080'
})
opener = urllib.request.build_opener(proxy_handler)
urllib.request.install_opener(opener)

request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))

1.6 認(rèn)證登錄

有些網(wǎng)站需要攜帶賬號和密碼進(jìn)行登錄之后才能繼續(xù)瀏覽網(wǎng)頁。碰到這樣的網(wǎng)站,我們需要用到認(rèn)證登錄。步驟如下:

  1. 使用HTTPPasswordMgrWithDefaultRealm() 實例化一個賬號密碼管理對象;
  2. 使用 add_password() 函數(shù)添加賬號和密碼;
  3. 接著使用 HTTPBasicAuthHandler() 得到 hander;
  4. 使用 build_opener() 獲取 opener 對象;
  5. 使用 opener 的 open() 函數(shù)發(fā)起請求。

例:攜帶賬號和密碼請求登錄百度貼吧,代碼如下:

import urllib.request

url = "http://tieba.baidu.com/"
user = 'user'  #賬號
password = 'password'  #密碼
pwdmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()  #實例化一個賬號密碼管理對象
pwdmgr.add_password(None,url ,user ,password)  #對實例化添加賬號和密碼

auth_handler = urllib.request.HTTPBasicAuthHandler(pwdmgr)
opener = urllib.request.build_opener(auth_handler)
response = opener.open(url)
print(response.read().decode('utf-8'))

1.7 Cookies設(shè)置

如果請求的頁面每次需要身份驗證,我們可以使用 Cookies 來自動登錄,免去重復(fù)登錄驗證的操作。步驟:

  1. 獲取 Cookies 需要使用 http.cookiejar.CookieJar() 實例化一個 Cookies 對象。
  2. urllib.request.HTTPCookieProcessor 構(gòu)建出 handler 對象。
  3. 使用 opener 的 open() 函數(shù)即可。

例:獲取請求百度的 Cookies 并保存到文件中,代碼如下:

import http.cookiejar
import urllib.request

url = "http://www.baidu.com/"
fileName = 'cookie.txt'

cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open(url)

f = open(fileName,'a')
for item in cookie:
    f.write(item.name+" = "+item.value+'\n')
f.close()

1.8 HTTPResponse

從上面的例子可知, 使用 urllib.request.urlopen() 或者 opener.open(url) 返回結(jié)果是一個 http.client.HTTPResponse 對象。它具有 msg、version、status、reason、debuglevel、closed等屬性以及read()readinto()getheader(name)getheaders()fileno()等函數(shù)。

2)urllib.error 異常處理模塊

發(fā)起請求難免會出現(xiàn)各種異常,我們需要對異常進(jìn)行處理,這樣會使得程序比較人性化。
異常處理主要用到兩個類,urllib.error.URLErrorurllib.error.HTTPError

2.1 URLError

URLError
urllib.error 異常類的基類, 可以捕獲由urllib.request 產(chǎn)生的異常。它具有一個屬性reason,即返回錯誤的原因。

URLError可能產(chǎn)生的原因:

  • 網(wǎng)絡(luò)無連接,即本機(jī)無法上網(wǎng)
  • 連接不到特定的服務(wù)器
  • 服務(wù)器不存在

捕獲 URL 異常的示例代碼:

import urllib.request
import urllib.error

url = "http://www.google.com"
try:
    response = urllib.request.urlopen(url)
except urllib.error.URLError as e:
    print(e.reason)

2.2 HTTPError

HTTPError
是 URLError 的子類,專門處理 HTTP 和 HTTPS 請求的錯誤。它具有三個屬性。

  • code:HTTP 請求返回的狀態(tài)碼。
  • renson:與父類用法一樣,表示返回錯誤的原因。
  • headers:HTTP 請求返回的響應(yīng)頭信息。

例:獲取 HTTP 異常的示例代碼, 輸出了錯誤狀態(tài)碼、錯誤原因、服務(wù)器響應(yīng)頭

import urllib.request
import urllib.error

url = "http://www.google.com"
try:
    response = urllib.request.urlopen(url)
except urllib.error.HTTPError as e:
   print('code: ' + e.code + '\n')
   print('reason: ' + e.reason + '\n')
   print('headers: ' + e.headers + '\n')

其他不能處理的,urlopen會產(chǎn)生一個HTTPError,對應(yīng)相應(yīng)的狀態(tài)嗎,HTTP狀態(tài)碼表示HTTP協(xié)議所返回的響應(yīng)的狀態(tài)。下面將狀態(tài)碼歸結(jié)如下

100:繼續(xù) 客戶端應(yīng)當(dāng)繼續(xù)發(fā)送請求。客戶端應(yīng)當(dāng)繼續(xù)發(fā)送請求的剩余部分,或者如果請求已經(jīng)完成,忽略這個響應(yīng)。
101: 轉(zhuǎn)換協(xié)議 在發(fā)送完這個響應(yīng)最后的空行后,服務(wù)器將會切換到在Upgrade 消息頭中定義的那些協(xié)議。只有在切換新的協(xié)議更有好處的時候才應(yīng)該采取類似措施。
102:繼續(xù)處理 由WebDAV(RFC 2518)擴(kuò)展的狀態(tài)碼,代表處理將被繼續(xù)執(zhí)行。
200:請求成功 處理方式:獲得響應(yīng)的內(nèi)容,進(jìn)行處理
201:請求完成,結(jié)果是創(chuàng)建了新資源。新創(chuàng)建資源的URI可在響應(yīng)的實體中得到 處理方式:爬蟲中不會遇到
202:請求被接受,但處理尚未完成 處理方式:阻塞等待
204:服務(wù)器端已經(jīng)實現(xiàn)了請求,但是沒有返回新的信 息。如果客戶是用戶代理,則無須為此更新自身的文檔視圖。 處理方式:丟棄
300:該狀態(tài)碼不被HTTP/1.0的應(yīng)用程序直接使用, 只是作為3XX類型回應(yīng)的默認(rèn)解釋。存在多個可用的被請求資源。 處理方式:若程序中能夠處理,則進(jìn)行進(jìn)一步處理,如果程序中不能處理,則丟棄
301:請求到的資源都會分配一個永久的URL,這樣就可以在將來通過該URL來訪問此資源 處理方式:重定向到分配的URL
302:請求到的資源在一個不同的URL處臨時保存 處理方式:重定向到臨時的URL
304:請求的資源未更新 處理方式:丟棄
400:非法請求 處理方式:丟棄
401:未授權(quán) 處理方式:丟棄
403:禁止 處理方式:丟棄
404:沒有找到 處理方式:丟棄
500:服務(wù)器內(nèi)部錯誤 服務(wù)器遇到了一個未曾預(yù)料的狀況,導(dǎo)致了它無法完成對請求的處理。一般來說,這個問題都會在服務(wù)器端的源代碼出現(xiàn)錯誤時出現(xiàn)。
501:服務(wù)器無法識別 服務(wù)器不支持當(dāng)前請求所需要的某個功能。當(dāng)服務(wù)器無法識別請求的方法,并且無法支持其對任何資源的請求。
502:錯誤網(wǎng)關(guān) 作為網(wǎng)關(guān)或者代理工作的服務(wù)器嘗試執(zhí)行請求時,從上游服務(wù)器接收到無效的響應(yīng)。
503:服務(wù)出錯 由于臨時的服務(wù)器維護(hù)或者過載,服務(wù)器當(dāng)前無法處理請求。這個狀況是臨時的,并且將在一段時間以后恢復(fù)。

HTTPError實例產(chǎn)生后會有一個code屬性,這就是是服務(wù)器發(fā)送的相關(guān)錯誤號。
寫一個例子來感受一下,捕獲的異常是HTTPError,它會帶有一個code屬性,就是錯誤代號,另外我們又打印了reason屬性,這是它的父類URLError的屬性。
我們知道,HTTPError的父類是URLError,根據(jù)編程經(jīng)驗,父類的異常應(yīng)當(dāng)寫到子類異常的后面,如果子類捕獲不到,那么可以捕獲父類的異常,所以上述的代碼可以這么寫

import urllib.request
import urllib.error
 
req = urllib.request.Request('http://www.google.com/123')
try:
    urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
    print (e.code)
except urllib.error.URLError as e:
    print (e.reason)
else:
  print ("OK")

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內(nèi)容