簡介html
Urllib是Python內置的HTTP請求庫。其主要做用就是能夠經過代碼模擬瀏覽器發送請求。它包含四個模塊:python
urllib.request :請求模塊
urllib.error :異常處理模塊
urllib.parse url : 解析模塊
urllib.robotparser :robots.txt解析模塊,用的比較少
相比Python2與3變化:瀏覽器
其常被用到的子模塊在Python3中的爲urllib.request和urllib.parse,在Python2中是urllib和urllib2。cookie
Python2: import urllib2 response=urllib2.urlopen(‘http://www.baidu.com’) Python3: import urllib.request response=urllib.request.urlopen(‘http://www.baidu.com’)
用於url解析轉換網絡
dic = {'wd': '趙麗穎'} ps = parse.urlencode(dic) print(ps) # wd=%E8%B5%B5%E4%B8%BD%E9%A2%96 print(parse.parse_qs(ps)) # {'wd': ['趙麗穎']}
這個函數很簡單,就是請求一個url的內容。其實這就是爬蟲的第一步:網頁請求,獲取內容。socket
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')) #轉換成字符串
print(response.read(10).decode('utf-8')) #只讀取10個
print(response.readline().decode('utf-8')) #只讀取一行
在交互模式下執行以上命令就能夠打印出百度首頁的源代碼了。
這是一種GET類型的請求,只傳入了一個參數(url)。
下面演示一種POST類型的請求: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')
獲取響應類型: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
若是咱們要在請求中加入別的信息怎麼辦呢?用上面的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' } dic={ 'name':'iron' } data=bytes(parse.urlencode(dic),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'
dic = {
'name': 'iron'
}
data = bytes(parse.urlencode(dict), encoding='utf8') # fontdata數據
req = request.Request(url=url, data=data, method='POST') # 構建一個Request()的一個結構
req.add_header('User-Angent', '................')
req.add_header('Host', 'httpbin.org')
response = request.urlopen(req)
print(response.read().decode('utf-8'))
add_header() 方法做爲添加header的另外一種方式,能夠用來比較複雜的狀況,好比要添加許多鍵值對,那麼能夠用一個for循環來不斷調用這個方法,這也是比較方便的。
直接打開url並寫到本地
request.urlretrieve('http://www.baidu.com', 'baidu.html')
例如直接下載圖片或者視頻
url = input('你想下載的視頻或圖片連接:') request.urlretrieve(url, 'binfile')
Headler至關於一個輔助工具,來幫助咱們處理一些額外的工做,好比FTP、Cache等等操做,咱們都須要藉助Headler來實現。好比在代理設置的時候,就須要用到一個ProxyHandler。更多的用法,請參閱官方文檔。
1.代理
用來對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())
2.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並打印
3.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文本文件:
4.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的另外一大模塊。
rom 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')
異常類型被打印出來了,確實是超時異常。
這是一個工具性質的模塊,即拿即用就行。
1.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個字段。
2.urlunparse(urlparse的反函數)
這個函數用來拼接url。
看看這個例子:
from urllib.parse import urlunparse #注意即便是空符號也要寫進去,否則會出錯 data = ['http', 'www.baidu.com', 'index.html','user','a=6' 'comment'] print(urlunparse(data))
3.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
4.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)
用來解析robot.txt。用的比較少,這裏再也不贅述。
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #導包 4 import urllib.request 5 import urllib.parse 6 if __name__ == "__main__": 7 #指定爬取的網頁url 8 url = 'http://www.baidu.com/' 9 #經過urlopen函數向指定的url發起請求,返回響應對象 10 reponse = urllib.request.urlopen(url=url) 11 #經過調用響應對象中的read函數,返回響應回客戶端的數據值(爬取到的數據) 12 data = reponse.read()#返回的數據爲byte類型,並不是字符串 13 print(data)#打印顯示爬取到的數據值。
#補充說明 urlopen函數原型:urllib.request.urlopen(url, data=None, timeout=<object object at 0x10af327d0>, *, cafile=None, capath=None, cadefault=False, context=None) 在上述案例中咱們只使用了該函數中的第一個參數url。在平常開發中,咱們能用的只有url和data這兩個參數。 url參數:指定向哪一個url發起請求 data參數:能夠將post請求中攜帶的參數封裝成字典的形式傳遞給該參數(暫時不須要理解,後期會講) urlopen函數返回的響應對象,相關函數調用介紹: response.headers():獲取響應頭信息 response.getcode():獲取響應狀態碼 response.geturl():獲取請求的url response.read():獲取響應中的數據值(字節類型)
#!/usr/bin/env python # -*- coding:utf-8 -*- import urllib.request import urllib.parse if __name__ == "__main__": url = 'http://news.baidu.com/' reponse = urllib.request.urlopen(url=url) #decode()做用是將響應中字節(byte)類型的數據值轉成字符串類型 data = reponse.read().decode() #使用IO操做將data表示的數據值以'w'權限的方式寫入到news.html文件中 with open('./news.html','w') as fp: fp.write(data) print('寫入文件完畢')
#!/usr/bin/env python # -*- coding:utf-8 -*- import urllib.request import urllib.parse #以下兩行代碼表示忽略https證書,由於下面請求的url爲https協議的請求,若是請求不是https則該兩行代碼可不用。 import ssl ssl._create_default_https_context = ssl._create_unverified_context if __name__ == "__main__": #url是https協議的 url = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1536918978042&di=172c5a4583ca1d17a1a49dba2914cfb9&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimgad%2Fpic%2Fitem%2F0dd7912397dda144f04b5d9cb9b7d0a20cf48659.jpg' reponse = urllib.request.urlopen(url=url) data = reponse.read()#由於爬取的是圖片數據值(二進制數據),則無需使用decode進行類型轉換。 with open('./money.jpg','wb') as fp: fp.write(data) print('寫入文件完畢')
url必須爲ASCII編碼的數據值。因此咱們在爬蟲代碼中編寫url時,若是url中存在非ASCII編碼的數據值,則必須對其進行ASCII編碼後,該url方可被使用。
案例:爬取使用百度根據指定詞條搜索到的頁面數據(例如爬取詞條爲‘周杰倫’的頁面數據)
import urllib.request import urllib.parse if __name__ == "__main__": #原始url中存在非ASCII編碼的值,則該url沒法被使用。使用會報錯須要轉碼 #url = 'http://www.baidu.com/s?ie=utf-8&wd=趙麗穎' #處理url中存在的非ASCII數據值 url = 'http://www.baidu.com/s?' #將帶有非ASCII的數據封裝到字典中,url中非ASCII的數據每每都是'?'後面鍵值形式的請求參數 param = { 'ie':'utf-8', 'wd':'趙麗穎' } #使用parse子模塊中的urlencode函數將封裝好的字典中存在的非ASCII的數值進行ASCII編碼 param = urllib.parse.urlencode(param) #將編碼後的數據和url進行整合拼接成一個完整可用的url url = url + param print(url) response = urllib.request.urlopen(url=url) data = response.read() with open('./趙麗穎.html','wb') as fp: fp.write(data) print('寫入文件完畢')
以前在講解http經常使用請求頭信息時,咱們講解過User-Agent參數,簡稱爲UA,該參數的做用是用於代表本次請求載體的身份標識。若是咱們經過瀏覽器發起的請求,則該請求的載體爲當前瀏覽器,則UA參數的值代表的是當前瀏覽器的身份標識表示的一串數據。若是咱們使用爬蟲程序發起的一個請求,則該請求的載體爲爬蟲程序,那麼該請求的UA爲爬蟲程序的身份標識表示的一串數據。有些網站會經過辨別請求的UA來判別該請求的載體是否爲爬蟲程序,若是爲爬蟲程序,則不會給該請求返回響應,那麼咱們的爬蟲程序則也沒法經過請求爬取到該網站中的數據值,這也是反爬蟲的一種初級技術手段。那麼爲了防止該問題的出現,則咱們能夠給爬蟲程序的UA進行假裝,假裝成某款瀏覽器的身份標識。
上述案例中,咱們是經過request模塊中的urlopen發起的請求,該請求對象爲urllib中內置的默認請求對象,咱們沒法對其進行UA進行更改操做。urllib還爲咱們提供了一種自定義請求對象的方式,咱們能夠經過自定義請求對象的方式,給該請求對象中的UA進行假裝(更改)操做。
import urllib.request import urllib.parse import ssl ssl._create_default_https_context = ssl._create_unverified_context # 證書相關 if __name__ == "__main__": #原始url中存在非ASCII編碼的值,則該url沒法被使用。 #url = 'http://www.baidu.com/s?ie=utf-8&wd=趙麗穎' #處理url中存在的非ASCII數據值 url = 'http://www.baidu.com/s?' #將帶有非ASCII的數據封裝到字典中,url中非ASCII的數據每每都是'?'後面鍵值形式的請求參數 param = { 'ie':'utf-8', 'wd':'趙麗穎' } #使用parse子模塊中的urlencode函數將封裝好的字典中存在的非ASCII的數值進行ASCII編碼 param = urllib.parse.urlencode(param) #將編碼後的數據和url進行整合拼接成一個完整可用的url url = url + param #將瀏覽器的UA數據獲取,封裝到一個字典中。該UA值能夠經過抓包工具或者瀏覽器自帶的開發者工具中獲取某請求,從中獲取UA的值 headers={ 'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' } #自定義一個請求對象 #參數:url爲請求的url。headers爲UA的值。data爲post請求的請求參數(後面講) request = urllib.request.Request(url=url,headers=headers) #發送咱們自定義的請求(該請求的UA已經進行了假裝) response = urllib.request.urlopen(request) data=response.read() with open('./趙麗穎.html','wb') as fp: fp.write(data) print('寫入數據完畢')