本文將介紹handler處理器和自定義opener,更多內容請參考:python學習指南
opener和handleer
- 我們之前一直使用的是urllib2.urlopen(url)這種形式來打開網頁,它是一個特殊的opener(也就是模塊幫我們建好的),opener是urllib2.OpenerDirectory的實例。
- 但是基本的urlopen()方法不支持代理、cookie等其他的HTTP/HTTPS高級功能。所以要支持這些功能:
- 使用相關的
Handler處理器
來創建特定功能的處理器對象; - 然后通過
urllib2.build_opener()
方法來使用這些處理器對象,創建自定義opener對象; - 使用自定義的opener對象,調用
open()
方法來發送請求。
- 使用相關的
- 如果程序里所有的請求都使用自定義的opener對象,可以使用
urllib2.install_opener()
將自定義的opener對象定義為全局opener,表示如果之后凡是調用urlopen,都將使用這個opener(根據自己的需求來選擇)
簡單的自定義opener()
#-*- coding:utf-8 -*-
#12.urllib2_opener.py
import urllib2
#構建一個HTTPHandler處理器
http_handler = urllib2.HTTPHandler();
#調用urllib2.build_opener()方法,創建支持處理HTTP請求的opener
opener = urllib2.build_opener(http_handler)
#構建Request請求
request = urllib2.Request("http://www.baidu.com")
#調用自定義的opener對象的open()方法,發送request請求
response = opener.open(request)
#獲取服務器響應內容
print(response.read())
這種方式發送請求得到的結果,和使用urllib2.urlopen()
發送HTTP/HTTPS請求得到的結果是一樣的。
如果在HTTPHandler()括號里面增加debuglevel=1
參數,還會將Debug Log打開,這樣程序在執行的時候,會把收包和發包的報頭在屏幕上自動打印出來,方便調試,有時可以省去抓包的工作。
#僅需要修改的代碼部分:
# 構建一個HTTPHandler 處理器對象,支持處理HTTP請求,同時開啟Debug Log,debuglevel 值默認 0
http_handler = urllib2.HTTPHandler(debuglevel=1)
ProxyHandler處理器(代理設置)
使用代理IP,這是爬蟲/反爬蟲的第二大招,通常也是最好用的。
很多網站會檢測某一段時間某個IP的訪問次數(通過流量統計,系統日志等),如果訪問字數多的不像正常人,它會禁止這個IP的訪問。
所以我們可以設置一些代理服務器,每隔一段時間換一個代理,就算IP被禁止,依然可以換個IP繼續爬取。
urllib2中通過ProxyHandler來設置使用代理服務器,下面代碼說明如何使用自定義opener來使用代理:
#-*- coding:utf-8 -*-
#urllib2_proxyhandler.py
import urllib2
#構建了兩個代理Handler,一個有代理IP,一個沒有代理IP
httpproxy_handler = urllib2.ProxyHandler({"http":"120.76.55.49:8088"})
nullproxy_handler = urllib2.ProxyHandler({})
proxyswitch = True #定義一個代理開關
#通過urllib2.build_opener()方法使用這些代理Handler對象,創建自定義opener
if proxyswitch:
opener = urllib2.build_opener(httpproxy_handler)
else:
opener = urllib2.build_opener(nullproxy_handler)
request = urllib2.Request("http://www.baidu.com/")
#1.如果這么寫,只有使用opener.open()方法發送請才使用自定義的代理,而urlopen()使用自定義代理
response = opener.open(request)
#2.如果這么寫,就是opener應用到全局,之后所有的,不管是opener.open()還是urlopen()發送請求,都將使用自定義代理
# urllib2.install_opener(opener)
# response = urllib2.urlopen(request)
print(response.read())
免費的開放代理獲取基本沒有成本,我們可以在一些代理網站上收集上收集這些免費代理,測試后如果可以用,就把它收集起來用在爬蟲上面。
免費短期代理網站舉例:
西刺免費代理IP
快代理免費代理
Proxy360代理
全網代理IP
如果代理IP足夠多,就可以像隨機獲取User-Agent一樣,隨機選擇一個代理去訪問網站。
#-*- coding:utf-8 -*-
#14.urllib2_proxylisthandler.py
import urllib2
import random
proxy_list = [
{"http" : "124.88.67.81:80"},
{"http" : "124.88.67.81:80"},
{"http" : "124.88.67.81:80"},
{"http" : "124.88.67.81:80"},
{"http" : "124.88.67.81:80"}
]
#隨機選擇一個代理
proxy = random.choice(proxy_list)
#使用選擇的代理創建一個代理處理器
proxy_handler = urllib2.ProxyHandler(proxy)
opener = urllib2.build_opener(proxy_handler)
urllib2.install_opener(opener)
request = urllib2.Request("http://www.baidu.com/")
response = opener.open(request)
print(response.read())
但是,這些免費開放代理一般會有很多人都在使用,而且代理壽命短、速度慢,匿名度不高,HTTP/HTTPS支持不穩定等缺點(免費沒好貨)。
但是,專業爬蟲工程師或爬蟲公司會使用高品質的私密代理,這些代理通常需要找專門的代理供應商購買,再通過用戶名/密碼授權使用(舍不得孩子討不到狼)
HTTPPasswordMgrWithDefaultRealm()
HTTPPasswordMgrWithDefaultRealm()
類創建一個密碼管理對象,用來保存HTTP請求相關的用戶名和密碼,主要應用兩個場景:
1. 驗證代理授權的用戶名和密碼(ProxyBasicAuthHandler())
2. 驗證web客戶端的用戶名和密碼(HTTPBasicAuthHandler())
ProxyBasicAuthHandler(代理授權驗證)
如果我們使用之前的代碼來使用私密代理,會報HTTP 407錯誤,表示代理沒有通過身份驗證:
urllib2.HTTPError:HTTP Error 407:Proxy Authentication Required
所以我們需要改寫代碼,通過:
HTTPPasswordMgrWithDefaultRealm()
:來保存私密代理的用戶密碼ProxyBasicAuthHandler()
:來處理代理的身份。
#-*- coding:utf-8 -*-
#15.urllib2_proxy2.py
import urllib2
import urllib
#私密代理授權的賬戶
user = "mr_mao_hacker"
#私密代理授權的密碼
passwd = "sffqry9r"
#私密代理IP
proxyserver = "61.23.123.43:16813"
# 1. 構建一個密碼管理對象,用來保存需要處理的用戶名和密碼
passwdmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
#2. 添加賬戶信息,第一個參數是realm是與遠程服務器相關的域信息,一般沒人管它都是寫None,后面三個參數分別是 代理服務器,用戶名,密碼
passwdmgr.add_password(None, proxyserver, user, passwd)
#3. 構建一個基礎用戶名/密碼驗證的ProxyBasicAuthHandler處理器對象,參數是創建的密碼管理對象
#注意:這里不再使用普通的ProxyHandler累了。
proxyauth_handler = urllib2.ProxyBasicAuthHandler(passwdmgr)
#4. 通過build_opener()方法使用代理handler對象,創建自定義opener對象,參數包括構建的proxyauth_handler
opener = urllib2.build_opener(proxyauth_handler)
#5.構建request請求
request = urllib2.Request("http://www.baidu.com/")
#6.使用自定義的opener發送請求
response = opener.open(request)
#7.打印響應內容
print(response.read())
HTTPBasicAuthHandler處理器(Web客戶端授權驗證)
有些Web服務器(包括HTTP/FTP等)訪問時,需要進行用戶身份驗證,爬蟲直接訪問會報HTTP 401錯誤,表示訪問身份未經授權:
urllib2.HTTPError:HTTP Error 401:Unauthorized
如果我們有客戶端的用戶名和密碼,我們可以通過下面的方法去訪問爬取:
# -*- coding:utf-8 -*-
import urllib
import urllib2
#用戶名
user = "test"
#密碼
passwd = "123456"
#web服務器IP
webserver = "18.123.123.1:16354"
#1. 構建一個用戶密碼管理對象,用來保存需要處理的用戶密碼
passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
#2. 添加賬戶信息,第一個參數是realm與遠程服務器相關的域消息,一般沒人管都是寫None,后面三個參數分別是服務器,用戶名,密碼
passmgr.add_password(None, webserver, user, passwd)
#3.構建一個Http基礎用戶名/密碼驗證的HTTPBasicAuthHandler處理器對象,參數是創建的密碼管理對象
httpauth_handler = urllib2.HTTPBasicAuthHandler(passmgr)
#4.通過build_opener()方法使用這些代理handler對象,創建自定義的opener對象,參數是創建的httpauth_handler
opener = urllib2.build_opener(httpauth_handler)
#5.可以選擇通過install_opener()方法定義全局opener
urllib2.install_opener(opener)
#6.構建request對象
request = urllib2.Request("http://www.baidu.com/")
#7.定義opener為全局opener后,可直接使用urlopen()請求
response = urllib2.urlopen(request)
#8.打印回應
print(response.read())
Cookie
Cookie是指某些網站服務器為了辯護用戶身份和進行Session,而存儲在用戶瀏覽器上的文本文件,Cookie可以保持登陸信息到用戶下次與服務器的會話。
Cookie原理
HTTP是無狀態的面向連接的協議,為了保持連接狀態,引入了Cookie機制,Cookie是http消息頭中的一種屬性,包括:
Cookie名字(Name)
Cookie的值(Value)
Cookie的過期時間(Expires/Max-Age)
Cookie的作用路徑(Path)
Cookie所在域名(Domain),
使用Cookie進行安全連接(Secure)。
前兩個參數是Cookie應用的必要條件,另外,還保活Cookie大小(size, 不同瀏覽器對Cookie個數及大小限制是有差異的)
Cookie由變量名和值組成,根據Netscape公司的規定,Cookie格式如下:
Set-Cookie:NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
Cookie應用
Cookies在爬蟲方面最典型的應用是判斷注冊用戶是否已經登錄網站,用戶可能會得到提示,是否在下一次進入此網站時保留用戶信息以便簡化登錄手續。
#-*- coding:utf-8 -*-
#16.urllib2_cookie.py
#獲取一個有登陸信息的Cookie模擬登陸
import urllib2
#1.構建一個已經登陸過的用戶的headers信息
headers = {
"Host":"www.renren.com",
"Connection":"keep-alive",
"Upgrade-Insecure-Requests":"1",
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language":"zh-CN,zh;q=0.8,en;q=0.6",
# 便於終端閱讀,表示不支持壓縮文件
# Accept-Encoding: gzip, deflate, sdch,
# 重點:這個Cookie是保存了密碼無需重復登錄的用戶的Cookie,這個Cookie里記錄了用戶名,密碼(通常經過RAS加密)
"Cookie": "anonymid=ixrna3fysufnwv; depovince=GW; _r01_=1; JSESSIONID=abcmaDhEdqIlM7riy5iMv; jebe_key=f6fb270b-d06d-42e6-8b53-e67c3156aa7e%7Cc13c37f53bca9e1e7132d4b58ce00fa3%7C1484060607478%7C1%7C1484060607173; jebecookies=26fb58d1-cbe7-4fc3-a4ad-592233d1b42e|||||; ick_login=1f2b895d-34c7-4a1d-afb7-d84666fad409; _de=BF09EE3A28DED52E6B65F6A4705D973F1383380866D39FF5; p=99e54330ba9f910b02e6b08058f780479; ap=327550029; first_login_flag=1; ln_uact=mr_mao_hacker@163.com; ln_hurl=http://hdn.xnimg.cn/photos/hdn521/20140529/1055/h_main_9A3Z_e0c300019f6a195a.jpg; t=214ca9a28f70ca6aa0801404dda4f6789; societyguester=214ca9a28f70ca6aa0801404dda4f6789; id=327550029; xnsid=745033c5; ver=7.0; loginfrom=syshome"
}
#2.通過headers里的報頭信息(主要是Cookie信息),構建request對象
request = urllib2.Request("http://www.renren.com", headers = hreaders)
#3.直接訪問人人主頁,服務器會根據headers信息(主要是Cookie信息),判斷是否是一個已經登陸的用戶,並返回相應的頁面
response = urllib2.urlopen(request)
#4.打印響應內容
print(response.read())
但是這樣做太過復雜,我們先需要在瀏覽器登錄賬戶,並且設置保存密碼,並且通過抓包才能獲取這個Cookie,那么有更簡單方便的方法呢?
cookielib庫 和 HTTPCookieProcessor處理器
在Python處理Cookie,一般是通過cookielib
模塊和urllib2模塊的HTTPCookieProcessor
處理器一起使用
cookielib
模塊:主要作用是提供用戶存儲cookie的對象
HTTPCoolieProcessor
處理器:主要作用是處理這些cookie對象,並構建handler對象。
cookielib庫
該模塊主要的對象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。
- CookieJar:管理HTTP cookie值、存儲HTTP請求生成的cookie、向傳出的HTTP請求添加cookie的對象。整個cookie都存儲在內存中,對CookieJar實例進行垃圾回收后cookie也將丟失。
- FileCookieJar(filename,delayload=None,policy=None):從CookieJar派生而來,用來創建FileCookieJar實例,檢索cookie信息並將cookie存儲到文件中。filename是存儲cookie的文件名。delayload為True時支持延遲訪問文件,即只有在需要時才讀取文件或在文件中存儲數據。
- MozillaCookieJar(filename, delayload=None, policy=None):從FileCookieJar派生而來,創建與Mozilla瀏覽器 cookies.txt兼容的FileCookieJar實例。
- LWPCookieJar(filename, delay=None, policy=None):從FileCookieJar派生而來,創建與libwww-perl標准的Set-Cookie3文件格式兼容的FileCookieJar實例。
其實大多數情況下,我們只用CookieJar(),如果需要和本地文件交互,就用MozillaCookieJar()或LWPCookieJar()
我們來做幾個案例:
- 獲取Cookie,並保存到CookieJar()對象中
#-*- coding:utf-8 -*-
#18.urllib2_cookielibtest1.py
import cookielib
import urllib2
#構建一個CookieJar對象實力來保存cookie
cookiejar = cookielib.CookieJar()
#使用HTTPCookieProcessor()來創建cookie處理器對象,參數為CookieJar()對象
handler = urllib2.HTTPCookieProcessor(cookiejar)
#通過build_opener()來構建opener
opener = urllib2.build_opener(handler)
#以get方式訪問頁面,訪問之后會自動保存cookie到cookiejar中
opener.open("http://www.baidu.com")
###可以按照標准格式將保存的cookie打印出來
cookieStr = ""
for item in cookiejar:
cookieStr = cookieStr + item.name + "=" +item.value+";"
##舍去最后一位的分號
print(cookieStr[:-1])
我們使用以上方法將Cookie保存到cookiejar對象中,然后打印出了cookie中的值,也就是訪問百度首頁的Cookie值。
運行結果如下:
BAIDUID=985AC680AE8947E7281186821669B597:FG=1;BIDUPSID=985AC680AE8947E7281186821669B597;H_PS_PSSID=1462_22533_21082_17001_25083_22157;PSTM=1511226559;BDSVRTM=0;BD_HOME=0
- 訪問網站獲得cookie,並把獲得的cookie保存在cookie文件中
#-*- coding:utf-8 -*-
#19.urllib2_cookielibtest2.py
import cookielib
import urllib2
#保存cookie的本地磁盤文件名
filename = "cookie.txt"
#聲明一個MozillaCookieJar(有save實現)對象實例來保存cookie,之后寫入文件
cookiejar = cookielib.MozillaCookieJar(filename)
#使用HTTPCookieProcessor()創建cookie處理器對象,參數為cookieJar()對象
handler = urllib2.HTTPCookieProcessor(cookiejar)
#通過build_opener()對象來構建opener
opener = build_opener(handler)
#創建一個請求,原理同urllib2的urlopen
response = opener.open("http://www.baidu.com")
#保存cookie到本地文件
cookiejar.save()
- 從文件中獲取cookies,做出請求的一部分去訪問
# urllib2_cookielibtest2.py
import cookielib
import urllib2
# 創建MozillaCookieJar(有load實現)實例對象
cookiejar = cookielib.MozillaCookieJar()
# 從文件中讀取cookie內容到變量
cookie.load('cookie.txt')
# 使用HTTPCookieProcessor()來創建cookie處理器對象,參數為CookieJar()對象
handler = urllib2.HTTPCookieProcessor(cookiejar)
# 通過 build_opener() 來構建opener
opener = urllib2.build_opener(handler)
response = opener.open("http://www.baidu.com")
利用cookielib和post登陸人人網
import urllib
import urllib2
import cookielib
# 1. 構建一個CookieJar對象實例來保存cookie
cookie = cookielib.CookieJar()
# 2. 使用HTTPCookieProcessor()來創建cookie處理器對象,參數為CookieJar()對象
cookie_handler = urllib2.HTTPCookieProcessor(cookie)
# 3. 通過 build_opener() 來構建opener
opener = urllib2.build_opener(cookie_handler)
# 4. addheaders 接受一個列表,里面每個元素都是一個headers信息的元祖, opener將附帶headers信息
opener.addheaders = [("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36")]
# 5. 需要登錄的賬戶和密碼
data = {"email":"mr_mao_hacker@163.com", "password":"alaxxxxxime"}
# 6. 通過urlencode()轉碼
postdata = urllib.urlencode(data)
# 7. 構建Request請求對象,包含需要發送的用戶名和密碼
request = urllib2.Request("http://www.renren.com/PLogin.do", data = postdata)
# 8. 通過opener發送這個請求,並獲取登錄后的Cookie值,
opener.open(request)
# 9. opener包含用戶登錄后的Cookie值,可以直接訪問那些登錄后才可以訪問的頁面
response = opener.open("http://www.renren.com/410043129/profile")
# 10. 打印響應內容
print response.read()
模擬登陸要注意幾點:
- 登陸一般都會先有一個HTTP GET,用於拉取一些信息及獲得Cookie,然后再HTTP POST登陸
- HTTP POST登陸的鏈接有可能是動態的,從GET返回的信息中獲取。
- password有些是明文發送給,有些是加密后發送。有些網站甚至采用動態加密的,同時包括了很多其他數據的加密信息,只能通過查看JS源碼獲得加密算法,再去破解加密,非常困難。
- 大多數網站的登陸整體流程是類似的,可能有些細節一些,所以不能保證其他網站登陸成功。
這個測試案例中,為了讓大家快速理解知識點,我們使用的人人網登陸接口是人人網改版前的隱藏接口(噓...),登陸比較方便。
當然,我們也可以直接發送賬號密碼到登陸界面模擬登陸,但是當網頁采用JavaScript動態技術以后,想封鎖基於HttpClient的模擬登陸就太容易了,甚至可以根據你的鼠標活動的的特征准確地判斷出是不是真人在操作。
所以,想做通用的模擬登陸還是選別的技術,比如用內置瀏覽器引擎的爬蟲(關鍵詞:Selenium, PyantomJS),這個我們將在以后會學習到。