python3 爬虫例子_如何让你写的爬虫速度像坐火箭一样快【并发请求】
這個系列內的文章將會讓你知道如何做到讓你寫的爬蟲在運行的時候速度能像火箭一樣快!
很多初學爬蟲的朋友對于這方面的知識似乎是空白的,甚至還有一些在爬蟲崗位上工作了一兩年的人也搞不清楚在不使用爬蟲框架的情況下,如何寫出一個速度足夠快的爬蟲,而網上的文章大多是基于多進程/Gevent來寫的,代碼看起來就極其復雜,甚至有些人抄來抄去連多進程和多線程沒搞清楚,如果是一個想學習這方面知識的人看到了這樣的文章,多半會一臉懵逼。
綜上所述,為了讓關注我公眾號的新手朋友們能快速掌握這些技巧,這個系列就這樣誕生了~
話不多說,我們正式開始。在提升爬蟲的速度這方面,最基礎、最有效、最直接的操作是什么呢?沒錯,就是并發請求,如果你的爬蟲整個邏輯是順序執行的,請求的時候永遠不會并發,那么你就會遇到像他這樣的情況:《小白寫了個壁紙的爬蟲,能跑起來,但是感覺很慢,不知道怎么回事,請大佬指點》。
上面這是我昨天刷V2的時候看到的一個帖子,樓主的代碼內容簡單概括一下就完全是順序執行的,每下載一個圖片都需要等待當前這個圖片下載完了才能繼續下載下一個,這樣子做當然會非常慢了!這篇文章就拿他的代碼作為樣例,在原來的基礎上進行一些調整,從而讓他寫的這個爬蟲的運行速度能從龜爬變成像坐火箭一樣快!
首先,我們需要知道什么是并發,這里的并發指的是“并行發送請求”,意思就是一次性發出多個請求,從而達到節省時間的效果!那么并發和不并發的區別在哪呢?簡單來說就是這樣子的:
把爬蟲比喻成工人,在不并發的情況下,一個工人一次只能做一件事情,所以必須要下載完一個圖片才能繼續下載下一個。
而在并發的情況下,就有很多個工人一起在干活,每個工人都被分配了一件事情做,所以可以同時下載多個圖片,速度自然就快了很多。
當然,上面說的這個例子只是從一個宏觀的角度上來看并發,實際在做的時候要讓你的爬蟲能并發請求的方式是分為多線程、多進程、協程三種的,并不是每一種方式在運行時的效果都像上面說的這樣,這里先不做深入探討,因為這不是本文的重點。我們現在只需要知道,只要能讓爬蟲并發請求,就能同時下載多個圖片,讓速度快得飛起,這樣就夠了。
那么我們要用上面說的三種方式里的哪一種來實現并發請求呢?這還用問嗎?當然是選擇代碼最簡單、改動最小,并且最容易看懂的協程啊!在Python3.4之后Python就引入了一個叫做asyncio的庫,原生支持了異步IO,而在3.5之后Python又支持了async和await這兩個語法,使得寫異步代碼可以像寫同步代碼一樣簡單易讀。
剛剛又提到了兩個詞,同步和異步,這兩個詞的含義其實就跟上面的并發差不多,同步代碼就是順序執行的,而異步則不是,這里同樣不做深入探討,先知道有這么個東西就行了。
看到這里肯定會有人開始有疑問了,雖然前面說我們要用協程來實現并發請求,但是后面說的卻是什么Python支持原生異步,那么這個異步跟協程的關系又是什么呢?
其實很簡單,協程可以讓你寫異步代碼的時候能像寫同步代碼一樣簡單,在Python3中寫協程代碼的核心語法就是async和await這兩個,舉個簡單的例子吧:
def func():print(1)time.sleep(10)print(2)這是一段普通的函數,它屬于同步代碼,里面的time.sleep是普通函數,也屬于同步代碼。
async def func(): # 調用協程函數的那個函數也需要是一個協程函數print(1)await asyncio.sleep(10) # 調用協程函數的時候要在前面加awaitprint(2)而這是一個協程函數,它屬于異步代碼,里面的asyncio.sleep是協程函數,也屬于異步代碼。
它們的區別顯而易見,用協程來寫異步代碼,除了需要換成異步的庫以外,就只是多了個async、await而已,是不是非常簡單?
那么我們在了解了怎么寫協程代碼之后,就能開始優化那段慢成龜速的代碼了嗎?答案是否定的,那段代碼中使用了requests庫進行網絡請求,而requests是一個同步庫,不能在異步環境下使用;同樣,文件操作用的open和file.write也是同步的,也不能在異步環境下使用。
所以在開始之前我們還需要了解兩個庫,分別是aiohttp和aiofiles,aiohttp是一個異步網絡請求庫,而aiofiles是一個異步文件操作庫。(aiofiles是基于線程池實現的,并不是真正的原生異步,但問題不大,不影響使用)
切記,異步代碼不能與同步代碼混用,否則如果同步代碼耗時過長,異步代碼就會被阻塞,失去異步的效果。而網絡請求和文件操作是整個流程中最耗時的部分,所以我們必須使用異步的庫來進行操作!否則就白搞了!
好了,先來看看aiohttp的用法吧,官方文檔上的示例大致如下:
async with aiohttp.ClientSession() as session:async with session.get(url) as resp:result = await resp.text()是不是覺得很麻煩,不像requests庫那么方便?還覺得兩層async with很丑?有沒有辦法讓它像requests庫一樣方便呢?
答案是有的,有一個叫作aiohttp-requests的庫,它能讓上面的這段代碼變成這樣:
resp = await requests.get(url) result = await resp.text()清爽多了對吧?我們等下就用它了!記得裝這個庫的前提是要先裝aiohttp哦!
然后我們來看看aiofiles的用法,官方文檔上的示例如下:
async with aiofiles.open('filename', mode='r') as f:contents = await f.read() print(contents)嗯,這個用起來就和用同步代碼操作文件差不多了,沒啥可挑剔的,直接用就完事了。
提示:aiohttp-requests默認是創建并使用了session的,對于一些需要不保留Cookie進行請求的場景需要自己實例化一個Requests類,并指定cookie_jar為aiohttp.DummyCookieJar。
了解完了要用的庫之后我們就可以開始對貼子中的代碼進行魔改了,如果你用的不是Python3.5以上版本的話需要先準備一下環境。除了版本號大于等于3.5的Python以外,你還需要安裝以下幾個庫:
- aiohttp(異步網絡請求庫)
- aiohttp-requests(讓aiohttp用起來更方便的庫)
- aiofiles(異步文件操作庫)
- pillow(其實就是PIL庫,代碼中的圖片操作有用到)
執行一下pip install aiohttp aiohttp-requests aiofiles pillow一次性裝完,如果存在多個不同版本的Python環境記得區分好。
然后我們打開編輯器,開始改代碼,首先調整一下導包的部分,將里面的requests替換成aiohttp-requests,像這樣:
然后搜索一下requests,看看哪些地方用到了它。
接著把所有搜到的部分都給改成異步請求的。
同時不要忘了將所有調用過requests.get的函數都變成協程函數。
然后我們把文件操作的部分也換成異步的,使用aiofiles.open代替open。
最主要的部分都換好了,接著我們將原先在if __name__ == '__main__':下的代碼移到一個新寫的協程函數run中,并且將調用前面協程函數的部分都加上await。
再導入一下asyncio庫,然后在if __name__ == '__main__':下寫出這樣的代碼:
上面這個是Python3.7之后才能用的寫法,低于Python3.7要這樣寫:
現在我們就可以運行一下看看修改后的代碼能不能跑通了。
這里報了個錯,從錯誤堆棧中可以看出問題是出在response = await requests.get(url=url, headers=headers)這里的,原因是self.session._request方法沒有key為url的參數。這個問題很好解決,只需要將url=url變成url就好了(本來也就沒必要這么指定參數寫)。將代碼中所有用到requests.get并且存在url=url這種寫法的都做一下調整:
調整完之后再運行一次就正常了,效果和原先的代碼相同。
注意!僅僅是這樣并不會讓速度發生很大的變化!我們最后還需要將這一堆代碼中最耗時且是順序執行、沒有并發請求的部分單獨放到一個協程函數中,并且用asyncio.gather來并發調用(由于原本的邏輯較為混亂,這里除了并發請求以外還進行了一些其他的微調,主要是計數和文件路徑的部分,無關緊要)。
運行一下看看效果,剛運行起來一瞬間就刷了一排的下載完成,跟修改之前比起來簡直是天差地別。
這就是并發請求的威力!我們僅僅是對他原本的代碼進行了一些微調,把最耗時的下載圖片部分簡單粗暴地使用asyncio.gather并發執行了一下,速度就從龜爬變成了像坐火箭一樣快!(其實代碼中還有很多可以優化的點,這里就不一一拿出來講了)
最后給大家提個醒:
雖然并發請求非常牛逼,可以讓你的爬蟲變得飛快,但它也不是不存在任何問題的!
如果你的并發請求數量過大(又稱并發數過高),你的爬蟲就相當于是在對他人的服務器進行Dos攻擊(拒絕服務攻擊)了!
舉個例子,你在爬一個小網站的時候為了自己爬的速度更快,對并發請求的數量毫無限制,使得你的爬蟲一次性發出了幾百、上千個請求,但一般的小網站根本扛不住這么高的并發!幾乎會在一瞬間就被你的爬蟲給打爆掉!試想一下,如果你是站長,看到這樣的情形你會怎么想?
如果你不能理解這個例子所產生的效果是什么樣的,可以自己搭建一個Web服務,只放一個簡單的頁面,然后開個幾百并發去請求這個頁面,這樣你就能切身地體會到別人是什么感受了。
所以記住,一定要合理控制并發請求的數量,不要對對方網站造成過大的壓力!你給別人留活路,別人才會給你留活路!
最后再留個小作業吧,如何對這個修改后的代碼增加一道并發數的限制?在留言區給出你的答案。(提示:可通過搜索引擎查找【aiohttp并發連接數限制】和【python 列表切割】相關的內容)
這個時代各種東西變化太快,而網絡上的垃圾信息又很多,你需要有一個良好的知識獲取渠道,很多時候早就是一種優勢,還不趕緊關注我的公眾號并置頂/星標一波~
發送消息“爬蟲速度提升之并發請求”到我的公眾號【小周碼字】即可獲得本文代碼下載地址~
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的python3 爬虫例子_如何让你写的爬虫速度像坐火箭一样快【并发请求】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 廖雪峰python教程整理笔记_廖雪峰p
- 下一篇: python123测验9程序题_pyth