轉載請注明出處http://blog.csdn.net/evankaka
摘要:本文將使用Python3.4爬網頁、爬圖片、自己主動登錄。並對HTTP協議做了一個簡單的介紹。在進行爬蟲之前,先簡單來進行一個HTTP協議的解說。這樣以下再來進行爬蟲就是理解更加清楚。
一、HTTP協議
HTTP是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫。
它的發展是萬維網協會(World Wide Web Consortium)和Internet工作小組IETF(Internet Engineering Task Force)合作的結果,(他們)終於公布了一系列的RFC。RFC 1945定義了HTTP/1.0版本號。當中最著名的就是RFC 2616。RFC 2616定義了今天普遍使用的一個版本號——HTTP 1.1。
HTTP協議(HyperText Transfer Protocol。超文本傳輸協議)是用於從WWWserver傳輸超文本到本地瀏覽器的傳送協議。它能夠使瀏覽器更加高效,使網絡傳輸降低。
它不僅保證計算機正確高速地傳輸超文本文檔。還確定傳輸文檔中的哪一部分。以及哪部分內容首先顯示(如文本先於圖形)等。
HTTP的請求響應模型
HTTP協議永遠都是client發起請求,server回送響應。見下圖:
這樣就限制了使用HTTP協議,無法實如今client沒有發起請求的時候。server將消息推送給client。
HTTP協議是一個無狀態的協議。同一個client的這次請求和上次請求是沒有相應關系。
工作流程
一次HTTP操作稱為一個事務。其工作過程可分為四步:
1)首先客戶機與server須要建立連接。僅僅要單擊某個超級鏈接,HTTP的工作開始。
2)建立連接后。客戶機發送一個請求給server。請求方式的格式為:統一資源標識符(URL)、協議版本號號。后邊是MIME信息包括請求修飾符、客戶機信息和可能的內容。
3)server接到請求后,給予相應的響應信息,其格式為一個狀態行,包括信息的協議版本號號、一個成功或錯誤的代碼,后邊是MIME信息包括server信息、實體信息和可能的內容。
4)client接收server所返回的信息通過瀏覽器顯示在用戶的顯示屏上,然后客戶機與server斷開連接。
假設在以上過程中的某一步出現錯誤,那么產生錯誤的信息將返回到client,有顯示屏輸出。
對於用戶來說。這些過程是由HTTP自己完畢的,用戶僅僅要用鼠標點擊,等待信息顯示就能夠了
請求報頭
請求報頭同意client向server端傳遞請求的附加信息以及client自身的信息。
經常使用的請求報頭
Accept
Accept請求報頭域用於指定client接受哪些類型的信息。eg:Accept:image/gif。表明client希望接受GIF圖象格式的資源。Accept:text/html,表明client希望接受html文本。
Accept-Charset
Accept-Charset請求報頭域用於指定client接受的字符集。eg:Accept-Charset:iso-8859-1,gb2312.假設在請求消息中沒有設置這個域,缺省是不論什么字符集都能夠接受。
Accept-Encoding
Accept-Encoding請求報頭域相似於Accept,可是它是用於指定可接受的內容編碼。eg:Accept-Encoding:gzip.deflate.假設請求消息中沒有設置這個域server假定client對各種內容編碼都能夠接受。
Accept-Language
Accept-Language請求報頭域相似於Accept,可是它是用於指定一種自然語言。eg:Accept-Language:zh-cn.假設請求消息中沒有設置這個報頭域,server假定client對各種語言都能夠接受。
Authorization
Authorization請求報頭域主要用於證明client有權查看某個資源。當瀏覽器訪問一個頁面時,假設收到server的響應代碼為401(未授權),能夠發送一個包括Authorization請求報頭域的請求,要求server對其進行驗證。
Host(發送請求時,該報頭域是必需的)
Host請求報頭域主要用於指定被請求資源的Internet主機和端口號。它通常從HTTP URL中提取出來的,eg:
我們在瀏覽器中輸入:http://www.guet.edu.cn/index.html
瀏覽器發送的請求消息中,就會包括Host請求報頭域。例如以下:
Host:www.guet.edu.cn
此處使用缺省端口號80,若指定了端口號,則變成:Host:www.guet.edu.cn:指定端口號
User-Agent
我們上網登陸論壇的時候,往往會看到一些歡迎信息。當中列出了你的操作系統的名稱和版本號,你所使用的瀏覽器的名稱和版本號,這往往讓非常多人感到非常奇妙,實際上,server應用程序就是從User-Agent這個請求報頭域中獲取到這些信息。User-Agent請求報頭域同意client將它的操作系統、瀏覽器和其他屬性告訴server。只是。這個報頭域沒必要的。假設我們自己編寫一個瀏覽器。不使用User-Agent請求報頭域。那么server端就無法得知我們的信息了。
請求報頭舉例:
GET /form.html HTTP/1.1 (CRLF)
Accept:image/gif,image/x-xbitmap,image/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/* (CRLF)
Accept-Language:zh-cn (CRLF)
Accept-Encoding:gzip,deflate (CRLF)
If-Modified-Since:Wed,05 Jan 2007 11:21:25 GMT (CRLF)
If-None-Match:W/"80b1a4c018f3c41:8317" (CRLF)
User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF)
Host:www.guet.edu.cn (CRLF)
Connection:Keep-Alive (CRLF)
(CRLF)
GET /form.html HTTP/1.1 (CRLF)Accept:image/gif,image/x-xbitmap,image/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/* (CRLF)Accept-Language:zh-cn (CRLF)Accept-Encoding:gzip,deflate (CRLF)If-Modified-Since:Wed,05 Jan 2007 11:21:25 GMT (CRLF)If-None-Match:W/"80b1a4c018f3c41:8317" (CRLF)User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF)Host:www.guet.edu.cn (CRLF)Connection:Keep-Alive (CRLF)(CRLF)
響應報頭
響應報頭同意server傳遞不能放在狀態行中的附加響應信息。以及關於server的信息和對Request-URI所標識的資源進行下一步訪問的信息。
經常使用的響應報頭
Location
Location響應報頭域用於重定向接受者到一個新的位置。Location響應報頭域經常使用在更換域名的時候。
Server
Server響應報頭域包括了server用來處理請求的軟件信息。
與User-Agent請求報頭域是相相應的。以下是
Server響應報頭域的一個樣例:
Server:Apache-Coyote/1.1
WWW-Authenticate
WWW-Authenticate響應報頭域必須被包括在401(未授權的)響應消息中,client收到401響應消息時候,並發送Authorization報頭域請求server對其進行驗證時,服務端響應報頭就包括該報頭域。
eg:WWW-Authenticate:Basic realm="Basic Auth Test!" //能夠看出server對請求資源採用的是基本驗證機制。
二、Python3.4爬蟲編程
1、第一個演示樣例,我們要來進行簡單的爬蟲來爬別人的網頁
#python3.4 爬蟲教程 #一個簡單的演示樣例爬蟲 #林炳文Evankaka(博客:http://blog.csdn.net/evankaka/) import urllib.request url = "http://www.douban.com/" webPage=urllib.request.urlopen(url) data = webPage.read() data = data.decode('UTF-8') print(data) print(type(webPage)) print(webPage.geturl()) print(webPage.info()) print(webPage.getcode())這是爬回來的網頁輸出:
這中間究竟發生了什么事呢?讓我們打開Fiddler來看看吧:
左邊標紅的就表示我們本次訪問成功,為http 200
右邊上方這是python生成 的請求報頭。不清楚看以下:
非常easy的一個報頭。然后再來看看響應回來的html
這里響應回來的就是我們上面在python的idle中打印出來的網頁了!
2、偽裝成瀏覽器來爬網頁
有些網頁,比方登錄的。假設你不是從瀏覽器發起的起求,這就不會給你響應,這時我們就須要自己來寫報頭。然后再發給網頁的server,這時它就以為你就是一個正常的瀏覽器。從而就能夠爬了!
#python3.4 爬蟲教程 #一個簡單的演示樣例爬蟲 #林炳文Evankaka(博客:http://blog.csdn.net/evankaka/) import urllib.request weburl = "http://www.douban.com/" webheader = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'} req = urllib.request.Request(url=weburl, headers=webheader) webPage=urllib.request.urlopen(req) data = webPage.read() data = data.decode('UTF-8') print(data) print(type(webPage)) print(webPage.geturl()) print(webPage.info()) print(webPage.getcode())來看看請求報頭,就是和我們設置的一個樣。
返回的是一樣的:
再來一個復雜一點的請求報頭:
#python3.4 爬蟲教程 #一個簡單的演示樣例爬蟲 #林炳文Evankaka(博客:http://blog.csdn.net/evankaka/) import urllib.request weburl = "http://www.douban.com/" webheader1 = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'} webheader2 = { 'Connection': 'Keep-Alive', 'Accept': 'text/html, application/xhtml+xml, */*', 'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko', #'Accept-Encoding': 'gzip, deflate', 'Host': 'www.douban.com', 'DNT': '1' } req = urllib.request.Request(url=weburl, headers=webheader2) webPage=urllib.request.urlopen(req) data = webPage.read() data = data.decode('UTF-8') print(data) print(type(webPage)) print(webPage.geturl()) print(webPage.info()) print(webPage.getcode())
看看生成 的結果:
返回還是:
3、爬取站點上的圖片
前面我們能夠爬網頁了,下一步我們就能夠批量的自己主動下載該網頁上的各種數據了~,比方。這里我要下載該網頁上的全部圖片
#python3.4 爬蟲教程 #爬取站點上的圖片 #林炳文Evankaka(博客:http://blog.csdn.net/evankaka/) import urllib.request import socket import re import sys import os targetDir = r"D:\PythonWorkPlace\load" #文件保存路徑 def destFile(path): if not os.path.isdir(targetDir): os.mkdir(targetDir) pos = path.rindex('/') t = os.path.join(targetDir, path[pos+1:]) return t if __name__ == "__main__": #程序執行入口 weburl = "http://www.douban.com/" webheaders = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'} req = urllib.request.Request(url=weburl, headers=webheaders) #構造請求報頭 webpage = urllib.request.urlopen(req) #發送請求報頭 contentBytes = webpage.read() for link, t in set(re.findall(r'(http:[^\s]*?這是正在執行的過程:(jpg|png|gif))', str(contentBytes))): #正則表達式查找全部的圖片 print(link) try: urllib.request.urlretrieve(link, destFile(link)) #下載圖片 except: print('失敗') #異常拋出
打開電腦上相應的目錄,然后來看看圖片。這里僅僅是一部分哦!!
。。
真實的網頁上的圖片
4、保存爬取回來的報文
def saveFile(data): save_path = 'D:\\temp.out' f_obj = open(save_path, 'wb') # wb 表示打開方式 f_obj.write(data) f_obj.close() # 這里省略爬蟲代碼 # ... # 爬到的數據放到 dat 變量里 # 將 dat 變量保存到 D 盤下 saveFile(dat)
比方:
#python3.4 爬蟲教程 #一個簡單的演示樣例爬蟲 #林炳文Evankaka(博客:http://blog.csdn.net/evankaka/) import urllib.request def saveFile(data): save_path = 'D:\\temp.out' f_obj = open(save_path, 'wb') # wb 表示打開方式 f_obj.write(data) f_obj.close() weburl = "http://www.douban.com/" webheader1 = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'} webheader2 = { 'Connection': 'Keep-Alive', 'Accept': 'text/html, application/xhtml+xml, */*', 'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko', #'Accept-Encoding': 'gzip, deflate', 'Host': 'www.douban.com', 'DNT': '1' } req = urllib.request.Request(url=weburl, headers=webheader2) webPage=urllib.request.urlopen(req) data = webPage.read() saveFile(data)# 將data變量保存到 D 盤下 data = data.decode('UTF-8') print(data) print(type(webPage)) print(webPage.geturl()) print(webPage.info()) print(webPage.getcode())然后看看D盤:
用NotePad打開:
是對的。網頁已經被爬下來了。
三、Python3.x 自己主動登錄
一般情況下我們輸入郵箱和密碼后,登錄。
來看看。
這就是提交表單的內容
python3.4代碼編寫:
import gzip import re import http.cookiejar import urllib.request import urllib.parse #解壓函數 def ungzip(data): try: # 嘗試解壓 print('正在解壓.....') data = gzip.decompress(data) print('解壓完畢!') except: print('未經壓縮, 無需解壓') return data #獲取_xsrf def getXSRF(data): cer = re.compile('name=\"_xsrf\" value=\"(.*)\"', flags = 0) strlist = cer.findall(data) return strlist[0] #構造文件頭 def getOpener(head): #設置一個cookie處理器,它負責從server下載cookie到本地。而且在發送請求時帶上本地的cookie cj = http.cookiejar.CookieJar() pro = urllib.request.HTTPCookieProcessor(cj) opener = urllib.request.build_opener(pro) header = [] for key, value in head.items(): elem = (key, value) header.append(elem) opener.addheaders = header return opener #構造header,一般header至少要包括一下兩項。這兩項是從抓到的包里分析得出的。 header = { 'Connection': 'Keep-Alive', 'Accept': 'text/html, application/xhtml+xml, */*', 'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko', 'Accept-Encoding': 'gzip, deflate', 'Host': 'www.zhihu.com', 'DNT': '1' } url = 'http://www.zhihu.com/' opener = getOpener(header) op = opener.open(url) data = op.read() data = ungzip(data) # 解壓 _xsrf = getXSRF(data.decode()) #post數據接收和處理的頁面(我們要向這個頁面發送我們構造的Post數據) url += 'login/email' id = '這里寫自己的郵箱' password = '這里寫自己的密碼' #構造Post數據,他也是從抓大的包里分析得出的。 postDict = { '_xsrf':_xsrf, #特有數據。不同站點可能不同 'email': id, 'password': password, 'rememberme': 'y' } #須要給Post數據編碼 postData = urllib.parse.urlencode(postDict).encode() op = opener.open(url, postData) data = op.read() data = ungzip(data) print(data.decode())
來看看結果:
這時執行返回的
發送出去的請求頭
接收回來 的報頭
返回的數據是什么意思呢:
非常easy, 我們轉碼下: