一個簡易的python 爬蟲源碼分析

爬蟲流程

之前沒了解過相關東西,覺得大體流程無非是發送http request, 然後把爬來的數據進行存儲。

讀了一個相關代碼實現後,往深裏鑽,裏面東西還特別多。核心流程還是一樣,但是考慮到效率就會涉及到很多東西。流程方面可以參考這裏

代碼倉庫

網上誰便找了個,代碼量不大,適合學習使用這裏

代碼解讀

類圖

在這裏插入圖片描述

  • 其中WebSpider 是對外的門面類,其中聚合了抓取線程、解析線程、保存結果線程;還聚合了抓取任務隊列、解析任務隊列、保存任務隊列。
  • 其中線程有反過來關聯WebSpider 一次來操作隊列; 線程中有關聯了具體幹活的Worker類。
  • Worker泛化具體的抓取、解析、保存操作。裏面自定義具體操作。抓取裏面進行http請求,解析裏面進行正則匹配, 保存進行輸出重定向。

流程圖

維護了三個任務隊列:Fetch, Parser, Saver 分別代表抓取隊列、解析隊列、保存隊列。
初始

Fetch QueueParser QueueSaver Queue1、http request的結果2、解析html文檔,提取需要的結果3、抓取當前頁面中的其他鏈接Fetch QueueParser QueueSaver Queue
  • 其中,1、2、3步驟都在各自的線程中運行,初始時會在Fetch Queue 中塞入一個初始url,以此保證後續動作的進行。
  • 整個流程由隊列中數據驅動進行,至於線程停止的時機,直到所有任務隊列爲空時,因爲Parser會把解析到的次級url放入Fetch隊列,那麼隊列如何保證可以有爲空的條件呢,這裏Paser幹活類中有個控制抓取url層級的值,當抓取的url層級超過限制的層級後,就不會再往Fetch 隊列中插入了。

關鍵知識

布隆過濾

  • python 引入pybloom_live 包來使用。可以理解爲hashmap的替代,但是在數據量巨大時hashmap 會有空間佔用巨大的問題。 但是布隆過濾器也有缺點,他可以告訴“某樣東西一定不存在或者可能存在”,即無法保證確定的存。
  • 此程序中在Fetch 獲取到新url後會更新過濾器中值。在數據量小時,看不出布隆過濾的優勢。

Queue

此程序的關鍵代碼理解我覺得主要是queue的使用,剖開具體的操作細節,代碼運行流程可以通過Queue 來看明白。
可以看下面一個生產者–消費者問題,代碼中的queue 的使用是一樣的。


def test_multiply_thread_queue():
    num_fetch_threads = 5
    enclosure_queue = Queue()

    def consumer(i, q):
        while True:
            print("consumer begin")
            url = q.get(block=True)
            print('%s: consumer: %s end. %s' % (i, url, time.ctime()))
            # time.sleep(1)
            q.task_done()

    for i in range(num_fetch_threads):
        worker = Thread(target=consumer, args=(i, enclosure_queue))
        worker.setDaemon(True)
        worker.start()

    def producer(i, q):
        itm = 'url {}'.format(i)
        # print('producer: %s begin. %s' % (itm, time.ctime()))
        #time.sleep(i)
        q.put(itm)
        print('producer: %s end. %s' % (itm, time.ctime()))

    num_product_threads = 5
    for i in range(num_product_threads):
        worker = Thread(target=producer, args=(i, enclosure_queue))
        worker.setDaemon(True)
        worker.start()

    # Now wait for the queue to be empty, indicating that we have
    # processed all of the downloads.
    print('*** Main thread waiting')
    enclosure_queue.join()
    print('*** Done')
  • Queue 自身保證線程安全。這裏join 爲’Blocks until all items in the queue have been gotten and processed.’,等待所有任務被取出, 而後往下執行。這裏join的效果可以通過調整consumer和producer中的sleep看出效果。
  • 當生產者執行較快,消費者線程肯定能消費完才結束程序;
    當生產者較慢,消費者較快時,生產者可能沒生產幾個就會退出程序,因爲在enclosure_queue.join()時發現隊列爲空,主線程直接往下執行了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章