Python爬虫从0到1(第八天)——多任务异步爬虫

第八天——多任务异步爬虫

一、异步爬虫概述

爬虫的本质:就是客户端向服务器请求批量获取响应数据。如果有多个待爬取的url的话,只用一个线程切采用串行的方式执行,那么只能等待一个爬虫任务完成之后才能继续下一个爬虫任务,这样的话效率是非常低的。

所以,在我们心中其实很自然的就可以想到使用异步机制来提高爬虫的速度。通过构建进程池或者线程池来完成异步爬虫,即使用多进程或多线程来处理多个请求,当别的进程或线程发生阻塞时,自动切换到另一进程或线程执行下一个爬虫任务。

高性能异步爬虫的目的:在爬虫中使用异步实现高性能的数据爬取操作。

注意:python中无法实现真正的多线程,因为其并不能充分利用多核CPU资源。如果想要充分利用CPU资源的话大部分情况下都是使用的多进程(multiprocessing)

import requests
import time


t1 = time.time()
urls = ['https://scpic1.chinaz.net/Files/pic/pic9/202201/hpic4949_s.jpg',
       'https://scpic.chinaz.net/Files/pic/pic9/202201/hpic4950_s.jpg',
       'https://scpic1.chinaz.net/Files/pic/pic9/202201/hpic4948_s.jpg',
        'https://scpic2.chinaz.net/Files/pic/pic9/202201/apic37939_s.jpg',
        'https://scpic3.chinaz.net/Files/pic/pic9/202201/apic37931_s.jpg',
        'https://scpic3.chinaz.net/Files/pic/pic9/202201/apic37934_s.jpg',
        'https://scpic.chinaz.net/Files/pic/pic9/202201/apic37936_s.jpg']
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'}
def get_content(url):
    print('正在爬取', url)
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        return response.content


def parse_content(content):
    print('响应数据长度为为:',len(content))


for url in urls:
    content = get_content(url)
    parse_content(content)

t2 = time.time()
print('程序总用时为:', t2-t1)

二、异步爬虫的方式

2.1 多线程、多进程(不推荐)
  • 优点:可以为相关阻塞的操作开启线程或者进程,阻塞操作就可以异步执行。

  • 缺点:不能够无限制的开启多进程或者多线程

    爬虫程序模拟(单线程):

    import time
    
    t1 = time.time()
    lst = []
    urls = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    result = []
    
    
    def GetResponse(url):
        global result
        time.sleep(2)
        r = url * url
        result.append(r)
        
        
    if __name__ == '__main__':
            for url in urls:
            	GetResponse(url)
           	t2 = time.time()
            print(result)
            print('消耗时间:', t2-t1)
    

    爬虫程序模拟(异步,多进程使用):

    import time
    import multiprocessing
    
    
    t1 = time.time()
    lst = []
    urls = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    result = []
    
    
    def GetResponse(url, queue):
        time.sleep(2)
        r = url * url
        queue.put(r)
    
    
    if __name__ == '__main__':
        jobs = []
        queues = []
        for url in urls:
            queue = multiprocessing.Queue()
            queues.append(queue)
            p = multiprocessing.Process(target=GetResponse, args=(url, queue))
            jobs.append(p)
            p.start()
    
        for i, t in enumerate(jobs):
            t.join()
            result.append(queues[i].get())
        t2 = time.time()
        print(result)
        print('消耗时间:', t2-t1)
    
    
    

    基于管道通信的多进程爬虫实现:

    import requests
    import time
    import multiprocessing
    from multiprocessing import Queue
    
    
    t1 = time.time()
    urls = ['https://scpic1.chinaz.net/Files/pic/pic9/202201/hpic4949_s.jpg',
           'https://scpic.chinaz.net/Files/pic/pic9/202201/hpic4950_s.jpg',
           'https://scpic1.chinaz.net/Files/pic/pic9/202201/hpic4948_s.jpg',
            'https://scpic2.chinaz.net/Files/pic/pic9/202201/apic37939_s.jpg',
            'https://scpic3.chinaz.net/Files/pic/pic9/202201/apic37931_s.jpg',
            'https://scpic3.chinaz.net/Files/pic/pic9/202201/apic37934_s.jpg',
            'https://scpic.chinaz.net/Files/pic/pic9/202201/apic37936_s.jpg']
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'}
    result = []     # 保存最终结果
    
    
    def get_content(url, queue):
        global result
        print('正在爬取', url)
        result.append(url)
        queue.put(url)      # 将当前url添加到队列中
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            return response.content
    
    
    def parse_content(content):
        print('响应数据长度为为:',len(content))
    
    
    if __name__ == '__main__':
        queues = []
        jobs = []
    
        for url in urls:
            queue = Queue()
            queues.append(queue)
            p = multiprocessing.Process(target=get_content, args=(url, queue))
            jobs.append(p)
            p.start()
        for i, t in enumerate(jobs):
            t.join()
            result.append(queues[i].get())
    
        t2 = time.time()
        print(result)
        print('程序总用时为:', t2-t1)
    
2.2 线程池、进程池(适当使用)
  • 优点:可以降低系统对进程或者线程创建和销毁的频率,从而很好的降低系统的开销
  • 缺点:池中线程或进程的数量是有上限的。

模拟爬虫操作:

import time
from multiprocessing.dummy import Pool


t1 = time.time()
lst = []
urls = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result = []


def GetResponse(url):
    time.sleep(2)
    r = url * url
    result.append(r)


if __name__ == '__main__':
    pools = Pool(9)
    pools.map(GetResponse, urls)
    t2 = time.time()
    print(result)
    print('占用时长:', t2-t1)

2.3 单线程+异步协程(推荐)

​ 协程概念是从python3.4开始,从3.6的时候进行简化封装,在本课程中的协程是基于python3.6之后

  • event_loop:事件循环,相当于一个无限循环,可以把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行。
  • coroutine:协程对象,可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用async关键字来定义一个方法,这个方法在调用时并不会立即被执行,而是返回一个协程对象
  • task:任务,是对协程对象的进一步封装,包含了任务的各个状态。
  • future:代表将来执行或还没有执行的任务,实际上和task没有本质区别。
  • async:定义一个协程。
  • await:用于挂起阻塞方法的执行
2.3.1 task的使用
import time
import asyncio


t1 = time.time()
lst = []
urls = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result = []


async def GetResponse(url):
    await asyncio.sleep(3)
    r = url * url
    print(r)
    result.append(r)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()		# 创建loop对象

    task = loop.create_task(GetResponse(9))		# 创建任务对象
    print(task)
    loop.run_until_complete(task)	# 运行直到任务结束
    t2 = time.time()
    print(task)
    print(result)
    print('占用时长:', t2-t1)

2.3.2 future的使用
import time
import asyncio


t1 = time.time()
lst = []
urls = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result = []


async def GetResponse(url):
    await asyncio.sleep(3)      # 注意阻塞操作的时候需要进行await手动挂起
    r = url * url
    print(r)
    result.append(r)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()     # 创建loop对象
    task = asyncio.ensure_future(GetResponse(9))    # 创建任务对象
    print(task)
    loop.run_until_complete(task)   # 运行直到任务结束
    t2 = time.time()
    print(task)
    print(result)
    print('占用时长:', t2-t1)

2.3.3 绑定回调
import time
import asyncio


t1 = time.time()
lst = []
urls = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result = []


async def GetResponse(url):
    await asyncio.sleep(3)      # 注意阻塞操作的时候需要进行await手动挂起
    r = url * url
    result.append(r)
    return r

def callback_func(task):
    # result返回的就是任务对象中封装的协程对象对应的函数的返回值
    print(task.result())

if __name__ == '__main__':
    loop = asyncio.get_event_loop()     # 创建loop对象
    task = asyncio.ensure_future(GetResponse(9))    # 创建任务对象
    print(task)
    # 将回调函数绑定到任务对象中
    task.add_done_callback(callback_func)   # 当任务执行成功之后就会回调到回调函数中,默认将任务对象传递到回调函数中
    loop.run_until_complete(task)   # 运行直到任务结束
    t2 = time.time()
    print(task)
    print(result)
    print('占用时长:', t2-t1)

三、多任务异步协程实现

import time
import asyncio


t1 = time.time()
lst = []
urls = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result = []
CONCURRENCY = 5     # 定义最大协程数(常量)
semaphore = asyncio.Semaphore(CONCURRENCY)      # 控制并发量


async def GetResponse(url):
    await asyncio.sleep(3)      # 注意阻塞操作的时候需要进行await手动挂起,如果不挂起,就不会等待阻塞操作结束直接运行后面的代码,可能导致数据紊乱
    r = url * url
    result.append(r)
    return r


def callback_func(task):
    # result返回的就是任务对象中封装的协程对象对应的函数的返回值
    print(task.result())


if __name__ == '__main__':
    loop = asyncio.get_event_loop()     # 创建loop对象
    tasks = []
    for url in urls:
        task = asyncio.ensure_future(GetResponse(url))    # 创建任务对象
        tasks.append(task)
    # 将回调函数绑定到任务对象中

    loop.run_until_complete(asyncio.wait(tasks))   # 运行直到任务结束,注意多任务的格式,需要将任务列表封装到wait中
    t2 = time.time()
    print(result)
    print('占用时长:', t2-t1)

四、多任务异步爬虫实现

在爬虫开发中,requests模块是一个同步模块,所以不能使用于异步任务中。为了实现真正的异步,需要我们引入aiohttp模块来替换requests模块来实现网络请求操作。

安装

pip install aiohttp

使用ClientSession来发送

import asyncio

import aiohttp
import time



t1 = time.time()
urls = ['https://scpic1.chinaz.net/Files/pic/pic9/202201/hpic4949_s.jpg',
       'https://scpic.chinaz.net/Files/pic/pic9/202201/hpic4950_s.jpg',
       'https://scpic1.chinaz.net/Files/pic/pic9/202201/hpic4948_s.jpg',
        'https://scpic2.chinaz.net/Files/pic/pic9/202201/apic37939_s.jpg',
        'https://scpic3.chinaz.net/Files/pic/pic9/202201/apic37931_s.jpg',
        'https://scpic3.chinaz.net/Files/pic/pic9/202201/apic37934_s.jpg',
        'https://scpic.chinaz.net/Files/pic/pic9/202201/apic37936_s.jpg']
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'}
result = []     # 保存最终结果


async def get_content(url):
    async with aiohttp.ClientSession() as session:
        #  注意代理使用的是proxy='http://ip:port'
        async with await session.get(url, headers=headers)as response:
            global result
            print('正在爬取', url)
            result.append(url)
            if response.status == 200:
                res = response.content
                return res


if __name__ == '__main__':
    tasks = []
    for url in urls:
        c = get_content(url)
        task = asyncio.ensure_future(c)
        tasks.append(task)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))

    t2 = time.time()
    print(result)
    print('程序总用时为:', t2-t1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python爬虫异步是指使用并发的方式进行网页数据的获取和处理,以提高效率和性能。在异步爬虫中,可以使用多种方法实现异步操作,如使用aiohttp和asyncio库进行异步HTTP请求,使用协程来处理异步任务,以及使用线程池和进程池来实现异步执行等。 一种常见的异步爬虫实现方式是使用aiohttp和asyncio库。通过创建一个信号量对象,可以控制并发量的大小,使用async with语句将信号量作为上下文对象,从而在爬取方法中控制并发量。另外,在main方法中使用asyncio.gather方法来同时执行多个爬取任务,以提高效率。 另一种实现异步爬虫的方式是使用asyncio库中的wait方法来执行多个请求任务。首先,将需要执行的任务封装成协程对象,并放入一个任务列表中。然后,使用asyncio.wait方法来执行任务列表中的任务,并通过循环获取每个任务的结果。 除了使用aiohttp和asyncio库,还可以使用其他库来实现异步爬虫。例如,使用aiohttp库结合时间模块来控制请求的延迟时间,从而模拟异步请求的效果。在get方法中使用await关键字挂起请求,并在得到响应后再继续执行。 此外,还可以使用线程池或进程池来实现异步爬虫。通过使用multiprocessing.dummy库中的Pool类,可以创建一个线程池,并使用map方法将任务分配到多个线程中并发执行。这样可以降低系统对线程或进程的创建和销毁频率,提高系统的性能和效率。 综上所述,Python爬虫异步是通过使用异步操作的方式来提高爬取效率和性能的一种方法。可以使用aiohttp和asyncio库、线程池或进程池等不同的工具和技术来实现异步爬虫。这些方法都能够提供更高并发量和更快的响应速度,从而提升爬虫的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

quanmoupy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值