Python爬蟲如何加速?異步、協程還是多進程?分享一個常用做法,萌新也能看懂
theme: nico
最近在知識星球:Python讀者圈,遇到讀者提問:Python爬蟲如何加速?
這個問題涉及到一個爬蟲裏,甚至是整個Python編程裏都非常重要的問題:
如果同時下載1w張圖片,如何有效地加速程序運行,縮短下載時間?
今天我們一起來看一下常用的解決方案。
1、為什麼慢?
首先我們先看一下,原來的代碼裏,是什麼原因導致程序慢的? 下面是代碼和運行結果:
```python import office
for i in range(1, 18):
url = 'http://www.python-office.com/api/img-cdn/test/spider/{}.jpg'.format(str(i))
office.image.down4img(url, output_name=str(i))
```
順序執行看起來很完美,但是完美的背後是不是有陷阱呢?
為了更好的理解這個代碼,我們先舉一個例子:你面前有10台洗衣機編號是從1到10,裏面轉滿了衣服需要你清洗,有的髒可能要強力洗洗的久,有的乾淨只需要速洗洗的快。
清洗以後,需要你記錄下他們的清理順序,有下列2種方案供你選擇:
1. 一個挨一個的洗完。
先啟動洗衣機1號,等1號洗完了,再啟動2號,依次類推。這樣你記錄的結果和上圖一樣,是完美的按順序完成。
2. 先同時打開所有的洗衣機,哪一個洗完了就記錄哪一個。
因為有的洗得快,有的洗得慢,這樣你記錄的結果是混亂的。
哪種方式最快呢?毫無疑問是第2種,因為可以讓所有的洗衣機同時工作,時間資源可以複用。
回到我們的程序,我們下載一張圖片也是分為2步:請求圖片資源,保存到本地。
上面的代碼之所以慢,就是因為它是請求到第1張的資源,保存到本地之後,再去請求第2張的資源。看起來很完美,但其實問題很大。
如何加快速度呢?我們如果可以先請求到所有的圖片資源(打開所有的洗衣機),然後再統一保存圖片(哪台洗完衣服,就先記錄哪台),這樣是不是就會快很多呢?
下面我們按照第2種思路,在Python裏的實現實現一下。
2、解決代碼
Talk is cheap,show me the code. 先上代碼和運行結果。 ```python import asyncio from aiohttp import ClientSession
tasks = [] url = "http://www.python-office.com/api/img-cdn/test/spider/{}.jpg"
async def hello(url, i='wanfeng', type='jpg'): async with ClientSession() as session: async with session.get(url) as response: if response.status==200: response = await response.read() # print(response) async with aiofiles.open('.'.join((str(i), type)), 'wb') as output_img: # for chunk in response: await output_img.write(response) output_img.close() print(f"下載成功,圖片名稱:{'.'.join((str(i), type))}")
def run(): for i in range(1, 18): task = asyncio.ensure_future(hello(url.format(i), i)) tasks.append(task)
def main(): loop = asyncio.get_event_loop() run() loop.run_until_complete(asyncio.wait(tasks))
if name == 'main': main()
```
主要使用的庫是:
- asyncio:協程,讓圖片下載不按順序,可以加快速度
- aiohttp:替代requests,用來異步發送請求。
- aiofiles:異步寫入文件內容
3、還有其它方法嗎?
還有多進程也可以試試,但是多進程更大的優勢體現在計算密集型的場景下。 爬蟲獲取網絡請求屬於I/O密集型操作,多進程的優勢不大。
```python
-- coding:utf-8 --
import multiprocessing import os, time from multiprocessing import Pool
import requests
url = "http://www.python-office.com/api/img-cdn/test/spider/{}.jpg"
def down4img(url, output_name, type): """ 下載指定url的一張圖片,支持所有格式:jpg\png\gif .etc """ # print("子進程開始執行>>> pid={},ppid={},編號{}".format(os.getpid(), os.getppid(), output_name))
response = requests.get(url, stream=True)
with open('.'.join((output_name, type)), 'wb') as output_img:
for chunk in response:
output_img.write(chunk)
output_img.close()
print(f"下載成功,圖片名稱:{'.'.join((output_name, type))}")
# print("子進程終止>>> pid={},ppid={},編號{}".format(os.getpid(), os.getppid(), output_name))
def main(): print("主進程開始執行>>> pid={}".format(os.getpid())) ps = Pool(multiprocessing.cpu_count()) ps = Pool(3) for i in range(1, 18): # ps.apply(worker,args=(i,)) # 同步執行 output_name = str(i) type = 'jpg' ps.apply_async(down4img, args=(url.format(str(i)), output_name, type,)) # 異步執行 # ps.apply(down4img, args=(url.format(str(i)), output_name[0], type,)) # 同步執行
# 關閉進程池,停止接受其它進程
ps.close()
# 阻塞進程
ps.join()
print("主進程終止")
if name == 'main': main()
``` 主要使用的庫是: - multiprocessing:創建進程池
4、寫在最後
希望能給你帶來幫助。如果想系統的學習Python,歡迎大家掃碼加入我的知識星球👉Python讀者圈,我們一起學習提高~
我正在參與掘金技術社區創作者簽約計劃招募活動,點擊鏈接報名投稿。
- 1行Python代碼,識別發票並且保存在Excel裏,智能辦公了解一下?
- 176萬!GPT-4發佈了,如何查看OpenAI的下載量?
- 28歲小公司程序員,無車無房不敢結婚,要不要轉行?
- 考研失敗,加入國企當程序員,真香!
- 實戰案例!用1行Python代碼識別身份證信息,準確率超過99%,YYDS
- Python爬蟲如何加速?異步、協程還是多進程?分享一個常用做法,萌新也能看懂
- 如何生成1億個手機號碼?Python生成隨機數的22種方法,random函數太強了~
- 官方推薦:6種Pandas讀取Excel的方法,正確答案都寫在源代碼裏了~太方便了
- 用Python爬蟲,尋找這個夏天最涼快的地方。
- Python 3.11 性能測評超 3.10 近 64%
- Excel自動化辦公(一) | 滿足你對Excel數據的所有幻想,python-office一鍵生成模擬數據
- 一行Python代碼,如何成為辦公小助手?這5個操作,超實用!