1. 程式人生 > >Urllib庫的詳解(urlopen,response,request,Headler,異常處理,URL解析)

Urllib庫的詳解(urlopen,response,request,Headler,異常處理,URL解析)

簡介

Urllib是Python內建的HTTP請求庫。它包含四個模組:

  • urllib.request :請求模組
  • urllib.error :異常處理模組
  • urllib.parse url : 解析模組
  • urllib.robotparser :robots.txt解析模組,用的比較少

相比Python2變化:
Python2: import urllib2 response=urllib2.urlopen(‘http://www.baidu.com’)
Python3: import urllib.request response=urllib.request.urlopen(‘

http://www.baidu.com’)

基礎函式用法

一、urlopen

這個函式很簡單,就是請求一個url的內容。其實這就是爬蟲的第一步:網頁請求,獲取內容。
函式用法:

urllib.request.urlopen(url,data = None,[timeout]*,cafile = None,capath = None,cadefault = False,context = None)#urlopen前三個分別是(網站,網站的資料,超時設定)

可以看到,這個函式可以有非常多的引數,前三個用的最多,我們來逐一看看。

1.url引數

示例程式碼:

import
urllib.request response =urllib.request.urlopen('http://www.baidu.com') #把請求的結果傳給response print(response.read().decode('utf-8'))#轉換成字串

在互動模式下執行以上命令就可以打印出百度首頁的原始碼了。
這是一種GET型別的請求,只傳入了一個引數(url)。
下面演示一種POST型別的請求:

2.data引數

示例程式碼:

import urllib.parse
import urllib.request

data=bytes(urllib.parse.
urlencode({'word':'hello'}),encoding='utf8') #需要傳入一個data引數,需要的是bytes型別,這裡用了urlencode方法傳入一個字典,並指定編碼 response=urllib.request.urlopen('http://httpbin.org/post',data=data) #給urlopen函式傳入兩個引數,一個是url,一個是data print(response.read())

http://httpbin.org/ 是一個HTTP測試頁面)
可以看到打印出了一些Json字串:

在這裡插入圖片描述
我們可以從列印結果看到,我們成功的把’word’:'hello’這個字典通過urlopen函式以POST形式把它傳遞過去了。這樣我們就完成了一個POST的請求。
總結:加了data引數就是以POST形式傳送請求,否則就是GET方式了

3.timeout

再來看看第三個引數:超時時間。如果超過了這個時間沒有得到響應的話,就會丟擲異常。
示例程式碼:

import urllib.request
response=urllib.request.urlopen('http://httpbin.org/get',timeout=1)#設定超時時間
print(response.read())

再看另一種情況,我們把超時時間設定為0.1:

import socket
import urllib.request
import urllib.error

try:
	response=urllib.request.urlopen('http://httpbin.org/get',timeout=0.1)
	#必須在0.1秒內得到響應,否則就會丟擲異常
except urllib.error.URLError as e:
	if isinstance(e.reason,socket.timeout):#型別判斷,如果是超時錯誤,那麼列印
		print('TIME OUT')
		

二、響應(response)

獲取響應型別:type()

示例:

import urllib.request
response =urllib.request.urlopen('https://www.python.org')
print(type(response))#列印響應的型別

在這裡插入圖片描述
通過呼叫這個方法我們可以看到響應的型別。

狀態碼、響應頭

一個響應裡面包含了兩個比較有用的資訊:狀態碼和響應頭。
在這裡插入圖片描述
以上面提到的http://httpbin.org/ 為例,我們可以在審查中,找到狀態碼和響應頭(上圖紅框所示)。
這兩個資訊是判斷響應是否成功的非常重要的標誌。
在這裡我們可以用status引數獲取響應的狀態碼,用getheaders()方法獲取響應頭。
示例:

import urllib.request

response =urllib.request.urlopen('https://www.python.org')
print(response.status)#狀態碼
print(response.getheaders())#響應頭
print(response.getheader('Server'))#可以取得響應頭中特定的資訊

另外,關於read()方法,它獲取的是響應體中的內容(bytes形式)

import urllib.request
response =urllib.request.urlopen('https://www.python.org')
print(response.read().decode('utf-8'))#將位元組流解碼為utf-8

三、請求(request)

如果我們要在請求中加入別的資訊怎麼辦呢?用上面的urlopen函式是無法滿足的。
例如我們要在請求中加上Headers引數,但是urlopen函式中是沒有提供這個引數的。
如果我們想要傳送一些更為複雜的請求的話,比如加上Headers,怎麼辦呢?
那麼我可以建立一個request物件——使用Request(它也是屬於urllib.request模組的)。
來看示例:

import urllib.request
request=urllib.request.Request('https://python.org')#把url構造成一個request物件
response=urllib.request.urlopen(request)#再把request物件傳給urlopen
print(response.read().decode('utf-8'))

這樣也可以成功實現請求。
有了這樣的方法,我們就能更方便地指定請求的方式,還可以加一些額外的資料。
那麼現在嘗試構造一個POST請求,並且把headers加進來。

from urllib import request,parse
#構造一個POST請求
url='http://httpbin.org/post'
headers={
	'Users-Agent':'..............',
	'Host':'httpbin.org'
}
dict={
	'name':'Germey'
}
data=bytes(parse.urlencode(dict),encoding='utf8')#fontdata資料
req=request.Request(url=url,data=data,headers=headers,method='POST')#構建一個Request()的一個結構
response = request.urlopen(req)
print(response.read().decode('utf-8'))

我們把以上程式碼寫成一個py檔案並執行之:
在這裡插入圖片描述
可以看到,我們構造的Request包含了之前所提到的資訊,請求的時候我們是把Request當成一個整體傳遞給了urlopen,就可以實現這樣的請求了。
好處是整個Request的結構是非常清晰的。
此外還有另外一種實現方式,就是用add_header()方法,也可以實現相同的效果:

from urllib import request,parse
#構造一個POST請求
url='http://httpbin.org/post'
dict={
	'name':'Germey'
}
data=bytes(parse.urlencode(dict),encoding='utf8')#fontdata資料
req=request.Request(url=url,data=data,method='POST')#構建一個Request()的一個結構
req.add_header('User-Agent','................')#用add_header方法來新增header資訊
response = request.urlopen(req)
print(response.read().decode('utf-8'))

add_header()方法作為新增header的另一種方式,可以用來比較複雜的情況,比如要新增許多鍵值對,那麼可以用一個for迴圈來不斷呼叫這個方法,這也是比較方便的。

四、Headler

Headler相當於一個輔助工具,來幫助我們處理一些額外的工作,比如FTP、Cache等等操作,我們都需要藉助Headler來實現。比如在代理設定的時候,就需要用到一個ProxyHandler。更多的用法,請參閱官方文件

代理

用來對ip地址進行偽裝成不同地域的,防止ip在爬蟲執行時被封掉。
示例:

from urllib import request
proxy_handler = request.ProxyHandler( #構建ProxyHandler,傳入代理的網址
{'http':'http://127.0.0.1:9743',
'https':'https://127.0.0.1:9743'
}) #實踐表明這個埠已經被封了,這裡無法演示了
 
opener = request.build_opener(proxy_handler)#再構建一個帶有Handler的opener
 
response = opener.open('http://www.baidu.com')
print(response.read())


Cookie

Cookie是在客戶端儲存的用來記錄使用者身份的文字檔案。
在爬蟲時,主要是用來維護登入狀態,這樣就可以爬取一些需要登入認證的網頁了。
在這裡插入圖片描述
在這裡插入圖片描述
例項演示:


from urllib import request
 
from http import cookiejar
cookie =cookiejar.CookieJar()#將cookie宣告為一個CookieJar物件
 
handler = request.HTTPCookieProcessor(cookie)
 
opener = request.build_opener(handler)
 
response  =opener.open('http://www.baidu.com')#通過opener傳入,並開啟網頁
 
for item in cookie:#通過遍歷把已經賦值的cookie打印出來
    print(item.name+'='+item.value)#通過item拿到每一個cookie並列印

在這裡插入圖片描述

Cookie的儲存

我們還可以把cookie儲存成文字檔案,若cookie沒有失效,我們可以從文字檔案中再次讀取cookie,在請求時把cookie附加進去,這樣就可以繼續保持登入狀態了。
示例程式碼:


from urllib import request
from http import cookiejar

filename="cookie.txt"
cookie=cookiejar.MozillaCookieJar(filename)
#把cookie宣告為cookiejar的一個子類物件————MozillaCookieJar,它帶有一個save方法,可以把cookie儲存為文字檔案
handler=request.HTTPCookieProcessor(cookie)
opener=request.build_opener(handler)
response=opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True,ignore_expires=True)#呼叫save方法

執行程式碼後,我們就可以在執行目錄下找到已經儲存好的cookie文字檔案了:
在這裡插入圖片描述
還有另外一種格式:
在上面那段程式碼的基礎上,換一個子類物件就可以了:

cookie=cookiejar.LWPCookieJar(filename)

可以看到,這次生了一個不同格式的cookie文字檔案:
在這裡插入圖片描述

Cookie的讀取

我們可以選擇相對應的格式來完成讀取。以上面的LWP格式為例:

from urllib import request
from http import cookiejar


cookie=cookiejar.LWPCookieJar() #z注意選擇相應的格式,這裡是LWP
cookie.load('cookie.txt',ignore_discard=True,ignore_expires=True)#load方法是讀取的關鍵
handler=request.HTTPCookieProcessor(cookie)
opener=request.build_opener(handler)
response=opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))

以上的程式碼就可以完成讀取了。這樣,我們就可以在對網頁進行請求時,自動把之前的cookie附著進去,以保持一個登入的狀態了。

五、異常處理

這是屬於urllib的另一大模組。

from urllib import request,error

#我們試著訪問一個不存在的網址
try:
    response = request.urlopen('http://www.cuiqingcai.com/index.html')
except error.URLError as e:
    print(e.reason)#通過審查可以查到我們捕捉的異常是否與之相符

在這裡插入圖片描述

可以看到,返回了錯誤資訊。這樣的異常處理可以保證爬蟲在工作時不會輕易中斷。
那麼,urllib可以捕捉哪些異常呢?詳見官方文件
其實一般碰到有兩個:HTTP和URL。我們一般只需要捕捉這兩個異常就可以了。

from urllib import request,error

#我們試著訪問一個不存在的網址
try:
    response = request.urlopen('http://www.cuiqingcai.com/index.html')
except error.HTTPError as e:#最好先捕捉HTTP異常,因為這個異常是URL異常的子類
	print(e.reason,e.code,e.headers,sep='\n')
except error.URLError as e:
    print(e.reason)
else:
	print('Request Successfully!')

在這裡插入圖片描述
如上,打印出了錯誤的相關資訊。
此外,e.reason也是一個類,它可以得到異常的型別。
我們試著看看:

from urllib import request,error
import socket


try:
    response = request.urlopen('http://www.baidu.com',timeout = 0.01)#超時異常
except error.URLError as e:
    print(type(e.reason))
    if isinstance(e.reason,socket.timeout):#判斷error型別
        print('TIME OUT')

異常型別被打印出來了,確實是超時異常。
在這裡插入圖片描述

六、URL解析

這是一個工具性質的模組,即拿即用就行。
官方文件

urlparse

這個方法將將url進行分割,分割成好幾個部分,再依次將其複製。

urllib.parse.urlparse(urlstring,scheme='',allow_fragments = True)
#分割成(url,協議型別,和#後面的東西)

來看具體的例子:

from urllib.parse import urlparse
 
result = urlparse('https://www.baidu.com/s?wd=urllib&ie=UTF-8')
print(type(result),result)  #<class 'urllib.parse.ParseResult'>
 
#無協議型別指定,自行新增的情況
result1 = urlparse('www.baidu.com/s?wd=urllib&ie=UTF-8',scheme = 'https')
print(result1)
 
#有指定協議型別,預設新增的情況?
result2 = urlparse('http://www.baidu.com/s?wd=urllib&ie=UTF-8',scheme = 'https')
print(result2)

#allow_fragments引數使用
result3 = urlparse('http://www.baidu.com/s?#comment',allow_fragments = False)
 
result4 = urlparse('http://www.baidu.com/s?wd=urllib&ie=UTF-8#comment',allow_fragments = False)
print(result3,result4)
#allow_fragments=False表示#後面的東西不能填,原本在fragment位置的引數就會往上一個位置拼接,可以對比result1和result2的區別

從這個例子我們也可以知道,一個url可以分成6個欄位。

urlunparse(urlparse的反函式)

這個函式用來拼接url。
看看這個例子:

from urllib.parse import urlunparse
#注意即使是空符號也要寫進去,不然會出錯

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

在這裡插入圖片描述

urljoin

這個函式用來拼合url。
通過例子感受以下:
以後面的引數為基準,會覆蓋掉前面的欄位。如果後面的url,存在空欄位而前面的url有這個欄位,就會用前面的作為補充。

from urllib.parse import urljoin
print(urljoin('http://www.baidu.com','FQA.html'))
#http://www.baidu.com/FQA.html
 
print(urljoin('http://www.baidu.com','http://www.caiqingcai.com/FQA.html'))
#http://www.caiqingcai.com/FQA.html
 
print(urljoin('https://www.baidu.com/about.html','http://www.caiqingcai.com/FQA.html'))
#http://www.caiqingcai.com/FQA.html
 
print(urljoin('http://www.baidu.com/about.html','https://www.caiqingcai.com/FQA.html'))
#https://www.caiqingcai.com/FQA.html

urlencode

這個函式用來將字典物件轉化為get請求引數。

from urllib.parse import urlencode
 
params = {
'name':'zhuzhu',
'age':'23'
}
 
base_url = 'http://www.baidu.com?'
 
url = base_url+urlencode(params)#將params物件編碼轉換
 
print(url)

在這裡插入圖片描述

七、robotparser

用來解析robot.txt。用的比較少,這裡不再贅述。
官方文件