【Python爬虫实战】深入理解Python异步编程:从协程基础到高效爬虫实现

#1024程序员节|征文#

  🌈个人主页:易辰君-CSDN博客
🔥 系列专栏:https://blog.csdn.net/2401_86688088/category_12797772.html

目录

前言

一、异步

(一)核心概念

(二)应用场景

(三)优缺点

二、协程异步实现方法

(一)基本的协程定义与运行

(二)并发执行多个协程

(三)创建与管理任务

(四)限制并发数

(五)超时控制

(六)队列管理

三、同步和异步的对比

(一)执行方式

(二) 阻塞与非阻塞

(三)性能和效率

(四)代码复杂性

(五)应用场景

(六)示例对比

四、异步爬虫

(一)异步爬虫的优点

(二)实现异步爬虫的基本步骤

(三)控制并发数量

(四)应用场景

(五)注意事项

五、总结


前言

随着网络和数据的迅速发展,越来越多的场景需要高效处理大量请求和数据。传统的同步编程模式在处理I/O密集型任务时会浪费大量等待时间,而Python的异步编程技术提供了一种更高效的方式。本文从Python异步编程的基础概念出发,深入讲解协程、asyncio库及其核心功能。通过详细的代码示例与解释,我们将逐步探索异步编程的应用场景


一、异步

在Python中,异步编程是一种并发编程方法,允许程序在处理耗时任务时不必等待任务完成,而是继续执行其他代码。这样可以提升程序的效率和响应速度,特别适合处理I/O密集型任务(如网络请求、文件读写等)。

(一)核心概念

(1)事件循环: 异步编程的核心是事件循环(Event Loop),它管理任务的调度。事件循环会不断地检查是否有任务完成或需要开始新任务,从而实现任务的非阻塞执行。

(2)协程(Coroutine): 协程是异步任务的基本单元,是一个可以被挂起并在稍后继续执行的函数。Python 通过 async def 定义协程函数,协程内部可以用 await 来暂停并等待其他协程的结果。

(3)asyncawait 关键字

  • async 用于定义一个协程函数,例如 async def my_function()

  • await 用于暂停协程的执行并等待其他协程完成。例如,await some_async_task() 会暂停当前协程,直到 some_async_task() 完成后再继续执行。

(4)asyncio: Python 的标准库 asyncio 提供了异步编程的核心功能,包含事件循环、任务管理、以及异步 I/O 操作等工具,帮助处理并发任务。

(二)应用场景

异步编程非常适合处理以下场景:

  • 网络请求(如 HTTP 请求、数据库查询等)

  • 文件读写操作

  • 大量并发任务(如网络爬虫、数据采集)

示例:

import asyncio

async def task(name, delay):
    print(f"Task {name} started")
    await asyncio.sleep(delay)  # 模拟一个异步I/O操作
    print(f"Task {name} completed")

async def main():
    # 并发执行多个任务
    await asyncio.gather(
        task("A", 2),
        task("B", 1),
        task("C", 3)
    )

# 运行主协程
asyncio.run(main())

在这个例子中,task 是一个协程,使用 await asyncio.sleep(delay) 来模拟一个耗时任务。在 main 函数中,asyncio.gather 可以并发地执行多个 task,而不需要等待其中一个任务完成才执行下一个。

(三)优缺点

  • 优点:相比传统同步方法,异步编程在 I/O 密集型任务上更高效、响应更快。

  • 缺点:异步代码的逻辑较难理解和调试,并且在 CPU 密集型任务上并不一定有优势。


二、协程异步实现方法

在Python中,使用协程实现异步的主要方法是通过 asyncawait 关键字以及 asyncio 库来管理协程和事件循环。这使得我们能够编写出非阻塞的代码,有效地进行异步任务调度。下面是几种常用的协程异步实现方法:

(一)基本的协程定义与运行

import asyncio

async def my_task():
    print("Task started")
    await asyncio.sleep(1)  # 模拟异步操作
    print("Task completed")

# 运行协程
asyncio.run(my_task())

在这个例子中:

  • my_task 是一个协程,使用 async def 定义。

  • await asyncio.sleep(1) 模拟一个异步操作。在实际应用中,这可以替换为其他 I/O 操作,比如网络请求。

(二)并发执行多个协程

可以使用 asyncio.gather 并发运行多个协程,将它们一起调度,以便程序在等待一个任务时可以继续执行其他任务:

async def task(name, delay):
    print(f"Task {name} started")
    await asyncio.sleep(delay)
    print(f"Task {name} completed")

async def main():
    await asyncio.gather(
        task("A", 2),
        task("B", 1),
        task("C", 3)
    )

asyncio.run(main())

在这里,asyncio.gather 接收多个协程,创建一个任务组,多个任务会并发执行,节省时间。

(三)创建与管理任务

asyncio.create_task 将协程封装成任务并立即调度,而不需要等待所有任务完成:

async def task(name):
    print(f"Task {name} started")
    await asyncio.sleep(1)
    print(f"Task {name} completed")

async def main():
    task1 = asyncio.create_task(task("A"))
    task2 = asyncio.create_task(task("B"))
    
    # 可以在这里添加其他逻辑
    await task1
    await task2

asyncio.run(main())

通过 asyncio.create_task,可以先开始任务,然后在稍后使用 await 等待其完成。这样可以在需要时调度多个任务,并在合适的位置等待结果。

(四)限制并发数

在某些场景中需要限制并发数,可以使用 asyncio.Semaphore 控制:

import asyncio

semaphore = asyncio.Semaphore(2)

async def limited_task(name, delay):
    async with semaphore:
        print(f"Task {name} started")
        await asyncio.sleep(delay)
        print(f"Task {name} completed")

async def main():
    await asyncio.gather(
        limited_task("A", 2),
        limited_task("B", 1),
        limited_task("C", 3),
        limited_task("D", 1)
    )

asyncio.run(main())

在这个例子中,asyncio.Semaphore(2) 设置同时最多只能有两个任务在执行,其他任务必须等待。

(五)超时控制

可以使用 asyncio.wait_for 来限制某个任务的最长等待时间:

async def my_task():
    await asyncio.sleep(3)
    print("Task completed")

async def main():
    try:
        await asyncio.wait_for(my_task(), timeout=2)
    except asyncio.TimeoutError:
        print("Task timed out")

asyncio.run(main())

(六)队列管理

asyncio.Queue 是实现生产者-消费者模式的常用方式,可以让多个协程通过队列共享数据:

async def producer(queue):
    for i in range(5):
        await asyncio.sleep(1)
        await queue.put(f"Item {i}")
        print(f"Produced Item {i}")

async def consumer(queue):
    while True:
        item = await queue.get()
        if item is None:
            break
        print(f"Consumed {item}")
        queue.task_done()

async def main():
    queue = asyncio.Queue()
    producer_task = asyncio.create_task(producer(queue))
    consumer_task = asyncio.create_task(consumer(queue))
    
    await producer_task
    await queue.put(None)  # 终止消费者
    await consumer_task

asyncio.run(main())

在这里,producer 生产数据并放入队列,consumer 从队列中消费数据。队列的使用可以很方便地控制协程之间的数据传递和同步。


三、同步和异步的对比

同步和异步是两种处理任务的不同方式。它们在任务的执行和等待机制上有显著的区别,适合不同的应用场景。以下是它们的详细对比:

(一)执行方式

  • 同步:任务按照顺序逐个执行,当前任务完成后才能执行下一个任务。如果一个任务正在执行,其他任务必须等待。

  • 异步:任务可以在不等待其他任务完成的情况下启动,任务之间的执行不严格依赖顺序,多个任务可以同时进行(在I/O操作上,异步非常有效)。


(二) 阻塞与非阻塞

  • 同步:同步方式是阻塞的,任务在执行期间会阻塞代码的后续执行,直到任务完成才会继续执行下一步。

  • 异步:异步方式是非阻塞的,一个任务开始后可以立即开始执行其他任务,不必等待前一个任务完成。

(三)性能和效率

  • 同步:在I/O操作频繁的程序中,同步会导致时间浪费,因为程序在等待I/O操作完成时处于空闲状态。适用于计算密集型或对顺序要求严格的场景。

  • 异步:通过避免等待,提高了效率和响应速度。特别适用于I/O密集型操作(如网络请求、文件读写等),异步允许程序在等待I/O操作完成时继续处理其他任务。

(四)代码复杂性

  • 同步:代码结构相对简单,因为任务是顺序执行的,不涉及任务切换和状态管理,容易理解和调试。

  • 异步:代码相对复杂,尤其是在大型项目中,由于任务非顺序执行,涉及事件循环、回调或await/async等机制,代码逻辑可能较难理解和维护。

(五)应用场景

  • 同步:适用于对任务顺序有严格要求的场景,例如:数据处理、算法计算、特定顺序要求的逻辑。

  • 异步:适合需要处理大量I/O操作、需要高并发支持的场景,例如:网络爬虫、聊天应用、实时数据流处理等。

(六)示例对比

同步示例

import time

def task(name):
    print(f"Starting task {name}")
    time.sleep(2)  # 模拟阻塞操作
    print(f"Task {name} completed")

# 顺序执行
task("A")
task("B")
task("C")

输出:

Starting task A
Task A completed
Starting task B
Task B completed
Starting task C
Task C completed

在同步示例中,task 函数按顺序执行,每个任务完成后才开始下一个。

异步示例

import asyncio

async def task(name):
    print(f"Starting task {name}")
    await asyncio.sleep(2)  # 模拟非阻塞操作
    print(f"Task {name} completed")

async def main():
    await asyncio.gather(task("A"), task("B"), task("C"))

asyncio.run(main())

输出:

Starting task A
Starting task B
Starting task C
Task A completed
Task B completed
Task C completed

在异步示例中,任务ABC几乎同时开始,await asyncio.sleep(2)不会阻塞其他任务,任务可以并发执行,最终加快了整体运行速度。

对比总结

特性同步异步
执行方式顺序执行并发执行
阻塞 阻塞非阻塞
效率I/O 密集型性能低I/O 密集型性能高
代码复杂度简单较复杂
适用场景计算密集型任务I/O 密集型、高并发任务

在实际应用中,同步和异步各有优缺点。选择同步或异步,主要取决于应用场景、任务需求和性能要求。


四、异步爬虫

异步爬虫是一种使用异步编程方法实现的网络爬虫,它能够在不等待网页响应的情况下,同时发送多个请求并处理返回的数据。这种方式特别适用于需要抓取大量网页内容的场景,因为它可以显著提升爬虫的效率。

在Python中,异步爬虫通常使用 asyncioaiohttp 两个库来实现:

  • asyncio:提供异步编程的核心框架,包括事件循环、协程和任务管理。

  • aiohttp:一个异步HTTP库,支持异步发送请求和获取响应,非常适合构建异步爬虫。

(一)异步爬虫的优点

  1. 高并发性:可以同时发送大量请求,而不必等待每个请求完成再发送下一个。

  2. 资源利用率高:在等待服务器响应时可以处理其他任务,减少了等待时间。

  3. 适合I/O密集型任务:异步爬虫特别适用于抓取数据量大、网络请求多的任务场景。

(二)实现异步爬虫的基本步骤

以下是一个使用 asyncioaiohttp 构建异步爬虫的示例,展示如何同时请求多个网页并处理响应。

示例:

import asyncio
import aiohttp

# 定义一个异步请求函数
async def fetch(session, url):
    try:
        async with session.get(url) as response:
            print(f"Fetching {url}")
            html = await response.text()  # 获取网页的 HTML 内容
            print(f"Completed {url}")
            return html
    except Exception as e:
        print(f"Error fetching {url}: {e}")

# 主爬虫函数
async def main(urls):
    # 创建一个aiohttp会话
    async with aiohttp.ClientSession() as session:
        # 使用 asyncio.gather 并发请求多个 URL
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks, return_exceptions=True)
        return results

# 运行爬虫
urls = [
    "https://example.com",
    "https://example.org",
    "https://example.net"
]

# 启动事件循环
asyncio.run(main(urls))

代码解析

  • fetch(session, url):这是异步请求函数,使用 session.get(url) 发送异步 HTTP 请求。async with 确保会话资源能够正确释放。

  • asyncio.gather(*tasks):将所有 fetch 请求作为任务传入 asyncio.gather,这样可以并发地执行这些任务,而不需要等待每个任务顺序完成。

  • asyncio.run(main(urls)):启动事件循环并运行 main 函数,main 中会创建多个并发任务并等待它们的完成。

(三)控制并发数量

在实际应用中,为了防止服务器拒绝请求,可以使用 asyncio.Semaphore 来限制并发请求数量。例如,限制并发请求数为5:

async def fetch(semaphore, session, url):
    async with semaphore:  # 使用信号量限制并发
        try:
            async with session.get(url) as response:
                print(f"Fetching {url}")
                html = await response.text()
                print(f"Completed {url}")
                return html
        except Exception as e:
            print(f"Error fetching {url}: {e}")

async def main(urls):
    semaphore = asyncio.Semaphore(5)  # 限制为5个并发请求
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(semaphore, session, url) for url in urls]
        results = await asyncio.gather(*tasks, return_exceptions=True)
        return results

(四)应用场景

  • 数据采集:高效采集电商网站、新闻网站等的商品或内容信息。

  • 实时数据爬取:抓取实时更新的内容,如股票数据、天气数据等。

  • 大规模网页抓取:异步爬虫非常适合抓取大量网页内容,因为它能在不等待单个网页响应的情况下发起多个请求。

(五)注意事项

  • 速率控制:可以加入请求延迟或限制并发请求数量,以防止被目标网站封禁。

  • 异常处理:要处理网络超时、连接错误等异常,保证爬虫在出现错误时不会中断。

  • 机器人检测:一些网站会检测并阻止大量并发请求,需要考虑反爬策略(如伪装请求头、模拟浏览器等)。


五、aiomysql的使用

aiomysql 是一个支持 Python 异步编程的 MySQL 数据库库,基于 asyncioPyMySQL 构建。它可以让开发者在异步框架中执行数据库操作,适合需要同时处理大量数据库请求的高并发应用,如爬虫数据存储、Web 服务等。

(一)特点

  • 异步支持:基于 asyncio 的异步支持,不会因为等待数据库响应而阻塞其他任务。

  • 高效连接池:提供了内置的数据库连接池,减少每次查询前创建新连接的开销。

  • 灵活的事务处理:支持事务和多种数据库操作,适合复杂的数据库事务操作。

(二)安装

在使用前,需要安装 aiomysql。可以通过以下命令进行安装:

pip install aiomysql

(三)使用示例

以下是一个简单的 aiomysql 示例,包括如何创建连接、执行查询、插入数据和使用连接池。

(1)创建连接并执行查询

import asyncio
import aiomysql

async def main():
    # 创建连接
    conn = await aiomysql.connect(
        host='localhost', port=3306,
        user='root', password='password',
        db='test_db'
    )

    async with conn.cursor() as cur:
        # 执行查询
        await cur.execute("SELECT * FROM example_table")
        result = await cur.fetchall()  # 获取所有查询结果
        print(result)

    conn.close()  # 关闭连接

# 运行异步主函数
asyncio.run(main())

(2)使用连接池

使用连接池可以避免频繁创建和关闭连接,适合大量并发请求的场景。

import asyncio
import aiomysql

async def query_with_pool(pool):
    async with pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute("SELECT * FROM example_table")
            result = await cur.fetchall()
            print(result)

async def main():
    # 创建连接池
    pool = await aiomysql.create_pool(
        host='localhost', port=3306,
        user='root', password='password',
        db='test_db', maxsize=10
    )

    # 执行查询
    await query_with_pool(pool)
    pool.close()
    await pool.wait_closed()  # 关闭连接池

asyncio.run(main())

(3)插入数据示例

import asyncio
import aiomysql

async def insert_data():
    conn = await aiomysql.connect(
        host='localhost', port=3306,
        user='root', password='password',
        db='test_db'
    )
    async with conn.cursor() as cur:
        await cur.execute(
            "INSERT INTO example_table (name, age) VALUES (%s, %s)",
            ('Alice', 25)
        )
        await conn.commit()  # 提交事务
    conn.close()

asyncio.run(insert_data())

(4)异步事务处理

在数据需要严格一致性时,可以使用事务:

async def transaction_example():
    conn = await aiomysql.connect(
        host='localhost', port=3306,
        user='root', password='password',
        db='test_db'
    )

    async with conn.cursor() as cur:
        try:
            await cur.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
            await cur.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
            await conn.commit()  # 提交事务
        except Exception as e:
            await conn.rollback()  # 回滚事务
            print("Transaction failed:", e)
        finally:
            conn.close()

asyncio.run(transaction_example())

(5)常见注意事项

  • 错误处理:在异步环境中,尽量在执行数据库操作时捕获异常,避免由于未处理的异常导致协程退出。

  • 事务一致性:在批量插入、转账等操作中,建议使用事务保证数据一致性。

  • 连接池管理:使用 aiomysql 的连接池,尤其在高并发场景中,能够显著提高数据库访问的性能。


六、总结

Python异步编程通过非阻塞的事件循环实现了并发任务调度,特别适合处理I/O密集型任务,如网络请求、文件读写等。在本文中,我们探讨了异步编程的核心概念与实现方式,包括协程、事件循环、并发控制等。基于这些技术,还展示了如何利用asyncioaiohttp构建高效的异步爬虫。掌握这些异步编程方法,不仅能大幅提升代码执行效率,还为处理大规模数据和并发任务提供了强有力的工具。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flutter InkWell组件去掉灰色遮罩

当InkerWell组件内部获取到焦点时,会展示一层灰色遮罩 将focusColor属性设置为透明即可 Flutter InkWell焦点效果源码分析 问题描述 当 InkWell 组件获得焦点时,会显示一层灰色遮罩效果。需要找出这个效果是由哪些组件控制的,以及具体的…

每天一题:洛谷P2041分裂游戏

题目描述 有一个无限大的棋盘,棋盘左下角有一个大小为 n 的阶梯形区域,其中最左下角的那个格子里有一枚棋子。你每次可以把一枚棋子“分裂”成两枚棋子,分别放在原位置的上边一格和右边一格。(但如果目标位置已有棋子&#xff0c…

频率限制:WAF保护网站免受恶意攻击的关键功能

频率限制(Rate Limiting)是一项有效的安全措施,用于控制每个 IP 地址的访问速率,以防止恶意用户利用大量请求对网站进行攻击,例如防止 CC 攻击等。频率限制不仅能保护网站资源,还能提升服务的稳定性。 下面…

植物源UDP-糖基转移酶及其分子改造-文献精读75

植物源UDP-糖基转移酶及其分子改造 摘要 糖基化能够增加化合物的结构多样性,有效改善水溶性、药理活性和生物利用度,对植物天然产物的药物开发至关重要。UDP-糖基转移酶(UGTs)能够催化糖基从活化的核苷酸糖供体转移到受体形成糖苷键,植物中天然产物的糖基化修饰主要通过UGTs实…

搜维尔科技:Xsens动作捕捉、Manus数据手套和Faceware面部捕捉技术集成,应用于元宇宙数字人制作解决方案

Xsens动作捕捉、Manus数据手套和Faceware面部捕捉技术集成,能够实现非常逼真且高效的数字人动作和表情捕捉! 硬件连接与数据传输方面: 1.Xsens与Manus的集成:Xsens惯性动作捕捉系统通常可以与Manus的数据手套直接集成。Xsens主要…

基于SpringBoot的汽车票网上预订系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…

块设备驱动的基本概念

块设备与字符设备 块设备只能以块为单位接收输入和返回输出,而字符设备则以字节为单位。大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作;字符设备只能被顺序读写,而块设备可以随机访问。 块设备对于I/O请求…

python 使用进程池并发执行 SQL 语句

这段代码使用了 Python 的 multiprocessing 模块来实现真正的并行处理,绕过 Python 的全局解释器锁(GIL)限制,从而在多核 CPU 上并发执行多个 SQL 语句。 from pyhive import hive import multiprocessing# 建立连接 conn hive.…

Ajax:请求 响应

Ajax:请求 & 响应 AjaxjQuery的Ajax接口$.get$.post$.ajax PostMan 接口测试getpost Ajax 浏览器中看到的数据,并不是保存在浏览器本地的,而是实时向服务器进行请求的。当服务器接收到请求,就会发回一个响应,此时浏…

ALIGN_ Tuning Multi-mode Token-level Prompt Alignment across Modalities

文章汇总 当前的问题 目前的工作集中于单模提示发现,即一种模态只有一个提示,这可能不足以代表一个类[17]。这个问题在多模态提示学习中更为严重,因为视觉和文本概念及其对齐都需要推断。此外,仅用全局特征来表示图像和标记是不…

Linux-计算机网络-epoll的LT,ET模式

一.epoll的LT和ET模式介绍 epol 对文件描述符有两种操作模式:LT(Level Trigger,电平触发)模式和 ET(EdgeTrigger,边沿触发)模式。LT模式是默认的工作模式。当往epol 内核事件表中注册一个文件描述符上的 EPOLLET 事件时,epoll将以高效的 ET …

新160个crackme - 087-d4ph1-crackme2

运行分析 需破解Name和Serial PE分析 ASM程序,32位,无壳 静态分析&动态调试 ida找到关键字符串 INT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4) {HICON IconA; // eaxint v5; // ediunsigned int v6; // ebxchar v7; // a…

leetcode 303.区域和检索-数组不可变

1.题目要求: 2.题目代码: class NumArray { public:vector<int> array;NumArray(vector<int>& nums) {array nums;}int sumRange(int left, int right) {int sum 0;while(left < right){sum array[left];left;}return sum;} };/*** Your NumArray obje…

【SVM手把手推导】对偶问题应用之支持向量机SVM(Hard Margin)

1. 对偶问题应用之支持向量机SVM 1.1 SVM 设给定数据集&#xff1a; { ( s i , y i ) : y i ∈ { 1 , − 1 } , i 1 , ⋯ , m } \{(\mathbf{s}^i,y^i):y^i\in\{1,-1\},i1,\cdots,m\} {(si,yi):yi∈{1,−1},i1,⋯,m}&#xff0c;我们想要找到一个决策超平面&#xff08;decis…

大数据技术的前景如何?

在当今数字化迅猛发展的时代&#xff0c;大数据技术的前景显得尤为广阔。随着数据量的激增&#xff0c;如何有效利用这些数据成为了各行各业关注的焦点。未来五年&#xff0c;大数据技术的发展趋势可以从市场规模、技术融合、行业应用和政策支持等多个方面进行深入分析。 1. 市…

【STM32】单片机ADC原理详解及应用编程

本篇文章主要详细讲述单片机的ADC原理和编程应用&#xff0c;希望我的分享对你有所帮助&#xff01; 目录 一、STM32ADC概述 1、ADC&#xff08;Analog-to-Digital Converter&#xff0c;模数转换器&#xff09; 2、STM32工作原理 二、STM32ADC编程实战 &#xff08;一&am…

推荐一款全新的视频编辑软件:CapCut剪映国际版

CapCut是一款全新的视频编辑应用程序&#xff0c;提供了各种功能和工具&#xff0c;让用户可以轻松地创建专业级别的视频。这款应用程序非常易于使用&#xff0c;功能强大&#xff0c;可供任何水平的用户使用。 CapCut包含了各种视频编辑工具&#xff0c;可以添加各种特效、滤镜…

提升用户体验优化全攻略

内容概要 用户体验&#xff08;UX&#xff09;在当今数字化时代扮演着举足轻重的角色。良好的用户体验不仅决定了用户对产品的满意度&#xff0c;还有助于提高转化率与客户忠诚度。因此&#xff0c;深入理解用户体验的重要性是每一个设计师和产品经理必须掌握的基础。在这一部…

关于springboot跨域与拦截器的问题

今天写代码的时候遇到的一个问题&#xff0c;在添加自己设置的token拦截器之后&#xff0c;报错&#xff1a; “ERROR Network Error AxiosError: Network Error at XMLHttpRequest.handleError (webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:112:14) at Axi…

SDK和API

什么是SDK&#xff1f; SDK就像是一个超级工具箱&#xff0c;里面装满了各种工具、说明书和配件&#xff0c;帮你快速、方便地完成一项工作。比如&#xff0c;你要搭建一个乐高模型&#xff0c;SDK就是那个包含了所有乐高积木、拼装图纸、甚至一些特殊工具的大盒子。 什么是A…