文 | 閑歡
來源:Python 技術(shù)「ID: pythonall」
今天在瀏覽知乎時(shí),發(fā)現(xiàn)一個(gè)有趣的問題:如何優(yōu)化 Python 爬蟲的速度?
他的問題描述是:
目前在寫一個(gè) Python 爬蟲,單線程 urllib 感覺過于慢了,達(dá)不到數(shù)據(jù)量的要求(十萬級(jí)頁面)。求問有哪些可以提高爬取效率的方法?
這個(gè)問題還蠻多人關(guān)注的,但是回答的人卻不多。
我今天就來嘗試著回答一下這個(gè)問題。
程序提速這個(gè)問題其實(shí)解決方案就擺在那里,要么通過并發(fā)來提高單位時(shí)間內(nèi)處理的工作量,要么從程序本身去找提效點(diǎn),比如爬取的數(shù)據(jù)用gzip傳輸、提高處理數(shù)據(jù)的速度等。
我會(huì)分別從幾種常見的并發(fā)方法去做同一件事情,從而比較處理效率。
簡單版本爬蟲
我們先來一個(gè)簡單的爬蟲,看看單線程處理會(huì)花費(fèi)多少時(shí)間?
1
2
3
4
5
6
7
8
9
10
11
12
|
import time import requests from datetime import datetime def fetch(url): r = requests.get(url) print (r.text) start = datetime.now() t1 = time.time() for i in range ( 100 ): fetch( 'http://httpbin.org/get' ) print ( 'requests版爬蟲耗時(shí):' , time.time() - t1) # requests版爬蟲耗時(shí):54.86306357383728 |
我們用一個(gè)爬蟲的測試網(wǎng)站,測試爬取100次,用時(shí)是54.86秒。
多線程版本爬蟲
下面我們將上面的程序改為多線程版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import threading import time import requests def fetch(): r = requests.get( 'http://httpbin.org/get' ) print (r.text) t1 = time.time() t_list = [] for i in range ( 100 ): t = threading.Thread(target = fetch, args = ()) t_list.append(t) t.start() for t in t_list: t.join() print ( "多線程版爬蟲耗時(shí):" , time.time() - t1) # 多線程版爬蟲耗時(shí):0.8038511276245117 |
我們可以看到,用上多線程之后,速度提高了68倍。其實(shí)用這種方式的話,由于我們并發(fā)操作,所以跑100次跟跑一次的時(shí)間基本是一致的。這只是一個(gè)簡單的例子,實(shí)際情況中我們不可能無限制地增加線程數(shù)。
多進(jìn)程版本爬蟲
除了多線程之外,我們還可以使用多進(jìn)程來提高爬蟲速度:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import requests import time import multiprocessing from multiprocessing import Pool MAX_WORKER_NUM = multiprocessing.cpu_count() def fetch(): r = requests.get( 'http://httpbin.org/get' ) print (r.text) if __name__ = = '__main__' : t1 = time.time() p = Pool(MAX_WORKER_NUM) for i in range ( 100 ): p.apply_async(fetch, args = ()) p.close() p.join() print ( '多進(jìn)程爬蟲耗時(shí):' , time.time() - t1) 多進(jìn)程爬蟲耗時(shí): 7.9846765995025635 |
我們可以看到多進(jìn)程處理的時(shí)間是多線程的10倍,比單線程版本快7倍。
協(xié)程版本爬蟲
我們將程序改為使用 aiohttp 來實(shí)現(xiàn),看看效率如何:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import aiohttp import asyncio import time async def fetch(client): async with client.get( 'http://httpbin.org/get' ) as resp: assert resp.status = = 200 return await resp.text() async def main(): async with aiohttp.ClientSession() as client: html = await fetch(client) print (html) loop = asyncio.get_event_loop() tasks = [] for i in range ( 100 ): task = loop.create_task(main()) tasks.append(task) t1 = time.time() loop.run_until_complete(main()) print ( "aiohttp版爬蟲耗時(shí):" , time.time() - t1) aiohttp版爬蟲耗時(shí): 0.6133313179016113 |
我們可以看到使用這種方式實(shí)現(xiàn),比單線程版本快90倍,比多線程還快。
結(jié)論
通過上面的程序?qū)Ρ龋覀兛梢钥吹剑瑢?duì)于多任務(wù)爬蟲來說,多線程、多進(jìn)程、協(xié)程這幾種方式處理效率的排序?yàn)椋篴iohttp > 多線程 > 多進(jìn)程。因此,對(duì)于簡單的爬蟲任務(wù),如果想要提高效率,可以考慮使用協(xié)程。但是同時(shí)也要注意,這里只是簡單的示例,實(shí)際運(yùn)用中,我們一般會(huì)用線程池、進(jìn)程池、協(xié)程池去操作。
這就是問題的答案了嗎?
對(duì)于一個(gè)嚴(yán)謹(jǐn)?shù)某绦騿T來說,當(dāng)然不是,實(shí)際上還有一些優(yōu)化的庫,例如grequests,可以從請(qǐng)求上解決并發(fā)問題。實(shí)際的處理過程中,肯定還有其他的優(yōu)化點(diǎn),這里只是從最常見的幾種并發(fā)方式去比較而已,應(yīng)付簡單爬蟲還是可以的,其他的方式歡迎大家在評(píng)論區(qū)留言探討。
以上就是熱門問題python爬蟲的效率如何提高的詳細(xì)內(nèi)容,更多關(guān)于python爬蟲效率提高的資料請(qǐng)關(guān)注服務(wù)器之家其它相關(guān)文章!
原文鏈接:https://blog.csdn.net/weixin_48923393/article/details/120944769