Python并发与异步编程

Python的并发与异步编程是两个不同的概念,但它们经常一起使用,以提高程序的性能和响应能力。以下是对这两个概念的详细讲解:

并发编程 (Concurrency)
并发编程是指在程序中同时执行多个任务的能力。Python提供了几种实现并发的机制:

1. 多线程 (Threading):
   - Python的`threading`模块允许你创建线程,从而在同一时间内执行多个操作。
   - 由于Python的全局解释器锁(GIL),真正的并行执行在多线程中受到限制,这意味着在任何给定时间点,只有一个线程可以执行Python字节码。
   - 多线程适合I/O密集型任务,例如网络请求或文件操作。

2. 多进程 (Multiprocessing):
   - `multiprocessing`模块提供了创建多个进程的方法,每个进程有自己的Python解释器和内存空间。
   - 由于进程之间没有GIL的限制,因此它们可以实现真正的并行执行。
   - 多进程适合CPU密集型任务,但进程间通信和创建进程的开销较大。

3. 协程 (Coroutines):
   - 协程是一种更轻量级的并发机制,通过`asyncio`库实现。
   - 协程允许你编写看似同步的代码,而实际上是异步执行的,这使得I/O操作更加高效。

异步编程 (Asynchronous Programming)
异步编程是一种编程范式,允许程序在等待操作完成时继续执行其他任务。Python中的异步编程主要通过`asyncio`库实现:

1. asyncio:
   - `asyncio`是一个用于编写单线程并发代码的库,使用`async`和`await`关键字。
   - `async def`用于定义一个异步函数,它可以包含`await`表达式。
   - `await`用于等待另一个异步操作完成,同时允许其他异步操作运行。
   - `asyncio`提供了事件循环(event loop),它是运行异步任务的核心。2. 使用asyncio的例子:
   ```python
   

import asyncioasync def fetch_data():# 模拟I/O操作await asyncio.sleep(2)return {"data": 1}async def main():# 获取事件循环引用data = await fetch_data()print(data)asyncio.run(main())


   ```

3. 异步I/O:
   - 除了`asyncio`,Python还提供了用于异步I/O操作的库,如`aiohttp`用于异步HTTP请求。

并发与异步的结合
在Python中,可以结合使用并发和异步编程来最大化性能。例如,可以使用`asyncio`进行异步编程,同时利用`multiprocessing`来实现CPU密集型任务的并行处理。

注意事项
- 并发编程可能会引入竞态条件和死锁,需要仔细设计。
- 异步编程的代码可能难以理解和调试,特别是对于初学者。
- 选择哪种并发或异步模型取决于具体的应用场景和性能要求。

多线程在爬虫程序中的应用可以显著提高数据抓取的效率。以下是一个使用Python的`threading`模块实现的简单多线程爬虫案例的详细讲解:

 1. 准备工作
在开始编写多线程爬虫之前,需要准备以下内容:
目标网站**:确定要爬取的网站和数据。
请求库**:如`requests`,用于发送网络请求。
解析库**:如`BeautifulSoup`,用于解析HTML页面。
线程模块**:`threading`,用于创建和管理线程。

 2. 安装必要的库
如果尚未安装`requests`和`BeautifulSoup`,可以通过以下命令安装:
```bash

pip install requests beautifulsoup4


```

 3. 编写爬虫函数
编写一个基本的爬虫函数,用于请求网页并解析数据。```python

import requests
from bs4 import BeautifulSoupdef crawl(url):try:response = requests.get(url)response.raise_for_status()  # 检查请求是否成功soup = BeautifulSoup(response.text, 'html.parser')# 假设我们爬取的是网页标题title = soup.find('title').get_text()print(f"页面标题: {title}")except requests.RequestException as e:print(f"请求错误: {e}")except Exception as e:print(f"解析错误: {e}")


```

 4. 创建线程工作函数
编写一个线程工作函数,它将作为线程执行的主体。

```python

def worker(url):crawl(url)


```

 5. 管理线程
创建一个函数来管理线程的创建和启动。```python

def manage_threads(urls):threads = []for url in urls:thread = threading.Thread(target=worker, args=(url,))threads.append(thread)thread.start()# 等待所有线程完成for thread in threads:thread.join()# 爬取的URL列表
urls = ['http://example.com','http://example.org','http://example.net',# 添加更多URL...
]


```

 6. 运行爬虫
调用`manage_threads`函数并传入URL列表。

```python

if __name__ == "__main__":manage_threads(urls)


```

 7. 注意事项
GIL限制**:由于Python的GIL,多线程在执行CPU密集型任务时可能不会带来太大的性能提升。但对于I/O密集型任务,如网络请求,多线程可以显著提高效率。
线程安全**:在多线程环境下,共享数据时需要注意线程安全问题,避免竞态条件。
资源限制**:过多的线程可能会导致资源竞争和调度问题,需要合理控制线程数量。
异常处理**:每个线程都应该能够妥善处理异常,避免线程崩溃导致整个程序异常。

 8. 扩展功能
限制速率**:可以引入时间延迟来遵守网站的爬虫政策。
用户代理**:设置用户代理(User-Agent)来模拟浏览器请求。
Cookies处理**:处理Cookies以维持会话状态。
重试机制**:对失败的请求实施重试策略。

这个案例展示了多线程爬虫的基本结构和实现方法。在实际应用中,你可能需要根据目标网站的特点和反爬措施进行相应的调整和优化。

让我们继续扩展上面的例子,创建一个更完整的多线程爬虫案例。这个案例将包括以下功能:

1. 多线程爬取网页
2. 解析网页内容
3. 限制请求速率
4. 简单的错误处理和日志记录

首先,我们需要安装所需的库(如果尚未安装):

```bash

pip install requests beautifulsoup4


```

然后,我们将创建一个更完整的多线程爬虫程序:```python

import requests
from bs4 import BeautifulSoup
import threading
import time
import logging
from queue import Queue# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')# 请求队列
url_queue = Queue()
# 存放结果的队列
result_queue = Queue()def crawl(url):try:response = requests.get(url, timeout=5)response.raise_for_status()  # 检查请求是否成功soup = BeautifulSoup(response.text, 'html.parser')# 假设我们爬取的是网页的标题和一些链接title = soup.find('title').get_text()links = [a['href'] for a in soup.find_all('a', href=True)]result_queue.put((url, title, links))logging.info(f"成功爬取: {url}")except requests.RequestException as e:logging.error(f"请求错误: {url} - {e}")except Exception as e:logging.error(f"解析错误: {url} - {e}")def worker():while not url_queue.empty():url = url_queue.get()crawl(url)# 模拟网络延迟time.sleep(1)def manage_threads(url_list, thread_count=5):# 将URL加入队列for url in url_list:url_queue.put(url)# 创建并启动线程threads = []for _ in range(thread_count):thread = threading.Thread(target=worker)threads.append(thread)thread.start()# 等待所有线程完成for thread in threads:thread.join()# 打印结果while not result_queue.empty():url, title, links = result_queue.get()print(f"URL: {url}, Title: {title}, Links: {links}")# 爬取的URL列表
urls = ['http://example.com','http://example.org','http://example.net',# 添加更多URL...
]if __name__ == "__main__":manage_threads(urls, thread_count=10)


```

### 案例详解:

- 日志记录:使用`logging`模块记录日志,方便跟踪爬虫的状态和错误。
- 请求队列:使用`Queue`来管理URL列表,线程安全地在多个线程间传递任务。
- 结果队列:同样使用`Queue`来存放爬取的结果。
- 速率限制:通过`time.sleep(1)`模拟网络延迟,限制请求速率,避免对目标网站造成过大压力。
- 线程管理:`manage_threads`函数负责初始化队列、启动线程和收集结果。

### 注意事项:

- 线程数量:创建的线程数量应根据目标网站和服务器性能进行调整。
- 异常处理:每个线程都应该能够处理请求和解析过程中可能出现的异常。
- 队列处理:确保队列在所有线程中正确地被管理,避免竞态条件。
- 资源管理:确保所有网络请求和线程都正确地被管理,避免资源泄露。

这个案例提供了一个基本的多线程爬虫框架,可以根据具体需求进行扩展和优化。

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

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

相关文章

嵌入式进阶——RTC时钟

🎬 秋野酱:《个人主页》 🔥 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 RTC时钟原理图PCF8563寄存器控制与状态寄存器 设备地址I2C环境初始化RTC寄存器数据读取RTC寄存器数据写入RTC闹钟设置RTC定时器设置…

2024.5.28晚训题解

提前预告&#xff0c;市赛初中组会考算法题&#xff0c;应该会有两道模板题 比如DFS BFS 二分 简单动态规划&#xff0c;虽然我们没学多久&#xff0c;但是模板题你还是要会写的 A题 编辑距离 动态规划 注意多组输入 #include<iostream> using namespace std; int dp[1…

9、C#【进阶】特性

特性 文章目录 1、特性概念2、自定义特性 Attribute3、特性的使用4、限制自定义特性的使用范围5、系统自带特性1、过时特性2、调用者信息特性3、条件编译特性4、外部dll包函数特性 1、特性概念 特性是一种允许我们向程序的程序集添加元数据的语言结构 它是用于保存程序机构信息…

【机器学习300问】103、简单的经典卷积神经网络结构设计成什么样?以LeNet-5为例说明。

一个简单的经典CNN网络结构由&#xff1a;输入层、卷积层、池化层、全连接层和输出层&#xff0c;这五种神经网络层结构组成。它最最经典的实例是LeNet-5&#xff0c;它最早被设计用于手写数字识别任务&#xff0c;包含两个卷积层、两个池化层、几个全连接层&#xff0c;以及最…

ansible批量漏洞升级openssh版本

1、ansible宿主机准备好环境&#xff0c;并写好hosts文件 [rootoxidized ansible]# cat hosts [all] 10.10.200.33 10.10.200.34 10.10.200.35跑playbook之前记得提前发送秘钥 ssh-copy-id 10.10.200.33/34/352、下载好安装包&#xff0c;然后编写yml [rootoxidized ansible]…

【实用的 IDEA 配置和操作技巧总结】

前置知识 IDEA的设置快捷键为ctrlalts键&#xff0c;后文介绍IDEA常见的配置就不再赘述这一点了。 基础配置 取消默认打开上次项目 日常开发都会打开不同的项目&#xff0c;初次安装IDEA之后&#xff0c;每次打开IDEA都会开启上一次启动的项目&#xff0c;所以我们需要进入设…

0基础学习Mybatis系列数据库操作框架——Mysql的Geometry数据处理之WKB方案

大纲 序列化反序列化完整TypeHandlerSQL XML完整XML Mapper测试代码代码 在《0基础学习Mybatis系列数据库操作框架——Mysql的Geometry数据处理之WKT方案》中&#xff0c;我们介绍WTK方案的优点&#xff0c;也感受到它的繁琐和缺陷。比如&#xff1a; 需要借助ST_GeomFromText…

element+ 引入图标报错 Failed to resolve import “@element-plus/icons-vue“ from “

element 引入图标报错 Internal server error: Failed to resolve import “element-plus/icons-vue” from “src\components\TimeLine.vue”. Does the file exist? 原因&#xff1a;element-plus需要单独引入 icons 文档 pnpm install element-plus/icons-vue之后就可以…

350种类型、10W+量级的API,企业应该怎么管?

忽如一夜春风来&#xff0c;万物皆可API。 在互联网时代&#xff0c;API无处不在&#xff1a;企业对外开放的数据、服务和业务能力&#xff0c;以API的形式提供给合作方&#xff1b;企业内部应用与应用、App与App之间的通信&#xff0c;通过API进行&#xff1b;甚至应用内部的…

php 连接sqlserver步骤

1.首先要确定使用的是sqlserver的哪个版本&#xff0c;比如sqlserver2012 2.确定服务器是64位还是32位的 3.确认一下使用php的哪个版本&#xff0c;比如php7.1 SQL Server 的 Microsoft PHP 驱动程序 Microsoft Drivers for PHP 支持矩阵 - PHP drivers for SQL Server | Mi…

Flutter 中的 CupertinoTabView 小部件:全面指南

Flutter 中的 CupertinoTabView 小部件&#xff1a;全面指南 在 Flutter 中&#xff0c;CupertinoTabView 是 Cupertino 组件库中的一个 widget&#xff0c;它用于创建 iOS 风格的标签页视图。这个 widget 通常与 CupertinoTabScaffold 结合使用&#xff0c;提供了一个底部带有…

怎么做好客户信息管理?

根据Forrester的调查表示&#xff0c;客户满意度的影响可能会使某些行业的收入每年增加高达 10 亿美元。而提升客户满意度的关键环节便是做好客户信息管理。但企业在进行客户信息管理中往往会遇到以下问题&#xff1a; 客户信息乱&#xff1a;客户信息存在各个 Excel表格、个人…

PMP报考条件怎么查询?如何判定自己是否符合条件?

PMP报考条件在PMI官网上就可以查询&#xff0c;PMP报考条件只需要符合项目管理培训经历和项目管理经验两个方面的要求即可&#xff0c;大家可以对照下方的规定判断自己是否符合PMP报名条件 PMP报考条件 以下是PMI&#xff08;中国&#xff09;官网对于PMP报名条件的规定&…

优秀的数据分析师需要具备哪些?

在数据驱动的时代&#xff0c;数据分析师的角色越来越被重视。本文将探讨优秀数据分析师必备的三大核心能力&#xff0c;并通过实际案例说明如何将这些能力转化为业务价值&#xff0c;帮助你在职业道路上更进一步。 在数字化迅速发展的今天&#xff0c;数据分析师扮演着极其重要…

ubuntu strace命令

strace 是 Linux 系统中的一个调试工具&#xff0c;用于跟踪并记录系统调用&#xff08;system calls&#xff09;和信号&#xff08;signals&#xff09;。在 Ubuntu 中&#xff0c;strace 命令可以帮助开发者和系统管理员了解一个程序在运行时如何与操作系统内核进行交互&…

TypeScript常见面试题第八节

题目三十六:什么是参数解构? 一、讲解视频 TS面试题三十六:什么是参数解构? 二、题目解析 本题目考察 ts 中的解构,解构是一种特殊语法,可以将对象解构到一个或多个局部变量中,可展开操作符相反,展开是允许将一个数组展开为另一个数组,或将一个对象展开为另一个对象,…

vue+antd实践:在输入框光标处插入内容

今天来看一个很简单的需求。 需求描述&#xff1a;在输入框光标处&#xff0c;插入指定的内容。 效果如下&#xff1a; 实现思路&#xff1a;刚开始还在想怎么获取光标的位置&#xff0c;但是发现所做的项目是基于vue3antd组件&#xff0c;那么不简单了嘛&#xff0c;只要调…

JAVA自制小游戏之推箱子

给家里孩子实现益智游戏开发,教会他怎么使用编程。以下是一个简单的推箱子游戏的Java实现,包含两个关卡: 这个程序包含两个关卡,每个关卡都是一个字符串表示的地图。游戏会提示玩家输入移动方向(WASD),然后根据输入的方向移动玩家。如果玩家成功将所有的箱子推到目标位…

配置物联网平台 保姆级教程

一、云平台配置&#xff08;我们这里使用阿里云&#xff09; 1、注册和登录 &#xff08;1&#xff09;找到云平台官网&#xff0c;点击右上角的注册登录&#xff0c;完成之后&#xff0c;进行实名认证&#xff0c;任选一种认证方式。 ​​​​​​​ 2、实例的开通和创建 …

Scala环境的搭建

要搭建Scala&#xff0c;我们必须先下载java&#xff0c;由于我的电脑已经搭建好了环境&#xff0c;因此我这里用截图来教大家搭建环境。 可以从网上搜索安装包对其进行安装 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE 不建议下载最新版的&#xff0c;大家下载的版本可以下…