一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何通過在Python中使用并發(fā)來加速網(wǎng)頁數(shù)據(jù)抓取項(xiàng)目

Linux愛好者 ? 來源:Linux愛好者 ? 作者:Linux愛好者 ? 2022-08-19 16:19 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

從網(wǎng)站中抓取數(shù)據(jù)是開發(fā)者的一個(gè)典型“用例”。無論它是屬于副業(yè)項(xiàng)目,還是你正在成立一個(gè)初創(chuàng)公司,抓取數(shù)據(jù)似乎都很有必要。

舉個(gè)例子,倘若您想要?jiǎng)?chuàng)建一個(gè)比價(jià)網(wǎng)站,那么您會(huì)需要從各種電商網(wǎng)站上抓取價(jià)格信息;或者您想要構(gòu)建一個(gè)可以識(shí)別商品并在亞馬遜上自動(dòng)查找價(jià)格的“人工智能”。類似的場(chǎng)景還有很多。

但是您有沒有注意到,獲取所有頁面信息的速度有多慢呢?您會(huì)選擇一個(gè)接一個(gè)地去抓取商品嗎?應(yīng)該會(huì)有更好的解決方案吧?答案是肯定的。

抓取網(wǎng)頁可能非常耗時(shí),因?yàn)槟仨毣〞r(shí)間等待服務(wù)器響應(yīng),抑或是速率受限。這就是為什么我們要向您展示如何通過在 Python 中使用并發(fā)來加速您的網(wǎng)頁數(shù)據(jù)抓取項(xiàng)目。

前提

為了使代碼正常運(yùn)行,您需要安裝 python 3[1]。部分系統(tǒng)可能已經(jīng)預(yù)裝了它。然后您還需要使用 pip install 安裝所有必要的庫。

pipinstallrequestsbeautifulsoup4aiohttpnumpy

如果您了解并發(fā)背后的基礎(chǔ)知識(shí),可以跳過理論部分直接進(jìn)入實(shí)際操作環(huán)節(jié)。

并發(fā)

并發(fā)是一個(gè)術(shù)語,用于描述同時(shí)運(yùn)行多個(gè)計(jì)算任務(wù)的能力。

當(dāng)您按順序向網(wǎng)站發(fā)出請(qǐng)求時(shí),您可以選擇一次發(fā)出一個(gè)請(qǐng)求并等待結(jié)果返回,然后再發(fā)出下一個(gè)請(qǐng)求。

不過,您也可以同時(shí)發(fā)送多個(gè)請(qǐng)求,并在它們返回時(shí)處理對(duì)應(yīng)的結(jié)果,這種方式的速度提升效果是非常顯著的。與順序請(qǐng)求相比,并發(fā)請(qǐng)求無論是否并行運(yùn)行(多個(gè) CPU),都會(huì)比前者快得多 -- 稍后會(huì)詳細(xì)介紹。

要理解并發(fā)的優(yōu)勢(shì)。我們需要了解順序處理和并發(fā)處理任務(wù)之間的區(qū)別。假設(shè)我們有五個(gè)任務(wù),每個(gè)任務(wù)需要 10 秒才能完成。當(dāng)按順序處理它們時(shí),完成五個(gè)任務(wù)所需的時(shí)間為 50 秒;而并發(fā)處理時(shí),僅需要 10 秒即可完成。

9a2c6896-1f72-11ed-ba43-dac502259ad0.png

除了提高處理速度之外,并發(fā)還允許我們通過將網(wǎng)頁抓取任務(wù)負(fù)載分布于多個(gè)進(jìn)程中,來實(shí)現(xiàn)在更短的時(shí)間內(nèi)完成更多的工作。

這里有幾種實(shí)現(xiàn)并行化請(qǐng)求的方式:例如 multiprocessing 和 asyncio。從網(wǎng)頁抓取的角度來看,我們可以使用這些庫來并行處理對(duì)不同網(wǎng)站或同一網(wǎng)站不同頁面的請(qǐng)求。在本文中,我們將重點(diǎn)關(guān)注 asyncio,這是一個(gè) Python 內(nèi)置的模塊,它提供了使用協(xié)程編寫單線程并發(fā)代碼的基礎(chǔ)設(shè)施。

由于并發(fā)意味著更復(fù)雜的系統(tǒng)和代碼,因此在使用前請(qǐng)考慮在您的使用場(chǎng)景中是否利大于弊。

并發(fā)的優(yōu)勢(shì)

在更短的時(shí)間內(nèi)完成更多的工作

可以將空閑的網(wǎng)絡(luò)時(shí)間投入到其他請(qǐng)求中

并發(fā)的危險(xiǎn)之處

更不易于開發(fā)和調(diào)試

可能存在競(jìng)爭(zhēng)條件

需要檢查并使用線程安全的函數(shù)

一不小心就會(huì)增加程序阻塞的概率

并發(fā)自帶系統(tǒng)開銷,因此需要設(shè)置合理的并發(fā)級(jí)別

針對(duì)小型站點(diǎn)請(qǐng)求過多的話,可能會(huì)變成 DDoS 攻擊

*同時(shí)釋放所有請(qǐng)求時(shí)要小心*

為何選擇 asyncio

在做出選擇之前,我們有必要了解一下 asyncio 和 multiprocessing 之間的區(qū)別,以及 IO 密集型與 CPU 密集型之間的區(qū)別。

asyncio[2] “是一個(gè)使用 async/await 語法編寫并發(fā)代碼的庫”,它在單個(gè)處理器上運(yùn)行。

multiprocessing[3] “是一個(gè)支持使用 API 生產(chǎn)進(jìn)程的包 [...] 允許程序員充分利用給定機(jī)器上的多個(gè)處理器”。每個(gè)進(jìn)程將在不同的 CPU 中啟動(dòng)自己的 Python 解釋器。

IO 密集型意味著程序?qū)⑹?I/O 影響而變得運(yùn)行緩慢。在我們的案例中,主要指的是網(wǎng)絡(luò)請(qǐng)求。

CPU 密集型意味著程序會(huì)由于 CPU 計(jì)算壓力導(dǎo)致運(yùn)行緩慢 -- 例如數(shù)學(xué)計(jì)算。

為什么這會(huì)影響我們選擇用于并發(fā)的庫?因?yàn)椴l(fā)成本的很大一部分是創(chuàng)建和維護(hù)線程/進(jìn)程。對(duì)于 CPU 密集型問題,在不同的 CPU 中擁有多個(gè)進(jìn)程將會(huì)提升效率。但對(duì)于 I/O 密集型的場(chǎng)景,情況可能并非如此。

由于網(wǎng)頁數(shù)據(jù)抓取主要受 I/O 限制,因此我們選擇了 asyncio。但如果有疑問(或只是為了好玩),您可以使用 multiprocessing 嘗試這個(gè)場(chǎng)景并比較一下結(jié)果。

9a558064-1f72-11ed-ba43-dac502259ad0.png

順序?qū)崿F(xiàn)的版本

我們將從抓取 scrapeme.live 作為示例開始,這是一個(gè)專門用于測(cè)試的電子商務(wù)網(wǎng)站。

首先,我們將從順序抓取的版本開始。以下幾個(gè)片段是所有案例的一部分,因此它們將保持不變。

通過訪問目標(biāo)主頁,我們發(fā)現(xiàn)它有 48 個(gè)子頁面。由于是測(cè)試環(huán)境,這些子頁面不會(huì)很快發(fā)生變化,我們會(huì)使用到以下兩個(gè)常量:

base_url="https://scrapeme.live/shop/page"
pages=range(1,49)#maxpage(48)+1

現(xiàn)在,從目標(biāo)產(chǎn)品中提取基礎(chǔ)數(shù)據(jù)。為此,我們使用 requests.get 獲取 HTML 內(nèi)容,然后使用 BeautifulSoup 解析它。我們將遍歷每個(gè)產(chǎn)品并從中獲取一些基本信息。所有選擇器都來自對(duì)內(nèi)容的手動(dòng)審查(使用 DevTools),但為簡(jiǎn)潔起見,我們不會(huì)在這里詳細(xì)介紹。

importrequests
frombs4importBeautifulSoup

defextract_details(page):
#concatenatepagenumbertobaseURL
response=requests.get(f"{base_url}/{page}/")
soup=BeautifulSoup(response.text,"html.parser")

pokemon_list=[]
forpokemoninsoup.select(".product"):#loopeachproduct
pokemon_list.append({
"id":pokemon.find(class_="add_to_cart_button").get("data-product_id"),
"name":pokemon.find("h2").text.strip(),
"price":pokemon.find(class_="price").text.strip(),
"url":pokemon.find(class_="woocommerce-loop-product__link").get("href"),
})
returnpokemon_list

extract_details 函數(shù)將獲取一個(gè)頁碼并將其連接起來,用于創(chuàng)建子頁面的 URL。獲取內(nèi)容并創(chuàng)建產(chǎn)品數(shù)組后返回。這意味著返回的值將是一個(gè)字典列表,這是一個(gè)后續(xù)使用的必要細(xì)節(jié)。

我們需要為每個(gè)頁面運(yùn)行上面的函數(shù),獲取所有結(jié)果,并存儲(chǔ)它們。

importcsv

#modifiedtoavoidrunningallthepagesunintentionally
pages=range(1,3)

defstore_results(list_of_lists):
pokemon_list=sum(list_of_lists,[])#flattenlists

withopen("pokemon.csv","w")aspokemon_file:
#getdictionarykeysfortheCSVheader
fieldnames=pokemon_list[0].keys()
file_writer=csv.DictWriter(pokemon_file,fieldnames=fieldnames)
file_writer.writeheader()
file_writer.writerows(pokemon_list)

list_of_lists=[
extract_details(page)
forpageinpages
]
store_results(list_of_lists)

運(yùn)行上面的代碼將獲得兩個(gè)產(chǎn)品頁面,提取產(chǎn)品(總共 32 個(gè)),并將它們存儲(chǔ)在一個(gè)名為 pokemon.csv 的 CSV 文件中。 store_results 函數(shù)不影響順序或并行模式下的抓取。你可以跳過它。

由于結(jié)果是列表,我們必須將它們展平以允許 writerows 完成其工作。這就是為什么我們將變量命名為list_of_lists(即使它有點(diǎn)奇怪),只是為了提醒大家它不是扁平的。

輸出 CSV 文件的示例:

id name price url
759 Bulbasaur £63.00 https://scrapeme.live/shop/Bulbasaur/
729 Ivysaur £87.00 https://scrapeme.live/shop/Ivysaur/
730 Venusaur £105.00 https://scrapeme.live/shop/Venusaur/
731 Charmander £48.00 https://scrapeme.live/shop/Charmander/
732 Charmeleon £165.00 https://scrapeme.live/shop/Charmeleon/

如果您要為每個(gè)頁面 (48) 運(yùn)行腳本,它將生成一個(gè)包含 755 個(gè)產(chǎn)品的 CSV 文件,并花費(fèi)大約 30 秒。

timepythonscript.py

real0m31,806s
user0m1,936s
sys0m0,073s

asyncio 介紹

我們知道我們可以做得更好。如果我們同時(shí)執(zhí)行所有請(qǐng)求,它應(yīng)該花費(fèi)更少時(shí)間,對(duì)吧?也許會(huì)和執(zhí)行最慢的請(qǐng)求所花費(fèi)的時(shí)間相等。

并發(fā)確實(shí)應(yīng)該運(yùn)行得更快,但它也涉及一些開銷。所以這不是線性的數(shù)學(xué)改進(jìn)。

為此,我們將使用上面提到的 asyncio。它允許我們?cè)谑录h(huán)中的同一個(gè)線程上運(yùn)行多個(gè)任務(wù)(就像 Javascript 一樣)。它將運(yùn)行一個(gè)函數(shù),并在運(yùn)行時(shí)允許時(shí)將上下文切換到不同的上下文。在我們的例子中,HTTP 請(qǐng)求允許這種切換。

我們將開始看到一個(gè) sleep 一秒鐘的示例。并且腳本應(yīng)該需要一秒鐘才能運(yùn)行。請(qǐng)注意,我們不能直接調(diào)用 main。我們需要讓 asyncio 知道它是一個(gè)需要執(zhí)行的異步函數(shù)。

importasyncio

asyncdefmain():
print("Hello...")
awaitasyncio.sleep(1)
print("...World!")

asyncio.run(main())
timepythonscript.py
Hello...
...World!

real0m1,054s
user0m0,045s
sys0m0,008s

簡(jiǎn)單的并行代碼

接下來,我們將擴(kuò)展一個(gè)示例案例來運(yùn)行一百個(gè)函數(shù)。它們每個(gè)都會(huì) sleep 一秒鐘并打印一個(gè)文本。如果我們按順序運(yùn)行它們大約需要一百秒。使用 asyncio,只需要一秒!

這就是并發(fā)背后的力量。如前所述,對(duì)于純 I/O 密集型任務(wù),它將執(zhí)行得更快 - sleep 不是,但它對(duì)示例很重要。

我們需要?jiǎng)?chuàng)建一個(gè)輔助函數(shù),它會(huì) sleep 一秒鐘并打印一條消息。然后,我們編輯 main 以調(diào)用該函數(shù)一百次,并將每個(gè)調(diào)用存儲(chǔ)在一個(gè)任務(wù)列表中。最后也是關(guān)鍵的部分是執(zhí)行并等待所有任務(wù)完成。這就是 asyncio.gather[4] 所做的事情。

importasyncio

asyncdefdemo_function(i):
awaitasyncio.sleep(1)
print(f"Hello{i}")

asyncdefmain():
tasks=[
demo_function(i)
foriinrange(0,100)
]
awaitasyncio.gather(*tasks)

asyncio.run(main())

正如預(yù)期的那樣,一百條消息和一秒鐘的執(zhí)行時(shí)間。完美!

使用 asyncio 進(jìn)行抓取

我們需要將這些知識(shí)應(yīng)用于數(shù)據(jù)抓取。遵循的方法是同時(shí)請(qǐng)求并返回產(chǎn)品列表,并在所有請(qǐng)求完成后存儲(chǔ)它們。每次請(qǐng)求后或者分批保存數(shù)據(jù)可能會(huì)更好,以避免實(shí)際情況下的數(shù)據(jù)丟失。

我們的第一次嘗試不會(huì)有并發(fā)限制,所以使用時(shí)要小心。在使用數(shù)千個(gè) URL 運(yùn)行它的情況下......好吧,它幾乎會(huì)同時(shí)執(zhí)行所有這些請(qǐng)求。這可能會(huì)給服務(wù)器帶來巨大的負(fù)載,并可能會(huì)損害您的計(jì)算機(jī)。

requests 不支持開箱即用的異步,因此我們將使用 aiohttp[5] 來避免復(fù)雜化。 requests 可以完成這項(xiàng)工作,并且沒有實(shí)質(zhì)性的性能差異。但是使用 aiohttp 代碼更具可讀性。

importasyncio
importaiohttp
frombs4importBeautifulSoup

asyncdefextract_details(page,session):
#similartorequests.getbutwithadifferentsyntax
asyncwithsession.get(f"{base_url}/{page}/")asresponse:

#noticethatwemustawaitthe.text()function
soup=BeautifulSoup(awaitresponse.text(),"html.parser")

#[...]sameasbefore
returnpokemon_list

asyncdefmain():
#createanaiohttpsessionandpassittoeachfunctionexecution
asyncwithaiohttp.ClientSession()assession:
tasks=[
extract_details(page,session)
forpageinpages
]
list_of_lists=awaitasyncio.gather(*tasks)
store_results(list_of_lists)

asyncio.run(main())

CSV 文件應(yīng)該像以前一樣包含每個(gè)產(chǎn)品的信息 (共 755 個(gè))。由于我們同時(shí)執(zhí)行所有頁面調(diào)用,結(jié)果不會(huì)按順序到達(dá)。如果我們將結(jié)果添加到 extract_details 內(nèi)的文件中,它們可能是無序的。但我們會(huì)等待所有任務(wù)完成然后處理它們,因此順序性不會(huì)有太大影響。

timepythonscript.py

real0m11,442s
user0m1,332s
sys0m0,060s

我們做到了!速度提升了 3 倍,但是……不應(yīng)該是 40 倍嗎?沒那么簡(jiǎn)單。許多因素都會(huì)影響性能(網(wǎng)絡(luò)、CPU、RAM 等)。

在這個(gè)演示頁面中,我們注意到當(dāng)執(zhí)行多個(gè)調(diào)用時(shí),響應(yīng)時(shí)間會(huì)變慢,這可能是設(shè)計(jì)使然。一些服務(wù)器/提供商可以限制并發(fā)請(qǐng)求的數(shù)量,以避免來自同一 IP 的過多流量。它不是一種阻塞,而是一個(gè)隊(duì)列。你會(huì)得到服務(wù)響應(yīng),但需要稍等片刻。

要查看真正的加速,您可以針對(duì)延遲[6]頁面進(jìn)行測(cè)試。這是另一個(gè)測(cè)試頁面,它將等待 2 秒然后返回響應(yīng)。

base_url="https://httpbin.org/delay/2"
#...

asyncdefextract_details(page,session):
asyncwithsession.get(base_url)asresponse:
#...

這里去掉了所有的提取和存儲(chǔ)邏輯,只調(diào)用了延遲 URL 48 次,并在 3 秒內(nèi)運(yùn)行完畢。

timepythonscript.py

real0m2,865s
user0m0,245s
sys0m0,031s

使用信號(hào)量限制并發(fā)

如上所述,我們應(yīng)該限制并發(fā)請(qǐng)求的數(shù)量,尤其是針對(duì)單個(gè)域名。

asyncio 帶有 Semaphore[7],一個(gè)將獲取和釋放鎖的對(duì)象。它的內(nèi)部功能將阻塞一些調(diào)用,直到獲得鎖,從而創(chuàng)建最大的并發(fā)性。

我們需要?jiǎng)?chuàng)建盡可能最大值的信號(hào)量。然后等待提取函數(shù)運(yùn)行,直到 async with sem 可用。

max_concurrency=3
sem=asyncio.Semaphore(max_concurrency)

asyncdefextract_details(page,session):
asyncwithsem:#semaphorelimitsnumofsimultaneousdownloads
asyncwithsession.get(f"{base_url}/{page}/")asresponse:
#...

asyncdefmain():
#...

loop=asyncio.get_event_loop()
loop.run_until_complete(main())

它完成了工作,并且相對(duì)容易實(shí)現(xiàn)!這是最大并發(fā)設(shè)置為 3 的輸出。

timepythonscript.py

real0m13,062s
user0m1,455s
sys0m0,047s

這表明無限并發(fā)的版本并沒有全速運(yùn)行。如果我們將限制增加到 10,總時(shí)間與未限制的腳本運(yùn)行時(shí)間相近。

使用 TCPConnector 限制并發(fā)

aiohttp 提供了一種替代解決方案,可提供進(jìn)一步的配置。我們可以創(chuàng)建傳入自定義 TCPConnector[8] 的客戶端會(huì)話。

我們可以使用兩個(gè)適合我們需求的參數(shù)來構(gòu)建它:

limit - “同時(shí)連接的總數(shù)”。

limit_per_host - “限制同時(shí)連接到同一端點(diǎn)的連接數(shù)”(同一主機(jī)、端口和 is_ssl)。

max_concurrency=10
max_concurrency_per_host=3

asyncdefmain():
connector=aiohttp.TCPConnector(limit=max_concurrency,limit_per_host=max_concurrency_per_host)
asyncwithaiohttp.ClientSession(connector=connector)assession:
#...

asyncio.run(main())

這種寫法也易于實(shí)施和維護(hù)!這是每個(gè)主機(jī)最大并發(fā)設(shè)置為 3 的輸出。

timepythonscript.py

real0m16,188s
user0m1,311s
sys0m0,065s

與 Semaphore 相比的優(yōu)勢(shì)是可以選擇限制每個(gè)域的并發(fā)調(diào)用和請(qǐng)求的總量。我們可以使用同一個(gè)會(huì)話來抓取不同的站點(diǎn),每個(gè)站點(diǎn)都有自己的限制。

缺點(diǎn)是它看起來有點(diǎn)慢。需要針對(duì)真實(shí)案例,使用更多頁面和實(shí)際數(shù)據(jù)運(yùn)行一些測(cè)試。

multiprocessing

就像我們之前看到的那樣,數(shù)據(jù)抓取是 I/O 密集型的。但是,如果我們需要將它與一些 CPU 密集型計(jì)算混合怎么辦?為了測(cè)試這種情況,我們將使用一個(gè)函數(shù),該函數(shù)將在每個(gè)抓取的頁面之后 count_a_lot。這是強(qiáng)制 CPU 忙碌一段時(shí)間的簡(jiǎn)單(且有些愚蠢)的方法。

defcount_a_lot():
count_to=100_000_000
counter=0
whilecounter

對(duì)于 asyncio 版本,只需像以前一樣運(yùn)行它??赡苄枰荛L時(shí)間。

timepythonscript.py

real2m37,827s
user2m35,586s
sys0m0,244s

現(xiàn)在,比較難理解的部分來了:

直接引入 multiprocessing 看起來有點(diǎn)困難。實(shí)際上,我們需要?jiǎng)?chuàng)建一個(gè) ProcessPoolExecutor,它能夠“使用一個(gè)進(jìn)程池來異步執(zhí)行調(diào)用”。它將處理不同 CPU 中每個(gè)進(jìn)程的創(chuàng)建和控制。

但它不會(huì)分配負(fù)載。為此,我們將使用 NumPy 的 array_split,它會(huì)根據(jù) CPU 的數(shù)量將頁面范圍分割成相等的塊。

main 函數(shù)的其余部分類似于 asyncio 版本,但更改了一些語法以匹配 multiprocessing 的語法風(fēng)格。

此處的本質(zhì)區(qū)別是我們不會(huì)直接調(diào)用extract_details。實(shí)際上是可以的,但我們將嘗試通過將 multiprocessing 與 asyncio 混合使用來獲得最好的執(zhí)行效率。

fromconcurrent.futuresimportProcessPoolExecutor
frommultiprocessingimportcpu_count
importnumpyasnp

num_cores=cpu_count()#numberofCPUcores

defmain():
executor=ProcessPoolExecutor(max_workers=num_cores)
tasks=[
executor.submit(asyncio_wrapper,pages_for_task)
forpages_for_taskinnp.array_split(pages,num_cores)
]
doneTasks,_=concurrent.futures.wait(tasks)

results=[
item.result()
foritemindoneTasks
]
store_results(results)

main()

長話短說,每個(gè) CPU 進(jìn)程都會(huì)有幾頁需要抓取。一共有 48 個(gè)頁面,假設(shè)你的機(jī)器有 8 個(gè) CPU,每個(gè)進(jìn)程將請(qǐng)求 6 個(gè)頁面(6 * 8 = 48)。

這六個(gè)頁面將同時(shí)運(yùn)行!之后,計(jì)算將不得不等待,因?yàn)樗鼈兪?CPU 密集型的。但是我們有很多 CPU,所以它們應(yīng)該比純 asyncio 版本運(yùn)行得更快。

asyncdefextract_details_task(pages_for_task):
asyncwithaiohttp.ClientSession()assession:
tasks=[
extract_details(page,session)
forpageinpages_for_task
]
list_of_lists=awaitasyncio.gather(*tasks)
returnsum(list_of_lists,[])


defasyncio_wrapper(pages_for_task):
returnasyncio.run(extract_details_task(pages_for_task))

這就是神奇的地方。每個(gè) CPU 進(jìn)程將使用頁面的子集啟動(dòng)一個(gè) asyncio(例如,第一個(gè)頁面從 1 到 6)。

然后,每一個(gè)都將調(diào)用幾個(gè) URL,使用已知的 extract_details 函數(shù)。

上述內(nèi)容需要花點(diǎn)時(shí)間來吸收它。整個(gè)過程是這樣的:

創(chuàng)建執(zhí)行器

拆分頁面

每個(gè)進(jìn)程啟動(dòng) asyncio

創(chuàng)建一個(gè) aiohttp 會(huì)話并創(chuàng)建頁面子集的任務(wù)

提取每一頁的數(shù)據(jù)

合并并存儲(chǔ)結(jié)果

下面是本次的執(zhí)行時(shí)間。雖然之前我們沒有提到它,但這里的 user 時(shí)間卻很顯眼。對(duì)于僅運(yùn)行 asyncio 的腳本:

timepythonscript.py

real2m37,827s
user2m35,586s
sys0m0,244s

具有 asyncio 和多個(gè)進(jìn)程的版本:

timepythonscript.py

real0m38,048s
user3m3,147s
sys0m0,532s

發(fā)現(xiàn)區(qū)別了嗎?實(shí)際運(yùn)行時(shí)間方面第一個(gè)用了兩分鐘多,第二個(gè)用了 40 秒。但是在總 CPU 時(shí)間(user 時(shí)間)中,第二個(gè)超過了三分鐘!看起來系統(tǒng)開銷的耗時(shí)確實(shí)有點(diǎn)多。

這表明并行處理“浪費(fèi)”了更多時(shí)間,但程序是提前完成的。顯然,您在決定選擇哪種方法時(shí),需要考慮到開發(fā)和調(diào)試的復(fù)雜度。

結(jié)論

我們已經(jīng)看到 asyncio 足以用于抓取,因?yàn)榇蟛糠诌\(yùn)行時(shí)間都用于網(wǎng)絡(luò)請(qǐng)求,這種場(chǎng)景屬于 I/O 密集型并且適用于單核中的并發(fā)處理。

如果收集的數(shù)據(jù)需要一些 CPU 密集型工作,這種情況就會(huì)改變。雖然有關(guān)計(jì)數(shù)的例子有一點(diǎn)愚蠢,但至少你理解了這種場(chǎng)景。

在大多數(shù)情況下,帶有 aiohttp 的 asyncio 比異步的 requests 更適合完成目標(biāo)工作。同時(shí)我們可以添加自定義連接器以限制每個(gè)域名的請(qǐng)求數(shù)、并發(fā)請(qǐng)求總數(shù)。有了這三個(gè)部分,您就可以開始構(gòu)建一個(gè)可以擴(kuò)展的數(shù)據(jù)抓取程序了。

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7256

    瀏覽量

    91827
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    13

    文章

    9793

    瀏覽量

    87933
  • 網(wǎng)頁
    +關(guān)注

    關(guān)注

    0

    文章

    74

    瀏覽量

    19647
  • python
    +關(guān)注

    關(guān)注

    56

    文章

    4827

    瀏覽量

    86704

原文標(biāo)題:抓取速度提升 3 倍!Python 的這個(gè)內(nèi)置庫你用上了嗎?

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    labview與IE瀏覽器如何通過網(wǎng)頁name值進(jìn)行網(wǎng)頁數(shù)據(jù)交互

    labview 如何通過調(diào)用IE瀏覽器打開網(wǎng)頁,修改網(wǎng)頁參數(shù),目前我可以通過修改網(wǎng)頁id值修改它的value,但是有的
    發(fā)表于 01-11 11:31

    使用curl+wget抓取網(wǎng)頁方法

    :8080然后再輸入wget抓取網(wǎng)頁的命令:wget http://www.baidu.com -O baidu_html2代理下載截圖:抓取的百度首頁數(shù)據(jù)(截圖):其它命令參數(shù)用法,
    發(fā)表于 02-25 09:54

    Python數(shù)據(jù)爬蟲學(xué)習(xí)內(nèi)容

    負(fù)責(zé)連接網(wǎng)站,返回網(wǎng)頁,Xpath 用于解析網(wǎng)頁,便于抽取數(shù)據(jù)。2.了解非結(jié)構(gòu)化數(shù)據(jù)的存儲(chǔ)。爬蟲抓取
    發(fā)表于 05-09 17:25

    通過html控件讀取網(wǎng)頁數(shù)據(jù)的控件在哪里?

    如下圖所示有3個(gè)html的控件,請(qǐng)問在哪里能找到?activeX里找到了Microsoft HTML Object Library Version 4.0,以及類似名稱的控件,但沒有找到完全符合的。或者誰有利用html控件讀取網(wǎng)頁數(shù)據(jù)的vi能共享下,謝謝!
    發(fā)表于 06-13 16:54

    labview抓取網(wǎng)頁數(shù)據(jù)和賣家?guī)齑娌杉ぞ適shtml應(yīng)用

    通過變體轉(zhuǎn)換為html,變體的數(shù)據(jù)類型就是html,所以這個(gè)類型是關(guān)鍵。我所上傳的附近程序內(nèi)就是解決這個(gè)問題的程序。我搜索了很多天都無法找到html的變體類型。最后NI的官方論壇上看到外國人也
    發(fā)表于 08-14 10:47

    請(qǐng)問packet capture爬蟲軟件抓取數(shù)據(jù)怎么做成網(wǎng)頁鏈接?

    爬蟲軟件抓取數(shù)據(jù)怎么做成網(wǎng)頁鏈接?
    發(fā)表于 05-21 20:17

    labview的網(wǎng)絡(luò)蜘蛛的實(shí)現(xiàn)(post請(qǐng)求,非python)

    ,編寫蜘蛛,運(yùn)行,分析整理數(shù)據(jù)。目標(biāo):獲取某論壇某貼評(píng)論分析網(wǎng)頁結(jié)構(gòu):找關(guān)鍵詞,發(fā)現(xiàn)規(guī)律,文本變?yōu)槲谋拘兄┲耄悍治?b class='flag-5'>網(wǎng)頁走向,獲取... ...... ...關(guān)鍵點(diǎn):網(wǎng)頁數(shù)據(jù)轉(zhuǎn)為GBK,
    發(fā)表于 06-24 11:13

    用QueryList實(shí)現(xiàn)網(wǎng)頁數(shù)據(jù)抓取

    之前抓取網(wǎng)頁數(shù)據(jù)都是用Java Jsoup,前幾天聽說用PHP抓更方便,今天就簡(jiǎn)單研究了一下,主要是用QueryList來實(shí)現(xiàn).
    發(fā)表于 08-08 07:32

    Python爬蟲簡(jiǎn)介與軟件配置

    腳本。另外一些不常使用的名字還有螞蟻、自動(dòng)索引、模擬程序或者蠕蟲。爬蟲從初始網(wǎng)頁的url開始, 不斷從當(dāng)前頁面抽取新的url放入隊(duì)列。直到滿足系統(tǒng)給定的停止條件才停止??梢詾樗阉匾鎻幕ヂ?lián)網(wǎng)中下載網(wǎng)頁數(shù)據(jù),是搜素引擎的重要組成部分。2. 軟件配置
    發(fā)表于 01-11 06:32

    網(wǎng)頁抓取之Headless Chrome技巧

    文章摘要: 1. 有很多庫可以控制Chrome,可以根據(jù)自己的喜歡選擇。 2. 使用Headless Chrome進(jìn)行網(wǎng)頁抓取非常簡(jiǎn)單,掌握下面的技巧之后更是如此。 3. Headless瀏覽器訪客
    發(fā)表于 09-28 15:09 ?0次下載

    python3.3抓取網(wǎng)頁數(shù)據(jù)的程序資料免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是python3.3抓取網(wǎng)頁數(shù)據(jù)的程序資料免費(fèi)下載。
    發(fā)表于 01-29 15:19 ?21次下載
    <b class='flag-5'>python</b>3.3<b class='flag-5'>抓取</b><b class='flag-5'>網(wǎng)頁數(shù)據(jù)</b>的程序資料免費(fèi)下載

    Python寫個(gè)小工具網(wǎng)頁

    簡(jiǎn)簡(jiǎn)單單的用 Python 擼一個(gè)計(jì)算年齡的工具網(wǎng)頁,不用對(duì)前端特別熟悉,只要專注于工具的邏輯,其他都交給 Python 吧。
    的頭像 發(fā)表于 03-03 14:52 ?1732次閱讀
    用<b class='flag-5'>Python</b>寫個(gè)小工具<b class='flag-5'>網(wǎng)頁</b>

    如何在Python中使用MQTT

    本文主要介紹如何在 Python 項(xiàng)目中使用?paho-mqtt?客戶端庫 ,實(shí)現(xiàn)客戶端與?MQTT?服務(wù)器的連接、訂閱、取消訂閱、收發(fā)消息等功能。
    的頭像 發(fā)表于 12-22 10:41 ?1.1w次閱讀
    如何在<b class='flag-5'>Python</b><b class='flag-5'>中使</b>用MQTT

    Python庫解析:通過庫實(shí)現(xiàn)代理請(qǐng)求與數(shù)據(jù)抓取

    Python中,有多個(gè)庫可以幫助你實(shí)現(xiàn)代理請(qǐng)求和數(shù)據(jù)抓取。這些庫提供了豐富的功能和靈活的API,使得你可以輕松地發(fā)送HTTP請(qǐng)求、處理響應(yīng)、解析HTML/XML/JSON
    的頭像 發(fā)表于 10-24 07:54 ?484次閱讀

    如何用Brower Use WebUI實(shí)現(xiàn)網(wǎng)頁數(shù)據(jù)智能抓取與分析?

    數(shù)據(jù)時(shí),不會(huì)被網(wǎng)站反爬機(jī)制識(shí)別和封禁,能穩(wěn)定有效地獲取數(shù)據(jù)和執(zhí)行任務(wù)。除了數(shù)據(jù)采集外,Browser-use還能抓取網(wǎng)頁全部交互元素,自動(dòng)完
    的頭像 發(fā)表于 04-17 17:48 ?423次閱讀
    如何用Brower Use WebUI實(shí)現(xiàn)<b class='flag-5'>網(wǎng)頁數(shù)據(jù)</b>智能<b class='flag-5'>抓取</b>與分析?