Python爬蟲實(shí)戰(zhàn):單線程、多線程和協(xié)程性能對比

一、前言


二、爬取測試
https://www.zhongnongwang.com/quote/product-htm-page-1.html
https://www.zhongnongwang.com/quote/product-htm-page-2.html
https://www.zhongnongwang.com/quote/product-htm-page-3.html
https://www.zhongnongwang.com/quote/product-htm-page-4.html
https://www.zhongnongwang.com/quote/product-htm-page-5.html
https://www.zhongnongwang.com/quote/product-htm-page-6.html

#?-*-?coding:?UTF-8?-*-
"""
@File ???:demo.py
@Author ?:葉庭云
@CSDN ???:https://yetingyun.blog.csdn.net/
"""
import?requests
import?logging
from?fake_useragent?import?UserAgent
from?lxml?import?etree
#?日志輸出的基本配置
logging.basicConfig(level=logging.INFO,?format='%(asctime)s?-?%(levelname)s:?%(message)s')
#?隨機(jī)產(chǎn)生請求頭
ua?=?UserAgent(verify_ssl=False,?path='fake_useragent.json')
url?=?'https://www.zhongnongwang.com/quote/product-htm-page-1.html'
#?偽裝請求頭
headers?=?{
????"Accept-Encoding":?"gzip",??#?使用gzip壓縮傳輸數(shù)據(jù)讓訪問更快
????"User-Agent":?ua.random
}
#?發(fā)送請求??獲取響應(yīng)
rep?=?requests.get(url,?headers=headers)
print(rep.status_code)????#?200
#?Xpath定位提取數(shù)據(jù)
html?=?etree.HTML(rep.text)
items?=?html.xpath('/html/body/div[10]/table/tr[@align="center"]')
logging.info(f'該頁有多少條信息:{len(items)}')??#?一頁有20條信息
#?遍歷提取出數(shù)據(jù)
for?item?in?items:
????name?=?''.join(item.xpath('.//td[1]/a/text()'))??#?品名
????price?=?''.join(item.xpath('.//td[3]/text()'))???#?最新報(bào)價(jià)
????unit?=?''.join(item.xpath('.//td[4]/text()'))????#?單位
????nums?=?''.join(item.xpath('.//td[5]/text()'))????#?報(bào)價(jià)數(shù)
????time_?=?''.join(item.xpath('.//td[6]/text()'))???#?報(bào)價(jià)時間
????logging.info([name,?price,?unit,?nums,?time_])

三、單線程爬蟲
#?-*-?coding:?UTF-8?-*-
"""
@File ???:單線程.py
@Author ?:葉庭云
@CSDN ???:https://yetingyun.blog.csdn.net/
"""
import?requests
import?logging
from?fake_useragent?import?UserAgent
from?lxml?import?etree
import?openpyxl
from?datetime?import?datetime
#?日志輸出的基本配置
logging.basicConfig(level=logging.INFO,?format='%(asctime)s?-?%(levelname)s:?%(message)s')
#?隨機(jī)產(chǎn)生請求頭
ua?=?UserAgent(verify_ssl=False,?path='fake_useragent.json')
wb?=?openpyxl.Workbook()
sheet?=?wb.active
sheet.append(['品名',?'最新報(bào)價(jià)',?'單位',?'報(bào)價(jià)數(shù)',?'報(bào)價(jià)時間'])
start?=?datetime.now()
for?page?in?range(1,?51):
????#?構(gòu)造URL
????url?=?f'https://www.zhongnongwang.com/quote/product-htm-page-{page}.html'
????#?偽裝請求頭
????headers?=?{
????????"Accept-Encoding":?"gzip",??#?使用gzip壓縮傳輸數(shù)據(jù)讓訪問更快
????????"User-Agent":?ua.random
????}
????#?發(fā)送請求??獲取響應(yīng)
????rep?=?requests.get(url,?headers=headers)
????#?print(rep.status_code)
????#?Xpath定位提取數(shù)據(jù)
????html?=?etree.HTML(rep.text)
????items?=?html.xpath('/html/body/div[10]/table/tr[@align="center"]')
????logging.info(f'該頁有多少條信息:{len(items)}')??#?一頁有20條信息
????#?遍歷提取出數(shù)據(jù)
????for?item?in?items:
????????name?=?''.join(item.xpath('.//td[1]/a/text()'))??#?品名
????????price?=?''.join(item.xpath('.//td[3]/text()'))???#?最新報(bào)價(jià)
????????unit?=?''.join(item.xpath('.//td[4]/text()'))????#?單位
????????nums?=?''.join(item.xpath('.//td[5]/text()'))????#?報(bào)價(jià)數(shù)
????????time_?=?''.join(item.xpath('.//td[6]/text()'))???#?報(bào)價(jià)時間
????????sheet.append([name,?price,?unit,?nums,?time_])
????????logging.info([name,?price,?unit,?nums,?time_])
wb.save(filename='data1.xlsx')
delta?=?(datetime.now()?-?start).total_seconds()
logging.info(f'用時:{delta}s')

單線程爬蟲必須上一個頁面爬取完成才能繼續(xù)爬取,還可能受當(dāng)時網(wǎng)絡(luò)狀態(tài)影響,用時48.528703s,才將數(shù)據(jù)爬取完,速度比較慢。
四、多線程爬蟲
#?-*-?coding:?UTF-8?-*-
"""
@File ???:多線程.py
@Author ?:葉庭云
@CSDN ???:https://yetingyun.blog.csdn.net/
"""
import?requests
import?logging
from?fake_useragent?import?UserAgent
from?lxml?import?etree
import?openpyxl
from?concurrent.futures?import?ThreadPoolExecutor,?wait,?ALL_COMPLETED
from?datetime?import?datetime
#?日志輸出的基本配置
logging.basicConfig(level=logging.INFO,?format='%(asctime)s?-?%(levelname)s:?%(message)s')
#?隨機(jī)產(chǎn)生請求頭
ua?=?UserAgent(verify_ssl=False,?path='fake_useragent.json')
wb?=?openpyxl.Workbook()
sheet?=?wb.active
sheet.append(['品名',?'最新報(bào)價(jià)',?'單位',?'報(bào)價(jià)數(shù)',?'報(bào)價(jià)時間'])
start?=?datetime.now()
def?get_data(page):
????#?構(gòu)造URL
????url?=?f'https://www.zhongnongwang.com/quote/product-htm-page-{page}.html'
????#?偽裝請求頭
????headers?=?{
????????"Accept-Encoding":?"gzip",????#?使用gzip壓縮傳輸數(shù)據(jù)讓訪問更快
????????"User-Agent":?ua.random
????}
????#?發(fā)送請求??獲取響應(yīng)
????rep?=?requests.get(url,?headers=headers)
????#?print(rep.status_code)
????#?Xpath定位提取數(shù)據(jù)
????html?=?etree.HTML(rep.text)
????items?=?html.xpath('/html/body/div[10]/table/tr[@align="center"]')
????logging.info(f'該頁有多少條信息:{len(items)}')??#?一頁有20條信息
????#?遍歷提取出數(shù)據(jù)
????for?item?in?items:
????????name?=?''.join(item.xpath('.//td[1]/a/text()'))???#?品名
????????price?=?''.join(item.xpath('.//td[3]/text()'))????#?最新報(bào)價(jià)
????????unit?=?''.join(item.xpath('.//td[4]/text()'))?????#?單位
????????nums?=?''.join(item.xpath('.//td[5]/text()'))?????#?報(bào)價(jià)數(shù)
????????time_?=?''.join(item.xpath('.//td[6]/text()'))????#?報(bào)價(jià)時間
????????sheet.append([name,?price,?unit,?nums,?time_])
????????logging.info([name,?price,?unit,?nums,?time_])
def?run():
????#?爬取1-50頁
????with?ThreadPoolExecutor(max_workers=6)?as?executor:
????????future_tasks?=?[executor.submit(get_data,?i)?for?i?in?range(1,?51)]
????????wait(future_tasks,?return_when=ALL_COMPLETED)
????wb.save(filename='data2.xlsx')
????delta?=?(datetime.now()?-?start).total_seconds()
????print(f'用時:{delta}s')
run()

五、異步協(xié)程爬蟲
#?-*-?coding:?UTF-8?-*-
"""
@File ???:demo1.py
@Author ?:葉庭云
@CSDN ???:https://yetingyun.blog.csdn.net/
"""
import?aiohttp
import?asyncio
import?logging
from?fake_useragent?import?UserAgent
from?lxml?import?etree
import?openpyxl
from?datetime?import?datetime
#?日志輸出的基本配置
logging.basicConfig(level=logging.INFO,?format='%(asctime)s?-?%(levelname)s:?%(message)s')
#?隨機(jī)產(chǎn)生請求頭
ua?=?UserAgent(verify_ssl=False,?path='fake_useragent.json')
wb?=?openpyxl.Workbook()
sheet?=?wb.active
sheet.append(['品名',?'最新報(bào)價(jià)',?'單位',?'報(bào)價(jià)數(shù)',?'報(bào)價(jià)時間'])
start?=?datetime.now()
class?Spider(object):
????def?__init__(self):
????????#?self.semaphore?=?asyncio.Semaphore(6)??#?信號量,有時候需要控制協(xié)程數(shù),防止爬的過快被反爬
????????self.header?=?{
????????????????"Accept-Encoding":?"gzip",????#?使用gzip壓縮傳輸數(shù)據(jù)讓訪問更快
????????????????"User-Agent":?ua.random
????????????}
????async?def?scrape(self,?url):
????????#?async?with?self.semaphore:??#?設(shè)置最大信號量,有時候需要控制協(xié)程數(shù),防止爬的過快被反爬
????????session?=?aiohttp.ClientSession(headers=self.header,?connector=aiohttp.TCPConnector(ssl=False))
????????response?=?await?session.get(url)
????????result?=?await?response.text()
????????await?session.close()
????????return?result
????async?def?scrape_index(self,?page):
????????url?=?f'https://www.zhongnongwang.com/quote/product-htm-page-{page}.html'
????????text?=?await?self.scrape(url)
????????await?self.parse(text)
????async?def?parse(self,?text):
????????#?Xpath定位提取數(shù)據(jù)
????????html?=?etree.HTML(text)
????????items?=?html.xpath('/html/body/div[10]/table/tr[@align="center"]')
????????logging.info(f'該頁有多少條信息:{len(items)}')??#?一頁有20條信息
????????#?遍歷提取出數(shù)據(jù)
????????for?item?in?items:
????????????name?=?''.join(item.xpath('.//td[1]/a/text()'))??#?品名
????????????price?=?''.join(item.xpath('.//td[3]/text()'))??#?最新報(bào)價(jià)
????????????unit?=?''.join(item.xpath('.//td[4]/text()'))??#?單位
????????????nums?=?''.join(item.xpath('.//td[5]/text()'))??#?報(bào)價(jià)數(shù)
????????????time_?=?''.join(item.xpath('.//td[6]/text()'))??#?報(bào)價(jià)時間
????????????sheet.append([name,?price,?unit,?nums,?time_])
????????????logging.info([name,?price,?unit,?nums,?time_])
????def?main(self):
????????#?50頁的數(shù)據(jù)
????????scrape_index_tasks?=?[asyncio.ensure_future(self.scrape_index(page))?for?page?in?range(1,?51)]
????????loop?=?asyncio.get_event_loop()
????????tasks?=?asyncio.gather(*scrape_index_tasks)
????????loop.run_until_complete(tasks)
if?__name__?==?'__main__':
????spider?=?Spider()
????spider.main()
????wb.save('data3.xlsx')
????delta?=?(datetime.now()?-?start).total_seconds()
????print("用時:{:.3f}s".format(delta))


六、總結(jié)回顧
多線程(multithreading):是指從軟件或者硬件上實(shí)現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)。具有多線程能力的計(jì)算機(jī)因有硬件支持而能夠在同一時間執(zhí)行多于一個線程,進(jìn)而提升整體處理性能。具有這種能力的系統(tǒng)包括對稱多處理機(jī)、多核心處理器以及芯片級多處理或同時多線程處理器。在一個程序中,這些獨(dú)立運(yùn)行的程序片段叫作 "線程" (Thread),利用它編程的概念就叫作 "多線程處理"。 異步(asynchronous):為完成某個任務(wù),不同程序單元之間過程中無需通信協(xié)調(diào),也能完成任務(wù)的方式,不相關(guān)的程序單元之間可以是異步的。例如,爬蟲下載網(wǎng)頁。調(diào)度程序調(diào)用下載程序后,即可調(diào)度其他任務(wù),而無需與該下載任務(wù)保持通信以協(xié)調(diào)行為。不同網(wǎng)頁的下載、保存等操作都是無關(guān)的,也無需相互通知協(xié)調(diào)。這些異步操作的完成時刻并不確定。簡言之,異步意味著無序。 協(xié)程(coroutine),又稱微線程、纖程,協(xié)程是一種用戶態(tài)的輕量級線程。協(xié)程擁有自己的寄存器上下文和棧。協(xié)程調(diào)度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復(fù)先前保存的寄存器上下文和棧。因此協(xié)程能保留上一次調(diào)用時的狀態(tài),即所有局部狀態(tài)的一個特定組合,每次過程重入時,就相當(dāng)于進(jìn)入上一次調(diào)用的狀態(tài)。協(xié)程本質(zhì)上是個單進(jìn)程,協(xié)程相對于多進(jìn)程來說,無需線程上下文切換的開銷,無需原子操作鎖定及同步的開銷,編程模型也非常簡單。我們可以使用協(xié)程來實(shí)現(xiàn)異步操作,比如在網(wǎng)絡(luò)爬蟲場景下,我們發(fā)出一個請求之后,需要等待一定的時間才能得到響應(yīng),但其實(shí)在這個等待過程中,程序可以干許多其他的事情,等到響應(yīng)得到之后才切換回來繼續(xù)處理,這樣可以充分利用 CPU 和其他資源,這就是協(xié)程的優(yōu)勢。
作者:葉庭云? CSDN:https://yetingyun.blog.csdn.net/ 熱愛可抵歲月漫長,發(fā)現(xiàn)求知的樂趣,在不斷總結(jié)和學(xué)習(xí)中進(jìn)步,與諸君共勉。
更多閱讀
特別推薦

點(diǎn)擊下方閱讀原文加入社區(qū)會員
評論
圖片
表情
