標準爬蟲初探,來自Python之父的大餐!

2021-06-26 23:24:02 字數 3964 閱讀 3883

首先不得不承認自己做了標題黨,本文實質是分析500lines or less的crawl工程,這個工程的位址是有興趣的同學可以看看,是乙個非常高質量的開源工程集合,據說要寫一本書,不過看著**提交記錄,這本書面世時間應該不會很快。這篇文章寫得很渣,錯誤一定要提啊。。。

網路爬蟲從乙個或若干初始網頁的url開始,獲得初始網頁上的url,在抓取網頁的過程中,不斷從當前頁面上抽取新的url放入佇列,直到滿足系統的一定停止條件。簡單的可以將網路爬蟲理解為乙個帶有終止條件的while迴圈,在條件不觸發的情況下,爬蟲就不斷的從每個以及獲取的url傳送請求獲取頁面資料,然後解析當前頁面的url,不斷迭代下去。在crawl工程當中,完成這一過程的是crawler類,他並未採用廣度優先或是深度優先的爬蟲,在當前請求失敗的時候就通過python掛起當前任務,然後在之後再進行排程,這可以勉強理解為基於網路連通性的a*搜尋,其執行方式如下所示:

對乙個初始化後的crawler物件,其中存在乙個url,乙個todo集合,儲存尚未繼續呢爬蟲操作的url;乙個busy集合,儲存等待其他爬蟲資料的url集合;乙個done集合,儲存完成頁面爬取的url集合。爬蟲的核心就是這個死迴圈,首先爬蟲從todo集合當中獲取乙個url,然後初始化fetch物件用於獲取頁面上的url,最後進行任務排程執行乙個url請求任務。這段流程的**如下所示。

1

@asyncio.coroutine

2def

crawl(self):

3"""

run the crawler until all finished.

"""4 with (yield

from

self.termination):

5while self.todo or

self.busy:6if

self.todo:

7 url, max_redirect =self.todo.popitem()

8 fetcher =fetcher(url,

9 crawler=self,

10 max_redirect=max_redirect,

11 max_tries=self.max_tries,12)

13 self.busy[url] =fetcher

14 fetcher.task =asyncio.task(self.fetch(fetcher))

15else:16

yield

from

self.termination.wait()

17 self.t1 = time.time()

乙個爬蟲很明顯不會僅僅由乙個死迴圈構成,在crawl外層需要其他模組支援其操作,包括網路連線,url獲取,任務排程等任務,整個crawl工程的排程框架如下所示:

在crawl建立初始化時候首先建立乙個connectionpool:

self.pool = connectionpool(max_pool, max_tasks)

其中保留屬性connections和queue,分別儲存連線的集合和佇列,用於後續排程;而connection中儲存host和埠號並支援ssl,通過asyncio.open_connection()獲取連線。

self.connections = {} #

self.queue = # [connection, ...]

任務執行時crawl方法首先通過loop.run_until_complete(crawler.crawl())載入到event loop當中,然後用

上述語句構建的鏈結池connectionpool中儲存connection物件,獲取連線物件然後通過

fetcher物件的fetch方法進行資料爬取。

對於乙個url請求任務,使用fetcher進行處理,排程則是用asyncio.task方法進行的排程。

其中fetch方法獲取被掛起的generator,交給asyncio.task執行。

通過yield from和asynico.coroutine語句,將這個方法變為執行過程中的generator,在執行fetcher.fetch()方法時候如果被掛起,則通過排程程式進行處理。

fetcher.fetch()方法是網路爬蟲的核心方法,負責從網路上獲取頁面資料並將其中的url載入到todo集合當中,該方法嘗試獲取頁面資料當嘗試次數達到上限時停止操作,獲取成功的html資料和外部鏈結以及重定向鏈結都將被儲存。在url鏈結次數到達上限的情況下,將停止這個url的鏈結操作,輸出出錯日誌。之後針對頁面的不同狀態,採取不同的處理方式。

下面的**是crawling.py檔案從333行開始(crawling.py)到對應方法結束的區域,通過對頁面status的判斷選擇不同的處理方式。其中通過正規表示式,獲取頁面上的url資訊,這裡選擇為href開頭的字串,核心url提取的**在下面: 

1

#replace href with (?:href|src) to follow image links.

2 self.urls = set(re.findall(r'

(?i)href=["\']?([^\s"\'<>]+)

',body))3if

self.urls:

4 logger.warn('

got %r distinct urls from %r

',len(self.urls), self.url)

5 self.new_urls =set()

6for url in

self.urls:

7 url =unescape(url)

8 url =urllib.parse.urljoin(self.url, url)

9 url, frag =urllib.parse.urldefrag(url)

10if

self.crawler.add_url(url):

11 self.new_urls.add(url)

通過**,很明顯就可以看出正則匹配結果儲存在urls集合當中並通過for迴圈依次進行處理,加入到當前fetcher的crawler物件的todo集合當中。

在之前分析的基礎上對主檔案crawl.py進行進一步分析,可以得到整體爬蟲的架構:

在主檔案當中首先通過argparse.argumentparser進行解析,設定控制台的資料讀取和控制,其中選擇了iocp作為windows環境下的event loop物件。主方法,首先通過parse_args返回儲存命令列資料的字典,如果沒有root屬性,則給出提示。然後配置日誌級別,指示日誌的輸出級別,低於最低級別的不輸出。

通過入口函式main方法進入程式的時候,首先根據來自命令列引數對crawler進行初始化,同時獲取

使用asyncio的loop event物件,執行run_until_complete方法,會一直執行到這個程式結束執行。

除此之外reporting.py用於列印當前任務執**況。其中fetcher_report(fetcher, stats, file=none)列印這個url的工作狀態,url就是fetcher的url屬性;report(crawler, file=none)列印整個工程所有完成的url工作狀態。

標準爬蟲初探,來自Python之父的大餐!

首先不得不承認自己做了標題黨,本文實質是分析500lines or less的crawl工程,這個工程的位址是有興趣的同學可以看看,是乙個 非常高質量的開源工程集合,據說要寫一本書,不過看著 提交記錄,這本書面世時間應該不會很快。這篇文章寫得很渣,錯誤一定要提啊。網路爬蟲從乙個或若干初始網頁的 ur...

標準爬蟲初探,來自Python之父的大餐!

首先不得不承認自己做了標題黨,本文實質是分析500lines or less的crawl工程,這個工程的位址是有興趣的同學可以看看,是乙個非常高質量的開源工程集合,據說要寫一本書,不過看著 提交記錄,這本書面世時間應該不會很快。這篇文章寫得很渣,錯誤一定要提啊。網路爬蟲從乙個或若干初始網頁的url開...

標準爬蟲初探,來自Python之父的大餐!

首先不得不承認自己做了標題黨。本文實質是分析500lines or less的crawlproject,這個project的位址是有興趣的同學能夠看看。是乙個非常高質量的開源project集合,據說要寫一本書,只是看著 提交記錄。這本書面世時間應該不會非常快。這篇文章寫得非常渣,錯誤一定要提啊。網路...