Python爬虫实战:单线程、多线程和协程性能对比
Python中文社区
共 9309字,需浏览 19分钟
·
2021-01-29 11:15
一、前言
二、爬取测试
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')
# 随机产生请求头
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压缩传输数据让访问更快
"User-Agent": ua.random
}
# 发送请求 获取响应
rep = requests.get(url, headers=headers)
print(rep.status_code) # 200
# Xpath定位提取数据
html = etree.HTML(rep.text)
items = html.xpath('/html/body/div[10]/table/tr[@align="center"]')
logging.info(f'该页有多少条信息:{len(items)}') # 一页有20条信息
# 遍历提取出数据
for item in items:
name = ''.join(item.xpath('.//td[1]/a/text()')) # 品名
price = ''.join(item.xpath('.//td[3]/text()')) # 最新报价
unit = ''.join(item.xpath('.//td[4]/text()')) # 单位
nums = ''.join(item.xpath('.//td[5]/text()')) # 报价数
time_ = ''.join(item.xpath('.//td[6]/text()')) # 报价时间
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')
# 随机产生请求头
ua = UserAgent(verify_ssl=False, path='fake_useragent.json')
wb = openpyxl.Workbook()
sheet = wb.active
sheet.append(['品名', '最新报价', '单位', '报价数', '报价时间'])
start = datetime.now()
for page in range(1, 51):
# 构造URL
url = f'https://www.zhongnongwang.com/quote/product-htm-page-{page}.html'
# 伪装请求头
headers = {
"Accept-Encoding": "gzip", # 使用gzip压缩传输数据让访问更快
"User-Agent": ua.random
}
# 发送请求 获取响应
rep = requests.get(url, headers=headers)
# print(rep.status_code)
# Xpath定位提取数据
html = etree.HTML(rep.text)
items = html.xpath('/html/body/div[10]/table/tr[@align="center"]')
logging.info(f'该页有多少条信息:{len(items)}') # 一页有20条信息
# 遍历提取出数据
for item in items:
name = ''.join(item.xpath('.//td[1]/a/text()')) # 品名
price = ''.join(item.xpath('.//td[3]/text()')) # 最新报价
unit = ''.join(item.xpath('.//td[4]/text()')) # 单位
nums = ''.join(item.xpath('.//td[5]/text()')) # 报价数
time_ = ''.join(item.xpath('.//td[6]/text()')) # 报价时间
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')
单线程爬虫必须上一个页面爬取完成才能继续爬取,还可能受当时网络状态影响,用时48.528703s,才将数据爬取完,速度比较慢。
四、多线程爬虫
# -*- 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')
# 随机产生请求头
ua = UserAgent(verify_ssl=False, path='fake_useragent.json')
wb = openpyxl.Workbook()
sheet = wb.active
sheet.append(['品名', '最新报价', '单位', '报价数', '报价时间'])
start = datetime.now()
def get_data(page):
# 构造URL
url = f'https://www.zhongnongwang.com/quote/product-htm-page-{page}.html'
# 伪装请求头
headers = {
"Accept-Encoding": "gzip", # 使用gzip压缩传输数据让访问更快
"User-Agent": ua.random
}
# 发送请求 获取响应
rep = requests.get(url, headers=headers)
# print(rep.status_code)
# Xpath定位提取数据
html = etree.HTML(rep.text)
items = html.xpath('/html/body/div[10]/table/tr[@align="center"]')
logging.info(f'该页有多少条信息:{len(items)}') # 一页有20条信息
# 遍历提取出数据
for item in items:
name = ''.join(item.xpath('.//td[1]/a/text()')) # 品名
price = ''.join(item.xpath('.//td[3]/text()')) # 最新报价
unit = ''.join(item.xpath('.//td[4]/text()')) # 单位
nums = ''.join(item.xpath('.//td[5]/text()')) # 报价数
time_ = ''.join(item.xpath('.//td[6]/text()')) # 报价时间
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()
五、异步协程爬虫
# -*- 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')
# 随机产生请求头
ua = UserAgent(verify_ssl=False, path='fake_useragent.json')
wb = openpyxl.Workbook()
sheet = wb.active
sheet.append(['品名', '最新报价', '单位', '报价数', '报价时间'])
start = datetime.now()
class Spider(object):
def __init__(self):
# self.semaphore = asyncio.Semaphore(6) # 信号量,有时候需要控制协程数,防止爬的过快被反爬
self.header = {
"Accept-Encoding": "gzip", # 使用gzip压缩传输数据让访问更快
"User-Agent": ua.random
}
async def scrape(self, url):
# async with self.semaphore: # 设置最大信号量,有时候需要控制协程数,防止爬的过快被反爬
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定位提取数据
html = etree.HTML(text)
items = html.xpath('/html/body/div[10]/table/tr[@align="center"]')
logging.info(f'该页有多少条信息:{len(items)}') # 一页有20条信息
# 遍历提取出数据
for item in items:
name = ''.join(item.xpath('.//td[1]/a/text()')) # 品名
price = ''.join(item.xpath('.//td[3]/text()')) # 最新报价
unit = ''.join(item.xpath('.//td[4]/text()')) # 单位
nums = ''.join(item.xpath('.//td[5]/text()')) # 报价数
time_ = ''.join(item.xpath('.//td[6]/text()')) # 报价时间
sheet.append([name, price, unit, nums, time_])
logging.info([name, price, unit, nums, time_])
def main(self):
# 50页的数据
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))
六、总结回顾
多线程(multithreading):是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作 "线程" (Thread),利用它编程的概念就叫作 "多线程处理"。 异步(asynchronous):为完成某个任务,不同程序单元之间过程中无需通信协调,也能完成任务的方式,不相关的程序单元之间可以是异步的。例如,爬虫下载网页。调度程序调用下载程序后,即可调度其他任务,而无需与该下载任务保持通信以协调行为。不同网页的下载、保存等操作都是无关的,也无需相互通知协调。这些异步操作的完成时刻并不确定。简言之,异步意味着无序。 协程(coroutine),又称微线程、纤程,协程是一种用户态的轻量级线程。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此协程能保留上一次调用时的状态,即所有局部状态的一个特定组合,每次过程重入时,就相当于进入上一次调用的状态。协程本质上是个单进程,协程相对于多进程来说,无需线程上下文切换的开销,无需原子操作锁定及同步的开销,编程模型也非常简单。我们可以使用协程来实现异步操作,比如在网络爬虫场景下,我们发出一个请求之后,需要等待一定的时间才能得到响应,但其实在这个等待过程中,程序可以干许多其他的事情,等到响应得到之后才切换回来继续处理,这样可以充分利用 CPU 和其他资源,这就是协程的优势。
作者:叶庭云 CSDN:https://yetingyun.blog.csdn.net/ 热爱可抵岁月漫长,发现求知的乐趣,在不断总结和学习中进步,与诸君共勉。
更多阅读
特别推荐
点击下方阅读原文加入社区会员
评论