一文带你掌握Python中多线程和线程池的使用方法
作者:小小鸟爱吃辣条 发布时间:2022-10-20 21:53:09
Python是一种高级编程语言,它在众多编程语言中,拥有极高的人气和使用率。Python中的多线程和线程池是其强大的功能之一,可以让我们更加高效地利用CPU资源,提高程序的运行速度。本篇博客将介绍Python中多线程和线程池的使用方法,并提供一些实用的案例供读者参考。
一、多线程
多线程是指在同一进程中,有多个线程同时执行不同的任务。Python中的多线程是通过threading模块来实现的。下面是一个简单的多线程示例:
import threading
def task(num):
print('Task %d is running.' % num)
if __name__ == '__main__':
for i in range(5):
t = threading.Thread(target=task, args=(i,))
t.start()
上述代码中,我们定义了一个task函数,它接受一个参数num,用于标识任务。在主程序中,我们创建了5个线程,每个线程都执行task函数,并传入不同的参数。通过start()方法启动线程。运行上述代码,可以看到输出结果类似于下面这样:
Task 0 is running.
Task 1 is running.
Task 2 is running.
Task 3 is running.
Task 4 is running.
由于多线程是并发执行的,因此输出结果的顺序可能会有所不同。
二、线程池
线程池是一种管理多线程的机制,它可以预先创建一定数量的线程,并将任务分配给这些线程执行。Python中的线程池是通过ThreadPoolExecutor类来实现的。下面是一个简单的线程池示例:
import concurrent.futures
def task(num):
print('Task %d is running.' % num)
if __name__ == '__main__':
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
for i in range(5):
executor.submit(task, i)
上述代码中,我们使用了with语句创建了一个ThreadPoolExecutor对象,其中max_workers参数指定了线程池中最大的线程数量。在主程序中,我们创建了5个任务,每个任务都通过executor.submit()方法提交给线程池执行。运行上述代码,可以看到输出结果类似于下面这样:
Task 0 is running.
Task 1 is running.
Task 2 is running.
Task 3 is running.
Task 4 is running.
由于线程池中最大的线程数量为3,因此只有3个任务可以同时执行,其他任务需要等待线程池中的线程空闲后再执行。
三、使用案例
下面是一个实际的案例,展示了如何使用多线程和线程池来加速数据处理过程。假设我们有一个包含1000个元素的列表,需要对每个元素进行某种运算,并将结果保存到另一个列表中。我们可以使用单线程的方式来实现:
def process(data):
result = []
for item in data:
result.append(item * 2)
return result
if __name__ == '__main__':
data = list(range(1000))
result = process(data)
print(result)
上述代码中,我们定义了一个process函数,它接受一个列表作为参数,对列表中的每个元素进行运算,并将结果保存到另一个列表中。在主程序中,我们创建了一个包含1000个元素的列表,并将其传递给process函数。运行上述代码,可以看到输出结果类似于下面这样:
[0, 2, 4, 6, 8, ..., 1996, 1998]
Python中的多线程和线程池可以提高爬虫的效率,本文将介绍一个爬取豆瓣电影Top250的案例,并通过多线程和线程池优化爬取过程。
1.单线程爬取
首先,我们先来看一下单线程爬取的代码:
# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
def get_html(url):
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
else:
return None
except Exception as e:
print(e)
def parse_html(html):
soup = BeautifulSoup(html, 'lxml')
movie_list = soup.find(class_='grid_view').find_all('li')
for movie in movie_list:
title = movie.find(class_='title').string
rating = movie.find(class_='rating_num').string
print(title, rating)
def main():
url = 'https://movie.douban.com/top250'
html = get_html(url)
parse_html(html)
if __name__ == '__main__':
main()
这是一个简单的爬取豆瓣电影Top250的代码,首先通过requests库获取网页的HTML代码,然后使用BeautifulSoup库解析HTML代码,获取电影名称和评分。
但是,这种单线程爬取的方式效率较低,因为在获取HTML代码的时候需要等待响应,而在等待响应的过程中CPU会空闲,无法充分利用计算机的性能。
2.多线程爬取
接下来,我们通过多线程的方式来优化爬取过程。首先,我们需要导入Python中的threading库:
import threading
然后,我们将获取HTML代码的代码放在一个函数中,并将其作为一个线程来运行:
def get_html(url):
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
else:
return None
except Exception as e:
print(e)
class GetHtmlThread(threading.Thread):
def __init__(self, url):
threading.Thread.__init__(self)
self.url = url
def run(self):
html = get_html(self.url)
parse_html(html)
在上面的代码中,我们首先定义了一个GetHtmlThread类,继承自threading.Thread类,然后在类的构造函数中传入需要爬取的URL。在run方法中,我们调用get_html函数获取HTML代码,并将其传入parse_html函数中进行解析。
接下来,我们通过循环创建多个线程来进行爬取:
def main():
urls = ['https://movie.douban.com/top250?start={}'.format(i) for i in range(0, 250, 25)]
threads = []
for url in urls:
thread = GetHtmlThread(url)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
在上面的代码中,我们首先定义了一个urls列表,包含了所有需要爬取的URL。然后通过循环创建多个GetHtmlThread线程,并将其加入到threads列表中。最后,通过循环调用join方法等待所有线程执行完毕。
通过多线程的方式,我们可以充分利用计算机的性能,提高爬取效率。
3.线程池爬取
在多线程的方式中,我们需要手动创建和管理线程,这样会增加代码的复杂度。因此,我们可以使用Python中的线程池来进行优化。
首先,我们需要导入Python中的concurrent.futures库:
import concurrent.futures
然后,我们将获取HTML代码的代码放在一个函数中,并将其作为一个任务来提交给线程池:
def get_html(url):
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
else:
return None
except Exception as e:
print(e)
def parse_html(html):
soup = BeautifulSoup(html, 'lxml')
movie_list = soup.find(class_='grid_view').find_all('li')
for movie in movie_list:
title = movie.find(class_='title').string
rating = movie.find(class_='rating_num').string
print(title, rating)
def main():
urls = ['https://movie.douban.com/top250?start={}'.format(i) for i in range(0, 250, 25)]
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(get_html, url) for url in urls]
for future in concurrent.futures.as_completed(futures):
html = future.result()
parse_html(html)
在上面的代码中,我们首先定义了一个urls列表,包含了所有需要爬取的URL。然后通过with语句创建一个线程池,并设置最大线程数为5。接下来,我们通过循环将每个URL提交给线程池,并将返回的Future对象加入到futures列表中。最后,通过concurrent.futures.as_completed函数来等待所有任务执行完毕,并获取返回值进行解析。
通过线程池的方式,我们可以更加简洁地实现多线程爬取,并且可以更加灵活地控制线程的数量,避免线程过多导致系统负载过高的问题。
来源:https://juejin.cn/post/7221966100807303229


猜你喜欢
- 今天在看实验室的项目时,碰到了一个让我“棘手”的问题,其实也是自己太笨了。先把 sql 语句扔出来// 这条语句在id没有1时,不能得到正确
- 这篇文章主要介绍了通过实例解析Python调用json模块,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要
- 写好脚本,注册好服务之后,经测试,ORACLE可以随RHEL启动而启动,但不能随系统关闭而关闭。在网上找答案,发现几乎所有的设置过程帖子都是
- 引言Requests是Python中一个常用的第三方库,用于向Web服务器发起HTTP请求并获取响应。该库的使用简单,功能强大,被广泛应用于
- linux下MySQL 5.6源码安装记录如下1、下载:当前mysql版本到了5.6.20http://dev.mysql.com/down
- 3.0迟迟没有发布release版本,现阶段在Vue项目中使用Typescript需要花不小的精力在工程的配置上面。主要的工作是webpac
- 前言本文的内容主要是介绍了MYSQL每隔10分钟进行分组统计的实现方法,在画用户登录、操作情况在一天内的分布图时会非常有用,之前我只知道用「
- 概述迁移学习 (Transfer Learning) 是把已学训练好的模型参数用作新训练模型的起始参数. 迁移学习是深度学习中非常重要和常用
- 一、 for 循环根据变量赋值的次数进行循环for item in ["tom","bob",&qu
- 上一次的错误太多,排版也出现了问题,重写了一遍,希望大家支持.循环遍历一个元素是开发中最常见的需求之一,那么让我们来看一个由框架BASE2和
- PyHook是一个基于Python的“钩子”库,主要用于监听当前电脑上鼠标和键盘的事件。这个库依赖于另一个Python库PyWin32,如同
- 本文研究的主要是Python enumerate索引迭代的问题,具体介绍如下。索引迭代Python中,迭代永远是取出元素本身,而非元素的索引
- 在 PHP 中表示空的map或空数组都是以空数组形式,在转化为json数据时,会将空数组统一 json 序列化成 [],这样就存在
- 本文实例讲述了Python捕捉和模拟鼠标事件的方法。分享给大家供大家参考。具体分析如下:这个假期玩了不少galgame,不过有些很老的游戏没
- 一、Servlet实现文件上传,需要添加第三方提供的jar包下载地址:1) commons-fileupload-1.2.2-bin.zip
- 数据库发生阻塞和死锁的现象:一、数据库阻塞的现象:第一个连接占有资源没有释放,而第二个连接需要获取这个资源。如果第一个连接没有提交或者回滚,
- react-native安装流程1.npx react-native init AwesomeProject报错运行 cd ./demo/i
- 这篇文章主要介绍了python全局变量引用与修改过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 最近在看吴恩达的机器学习课程,自己用python实现了其中的logistic算法,并用梯度下降获取最优值。logistic分类是一个二分类问
- linux平台及windows平台mysql重启方 * inux下重启MySQL的正确方法:1、通过rpm包安装的MySQLservice m