作者:xiaoyu
微信公眾號:Python資料科學
知乎:Python資料分析師
前情回顧,urllib的基本用法
- urllib庫的基本組成
- 利用最簡單的urlopen方法爬取網頁html
- 利用Request方法構建headers模擬瀏覽器操作
- error的異常操作
具體內容參見Python從零學爬蟲。urllib庫除了以上基礎的用法外,還有很多高階的功能,可以更加靈活的適用在爬蟲應用中,比如:
- 使用HTTP的POST請求方法向伺服器提交資料實現使用者登入
- 使用代理IP解決防止反爬
- 設定超時提高爬蟲效率
- 解析URL的方法
本次將會對這些內容進行詳細的分析和講解。
POST請求
POST是HTTP協議的請求方法之一,也是比較常用到的一種方法,用於向伺服器提交資料。博主先介紹進行post請求的一些準備工作,然後舉一個例子,對其使用以及更深層概念進行詳細的的剖析。
POST請求的準備工作
既然要提交資訊給伺服器,我們就需要知道資訊往哪填,填什麼,填寫格式是什麼?帶這些問題,我們往下看。
同樣提交使用者登入資訊(使用者名稱和密碼),不同網站可能需要的東西不一樣,比如淘寶反爬機制較複雜,會有其它一大串的額外資訊。這裡,我們以豆瓣為例(相對簡單),目標是弄清楚POST是如何使用的,複雜內容會在後續實戰部分與大家繼續分享。
丟擲上面像淘寶一樣需要的複雜資訊,如果僅考慮使用者名稱和密碼的話,我們的準備工作其實就是要弄明白使用者名稱和密碼標籤的屬性name是什麼,以下兩種方法可以實現。
- 瀏覽器F12檢視element獲取
- 也可以通過抓包工具Fiddler獲取,fiddler的下載地址
https://www.telerik.com/downl...
廢話不多說了,讓我們看看到底如何找到name?
1. 瀏覽器F12
通過瀏覽器F12元素逐層檢視到(我是用的Chrome),郵箱/手機號標籤的name="form_email", 密碼的標籤name="form_email",如下圖紅框所示。
但要說明的是,兩個標籤的name名稱並不是固定的,上面檢視的name名稱只是豆瓣網站定義的,不代表所有。其它的網站可能有會有不同的名稱,比如name="username", name="password"之類的。因此,針對不同網站的登入,需要每次檢視name是什麼。
2. 通過fiddler抓包工具
博主推薦使用fiddler工具,非常好用。爬蟲本身就是模擬瀏覽器工作,我們只需要知道瀏覽器是怎麼工作的就可以了。
fiddler會幫助我們抓取瀏覽器POST請求的所有內容,這樣我們得到了瀏覽器POST的資訊,把它填到爬蟲程式裡模擬瀏覽器操作就OK了。另外,也可以通過fiddler抓到瀏覽器請求的headers,非常方便。
安裝fiddler的小夥伴們注意:fiddler證照問題的坑(無法抓取HTTPs包),可以通過Tools —> Options —>HTTPS裡面打勾Decrypt HTTPS traffic修改證照來解決。否則會一直顯示抓取 Tunnel 資訊包...
好了,完成了準備工作,我們直接上一段程式碼理解下。
POST請求的使用
# 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的錯誤。
POST請求程式碼分析
我們來分析一下上面的程式碼,與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取代我們自己的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網站上查詢,博主整理出幾個,如:
- http://www.xicidaili.com/
- http://www.66ip.cn/
- http://www.mimiip.com/gngao/
- http://www.kuaidaili.com/
執行得到的結果與使用本機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超時')
urllib庫parse解析
除了上面提到的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庫的一些高階使用用法:
- POST請求的準備和使用方法
- 代理IP的使用
- 超時的使用
- parse解析
關注微信公眾號Python資料科學,獲取 120G
人工智慧 學習資料。