天天看點

Python爬蟲學習之-從零開始

大家好,相信點進來看的小夥伴都對爬蟲非常感興趣,部落客也是一樣的。部落客剛開始接觸爬蟲的時候,就被深深吸引了,因為感覺SO COOL啊!每當敲完代碼後看着一串串資料在螢幕上浮動,感覺很有成就感,有木有?更厲害的是,爬蟲的技術可以應用到很多生活場景中,例如,自動投票啊,批量下載下傳感興趣的文章、小說、視訊啊,微信機器人啊,爬取重要的資料進行資料分析啊,切實的感覺到這些代碼是給自己寫的,能為自己服務,也能為他人服務,是以人生苦短,我選爬蟲。

說實在的,部落客也是個朝九晚五的上班族,學習爬蟲也是利用業餘時間,但就憑着對爬蟲的熱情開始了爬蟲的學習之旅,俗話說嘛,興趣是最好的老師。部落客也是一個小白,開這個公衆号的初衷就是想和大家分享一下我學習爬蟲的一些經驗以及爬蟲的技巧,當然網上也有各種各樣的爬蟲教程都可供大家參考學習,在後面部落客會分享一些開始學習時用到的資源。好了,不廢話了,開始我們的正題。

1. 什麼是爬蟲?

首先應該弄明白一件事,就是什麼是爬蟲,為什麼要爬蟲,部落客百度了一下,是這樣解釋的:

網絡爬蟲(又被稱為網頁蜘蛛,網絡機器人,在FOAF社群中間,更經常的稱為網頁追逐者),是一種按照一定的規則,自動地抓取網際網路資訊的程式或者腳本。另外一些不常使用的名字還有螞蟻、自動索引、模拟程式或者蠕蟲。

其實,說白了就是爬蟲可以模拟浏覽器的行為做你想做的事,訂制化自己搜尋和下載下傳的内容,并實作自動化的操作。比如浏覽器可以下載下傳小說,但是有時候并不能批量下載下傳,那麼爬蟲的功能就有用武之地了。

實作爬蟲技術的程式設計環境有很多種,Java,Python,C++等都可以用來爬蟲。但是部落客選擇了Python,相信很多人也一樣選擇Python,因為Python确實很适合做爬蟲,豐富的第三方庫十分強大,簡單幾行代碼便可實作你想要的功能,更重要的,Python也是資料挖掘和分析的好能手。這樣爬取資料和分析資料一條龍的服務都用Python真的感覺很棒啊!

2. 爬蟲學習路線

知道了什麼是爬蟲,給大家說說部落客總結出的學習爬蟲的基本路線吧,隻供大家參考,因為每個人都有适合自己的方法,在這裡隻是提供一些思路。

學習Python爬蟲的大緻步驟如下:

  • 首先學會基本的Python文法知識
  • 學習Python爬蟲常用到的幾個重要内置庫urllib, http等,用于下載下傳網頁
  • 學習正規表達式re、BeautifulSoup(bs4)、Xpath(lxml)等網頁解析工具
  • 開始一些簡單的網站爬取(部落客從百度開始的,哈哈),了解爬取資料過程
  • 了解爬蟲的一些反爬機制,header,robot,時間間隔,代理ip,隐含字段等
  • 學習一些特殊網站的爬取,解決登入、Cookie、動态網頁等問題
  • 了解爬蟲與資料庫的結合,如何将爬取資料進行儲存
  • 學習應用Python的多線程、多程序進行爬取,提高爬蟲效率
  • 學習爬蟲的架構,Scrapy、PySpider等
  • 學習分布式爬蟲(資料量龐大的需求)

以上便是一個整體的學習概況,好多内容部落客也需要繼續學習,關于提到的每個步驟的細節,部落客會在後續内容中以實戰的例子逐漸與大家分享,當然中間也會穿插一些關于爬蟲的好玩内容。

3. 從第一個爬蟲開始

第一個爬蟲代碼的實作我想應該是從urllib開始吧,部落客開始學習的時候就是使用urllib庫敲了幾行代碼就實作了簡單的爬資料功能,我想大多夥伴們也都是這麼過來的。當時的感覺就是:哇,好厲害,短短幾行竟然就可以搞定一個看似很複雜的任務,于是就在想這短短的幾行代碼到底是怎麼實作的呢,如何進行更進階複雜的爬取呢?帶着這個問題我也就開始了urllib庫的學習。

首先不得不提一下爬取資料的過程,弄清楚這到底是怎樣一個過程,學習urllib的時候會更友善了解。

爬蟲的過程

其實,爬蟲的過程和浏覽器浏覽網頁的過程是一樣的。道理大家應該都明白,就是當我們在鍵盤上輸入網址點選搜尋之後,通過網絡首先會經過DNS伺服器,分析網址的域名,找到了真正的伺服器。然後我們通過HTTP協定對伺服器發出GET或POST請求,若請求成功,我們就得到了我們想看到的網頁,一般都是用HTML, CSS, JS等前端技術來建構的,若請求不成功,伺服器會傳回給我們請求失敗的狀态碼,常見到的503,403等。

爬蟲的過程亦是如此,通過對伺服器送出請求得到HTML網頁,然後對下載下傳的網頁進行解析,得到我們想要的内容。當然,這是一個爬蟲過程的一個概況,其中還有很多細節的東西需要我們處理的,這些在後續會繼續與大家分享。

了解了爬蟲的基本過程後,就可以開始我們真正的爬蟲之旅了。

urllib庫

Python有一個内置的urllib庫,可謂是爬蟲過程非常重要的一部分了。這個内置庫的使用就可以完成向伺服器送出請求并獲得網頁的功能,是以也是學習爬蟲的第一步了。

部落客用的是Python3.x,urllib庫的結構相對于Python2.x有一些出入,Python2.x中使用的urllib2和urllib庫,而Python3.x中合并成一個唯一的urllib庫。

首先,我們來看看Python3.x的urllib庫都有什麼吧。

部落客用的IDE是Pycharm,編輯調試非常友善,很贊。 在控制台下輸入如下代碼:

>>importurllib >>dir(urllib) ['__builtins__','__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__','__path__', '__spec__', 'error', 'parse', 'request', 'response']

可以看到urllib除了以雙下劃線開頭結尾的内置屬性外,還有4個重要的屬性,分别是error,parse,request,response。

在Python的urllib庫中doc開頭是這樣簡短描述的:

  • Error:“Exception classesraised by urllib.”----就是由urllib舉出的exception類
  • Parse:“Parse (absolute andrelative) URLs.”----解析絕對和相對的URLs
  • Request:“An extensiblelibrary for opening URLs using a variety of protocols” ----用各種協定打開URLs的一個擴充庫
  • Response:“Response classesused by urllib.”----被urllib使用的response類

這4個屬性中最重要的當屬request了,它完成了爬蟲大部分的功能,我們先來看看request是怎麼用的。

request的使用

request請求最簡單的操作是用urlopen方法,代碼如下:

import urllib.request response = urllib.request.urlopen('http://python.org/') result = response.read() print(result)

運作結果如下:

b'<!doctype html>\n<!--[if lt IE 7]>...</body>\n</html>\n'

發現得到的運作結果竟然是亂碼!!别着急,這是因為編碼的問題,我們隻需要将請求的類檔案讀取再解碼就可以了。

修改代碼如下:

import urllib.request response = urllib.request.urlopen('http://python.org/') result = response.read().decode('utf-8') print(result)

運作結果如下:

<!doctype html> <!--[if lt IE 7]> <html class="no-js ie6 lt-ie7 lt-ie8>.. <!--[if IE 7]> <html class="no-js ie7 lt-ie8 lt-ie9">.. <!--[if IE 8]> <html class="no-js ie8 lt-ie9"> <![endif]--> <!--[if gt IE 8]><!--><html class="no-js" dir="ltr" <head> <meta charset="utf-8"> ...

得到的就是我們想要的html的網頁了,怎麼樣,簡單吧。

下面來介紹一下這個urlopen方法和其中應用的參數。

urlopen方法

def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TI MEOUT,*, cafile=None, capath=None, cadefault=False, context=None):

urlopen是request的其中一個方法,功能是打開一個URL,URL參數可以是一串字元串(如上例子中一樣),也可以是Request對象(後面會提到)。

  • url:即是我們輸入的url網址,(如:http://www.xxxx.com/);
  • data:是我們要發給伺服器請求的額外資訊(比如登入網頁需要主動填寫的使用者資訊)。如果需要添加data參數,那麼是POST請求,預設無data參數時,就是GET請求;
  • 一般來講,data參數隻有在http協定下請求才有意義
  • data參數被規定為byte object,也就是位元組對象
  • data參數應該使用标準的結構,這個需要使用urllib.parse.urlencode()将data進行 轉換,而一般我們把data設定成字典格式再進行轉換即可;data在以後實戰中會介紹如何使用
  • timeout:是選填的内容,定義逾時時間,機關是秒,防止請求時間過長,不填就是預設的時間;
  • cafile:是指向單獨檔案的,包含了一系列的CA認證 (很少使用,預設即可);
  • capath:是指向文檔目标,也是用于CA認證(很少使用,預設即可);
  • cafile:可以忽略
  • context:設定SSL加密傳輸(很少使用,預設即可);

它會傳回一個類檔案對象,并可以針對這個對象進行各種操作(如上例中的read操作,将html全部讀出來),其它常用方法還有:

  • geturl(): 傳回URL,用于看是否有重定向。result = response.geturl() 結果: https://www.python.org/
  • info():傳回元資訊,例如HTTP的headers。 result = response.info() 結果: x-xss-protection: 1; mode=block X-Clacks-Overhead: GNU Terry Pratchett ... Vary: Cookie Strict-Transport-Security: max-age=63072000;includeSubDomains
  • getcode():傳回回複的HTTP狀态碼,成功是200,失敗可能是503等,可以用來檢查代理IP的可使用性。result = response.getcode() 結果:200

Request方法

class Request: def __init__(self, url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None):

如上定義,Request是一個類,初始化中包括請求需要的各種參數:

  • url,data和上面urlopen中的提到的一樣。
  • headers是HTTP請求的封包資訊,如User_Agent參數等,它可以讓爬蟲僞裝成浏覽器而不被伺服器發現你正在使用爬蟲。
  • origin_reg_host, unverifiable, method等不太常用

headers很有用,有些網站設有反爬蟲機制,檢查請求若沒有headers就會報錯,是以部落客為保證爬蟲的穩定性,基本每次都會将headers資訊加入進去,這是反爬的簡單政策之一。

那麼如何找到你所在浏覽器的headers呢?

可以通過進入浏覽器F12檢視到 比如,部落客用的Chrome浏覽器,按F12->network就可以檢視request的headers,可以把這個浏覽器的headers資訊複制下來使用。

Python爬蟲學習之-從零開始

下面來看看Request如何使用吧,代碼如下:

import urllib.request headers = {'User_Agent': ''} response = urllib.request.Request('http://python.org/', headers=headers) html = urllib.request.urlopen(response) result = html.read().decode('utf-8') print(result)

結果和前面urlopen是一樣的,前面提到urlopen除了可以接受指定參數,也可以接受Request類的對象。' '裡面填寫自己浏覽器的資訊即可。

urllib庫的requset屬性裡面還有很多其它方法,代理、逾時、認證、HTTP的POST模式下請求等内容将在下次進行分享,這次主要介紹基本功能。

下面來說說異常,urllib庫的error方法。

error的使用

error屬性裡面主要包括了兩個重要的exception類,URLError類和HTTPError類。

1. URLError類

def __init__(self, reason, filename=None): self.args = reason, self.reason = reason if filename is not None: self.filename = filename

  • URLError類是OSError的子類,繼承OSError,沒有自己的任何行為特點,但是将作為error裡面所有其它類型的基類使用。
  • URLError類初始化定義了reason參數,意味着當使用URLError類的對象時,可以檢視錯誤的reason。

2. HTTPErro類

def __init__(self, url, code, msg, hdrs, fp): self.code = code self.msg = msg self.hdrs = hdrs self.fp = fp self.filename = url

  • HTTPError是URLError的子類,當HTTP發生錯誤将舉出HTTPError。
  • HTTPError也是HTTP有效回應的執行個體,因為HTTP協定錯誤是有效的回應,包括狀态碼,headers和body。是以看到在HTTPError初始化的時候定義了這些有效回應的參數。
  • 當使用HTTPError類的對象時,可以檢視狀态碼,headers等。

下面我們用一個例子來看一下如何使用這兩個exception類。

import urllib.request import urllib.error try: headers = {'User_Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0'} response = urllib.request.Request('http://python.org/', headers=headers) html = urllib.request.urlopen(response) result = html.read().decode('utf-8') 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('請求成功通過。')

以上代碼使用了try..exception的結構,實作了簡單的網頁爬取,當有異常時,如URLError發生時,就會傳回reason,或者HTTPError發生錯誤時就會傳回code。異常的增加豐富了爬取的結構,使其更加健壯。

為什麼說更加健壯了呢?

不要小看了這些異常的錯誤,這些異常的錯誤非常好用,也非常關鍵。想想看,當你編寫一個需要不斷自動運作爬取并解析的代碼時,你是不希望程式中間被打斷而終止的。如果這些異常狀态沒有設定好,那麼就很有可能彈出錯誤而被終止,但如果設定好了完整的異常,則遇到錯誤時就會執行發生錯誤的代碼而不被打斷(比如向上面代碼一樣列印錯誤code等)。

這些打斷程式的錯誤可能是很多種,尤其當你使用代理ip池的時候,會發生很多不同錯誤,這時異常就起到作用了。

4. 總結

  • 介紹了爬蟲的定義和學習路線
  • 介紹了爬蟲的過程
  • 介紹開始爬蟲學習的urllib庫的使用,包含以下幾個方法:
  • request請求: urlopen, Request
  • error異常

繼續閱讀