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。用的比較少,這裡不再贅述。
官方文件