做者:xiaoyu
微信公衆號:Python數據科學
知乎:Python數據分析師html
前情回顧,urllib的基本用法web
具體內容參見Python從零學爬蟲。urllib庫除了以上基礎的用法外,還有不少高級的功能,能夠更加靈活的適用在爬蟲應用中,好比:segmentfault
本次將會對這些內容進行詳細的分析和講解。windows
POST是HTTP協議的請求方法之一,也是比較經常使用到的一種方法,用於向服務器提交數據。博主先介紹進行post請求的一些準備工做,而後舉一個例子,對其使用以及更深層概念進行詳細的的剖析。瀏覽器
既然要提交信息給服務器,咱們就須要知道信息往哪填,填什麼,填寫格式是什麼?帶這些問題,咱們往下看。服務器
一樣提交用戶登陸信息(用戶名和密碼),不一樣網站可能須要的東西不同,好比淘寶反爬機制較複雜,會有其它一大串的額外信息。這裏,咱們以豆瓣爲例(相對簡單),目標是弄清楚POST是如何使用的,複雜內容會在後續實戰部分與你們繼續分享。微信
拋出上面像淘寶同樣須要的複雜信息,若是僅考慮用戶名和密碼的話,咱們的準備工做其實就是要弄明白用戶名和密碼標籤的屬性name是什麼,如下兩種方法能夠實現。app
廢話很少說了,讓咱們看看到底如何找到name?socket
經過瀏覽器F12元素逐層查看到(我是用的Chrome),郵箱/手機號標籤的name="form_email", 密碼的標籤name="form_email",以下圖紅框所示。工具
但要說明的是,兩個標籤的name名稱並非固定的,上面查看的name名稱只是豆瓣網站定義的,不表明全部。其它的網站可能有會有不一樣的名稱,好比name="username", name="password"之類的。所以,針對不一樣網站的登陸,須要每次查看name是什麼。
博主推薦使用fiddler工具,很是好用。爬蟲自己就是模擬瀏覽器工做,咱們只須要知道瀏覽器是怎麼工做的就能夠了。
fiddler會幫助咱們抓取瀏覽器POST請求的全部內容,這樣咱們獲得了瀏覽器POST的信息,把它填到爬蟲程序裏模擬瀏覽器操做就OK了。另外,也能夠經過fiddler抓到瀏覽器請求的headers,很是方便。
安裝fiddler的小夥伴們注意:fiddler證書問題的坑(沒法抓取HTTPs包),能夠經過Tools —> Options —>HTTPS裏面打勾Decrypt HTTPS traffic修改證書來解決。不然會一直顯示抓取 Tunnel 信息包...
好了,完成了準備工做,咱們直接上一段代碼理解下。
# coding: utf-8 import urllib.request import urllib.error import urllib.parse # headers 信息,從fiddler上或你的瀏覽器上可複製下來 headers = {'Accept': 'text/html,application/xhtml+xml, application/xml;q=0.9,image/webp,image/apng, */*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/48.0 .2564.48 Safari/537.36' } # POST請求的信息,填寫你的用戶名和密碼 value = {'source': 'index_nav', 'form_password': 'your password', 'form_email': 'your username' } try: data = urllib.parse.urlencode(value).encode('utf8') response = urllib.request.Request( 'https://www.douban.com/login', data=data, headers=headers) html = urllib.request.urlopen(response) result = html.read().decode('utf8') print(result) except urllib.error.URLError as e: if hasattr(e, 'reason'): print('錯誤緣由是' + str(e.reason)) except urllib.error.HTTPError as e: if hasattr(e, 'code'): print('錯誤編碼是' + str(e.code)) else: print('請求成功經過。')
運行結果:
<!DOCTYPE HTML> <html lang="zh-cmn-Hans" class="ua-windows ua-webkit"> <head> <meta charset="UTF-8"> <meta name="description" content="提供圖書、電影、音樂唱片的 推薦、評論和價格比較,以及城市獨特的文化生活。"> ..... window.attachEvent('onload', _ga_init); } </script> </body> </html>
注意:複製header的時候請去掉 這一項'Accept-Encoding':' gzip, deflate, 不然會提示decode的錯誤。
咱們來分析一下上面的代碼,與urllib庫request的使用基本一致,urllib庫request的基本用法可參考上篇文章Python從零學爬蟲,這裏多出了post的data參數和一些解析的內容,着重講解一下。
data = urllib.parse.urlencode(value).encode('utf8')
這句的意思是利用了urllib庫的parse來對post內容解析,爲何要解析呢?
這是由於post內容須要進行必定的編碼格式處理後才能發送,而編碼的規則須要聽從RFC標準,百度了一下RFC定義,供你們參考:
*
Request ForComments(RFC),是一系列以編號排定的文件。文件收集了有關互聯網相關信息,以及UNIX和互聯網社區的軟件文件。目前RFC文件是由InternetSociety(ISOC)贊助發行。基本的互聯網通訊協議都有在RFC文件內詳細說明。RFC文件還額外加入許多的論題在標準內,例如對於互聯網新開發的協議及發展中全部的記錄。所以幾乎全部的互聯網標準都有收錄在RFC文件之中。
*
而parse的urlencode方法是將一個字典或者有順序的二元素元組轉換成爲URL的查詢字符串(說白了就是按照RFC標準轉換了一下格式)。而後再將轉換好的字符串按UTF-8的編碼轉換成爲二進制格式才能使用。
注:以上是在Python3.x環境下完成,Python3.x中編碼解碼規則爲 byte—>string—>byte的模式,其中byte—>string爲解碼,string—>byte爲編碼
爲何要使用代理IP?由於各類反爬機制會檢測同一IP爬取網頁的頻率速度,若是速度過快,就會被認定爲機器人封掉你的IP。可是速度過慢又會影響爬取的速度,所以,咱們將使用代理IP取代咱們本身的IP,這樣不斷更換新的IP地址就能夠達到快速爬取網頁而下降被檢測爲機器人的目的了。
一樣利用urllib的request就能夠完成代理IP的使用,可是與以前用到的urlopen不一樣,咱們須要本身建立訂製化的opener。什麼意思呢?
urlopen就好像是opener的通用版本,當咱們須要特殊功能(例如代理IP)的時候,urlopen知足不了咱們的需求,咱們就不得不本身定義並建立特殊的opener了。
request裏面正好有處理各類功能的處理器方法,以下:
ProxyHandler, UnknownHandler, HTTPHandler, HTTPDefaultErrorHandler, HTTPRedirectHandler, FTPHandler, FileHandler, HTTPErrorProcessor, DataHandler
咱們要用的是第一個ProxyHandler來處理代理問題。
讓咱們看一段代碼如何使用。
# coding:utf-8 import urllib.request import urllib.error import urllib.parse # headers信息,從fiddler上或瀏覽器上可複製下來 headers = {'Accept': 'text/html,application/xhtml+xml, application/xml;q=0.9,image/webp,image/apng, */*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/48.0.2564.48 Safari/537.36' } # POST請求的信息 value = {'source': 'index_nav', 'form_password': 'your password', 'form_email': 'your username' } # 代理IP信息爲字典格式,key爲'http',value爲'代理ip:端口號' proxy = {'http': '115.193.101.21:61234'} try: data = urllib.parse.urlencode(value).encode('utf8') response = urllib.request.Request( 'https://www.douban.com/login', data=data, headers=headers) # 使用ProxyHandler方法生成處理器對象 proxy_handler = urllib.request.ProxyHandler(proxy) # 建立代理IP的opener實例 opener = urllib.request.build_opener(proxy_handler) # 將設置好的post信息和headers的response做爲參數 html = opener.open(response) result = html.read().decode('utf8') print(result) except urllib.error.URLError as e: if hasattr(e, 'reason'): print('錯誤緣由是' + str(e.reason)) except urllib.error.HTTPError as e: if hasattr(e, 'code'): print('錯誤編碼是' + str(e.code)) else: print('請求成功經過。')
在上面post請求代碼的基礎上,用本身建立的opener替換urlopen便可完成代理IP的操做,代理ip能夠到一些免費的代理IP網站上查找,博主整理出幾個,如:
運行獲得的結果與使用本機IP同樣。
# 這個代理IP數據類型爲字典,若是是http協議,key值就爲**"http"**,value值應爲**"代理IP:端口號"的格式**。 proxy = {'http': '115.193.101.21:61234'} # 使用ProxyHandler方法建立proxy處理器對象 proxy_handler = urllib.request.ProxyHandler(proxy) # 建立代理IP的opener實例,參數爲proxy處理器對象 opener = urllib.request.build_opener(proxy_handler) # 用代理IP的opener打開指定狀態的URL信息 html = opener.open(response)
設置超時的目的是爲了防止爬取網站的時候,等待時間過長而致使效率的下降。有效的超時設置能夠強制結束等待而進行下一次的爬取,下面來一段代碼看如何使用。
# coding:utf-8 import urllib.request import urllib.error import urllib.parse import socket # headers信息,從fiddler上或瀏覽器上可複製下來 headers = {'Accept': 'text/html,application/xhtml+xml, application/xml;q=0.9,image/webp,image/apng, */*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/48.0 .2564.48 Safari/537.36' } # POST請求的信息 value = {'source': 'index_nav', 'form_password': 'your password', 'form_email': 'your username' } # 代理IP爲字典格式,key爲'http',value爲'代理ip:端口號' proxy = {'http': '115.193.101.21:61234'} # 設置超時爲2秒,單位爲秒 timeout = 2 try: # 設置socket超時 socket.setdefaulttimeout(timeout) data = urllib.parse.urlencode(value).encode('utf8') response = urllib.request.Request( 'https://www.douban.com/login', data=data, headers=headers) # 使用ProxyHandler方法生成處理器對象 proxy_handler = urllib.request.ProxyHandler(proxy) # 建立代理IP的opener實例 opener = urllib.request.build_opener(proxy_handler) # 將設置好的post信息和headers的response做爲參數 html = opener.open(response) result = html.read().decode('utf8') print(result) except urllib.error.URLError as e: if hasattr(e, 'reason'): print('錯誤緣由是' + str(e.reason)) except urllib.error.HTTPError as e: if hasattr(e, 'code'): print('錯誤編碼是' + str(e.code)) except socket.timeout: print('socket超時') else: print('請求成功經過。')
在post和代理IP使用的基礎上又增長了超時的使用。
# 設置超時爲2秒,單位爲秒 timeout = 2 #設置socket超時時間,若是不設置,則會使用默認時間。 socket.setdefaulttimeout(timeout) # 同時對socket超時timeout的錯誤設置了異常,timeout錯誤屬於OSerror的子類,時間超出指定timeout就會提示socket超時。 except socket.timeout: print('socket超時')
除了上面提到的urlencode方法,urllib庫的parse中還有不少其它的方法可使用,如:
#urlparse:把URL解析成6個部分 <scheme>://<netloc>/<path>;<params>?<query>#<fragment> #urlsplit:把URL解析成5個部分 <scheme>://<netloc>/<path>?<query>#<fragment> # urlunsplit,urlunparse:進行URL的重組 # 還有urljoin,urldefrag等。
更多用法能夠查找官方request源碼,也會在後續實戰例子中陸續使用介紹。
主要介紹了urllib庫的一些高級使用用法:
關注微信公衆號Python數據科學,獲取 120G
人工智能 學習資料。