您的位置:首頁>科技>正文

一名合格的資料分析師分享Python網路爬蟲二三事(上)

一 前言

作為一名合格的資料分析師, 其完整的技術知識體系必須貫穿資料獲取、資料存儲、資料提取、資料分析、資料採擷、資料視覺化等各大部分。

在此作為初出茅廬的資料小白, 我將會把自己學習資料科學過程中遇到的一些問題記錄下來, 以便後續的查閱, 同時也希望與各路同學一起交流、一起進步。 剛好前段時間學習了Python網路爬蟲, 在此將網路爬蟲做一個總結。

二 何為網路爬蟲?

2.1 爬蟲場景

我們先自己想像一下平時到天貓商城購物(PC端)的步驟, 可能就是:

打開流覽器==》搜索天貓商城==》點選連結進入天貓商城==》選擇所需商品類目(站內搜索)==》流覽商品(價格、詳情參數、評論等)==》點選連結==》進入下一個商品頁面......

這樣子周而復始。 當然這其中的搜索也是爬蟲的應用之一。 簡單講, 網路爬蟲是類似又區別於上述場景的一種程式。

2.2 爬蟲分類

分類與關係

一般最常用的爬蟲類型主要有通用爬蟲和聚焦爬蟲, 其中聚焦爬蟲又分為淺聚焦與深聚焦, 三者關係如下圖:

區別

通用爬蟲與聚焦爬蟲的區別就在有沒有對資訊進行過濾以儘量保證只抓取與主題相關的網頁資訊。

聚焦爬蟲過濾方法

淺聚焦爬蟲

選取符合目標主題的種子URL, 例如我們定義抓取的資訊為招聘資訊, 我們便可將招聘網站的URL(拉勾網、大街網等)作為種子URL, 這樣便保證了抓取內容與我們定義的主題的一致性。

深聚焦爬蟲

一般有兩種, 一是針對內容二是針對URL。 其中針對內容的如頁面中絕大部分超連結都是帶有錨文本的, 我們可以根據錨文本進行篩選;針對URL的如現有連結http://geek.csdn.net/news/detail/126572 , 該連結便向我們透漏主題是新聞(news)。

2.3 爬蟲原理

總的來說, 爬蟲就是從種子URL開始, 通過 HTTP 請求獲取頁面內容, 並從頁面內容中通過各種技術手段解析出更多的 URL, 遞迴地請求獲取頁面的程式網路爬蟲, 總結其主要原理如下圖(其中紅色為聚焦爬蟲相對通用爬蟲所需額外進行步驟):

2.4 爬蟲應用

網路爬蟲可以做的事情很多, 如以下列出:

搜尋引擎

採集資料(金融、商品、競品等)

廣告過濾

……

其實就我們個人興趣, 學完爬蟲我們可以看看當當網上哪種技術圖書賣得比較火(銷量、評論等資訊)、看某個線上教育網站哪門網路課程做得比較成功、看雙十一天貓的活動情況等等, 只要我們感興趣的資料, 一般的話都可以爬取得到, 不過有些網站比較狡猾, 設置各種各樣的反扒機制。 總而言之, 網路爬蟲可以説明我們做很多有趣的事情。

三 網路爬蟲基礎

個人建議本章除3.3以外, 其他內容可以大致先看一下, 有些許印象即可, 等到後面已經完成一些簡單爬蟲後或者在寫爬蟲過程中遇到一些問題再回頭來鞏固一下,

這樣子或許更有助於我們進一步網路理解爬蟲。

3.1 HTTP協定

HTTP 協定是爬蟲的基礎, 通過封裝 TCP/IP 協定連結, 簡化了網路請求的流程, 使得使用者不需要關注三次握手, 丟包超時等底層交互。

3.2 前端技術

作為新手, 個人覺得入門的話懂一點HTML與JavaScript就可以實現基本的爬蟲項目, HTML主要協助我們處理靜態頁面, 而實際上很多資料並不是我們簡單的右擊查看網頁源碼便可以看到的, 而是存在JSON(JavaScript Object Notation)檔中, 這時我們便需要採取抓包分析, 詳見《5.2 爬取基於Ajax技術網頁數據》。

3.3 規則運算式與XPath

做爬蟲必不可少的步驟便是做解析。 規則運算式是文本匹配提取的利器, 並且被各種語言支援。 XPath即為XML路徑語言, 類似Windows的檔路徑, 區別就在XPath是應用在網頁頁面中來定位我們所需內容的精確位置。

四 網路爬蟲常見問題

4.1爬蟲利器——python

Python 是一種十分便利的指令碼語言,廣泛被應用在各種爬蟲框架。Python提供了如urllib、re、json、pyquery等模組,同時前人又利用Python造了許許多多的輪,如Scrapy框架、PySpider爬蟲系統等,所以做爬蟲Python是一大利器。

說明:本章開發環境細節如下

系統環境:windows 8.1

開發語言:Python3.5

開發工具:Spyder、Pycharm

輔助工具:Chrome流覽器

4.2 編碼格式

Python3中,只有Unicode編碼的為str,其他編碼格式如gbk,utf-8,gb2312等都為bytes,在編解碼過程中位元組bytes通過解碼方法decode()解碼為字串str,然後字串str通過編碼方法encode()編碼為位元組bytes,關係如下圖:

4.2.1 實戰——爬取當當網

爬取網頁

In [5]:import urllib.request

...:data = urllib.request.urlopen("http://www.dangdang.com/").read()

#爬取的data中的

查看編碼格式

In [5]:import chardet

...:chardet.detect(data)

Out[5]: {'confidence': 0.99, 'encoding': 'GB2312'}

可知爬取到的網頁是GB2312編碼,這是漢字的國標碼,專門用來表示漢字。

解碼

In [5]:decodeData = data.decode("gbk")

#此時bytes已經解碼成str,

重編碼

dataEncode = decodeData.encode("utf-8","ignore")

#重編碼結果

4.3 超時設置

允許超時

data = urllib.request.urlopen(“http://www.dangdang.com/”,timeout=3).read()

執行緒推遲(單位為秒)

import timetime.sleep(3)

4.4 異常處理

每個程式都不可避免地要進行異常處理,爬蟲也不例外,假如不進行異常處理,可能導致爬蟲程式直接崩掉。

網路爬蟲中處理異常的種類與關係

URLError

通常,URLError在沒有網路連接(沒有路由到特定伺服器),或者伺服器不存在的情況下產生。

HTTPError

首先我們要明白伺服器上每一個HTTP 應答物件response都包含一個數位“狀態碼”,該狀態碼表示HTTP協定所返回的回應的狀態,這就是HTTPError。比如當產生“404 Not Found”的時候,便表示“沒有找到對應頁面”,可能是輸錯了URL位址,也可能IP被該網站遮罩了,這時便要使用代理IP進行爬取資料,關於代理IP的設定我們下面會講到。

兩者關係

兩者是父類與子類的關係,即HTTPError是URLError的子類,HTTPError有異常狀態碼與異常原因,URLError沒有異常狀態碼。所以,我們在處理的時候,不能使用URLError直接代替HTTPError。同時,Python中所有異常都是基類Exception的成員,所有異常都從此基類繼承,而且都在exceptions模組中定義。如果要代替,必須要判斷是否有狀態碼屬性。

Python中有一套異常處理機制語法

try-except語句

try:

blockexcept Exception as e:

blockelse:

block

try 語句:捕獲異常

except語句:處理不同的異常,Exception是異常的種類,在爬蟲中常見如上文所述。

e:異常的資訊,可供後面列印輸出

else: 表示若沒有發生異常,當try執行完畢之後,就會執行else

try-except-finally語句

try:

block except Exception as e:

blockfinally:

block

假如try沒有捕獲到錯誤資訊,則直接跳過except語句轉而執行finally語句,其實無論是否捕獲到異常都會執行finally語句,因此一般我們都會將一些釋放資源的工作放到該步中,如關閉檔案控制代碼或者關閉資料庫連接等。

實戰——爬取CSDN博客

#(1)可捕獲所有異常類型import urllib.requestimport urllib.errorimport tracebackimport systry:

urllib.request.urlopen("http://blog.csdn.net")except Exception as er1:

print("異常概要:")

print(er1)

print("---------------------------")

errorInfo = sys.exc_info()

print("異常類型:"+str(errorInfo[0]))

print("異常資訊或參數:"+str(errorInfo[1]))

print("調用棧資訊的物件:"+str(errorInfo[2]))

print("已從堆疊中“輾轉開解”的函數有關的資訊:"+str(traceback.print_exc()))#--------------------------------------------------#(2)捕獲URLErrorimport urllib.requestimport urllib.errortry:

urllib.request.urlopen("http://blog.csdn.net")except urllib.error.URLError as er2:

if hasattr(er2,"code"):

print("URLError異常代碼:")

print(er2.code)

if hasattr(er2,"reason"):

print("URLError異常原因:")

print(er2.reason)#--------------------------------------------------#(3)捕獲HTTPErrorimport urllib.requestimport urllib.errortry:

urllib.request.urlopen("http://blog.csdn.net") except urllib.error. HTTPError as er3:

print("HTTPError異常概要:")

print(er3)

Exception異常捕獲輸出結果如下:

...:

異常概要:

HTTP Error 403: Forbidden

異常類型:

異常資訊或參數:HTTP Error 403: Forbidden

調用棧資訊的物件:

已從堆疊中“輾轉開解”的函數有關的資訊:None

4.5 自動類比HTTP請求

一般用戶端需要通過HTTP請求才能與服務端進行通信,常見的HTTP請求有POST與GET兩種。例如我們打開淘寶網頁後一旦HTML載入完成,流覽器將會發送GET請求去獲取圖片等,這樣子我們才能看到一個完整的動態頁面,假如我們流覽後需要下單那麼還需要向伺服器傳遞登錄資訊。

GET方式

向伺服器發索取資料的一種請求,將請求資料融入到URL之中,資料在URL中可以看到。

POST方式

向伺服器提交資料的一種請求,將資料放置在HTML HEADER內提交。從安全性講,POST方式相對於GET方式較為安全,畢竟GET方式是直接將請求資料以明文的形式展現在URL中。

實戰——登錄CSDN/百度搜索簡書

import urllib.requestimport urllib.parsedef postData():

'''1_POST方式登錄CSDN'''

values={}

values['username'] = "xxx@qq.com" #帳號

values['password']="xxx" #密碼

info = urllib.parse.urlencode(values).encode("utf-8")

url = "http://passport.csdn.net/account/login"

try:

req = urllib.request.Request(url,info)

data = urllib.request.urlopen(req).read()

except Exception as er:

print("異常概要:")

print(er)

return datadef getData():

'''2_GET方式搜索簡書'''

keyword = "簡書" #搜索關鍵字

keyword = urllib.request.quote(keyword)#編碼

url = "http://www.baidu.com/s?wd="+keyword

try:

req = urllib.request.Request(url)

data = urllib.request.urlopen(req).read()

except Exception as er:

print("異常概要:")

print(er)

return data if __name__=="__main__":

print(postData())

print(getData())

4.6 cookies處理

cookies是某些網站為了辨別用戶身份、進行session跟蹤而儲存在使用者本地終端上的資料(通常經過加密)。

4.7 流覽器偽裝

原理

流覽器偽裝是防遮罩的方法之一,簡言之,其原理就是在用戶端在向服務端發送的請求中添加報頭資訊,告訴伺服器“我是流覽器”

如何查看用戶端資訊?

通過Chrome流覽器按F12==》選擇Network==》刷新後點擊Name下任一個位址,便可以看到請求報文和相應報文資訊。以下是在百度上搜索簡書的請求報文資訊,在爬蟲中我們只需添加報頭中的User-Agent便可實現流覽器偽裝。

實戰——爬取CSDN博客

在上面的實例中我們已知道對CSDN博客直接進行爬取的時候會返回403錯誤,接下來將我們偽裝成流覽器爬取CSDN博客

'''流覽器偽裝'''import urllib.request

url = "http://blog.csdn.net/"

headers=("User-Agent","Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36")

opener = urllib.request.build_opener() #自訂opener

opener.addheaders = [headers] #添加用戶端信息#urllib.request.install_opener(opener) #如解除注釋,則可以使用方法2try:

data = opener.open(url,timeout=10).read() #打開方法1

#data=urllib.request.urlopen(url).read() #打開方法2except Exception as er:

print("爬取的時候發生錯誤,具體如下:")

print(er)

f = open("F:/spider_ret/csdnTest.html","wb") #創建本地HTML檔

f.write(data) #將首頁內容寫入檔中

f.close()

4.8 代理伺服器

原理

代理伺服器原理如下圖,利用代理伺服器可以很好處理IP限制問題。

個人認為IP限制這一點對爬蟲的影響是很大的,畢竟我們一般不會花錢去購買正規的代理IP,我們一般都是利用互聯網上提供的一些免費代理IP進行爬取,而這些免費IP的品質殘次不齊,出錯是在所難免的,所以在使用之前我們要對其進行有效性測試。

實戰——代理伺服器爬取百度首頁

import urllib.requestdef use_proxy(url,proxy_addr,iHeaders,timeoutSec):

'''

功能:偽裝成流覽器並使用代理IP防遮罩

@url:目標URL

@proxy_addr:代理IP位址

@iHeaders:流覽器頭資訊

@timeoutSec:超時設置(單位:秒)

'''

proxy = urllib.request.ProxyHandler({"http":proxy_addr})

opener = urllib.request.build_opener(proxy,urllib.request.HTTPHandler)

urllib.request.install_opener(opener)

try:

req = urllib.request.Request(url,headers = iHeaders) #偽裝為流覽器並封裝request

data = urllib.request.urlopen(req).read().decode("utf-8","ignore")

except Exception as er:

print("爬取時發生錯誤,具體如下:")

print(er)

return data

url = "http://www.baidu.com"

proxy_addr = "125.94.0.253:8080"

iHeaders = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0"}

timeoutSec = 10

data = use_proxy(url,proxy_addr,iHeaders,timeoutSec)

print(len(data))

4.9 抓包分析

Ajax(非同步載入)的技術

網站中使用者需求的資料如連絡人清單,可以從獨立於實際網頁的服務端取得並且可以被動態地寫入網頁中。簡單講就是打開網頁,先展現部分內容,再慢慢載入剩下的內容。顯然,這樣的網頁因為不用一次載入全部內容其載入速度特別快,但對於我們爬蟲的話就比較麻煩了,我們總爬不到我們想要的內容,這時候就需要進行抓包分析。

抓包工具

推薦Fiddler與Chrome流覽器

實戰

請轉《5.2 爬取基於Ajax技術網頁數據》。

4.10多執行緒爬蟲

一般我們程式是單執行緒運行,但多執行緒可以充分利用資源,優化爬蟲效率。實際上Python 中的多執行緒並行化並不是真正的並行化,但是多執行緒在一定程度上還是能提高爬蟲的執行效率,下面我們就針對單執行緒和多執行緒進行時間上的比較。

· 實戰——爬取豆瓣科幻電影網頁

'''多執行緒'''import urllibfrom multiprocessing.dummy import Poolimport timedef getResponse(url):

'''獲取回應資訊'''

try:

req = urllib.request.Request(url)

res = urllib.request.urlopen(req)

except Exception as er:

print("爬取時發生錯誤,具體如下:")

print(er)

return resdef getURLs():

'''獲取所需爬取的所有URL'''

urls = []

for i in range(0, 101,20):#每翻一頁其start值增加20

keyword = "科幻"

keyword = urllib.request.quote(keyword)

newpage = "https://movie.douban.com/tag/"+keyword+"?start="+str(i)+"&type=T"

urls.append(newpage)<>

return urls def singleTime(urls):

'''單進程計時'''

time1 = time.time()

for i in urls:

print(i)

getResponse(i)

time2 = time.time()

return str(time2 - time1) def multiTime(urls):

'''多進程計時'''

pool = Pool(processes=4) #開啟四個進程

time3 = time.time()

pool.map(getResponse,urls)

pool.close()

pool.join() #等待進程池中的worker進程執行完畢

time4 = time.time()

return str(time4 - time3) if __name__ == '__main__':

urls = getURLs()

singleTimes = singleTime(urls) #單執行緒計時

multiTimes = multiTime(urls) #多執行緒計時

print('單執行緒耗時 : ' + singleTimes + ' s')

print('多執行緒耗時 : ' + multiTimes + ' s')

· 結果:

單執行緒耗時 : 3.850554943084717 s

多執行緒耗時 : 1.3288819789886475 s

4.11 資料存儲

本地檔(excel、txt)

資料庫(如MySQL)

備註:具體實戰請看5.1

4.12 驗證碼處理

在登錄過程中我們常遇到驗證碼問題,此時我們有必要對其進行處理。

簡單驗證碼識別

利用pytesser識別簡單圖形驗證碼,

複雜驗證碼識別

這相對有難度,可以調用協力廠商介面(如打碼兔)、利用資料採擷演算法如SVM

作者:whenif

原文地址:http://www.jianshu.com/p/0bfd0c48457f#

四 網路爬蟲常見問題

4.1爬蟲利器——python

Python 是一種十分便利的指令碼語言,廣泛被應用在各種爬蟲框架。Python提供了如urllib、re、json、pyquery等模組,同時前人又利用Python造了許許多多的輪,如Scrapy框架、PySpider爬蟲系統等,所以做爬蟲Python是一大利器。

說明:本章開發環境細節如下

系統環境:windows 8.1

開發語言:Python3.5

開發工具:Spyder、Pycharm

輔助工具:Chrome流覽器

4.2 編碼格式

Python3中,只有Unicode編碼的為str,其他編碼格式如gbk,utf-8,gb2312等都為bytes,在編解碼過程中位元組bytes通過解碼方法decode()解碼為字串str,然後字串str通過編碼方法encode()編碼為位元組bytes,關係如下圖:

4.2.1 實戰——爬取當當網

爬取網頁

In [5]:import urllib.request

...:data = urllib.request.urlopen("http://www.dangdang.com/").read()

#爬取的data中的

查看編碼格式

In [5]:import chardet

...:chardet.detect(data)

Out[5]: {'confidence': 0.99, 'encoding': 'GB2312'}

可知爬取到的網頁是GB2312編碼,這是漢字的國標碼,專門用來表示漢字。

解碼

In [5]:decodeData = data.decode("gbk")

#此時bytes已經解碼成str,

重編碼

dataEncode = decodeData.encode("utf-8","ignore")

#重編碼結果

4.3 超時設置

允許超時

data = urllib.request.urlopen(“http://www.dangdang.com/”,timeout=3).read()

執行緒推遲(單位為秒)

import timetime.sleep(3)

4.4 異常處理

每個程式都不可避免地要進行異常處理,爬蟲也不例外,假如不進行異常處理,可能導致爬蟲程式直接崩掉。

網路爬蟲中處理異常的種類與關係

URLError

通常,URLError在沒有網路連接(沒有路由到特定伺服器),或者伺服器不存在的情況下產生。

HTTPError

首先我們要明白伺服器上每一個HTTP 應答物件response都包含一個數位“狀態碼”,該狀態碼表示HTTP協定所返回的回應的狀態,這就是HTTPError。比如當產生“404 Not Found”的時候,便表示“沒有找到對應頁面”,可能是輸錯了URL位址,也可能IP被該網站遮罩了,這時便要使用代理IP進行爬取資料,關於代理IP的設定我們下面會講到。

兩者關係

兩者是父類與子類的關係,即HTTPError是URLError的子類,HTTPError有異常狀態碼與異常原因,URLError沒有異常狀態碼。所以,我們在處理的時候,不能使用URLError直接代替HTTPError。同時,Python中所有異常都是基類Exception的成員,所有異常都從此基類繼承,而且都在exceptions模組中定義。如果要代替,必須要判斷是否有狀態碼屬性。

Python中有一套異常處理機制語法

try-except語句

try:

blockexcept Exception as e:

blockelse:

block

try 語句:捕獲異常

except語句:處理不同的異常,Exception是異常的種類,在爬蟲中常見如上文所述。

e:異常的資訊,可供後面列印輸出

else: 表示若沒有發生異常,當try執行完畢之後,就會執行else

try-except-finally語句

try:

block except Exception as e:

blockfinally:

block

假如try沒有捕獲到錯誤資訊,則直接跳過except語句轉而執行finally語句,其實無論是否捕獲到異常都會執行finally語句,因此一般我們都會將一些釋放資源的工作放到該步中,如關閉檔案控制代碼或者關閉資料庫連接等。

實戰——爬取CSDN博客

#(1)可捕獲所有異常類型import urllib.requestimport urllib.errorimport tracebackimport systry:

urllib.request.urlopen("http://blog.csdn.net")except Exception as er1:

print("異常概要:")

print(er1)

print("---------------------------")

errorInfo = sys.exc_info()

print("異常類型:"+str(errorInfo[0]))

print("異常資訊或參數:"+str(errorInfo[1]))

print("調用棧資訊的物件:"+str(errorInfo[2]))

print("已從堆疊中“輾轉開解”的函數有關的資訊:"+str(traceback.print_exc()))#--------------------------------------------------#(2)捕獲URLErrorimport urllib.requestimport urllib.errortry:

urllib.request.urlopen("http://blog.csdn.net")except urllib.error.URLError as er2:

if hasattr(er2,"code"):

print("URLError異常代碼:")

print(er2.code)

if hasattr(er2,"reason"):

print("URLError異常原因:")

print(er2.reason)#--------------------------------------------------#(3)捕獲HTTPErrorimport urllib.requestimport urllib.errortry:

urllib.request.urlopen("http://blog.csdn.net") except urllib.error. HTTPError as er3:

print("HTTPError異常概要:")

print(er3)

Exception異常捕獲輸出結果如下:

...:

異常概要:

HTTP Error 403: Forbidden

異常類型:

異常資訊或參數:HTTP Error 403: Forbidden

調用棧資訊的物件:

已從堆疊中“輾轉開解”的函數有關的資訊:None

4.5 自動類比HTTP請求

一般用戶端需要通過HTTP請求才能與服務端進行通信,常見的HTTP請求有POST與GET兩種。例如我們打開淘寶網頁後一旦HTML載入完成,流覽器將會發送GET請求去獲取圖片等,這樣子我們才能看到一個完整的動態頁面,假如我們流覽後需要下單那麼還需要向伺服器傳遞登錄資訊。

GET方式

向伺服器發索取資料的一種請求,將請求資料融入到URL之中,資料在URL中可以看到。

POST方式

向伺服器提交資料的一種請求,將資料放置在HTML HEADER內提交。從安全性講,POST方式相對於GET方式較為安全,畢竟GET方式是直接將請求資料以明文的形式展現在URL中。

實戰——登錄CSDN/百度搜索簡書

import urllib.requestimport urllib.parsedef postData():

'''1_POST方式登錄CSDN'''

values={}

values['username'] = "xxx@qq.com" #帳號

values['password']="xxx" #密碼

info = urllib.parse.urlencode(values).encode("utf-8")

url = "http://passport.csdn.net/account/login"

try:

req = urllib.request.Request(url,info)

data = urllib.request.urlopen(req).read()

except Exception as er:

print("異常概要:")

print(er)

return datadef getData():

'''2_GET方式搜索簡書'''

keyword = "簡書" #搜索關鍵字

keyword = urllib.request.quote(keyword)#編碼

url = "http://www.baidu.com/s?wd="+keyword

try:

req = urllib.request.Request(url)

data = urllib.request.urlopen(req).read()

except Exception as er:

print("異常概要:")

print(er)

return data if __name__=="__main__":

print(postData())

print(getData())

4.6 cookies處理

cookies是某些網站為了辨別用戶身份、進行session跟蹤而儲存在使用者本地終端上的資料(通常經過加密)。

4.7 流覽器偽裝

原理

流覽器偽裝是防遮罩的方法之一,簡言之,其原理就是在用戶端在向服務端發送的請求中添加報頭資訊,告訴伺服器“我是流覽器”

如何查看用戶端資訊?

通過Chrome流覽器按F12==》選擇Network==》刷新後點擊Name下任一個位址,便可以看到請求報文和相應報文資訊。以下是在百度上搜索簡書的請求報文資訊,在爬蟲中我們只需添加報頭中的User-Agent便可實現流覽器偽裝。

實戰——爬取CSDN博客

在上面的實例中我們已知道對CSDN博客直接進行爬取的時候會返回403錯誤,接下來將我們偽裝成流覽器爬取CSDN博客

'''流覽器偽裝'''import urllib.request

url = "http://blog.csdn.net/"

headers=("User-Agent","Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36")

opener = urllib.request.build_opener() #自訂opener

opener.addheaders = [headers] #添加用戶端信息#urllib.request.install_opener(opener) #如解除注釋,則可以使用方法2try:

data = opener.open(url,timeout=10).read() #打開方法1

#data=urllib.request.urlopen(url).read() #打開方法2except Exception as er:

print("爬取的時候發生錯誤,具體如下:")

print(er)

f = open("F:/spider_ret/csdnTest.html","wb") #創建本地HTML檔

f.write(data) #將首頁內容寫入檔中

f.close()

4.8 代理伺服器

原理

代理伺服器原理如下圖,利用代理伺服器可以很好處理IP限制問題。

個人認為IP限制這一點對爬蟲的影響是很大的,畢竟我們一般不會花錢去購買正規的代理IP,我們一般都是利用互聯網上提供的一些免費代理IP進行爬取,而這些免費IP的品質殘次不齊,出錯是在所難免的,所以在使用之前我們要對其進行有效性測試。

實戰——代理伺服器爬取百度首頁

import urllib.requestdef use_proxy(url,proxy_addr,iHeaders,timeoutSec):

'''

功能:偽裝成流覽器並使用代理IP防遮罩

@url:目標URL

@proxy_addr:代理IP位址

@iHeaders:流覽器頭資訊

@timeoutSec:超時設置(單位:秒)

'''

proxy = urllib.request.ProxyHandler({"http":proxy_addr})

opener = urllib.request.build_opener(proxy,urllib.request.HTTPHandler)

urllib.request.install_opener(opener)

try:

req = urllib.request.Request(url,headers = iHeaders) #偽裝為流覽器並封裝request

data = urllib.request.urlopen(req).read().decode("utf-8","ignore")

except Exception as er:

print("爬取時發生錯誤,具體如下:")

print(er)

return data

url = "http://www.baidu.com"

proxy_addr = "125.94.0.253:8080"

iHeaders = {"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0"}

timeoutSec = 10

data = use_proxy(url,proxy_addr,iHeaders,timeoutSec)

print(len(data))

4.9 抓包分析

Ajax(非同步載入)的技術

網站中使用者需求的資料如連絡人清單,可以從獨立於實際網頁的服務端取得並且可以被動態地寫入網頁中。簡單講就是打開網頁,先展現部分內容,再慢慢載入剩下的內容。顯然,這樣的網頁因為不用一次載入全部內容其載入速度特別快,但對於我們爬蟲的話就比較麻煩了,我們總爬不到我們想要的內容,這時候就需要進行抓包分析。

抓包工具

推薦Fiddler與Chrome流覽器

實戰

請轉《5.2 爬取基於Ajax技術網頁數據》。

4.10多執行緒爬蟲

一般我們程式是單執行緒運行,但多執行緒可以充分利用資源,優化爬蟲效率。實際上Python 中的多執行緒並行化並不是真正的並行化,但是多執行緒在一定程度上還是能提高爬蟲的執行效率,下面我們就針對單執行緒和多執行緒進行時間上的比較。

· 實戰——爬取豆瓣科幻電影網頁

'''多執行緒'''import urllibfrom multiprocessing.dummy import Poolimport timedef getResponse(url):

'''獲取回應資訊'''

try:

req = urllib.request.Request(url)

res = urllib.request.urlopen(req)

except Exception as er:

print("爬取時發生錯誤,具體如下:")

print(er)

return resdef getURLs():

'''獲取所需爬取的所有URL'''

urls = []

for i in range(0, 101,20):#每翻一頁其start值增加20

keyword = "科幻"

keyword = urllib.request.quote(keyword)

newpage = "https://movie.douban.com/tag/"+keyword+"?start="+str(i)+"&type=T"

urls.append(newpage)<>

return urls def singleTime(urls):

'''單進程計時'''

time1 = time.time()

for i in urls:

print(i)

getResponse(i)

time2 = time.time()

return str(time2 - time1) def multiTime(urls):

'''多進程計時'''

pool = Pool(processes=4) #開啟四個進程

time3 = time.time()

pool.map(getResponse,urls)

pool.close()

pool.join() #等待進程池中的worker進程執行完畢

time4 = time.time()

return str(time4 - time3) if __name__ == '__main__':

urls = getURLs()

singleTimes = singleTime(urls) #單執行緒計時

multiTimes = multiTime(urls) #多執行緒計時

print('單執行緒耗時 : ' + singleTimes + ' s')

print('多執行緒耗時 : ' + multiTimes + ' s')

· 結果:

單執行緒耗時 : 3.850554943084717 s

多執行緒耗時 : 1.3288819789886475 s

4.11 資料存儲

本地檔(excel、txt)

資料庫(如MySQL)

備註:具體實戰請看5.1

4.12 驗證碼處理

在登錄過程中我們常遇到驗證碼問題,此時我們有必要對其進行處理。

簡單驗證碼識別

利用pytesser識別簡單圖形驗證碼,

複雜驗證碼識別

這相對有難度,可以調用協力廠商介面(如打碼兔)、利用資料採擷演算法如SVM

作者:whenif

原文地址:http://www.jianshu.com/p/0bfd0c48457f#

Next Article
喜欢就按个赞吧!!!
点击关闭提示