【實戰篇】 解析 Python 之父寫的 web crawler 異步爬蟲

python async爬蟲

以下程式碼範例是來自於 Python 之父 Guido van RossumA. Jesse Jiryu Davis 所一起撰寫的 web crawler ,主要是展示如何使用 asyncio module + aiohttp 來寫網頁異步爬蟲。

Authors: A. Jesse Jiryu Davis and Guido van Rossum
Project: Web crawler

This is a web crawler. You give it a URL and it will crawl that website by following href links in the HTML pages.

The point of the example is to show off how to write a reasonably complex HTTP client application using the asyncio module. This module, originally nicknamed Tulip, is new in the Python 3.4 standard library, based on PEP 3156. The example uses an HTTP client implementation for asyncio called “aiohttp”, by Andrew Svetlov, Nikolay Kim, and others.

https://github.com/aosabook/500lines/tree/master/crawler

ㄧ. 將部分程式更新

原始版本 GitHub 位置於此:500lines/crawling.py at master · aosabook/500lines · GitHub

因為 GitHub 更新時間是四年前,clone 下來後,有些程式使用方法需要被更新:

▍Python 3.8+ 移除 urllib.parse.splitport()

urllib.parse.splitport() 將在 Python 3.8 被移除,所以改使用 urlparse 和 hostname 的方法取得 hostname

參考文件:問題 27485: urllib.splitport — is it official or not? – Python tracker

▍Python 3.10+ 即將移除 @asyncio.coroutine

Generator-based coroutine 的方式將在 Python 3.10 中被移除,所以這邊改用 Native coroutine 的方式,使用 Python 3.5+ library 中 async / await 語法來取代 @asyncio.coroutine

參考文件:Coroutines and Tasks — Python 3.8.2 documentation

▍asyncio.get_event_loop

Python 3.7 推出更簡潔的方法,將 event_loop 封裝,使用 asyncio.run() 一行程式就結束,不用在建立 event_loop 結束時也不需要 loop.close。

參考文件:cpython/runners.py at 3.8 · python/cpython · GitHub

二. 開始解析

▍Python 環境配置:

  • Python 3.7+

▍pip install 安裝套件:

▍開始解析

可以看到 Crawler 類內,我使用註解來區分成三個部分

# 解析爬取到的 url 是否符合需求規範
# 將爬取到的 url 放入列隊
# 主要運行的異步函式

▍完整程式如下:

我將程式閱讀步驟 Step 註解寫在程式旁邊,建議由下往上開始閱讀

三. 談談 namedtuple

過去寫爬蟲都是用字典 dict 來處理爬下來的數據,可以看到 Python 之父是使用 namedtuple,所以我們來看看 tuple vs namedtuple vs dict 之間有什麼差別?

1. tuple、namedtuple 與 dict 之間差異?

▍namedtuple vs tuple

在使用 tuple 結構的時候,要訪問 tuple 裡的的其中一個值,需使用他的 index 值來取得,而 index 通常會有可讀性與維護性上的困擾。
namedtuple 不但保有 tuple 不可變動的特性,還解決了 index 的問題,可以使用 name 來訪問其中的值,待會下面範例會再講解。

▍namedtuple vs dict

namedtuple 是一個 immutable object,因此他所需要的空間比字典 dict 還要來的少,但字典對於 key 值的搜尋速度比 namedtuple 快,理想上 python 的字典 dict 在搜尋 key 值的時間複雜度是 O(1),而 namedtuple 基本上還是 tuple 結構,所以其時間複雜度為 O(n),若是較重視空間效率的程式會偏好使用 namedtuple,但搜尋速度和 dict 實在是差太多了

如果是像這次爬蟲範例中,單純僅需要寫入儲存資料的話,使用 namedtuple 確實會比 dict 和 tuple 都還適合。

2. namedtuple 使用方法

▍宣告 namedtuple

▍將 list 轉換成 namedtuple

▍將 dict 轉換成 namedtuple

▍將 namedtuple 轉換成 dict

四. 談談 urllib.parse

可以看到 Python 之父使用很多 urllib 的套件來解析爬取到的 url,所以我們來了解 urllib 解析 url 的方式:

1. urllib.parse 使用方法

▍ urllib.parse.urlparse()

用來解析 url 的 hostname、port、scheme、query 各式參數,這次 urlparse() 主要用來取得 hostname 並判斷是否是內部網址。

▍urllib.parse.urljoin()

爬取到的網址有時後不會有 netloc 的部分,使用 urljoin 來合併 url

▍urllib.parse.urldefrag()

不同的 fragment 其實還是同一個頁面,所以在爬取時會將 fragment 進行處理

最後~

▍關於 Async IO 相關其他文章,可以參考:

▍關於與 Concurrency Programming 相關其他文章,可以參考:

那麼有關於【實戰篇】 解析 Python 之父寫的 web crawler 異步爬蟲 的介紹就到這邊告一個段落囉!有任何問題可以在以下留言~

有關 Max行銷誌的最新文章,都會發佈在 Max 的 Facebook 粉絲專頁,如果想看最新更新,還請您按讚或是追蹤唷!

在〈【實戰篇】 解析 Python 之父寫的 web crawler 異步爬蟲〉中有 1 則留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *