[[378975]]
创新互联建站-专业网站定制、快速模板网站建设、高性价比市中网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式市中网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖市中地区。费用合理售后完善,十年实体公司更值得信赖。
一、前言
今天我要给大家分享的是如何爬取中农网产品报价数据,并分别用普通的单线程、多线程和协程来爬取,从而对比单线程、多线程和协程在网络爬虫中的性能。
目标URL:https://www.zhongnongwang.com/quote/product-htm-page-1.html
爬取产品品名、最新报价、单位、报价数、报价时间等信息,保存到本地Excel。
二、爬取测试
翻页查看 URL 变化规律:
- 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
检查网页,可以发现网页结构简单,容易解析和提取数据。
思路:每一条产品报价信息在 class 为 tb 的 table 标签下的 tbody 下的 tr 标签里,获取到所有 tr 标签的内容,然后遍历,从中提取出每一个产品品名、最新报价、单位、报价数、报价时间等信息。
- # -*- 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, headersheaders=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_])
运行结果如下:
可以成功爬取到数据,接下来分别用普通的单线程、多线程和协程来爬取 50 页的数据、保存到Excel。
三、单线程爬虫
- # -*- 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, headersheaders=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, headersheaders=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()
运行结果如下:
多线程爬虫爬取效率提升非常可观,用时 2.648128s,爬取速度很快。
五、异步协程爬虫
- # -*- 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))
运行结果如下:
而到了协程异步爬虫,爬取速度更快,嗖的一下,用时 0.930s 就爬取完 50 页数据,aiohttp + asyncio 异步爬虫竟恐怖如斯。异步爬虫在服务器能承受高并发的前提下增加并发数量,爬取效率提升是非常可观的,比多线程还要快一些。
三种爬虫都将 50 页的数据爬取下来保存到了本地,结果如下:
六、总结回顾
今天我演示了简单的单线程爬虫、多线程爬虫和协程异步爬虫。可以看到一般情况下异步爬虫速度最快,多线程爬虫略慢一点,单线程爬虫速度较慢,必须上一个页面爬取完成才能继续爬取。
但协程异步爬虫相对来说并不是那么好编写,数据抓取无法使用 request 库,只能使用aiohttp,而且爬取数据量大时,异步爬虫需要设置最大信号量来控制协程数,防止爬的过快被反爬。所以在实际编写 Python 爬虫时,我们一般都会使用多线程爬虫来提速,但必须注意的是网站都有 ip 访问频率限制,爬的过快可能会被封ip,所以一般我们在多线程提速的同时可以使用代理 ip 来并发地爬取数据。
当前标题:Python爬虫实战:单线程、多线程和协程性能对比
网址分享:http://www.csdahua.cn/qtweb/news39/511289.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网