1. 程式人生 > >Python爬蟲-速度(3)

Python爬蟲-速度(3)

Python爬蟲-速度(3)

文章目錄


018.11.11

Python爬蟲-速度(1)
Python爬蟲-速度(2)

前言

早之前是以為會一口氣把爬蟲這個系列了結的,但不知何故,居然沒做到——我肯定不會怪罪自己的拖延症呀!只是有開頭就得有結尾,畢竟我是那麼那麼注重儀式感的人。

再從GitHub把程式碼clone下來,發現不能用了。刨根問底,居然是學校就業網站改版,這倒令我驚奇。會是因為哪個老師或者學生確實無聊所以動了“美好校園”的念頭?緣由於我來說肯定是不得而知的。

重寫一份吧。結構上相比兩個月前的程式碼是有所優化的,但不至於天壤之別。相似處多,所以不再單獨對此次基礎程式碼進行說明。

此篇目的是:基於(2)的提速方法,改寫(1)爬蟲程式碼。當然了,因為網頁改版,接下來我會是基於新的程式碼來改寫。

那就開始啦。

普通爬蟲

此次程式碼對應目錄:NewSchoolJobsCrawl

效果如下:在這裡插入圖片描述

在這裡插入圖片描述

多次執行,整個程式跑完在60s左右。
在這裡插入圖片描述

多程序提速

在ProcessPoolExecutor助攻下,多程序爬蟲的實現真的會很簡單。我們只需要把瀏覽頁處理的邏輯提取出來(我把它放到了main()中),然後構建一個程序池去執行這部分的程式碼就好。這樣說會很抽象,將下面的程式碼與普通爬蟲的程式碼做對比,理解起來容易些。

def main(url):
    html, __ = get_one_html(url)
    if html:
        for title, url in extract_title_url(html):
            # 只保留公司名
            companyName = re.split(r"(|\(|\s", title)[0]
            mkdir(companyName)
            # 詳情頁面,詳情頁連結
            detailHtml, curUrl = get_one_html(join_url(
url)) # 詳情頁招聘資訊,doc連結 positionInfo, docLink = extract_detail_data(detailHtml) save_to_text(companyName, positionInfo) download_doc(companyName, docLink, curUrl) print(companyName, curUrl) if __name__ == "__main__": start = time.time() init() # fstring python3.7特性 suffix = (f"/{i}" for i in range(1, 23)) urls = [re.sub(r".htm", str(page)+".htm", MAIN_URL) for page in chain([""], suffix)] with ProcessPoolExecutor() as pool: pool.map(main, urls) end = time.time() takeTime = end - start print(f"【總共耗時】{takeTime}")

平均耗時:10s上下

多執行緒提速

同樣的,有了ThreadPoolExecutor後,多執行緒也變得簡單起來。除了類名,其他部分可以完全和多程序的程式碼一樣。

from concurrent.futures import ThreadPoolExecutor

...

if __name__ == "__main__":
	...
    with ThreadPoolExecutor() as pool:
        pool.map(main, urls)
    ...

平均耗時:9s+

非同步協程提速

非同步協程的改動會大一些,這是因為requests模組不支援非同步操作。我們需要藉助aiohttp來封裝一個get請求方式的get()方法如下:

async def get(url, referer=None):
    headers = HEADERS
    if referer is not None:
        r = {"Referer": str(referer)}
        headers.update(r)

    async with aiohttp.ClientSession() as session:
        async with session.get(url, headers=headers) as response:
            text = await response.read()
            url = response.url
            assert response.status == 200

    return text, url

通過這個方法,就可以實現非同步操作了。

其他地方也需要做相應的調整,比如async修飾的函式,呼叫時需要await關鍵字,但都是小問題。更多需要在意協程是如何使用的。

if __name__ == "__main__":
    start = time.time()
    init()
    # fstring python3.7特性
    suffix = (f"/{i}" for i in range(1, 23))

	# 注意這一部分的改動
    urls = [re.sub(r".htm", str(page)+".htm", MAIN_URL) for page in chain([""], suffix)]
    tasks = [asyncio.ensure_future(main(url)) for url in urls]

    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    # --------注意線-------------

    end = time.time()
    takeTime = end - start
    print(f"【總共耗時】{takeTime}")

平均耗時:9s+

最後

感覺這一次寫得很“潦草”,這是沒辦法的事兒。因為以上三個內容(多程序,多執行緒,協程)每一個都可以拿出來單獨寫好長一篇。可我這個系列是:爬蟲。為了不客奪主位,只好一切從簡。

但無論如何是有說到的爬蟲提速的,讓我的爬蟲系列有頭有尾——雖然虎頭蛇尾。

這次展示的三個提速程式碼,並非將速度達到極致,許多地方還可以優化,但我並不建議如此。因為出於興趣愛好的爬蟲,真的沒有必要分秒必爭,況且網站的運維人員也需要休息、娛樂,也需要陪陪家人。而不是一直待公司跟你玩兒“貓捉老鼠”吧!

完整程式碼已經上傳GitHub