python-39-异步aiohttp爬取数据

news2024/11/24 3:34:59

Python分别用单线程,多线程,异步协程爬取一部小说,最快仅需要5s
Python异步爬虫之协程抓取妹子图片(aiohttp、aiofiles)
Python爬虫——教你异步爬虫二十秒爬完两百多万字六百多章的小说
python爬虫-异步爬虫
用python批量把小说编号由大写数字替换成阿拉伯数字

1 同步与异步

1.1 同步

同步是有序,为了完成某个任务,在执行的过程中,按照顺序一步一步执行下去,直到任务完成。

爬虫是IO密集型任务,我们使用requests请求库来爬取某个站点时,网络顺畅无阻塞的时候,正常情况如下图所示:
在这里插入图片描述
但在网络请求返回数据之前,程序是处于阻塞状态的,程序在等待某个操作完成期间,自身无法继续干别的事情,如下图所示:
在这里插入图片描述
当然阻塞可以发生在站点响应后的执行程序那里,执行程序可能是下载程序,下载是需要时间的。当站点没响应或者程序卡在下载程序的时候,CPU一直在等待而不去执行其他程序,那么就白白浪费了CPU的资源,导致我们的爬虫效率很低。

1.2 异步

异步是一种比多线程高效得多的并发模型,是无序的,为了完成某个任务,在执行的过程中,不同程序单元之间过程中无需通信协调,也能完成任务的方式,也就是说不相关的程序单元之间可以是异步的。如下图所示:
在这里插入图片描述
当请求程序发送网络请求1并收到某个站点的响应后,开始执行程序中的下载程序,由于下载需要时间或者其他原因使处于阻塞状态,请求程序和下载程序是不相关的程序单元,所以请求程序发送下一个网络请求,也就是异步。

(1)微观上异步协程是一个任务一个任务的进行切换,切换条件一般就是IO操作;
(2)宏观上异步协程是多个任务一起在执行;
注意:上面我们所讲的一切都是在单线程的条件下实现。

1.3 协程

协程,英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程 。最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是用户执行)。

优势就是性能得到了很大的提升,不会像线程切换那样消耗资源。协程的开销远远小于线程的开销。协程本质是单线程,在不占用更多系统资源的情况下,将IO操作进行了挂起,然后继续执行其他任务,等待IO操作完成之后,再返回原来的任务继续执行。

2 协程

import requests #同步的网络请求模块
import re       #正则模块,用于提取数据
import asyncio  #创建并管理事件循环的模块
import aiofiles #可异步的文件操作模块
import aiohttp  #可异步的网络请求模块
import os       #可调用操作系统的模块

2.1 协程库asyncio

从asyncio模块中直接获取一个EventLoop的引用,把需要执行的协程放在EventLoop中执行,这就实现了异步协程。协程通过async语法进行声明为异步协程方法,await语法进行声明为异步协程可等待对象,是编写asyncio应用的推荐方式。
(1)event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事情循环上,当满足发生条件时,就调用对应的处理方法。
(2)coroutine:中文翻译叫协程,在Python中常指代协程对象类型,我们可以将协程对象注册到时间循环中,它会被事件训话调用。我们可以使用async关键字来定义一个方法,这个方法在调用时不会立即被执行,而是会返回一个协程对象。
(3)task:任务,这是对协程对象的进一步封装,包含协程对象的各个状态。
(4)future:代表将来执行或者没有执行的任务的结果,实际上和task没有本质区别。

2.1.1 定义协程

# -*- coding: utf-8 -*-
import asyncio
# async定义的方法会变成一个无法直接执行的协程对象,
# 必须将此对象注册到事件循环中才可以执行。
async def execute(x):
    print('Number:', x)
coroutine = execute(1)
# 此时直接调用async定义的方法,返回的只是一个协程对象
print('Coroutine:', coroutine)
print('after execute')
# 使用get_event_loop()方法创建一个事件循环loop,
# 并调用loop对象的run_until_complete方法将协程对象注册到了事件循环中,才会触发定义的方法。
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine)
print('after loop')

在这里插入图片描述

2.1.2 定义任务

# -*- coding: utf-8 -*-
import asyncio
# (1)async定义的方法会变成一个无法直接执行的协程对象
async def execute(x):
    print('Number:', x)
    return x
coroutine = execute(555)

# (2)使用get_event_loop()方法创建一个事件循环loop
loop = asyncio.get_event_loop()

# (3)将协程对象转化为task任务,此时的任务还是pending状态
# (3-1)方式一
task = loop.create_task(coroutine)
# (3-2)方式二
# task = asyncio.ensure_future(coroutine)
print('Task:', task)

# (4)将task任务注册到事件循环中,然后task状态变为了finished,
# result=555是execute()执行的结果
loop.run_until_complete(task)
print('Task:', task)
print('after loop')

在这里插入图片描述

2.1.3 多任务协程

执行多次请求,可以定义一个task列表,然后使用asyncio包中的wait方法执行。

# -*- coding: utf-8 -*-
# @Time : 2023/5/12 15:48
# @Author : zb
# @File : xieCheng.py
# @Project : 爬虫

import asyncio
# (1)async定义的方法会变成一个无法直接执行的协程对象
async def execute(x):
    print('Number:', x)
    return x

# (2)创建task任务列表
tasks = []
for i in range(3):
    tasks.append(asyncio.ensure_future(execute(i)))

# (3)使用get_event_loop()方法创建一个事件循环loop
loop = asyncio.get_event_loop()

# (4)将task任务注册到事件循环中
loop.run_until_complete(asyncio.wait(tasks))

for task in tasks:
    print('Task Result:', task.result())

2.2 HTTP框架aiohttp

aiohttp是基于asyncio实现的HTTP框架,用于HTTP服务器和客户端。

# -*- coding: utf-8 -*-
import aiohttp
import asyncio
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'
}


async def Main():
    async with aiohttp.ClientSession() as session:
        async with session.get('https://www.baidu.com',headers=headers) as response:
            html = await response.text()
            print(html)
loop = asyncio.get_event_loop()
loop.run_until_complete(Main())

其实aiohttp.ClientSession() as session相当于将requests赋给session,也就是说session相当于requests,而发送网络请求、传入的参数、返回响应内容都和requests请求库大同小异,只是aiohttp请求库需要用async和await进行声明,然后调用asyncio.get_event_loop()方法进入事件循环,再调用loop.run_until_complete(Main())方法运行事件循环,直到Main方法运行结束。

3 下载速度对比

分别运用了单线程爬取,多线程爬取和异步协程爬取。
小说地址http://www.doupo321.com/doupocangqiong/。
步骤就是先爬取到网页下的子链接,然后通过子链接爬取到每章小说内容。

3.1 单线程

# -*- coding: utf-8 -*-
import time
import requests
from lxml import etree


def download(url, title):  # 下载内容
    resp = requests.get(url)
    resp.encoding = 'utf-8'
    html = resp.text
    tree = etree.HTML(html)
    body = tree.xpath("/html/body/div/div/div[4]/p/text()")
    body = '\n'.join(body)
    with open(f'斗破2/{title}.txt', mode='a+', encoding='utf-8') as fw:
        fw.write(body)


def geturl(url):  # 获取子链接
    resp = requests.get(url)
    resp.encoding = 'utf-8'
    html = resp.text
    tree = etree.HTML(html)
    lis = tree.xpath("/html/body/div[1]/div[2]/div[1]/div[3]/div[2]/ul/li")
    print("总共{}章".format(len(lis)))
    for li in lis:
        href = li.xpath("./a/@href")[0].strip('//')
        href = "http://"+href
        title = li.xpath("./a/text()")[0]
        download(href, title)
        print(title)


if __name__ == '__main__':
    url1 = "http://www.doupo321.com/doupocangqiong/"
    st = time.time()
    geturl(url1)
    ed = time.time()
    print("耗时:", ed-st)

大约花费了30min。
在这里插入图片描述

3.2 多线程

# -*- coding: utf-8 -*-
import time
import requests
from lxml import etree
from concurrent.futures import ThreadPoolExecutor


def download(url, title):
    resp = requests.get(url)
    resp.encoding = 'utf-8'
    html = resp.text
    tree = etree.HTML(html)
    body = tree.xpath("/html/body/div/div/div[4]/p/text()")
    body = '\n'.join(body)
    with open(f'斗破1/{title}.txt', mode='w', encoding='utf-8')as fw:
        fw.write(body)


def geturl(url):
    # 获取每一章的链接
    resp = requests.get(url)
    resp.encoding = 'utf-8'
    html = resp.text
    tree = etree.HTML(html)
    lis1 = tree.xpath("/html/body/div[1]/div[2]/div[1]/div[3]/div[2]/ul/li")
    print("总共{}章".format(len(lis1)))
    return lis1


if __name__ == '__main__':
    url1 = "http://www.doupo321.com/doupocangqiong/"
    st = time.time()
    lis = geturl(url1)
    with ThreadPoolExecutor(1000) as t:  # 创建线程池,最多有1000个线程
        for li in lis:
            href = li.xpath("./a/@href")[0].strip('//')
            href = "http://" + href
            title = li.xpath("./a/text()")[0]
            t.submit(download, url=href, title=title)
            print(title)
    ed = time.time()
    print("耗时:", ed-st)

仅耗时22秒。
在这里插入图片描述

3.3 异步协程

安装依赖库pip install aiohttp
安装依赖库pip install aiofiles

# -*- coding: utf-8 -*-
import requests
import aiohttp
import asyncio
import aiofiles
from lxml import etree
import time


async def download(url,title,session):
    async with session.get(url) as resp:  # resp = requests.get()
        html = await resp.text()
        tree = etree.HTML(html)
        body = tree.xpath("/html/body/div/div/div[4]/p/text()")
        body = '\n'.join(body)
    async with aiofiles.open(f'斗破/{title}.txt', mode='w', encoding='utf-8')as f:  # 保存下载内容
        await f.write(body)


async def geturl(url):
    resp = requests.get(url)
    resp.encoding = 'utf-8'
    html = resp.text
    tree = etree.HTML(html)
    lis = tree.xpath("/html/body/div[1]/div[2]/div[1]/div[3]/div[2]/ul/li")
    print("总共{}章".format(len(lis)))
    tasks = []
    async with aiohttp.ClientSession() as session:  # request
        for li in lis:
            href = li.xpath("./a/@href")[0].strip('//')
            href = "http://"+href
            title = li.xpath("./a/text()")[0]
            print(title)
            # 插入异步操作
            tasks.append(asyncio.ensure_future(download(href, title, session)))
        await asyncio.wait(tasks)

if __name__ == '__main__':
    url1 = "http://www.doupo321.com/doupocangqiong/"
    st = time.time()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(geturl(url1))
    ed = time.time()
    print("耗时:", ed-st)

大约需要32秒
在这里插入图片描述
可以看出,用多线程,仅仅22秒就扒完了一部1600多章的小说,但是多线程会对系统的开销较大;如果用异步协程,爬取速度会稍微慢些,需要大概32秒,但是对系统开销较小,建议大家采用异步协程的方式,但是用单线程去爬取会慢很多,扒完一部小说耗时需要30min,不是很推荐。

拿到的小说是乱序的,后期想办法解决。

4 大写数字替换成阿拉伯数字

# -*- coding: utf-8 -*-
import re

pattern = re.compile(u'第(.*)章')

mulu = u'''第一千五百四十六章 道韵高低'''

# constants for chinese_to_arabic
# 中文对应阿拉伯数字
CN_NUM = {
    '〇': 0, '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '零': 0,
    '壹': 1, '贰': 2, '叁': 3, '肆': 4, '伍': 5, '陆': 6, '柒': 7, '捌': 8, '玖': 9, '貮': 2, '两': 2,
}
# 中文对应单位
CN_UNIT = {
    '十': 10,
    '拾': 10,
    '百': 100,
    '佰': 100,
    '千': 1000,
    '仟': 1000,
    '万': 10000,
    '萬': 10000,
    '亿': 100000000,
    '億': 100000000,
    '兆': 1000000000000,
}


def chinese_to_arabic(cn):
    print(cn)
    unit = 0  # current
    ldig = []  # digest
    for cndig in reversed(cn):
        print(cndig)
        if cndig in CN_UNIT:
            unit = CN_UNIT.get(cndig)
            if unit == 10000 or unit == 100000000:
                ldig.append(unit)
                unit = 1

        else:
            dig = CN_NUM.get(cndig)
            if unit:
                dig *= unit
                unit = 0
            ldig.append(dig)

    if unit == 10:
        ldig.append(10)
    val, tmp = 0, 0
    for x in reversed(ldig):
        if x == 10000 or x == 100000000:
            val += tmp * x
            tmp = 0
        else:
            tmp += x
    val += tmp
    return val


print(pattern.findall(mulu))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/518927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RVO的寻路障碍物改进

先说下原理&#xff1a;RVO的障碍识别是通过链接的坐标点数组围成的区域&#xff0c;收尾相接形成的。 namespace RVO { /** * <summary>Defines static obstacles in the simulation.</summary> * 障碍区的点坐标数据结构&#xff0c;是个双向链表结构 *通过Sim…

【Java】认识String类

【Java】认识String类 1. String类的重要性2. 常用方法2.1 字符串构造求字符串长度2.2 String对象的比较2.3 字符串查找2.4 转化2.5 字符串替换2.6 字符串截取2.7 字符串拆分2.8 其他操作方法2.8.1 String trim()2.8.2 String toUpperCase()2.8.3 String.format&#xff08;&am…

在C#WinForm中调用julia函数及在C#中传递参数到julia函数的使用示例

特别声明&#xff1a;未经允许&#xff0c;请勿转载&#xff01; https://discourse.juliacn.com/t/topic/7189https://discourse.juliacn.com/t/topic/7189我在julia中国社区已提交了文章的最后部分未解决问题&#xff0c;大家后续可以在该链接中跟踪问题的回答进度。 好几…

Neck网络 FPN + PAN 改进解读

呃 这篇文章的目的在于补充一些知识以便于理解Neck部分的网络 特征提取网络 与 目标检测之间的关系 一个特征提取网络&#xff0c;假设有1000层&#xff0c;开始的特征图包含的细节信息就很多&#xff0c;而随着网络的加深&#xff0c;特征提取网络经过多次被卷积和池化操作&…

让你的SQL变得更简洁:学会ChatGPT活用技巧,轻松实现SQL格式化

文章目录 前言让你的SQL变得更简洁&#xff01;学会ChatGPT活用技巧&#xff0c;轻松实现SQL格式化ChatGPT格式化SQL效果讯飞星火认知大模型格式化SQL效果文心一言格式化SQL效果格式化SQL效果结果比较 总结 【免责声明】文章仅供学习交流&#xff0c;观点代表个人&#xff0c;与…

Vue--》如何在Vue3中书写TSX

在vue2的时候就已经可以使用 jsx 语法&#xff0c;但是不是很友好&#xff0c;写起来是一件很痛苦的事情&#xff0c;所以你很少见到有人会在vue2中书写 jsx 语法&#xff0c;官方也不建议我们在vue2中进行书写 jsx 的代码风格&#xff1a; 但随着vue3版本的到来&#xff0c;对…

SBUS协议解析图解

SBUS协议在硬件上是串口反相协议。 在软件上就是普通串口协议&#xff0c;波特率100k&#xff0c;8位数据位&#xff0c;2位停止位&#xff0c;偶校验even。注意&#xff0c;stm32把校验位也当做数据&#xff0c;所以数据位要设置成9&#xff0c;而不是8。 其报文格式为&…

scala---spark本地调式远程获取hdfs数据注意事项

文章目录 前言一、Hadoop配置注意事项1.1 core-site.xml1.2 core-site.xml 二、本地hadoop环境配置注意事项三、本地scala项目spark代码调试总结 前言 这篇文章主要帮大家绕开一些本地使用spark调试获取远程hdfs数据的坑&#xff0c;个人在使用时也是基本把这些坑踩了一遍。希望…

永远不该忘记!科技才是硬道理,手中没有剑,跟有剑不用,是两回事

今天是全国防灾减灾日&#xff0c;距离2008年汶川大地震也已经过去15年了。但时至今日&#xff0c;看到那些图像视频资料&#xff0c;那种触及灵魂的疼痛仍是存在的&#xff0c;2008年的大地震在每个中国人身上都留下了无法抚平的伤疤。 2008年是所有中国人都无法忘记的一年&am…

设计模式——单一职责模式之装饰模式

文章目录 前言一、“单一职责” 模式二、Decorator 装饰模式1、动机2、模式定义3、伪代码示例①、第一种写法②、第二种写法③、第三种写法 4、结构 总结 前言 一、“单一职责” 模式 在软件组件的设计中&#xff0c;如果责任划分的不清晰&#xff0c;使用继承得到的结果往往是…

深度指南:WhatsApp广播vs WhatsApp群组(二)

如何为创建WhatsApp群组&#xff1f; 建立 WhatsApp 群组&#xff0c;请按下列步骤操作。 1. 导航到您的WhatsApp或WhatsApp for Business&#xff08;布局或界面相同&#xff09;&#xff0c;然后点击“新建组”。 2.现在&#xff0c;您可以将参与者添加到您的WhatsApp群组…

华为OD机试真题 Java 实现【静态代码扫描服务】【2023Q1 100分】

一、题目描述 静态扫描快速识别源代码的缺陷&#xff0c;静态扫描的结果以扫描报告作为输出&#xff1a; 文件扫描的成本和文件大小相关&#xff0c;如果文件大小为N&#xff0c;则扫描成本为N个金币&#xff1b;扫描报告的缓存成本和文件大小无关&#xff0c;每缓存一个报告…

算法修炼之练气篇——练气十层

博主&#xff1a;命运之光 专栏&#xff1a;算法修炼之练气篇 前言&#xff1a;每天练习五道题&#xff0c;炼气篇大概会练习200道题左右&#xff0c;题目有C语言网上的题&#xff0c;也有洛谷上面的题&#xff0c;题目简单适合新手入门。&#xff08;代码都是命运之光自己写的…

华为网络篇 静态路由与BFD联动-21

实验难度2实验复杂度2 目录 一、实验原理 二、实验拓扑 三、实验步骤 四、实验过程 总结 一、实验原理 BFD&#xff08;Bidirectional Forwarding Detection&#xff0c;双向转发检测&#xff09;是一种实验网络可靠性的机制&#xff0c;它可用于快速检测网络中的链路状况…

常见自动化测试工具及框架有哪些?怎么选?

目录 一、自动化测试简介 1、什么是自动化测试 2、它可以做自动化测试么 二、工具篇 1、UFT&#xff08;QTP&#xff09; 2、Selenium 3、Appium 4、工具对比 三、框架篇 1、TestNG 2、unittest 3、pytest 4、Robot Framework 5、框架对比 四、如何选择 总结 一…

ES6知识点

目录 1、let、cons、var的区别 2、const对象的属性可以修改吗 3、如果new一个箭头函数会怎么样 4、箭头函数和普通函数的区别&#xff1a; 5、扩展运算符的作用及使用场景 1、let、cons、var的区别 1️⃣&#xff1a;块级作用域&#xff1a;块作用域由{}包括&#xff0c;le…

Flink 常用API(1)—— 源算子

目录 执行环境&#xff08;Execution Environment&#xff09; 创建执行环境 执行模式配置 触发程序执行 源算子&#xff08;Source&#xff09; 从集合中读取数据 从文件中读取数据 从Socket读取数据 从Kafka读取数据*** 自定义 Source&#xff08;数据源&#xff0…

【结构与算法】—— 游戏概率常用算法整理 | 游戏中的常见概率设计分析

&#x1f4e2;博客主页&#xff1a;肩匣与橘&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由肩匣与橘编写&#xff0c;首发于CSDN&#x1f649;&#x1f4e2;生活依旧是美好而又温柔的&#xff0c;你也是✨ …

Smartbi X 广州轻工集团,打造集团价值创造型总部

广州轻工工贸集团有限公司&#xff08;简称“广州轻工集团”&#xff09;是广州市第一家工贸合一的大型企业集团公司&#xff0c;最早起源于1950年9月成立的广州市合作事业管理局&#xff0c;逐渐演化形成广州市轻工业局。1995年10月&#xff0c;广州市轻工业局成建制改建为经济…

【C++】搜索二叉树

文章目录 &#x1f4d5; 概念&#x1f4d5; 搜索二叉树的实现框架插入节点查找节点★ 删除节点 ★ &#x1f4d5; 源代码 &#x1f4d5; 概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树&#xff1a; 若它的左子树不为空&…