爬虫--高性能异步爬虫

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

# 单线程下的串行数据爬取

1.阻塞式的爬虫

2.依次,单线程,效率低

复制代码
import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome...'
}
urls = [
    'http:www.baidu.com/ceshi/aaa.rar',
    'http:www.baidu.com/ceshi/bbb.rar',
    'http:www.baidu.com/ceshi/bbb.rar',
]

def get_content(url):
    print('正在爬取:',url)
    response = requests.get(url=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)
复制代码

异步爬虫的方式:

  --多线程,多进程(不建议)

    优点:为阻塞操作开启线程或者进程,阻塞操作异步执行

    弊端:无法无限制的开启多线程或者多进程,需要频繁的创建和销毁进程和线程,过分耗费资源

  --线程池,进程池(适当使用)

    优点:可以降低进程和线程创建和销毁的频率,减少系统资源耗费

    弊端:池中线程或进程数量是有上限的  进程process  线程thread

多进程池基础代码:  

复制代码
import requests
from multiprocessing.dummy import Pool

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome...'
}
urls = [
    'https://xxxxxxx/page/1/',
    'https://xxxxxxx/page/2/',
    'https://xxxxxxx/page/3/',
    'https://xxxxxxx/page/4/',
]

pool = Pool(4)
def get_content(url):
    print('正在爬取:', url)
    response = requests.get(url=url, headers=headers)
    if response.status_code == 200:
        return response.text

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

def test(url):
    content = get_content(url)
    parse_content(content)
    
pool.map(test,urls)
# 调用结束后关闭线程池,主线程等待子线程结束后再结束
poll.close()
poll.join()
复制代码

线程池进程池使用基本原则: 进程线程池处理的是阻塞且耗时的操作

案例:梨视频视频地址抓取 (点我)

 

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

evnet_loop:事件循环,相当于一个无限循环,当把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行.

coroutine:协程对象,将协程对象注册到事件循环中,它会被事件循环调用;一般使用async关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象.

task:任务,它是对协程对象的进一步封装,包含了任务的各个状态

future:代表将来执行或还没有执行的任务,实际上和task没有本质区别

async:定义一个协程

await:用来挂起阻塞方法的执行

协程基础代码:

复制代码
import asyncio

async def request(url):
    print('正在请求的url是',url)
    print('请求成功,',url)

# async修饰的函数,调用之后返回一个协程对象
cort = request('www.baidu.com')

# 创建一个事件循环对象
loop = asyncio.get_event_loop()

# 将协程对象注册到loop中,然后启动loop
loop.run_until_complete(cort)

# task的使用
loop = asyncio.get_event_loop()
# 基于loop创建了一个task对象
task = loop.create_task(cort)
# 将task注册到loop中,然后启动loop
loop.run_until_complete(task) # future的使用,和task非常类似
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(cort)
loop.run_until_complete(future) # 需求:当事件循环对象执行结束后运行一个回调函数
# 回调绑定
def callback_func(future):
print(future.result())
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(cort)
# 将回调函数绑定到任务对象中
future.add_done_callback(callback_func)
loop.run_until_complete(future)
复制代码

多任务异步协程基础代码:

复制代码
import asyncio
import time

async def request(url):
    print('正在下载',url)
    # 在异步协程中如果出现了同步模块相关的代码,那么就无法实现异步,time模块就是同步模块
    # time.sleep(2)
    # 当在asyncio中遇到阻塞操作必须进行手动挂起
    await asyncio.sleep(2) # 异步模块中的时间
    print('下载完毕',url)

urls = [
    'www.baidu.com',
    'www.sougou.com',
    'www.163.com'
]

# 任务列表:存放多个任务对象
tasks = []
for url in urls:
    c = request(url)
    task =asyncio.ensure_future(c)
    tasks.append(task)

loop = asyncio.get_event_loop()
# 需要将任务列表封装到wait中,列表的固定语法格式
# 多任务协程异步执行
loop.run_until_complete(asyncio.wait(tasks))
复制代码

 

aiohttp模块:基于异步网络请求的模块

requests模块是基于同步网络请求的模块,而在异步协程中如果出现了同步模块相关的代码,那么就无法实现异步,time模块也是同步模块

因此,aiohttp模块就能够在异步工作中代替requests模块

复制代码
import asyncio
import aiohttp
import time


urls = [
    'https://www.baidu.com',
    'https://www.sougou.com',
    'https://www.163.com'
]

async def get_page(url):
    async with aiohttp.ClientSession() as session:
        # get(),post():
        # headers,params/data,proxy='http://ip:port',同步中是用的proxies
        # 任何会有阻塞的地方都要手动挂起await
        async with await session.get(url) as response:
            # text()返回字符串形式的响应数据
            # read()返回的二进制形式的相应数据
            # json()返回的就是json对象
            page_text = await response.text()
            print(page_text)

# 任务列表:存放多个任务对象
tasks = []
for url in urls:
    c = get_page(url)
    task =asyncio.ensure_future(c)
    tasks.append(task)

loop = asyncio.get_event_loop()
# 需要将任务列表封装到wait中,列表的固定语法格式
# 多任务协程异步执行
loop.run_until_complete(asyncio.wait(tasks))
复制代码

 

posted @   EricYJChung  阅读(100)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示