python爬虫-高能异步执行

背景

前几天看了网上的一个视频,将异步执行的,试了一下,效果杠杠的
文章地址:https://www.cnblogs.com/bobo-zhang/p/10735140.html

对比

与上一篇非异步执行的放在一起测试,立竿见影

非异步执行:

-------非异步执行,爬取10个页面并写入本地,耗时3秒多
非异步执行,爬取10个页面并写入本地,耗时3秒多

异步执行:

-------异步执行,爬取10个页面并写入本地,耗时仅0.4秒
异步执行,爬取10个页面并写入本地,耗时仅0.4秒

异步执行背景

转载自博客园:博客网址:https://www.cnblogs.com/bobo-zhang/p/10735140.html

背景:
其实爬虫的本质就是client发请求批量获取server的响应数据,
如果我们有多个url待爬取,只用一个线程且采用串行的方式执行,那只能等待爬取一个结束后才能继续下一个,效率会非常低。
需要强调的是:对于单线程下串行N个任务,并不完全等同于低效,如果这N个任务都是纯计算的任务,
那么该线程对cpu的利用率仍然会很高,之所以单线程下串行多个爬虫任务低效,是因为爬虫任务是明显的IO密集型(阻塞)程序。
那么该如何提高爬取性能呢?

  • a. 解决同步调用方案之多线程/多进程
    好处:在服务器端使用多线程(或多进程)。
    多线程(或多进程)的目的是让每个连接都拥有独立的线程(或进程),这样任何一个连接的阻塞都不会影响其他的连接。
    弊端:开启多进程或都线程的方式,我们是无法无限制地开启多进程或多线程的:在遇到要同时响应成百上千路的连接请求,
    则无论多线程还是多进程都会严重占据系统资源,降低系统对外界响应效率,而且线程与进程本身也更容易进入假死状态。
  • b. 解决同步调用方案之线程/进程池
    好处:很多程序员可能会考虑使用“线程池”或“连接池”。
    “线程池”旨在减少创建和销毁线程的频率,其维持一定合理数量的线程,并让空闲的线程重新承担新的执行任务。可以很好的降低系统开销。
    弊端:“线程池”和“连接池”技术也只是在一定程度上缓解了频繁调用IO接口带来的资源占用。
    而且,所谓“池”始终有其上限,当请求大大超过上限时,“池”构成的系统对外界的响应并不比没有池的时候效果好多少。
    所以使用“池”必须考虑其面临的响应规模,并根据响应规模调整“池”的大小。

以下是测试的源码,具体用法解析参见原博客文章

源码

"""
高性能异步爬虫
"""
from lxml import etree
import time
import asyncio
import aiohttp

headers = {'User-Agent': 'Mozilla/5.0'}
# 抓取一个网址是0.3秒
# url = 'https://www.qiushibaike.com/text/page/1'


# 需要用asihttp来请求,因为requests不支持异步模式
async def req(url):
    async with aiohttp.ClientSession() as s:
        async with await s.get(url) as response:
            page_text = await response.text()
            return page_text


def parse(task):
    page_text = task.result()
    tree = etree.HTML(page_text)
    div_list = tree.xpath('//div[@class="col1 old-style-col1"]/div')
    for div in div_list:
        author = div.xpath('./div[@class="author clearfix"]/a[2]/h2/text()')[0]
        detail_text = div.xpath('.//div[@class="content"]/span[1]//text()')
        detail_text = ''.join(detail_text)
        fp.write(author + detail_text + '\n\n\n\n')


print('----单线程多任务异步执行----')
fp = open('./qiubai_download_yibuzhixing.txt', 'w', encoding='utf-8')
page = input('请输入爬取页数:')
start = time.time()
url_head = 'https://www.qiushibaike.com/text/page/'
urls = []
for i in range(1, int(page) + 1):
    url_each = url_head + str(i)
    urls.append(url_each)

tasks = []
for url in urls:
    c = req(url)
    # 将异步执行进行task对象封装
    task = asyncio.ensure_future(c)
    # 给task任务对象添加callback
    task.add_done_callback(parse)
    # 将多任务task添加到列表
    tasks.append(task)

# 创建循环体
loop = asyncio.get_event_loop()
# 将task放入循环体,并开始执行,需添加wait
loop.run_until_complete(asyncio.wait(tasks))
fp.close()
print(time.time() - start)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值