簡介
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。用的比較少,這裏不再贅述。
官方文檔
八、實例
request = urllib2.Request(self.url)
request.add_header('Cookie','PHPSESSID=79lo60cmtl1ck70h4ufruq6n53; mmf_searchhotkeyandroid=%E5%A4%A9%E6%B6%AF%E7%A4%BE%E5%8C%BA%2C%E7%A9%BF%E8%A1%A3%E5%8A%A9%E6%89%8B%2C%E5%A4%A9%E6%B0%94%2C%E9%B3%84%E9%B1%BC%E5%B0%8F%E9%A1%BD%E7%9A%AE%E7%88%B1%E6%B4%97%E6%BE%A12%2C%E6%B0%B4%E6%9E%9C%E5%BF%8D%E8%80%85%2C%E4%B8%96%E7%95%8COL%2C%E6%88%98%E5%A4%A9; mmf_msisdn=08e2b01ad5dd5b3d297ef6558a60ec26; mmf_us=08e2b01ad5dd5b3d297ef6558a60ec26.39; mmf_userVisitPageIndex=79lo60cmtl1ck70h4ufruq6n53.2')
request.add_header('Connection','keep-alive')
request.add_header('Accept','*/*')
request.add_header('Accept-Language','zh-CN,zh;q=0.8')
request.add_header('Accept-Encoding','gzip,deflate,sdch')
request.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36')
response = urllib2.urlopen(request)
print response.code
if response.info().get('Content-Encoding')=='gzip':
buf = StringIO(response.read())
f = gzip.GzipFile(fileobj = buf)
data = f.read()
f.close()