Python爬虫中的多线程、线程池

2023-12-25 22:28
文章标签 python 线程 多线程 爬虫

本文主要是介绍Python爬虫中的多线程、线程池,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

进程和线程的基本介绍

进程是一个资源单位,线程是一个执行单位,CPU调度线程来执行程序代码。

当运行一个程序时,会给这个程序分配一个内存空间,存放变量等各种信息资源,而这个内存空间可以说是一个进程, 一个进程默认情况下会有一个线程,称为主线程(因为执行是靠线程的,CPU调度线程来执行程序代码,如果没有线程,那么进程中的资源就不能被使用,代码也就不能被执行)

做个比喻:一个进程相当于一个公司,公司里有各种办公资源,而公司里的员工就相当于线程,工作由员工使用办公资源完成, 如果没有员工,那么那些办公资源不会自动把工作完成,而一个公司必须至少有一个员工,不然公司还能自己成立嘛?

单线程

def func():for i in range(3):print('func: ', i)if __name__ == '__main__':func()for i in range(3):print('main: ', i)

执行效果:

 

多线程(写法一)

from threading import Thread# 多线程
def func():for i in range(3):print('func: ', i)if __name__ == '__main__':# 创建一个线程,target参数是告诉这个线程该干什么事# 就比如招聘了一个新员工,要给他安排任务# 此处给线程的任务就是调用func方法t = Thread(target=func)# 启动线程,告诉线程可以开始干活了# 注意,start方法只是说线程的状态是工作状态,但是什么时候真的开始执行,由CPU决定# 就是说线程已经随时准备就绪,等待CPU的调度执行t.start()for i in range(3):print('main: ', i)# 多线程
def func(name):for i in range(3):print(name, i)if __name__ == '__main__':# 要给func传递参数,则可以利用args参数# 注意,args接收的是元祖,所以只有一个参数的话,要加上逗号t1 = Thread(target=func, args=('线程1',))t1.start()# 创建第二个线程t2 = Thread(target=func, args='线程2',)for i in range(3):print('main: ', i)

 执行结果:

多线程(写法二)

from threading import Thread# 多线程(写法二)
# 自定义线程类,要继承 Thread
class MyThread(Thread):def run(self):  # 重写run方法for i in range(3):print('子线程: ', i)if __name__ == '__main__':t = MyThread()# 注意,不能 t.run() ,否则就相当于方法调用,那么就是单线程而不是多线程# 必须通过start方法调用# t.run()  # 错误# 调用start方法时,start方法会自己去调用run方法# 所以当线程被执行时,执行的是run方法里的代码逻辑t.start()for i in range(3):print('主线程: ', i)

执行效果

创建多进程

和创建多线程差不多 

from multiprocessing import Processdef func():for i in range(3):print('子进程:', i)# 创建进程比创建线程所耗费的资源要多很多,所以一般我们使用的都是线程
if __name__ == '__main__':# 如果打印结果不是混着的,应该也是正常的,因为我执行的时候结果和单线程一样p = Process(target=func)p.start()for i in range(3):print('主进程:', i)

线程池和进程池基本介绍

假设现在我们要爬取一个网站的评论信息,这个网站的评论由于有很多,被分为1000页,那么我们可以用for循环去爬取每一页的评论数据,但我们想提高效率,利用线程来完成。

我们可以创建1000个线程,每个线程分别爬取一个分页,但是创建线程也需要耗费时间和资源,创建1000个线程的效率可能不一定比for循环效率高。

那我们可以考虑少创建一些线程,比如创建50个线程,然后让这50个线程依次去爬取1000个分页的评论,这就是线程池的作用。我们不需要关心这50个线程的具体调度情况,比如哪个进程爬取了哪些网页之类的,我们只需要知道,这50个线程会一起合作,完成1000个URL的爬取任务即可。而具体的每个线程的调度安排由线程池管理。

所以线程池就是一次性开辟一些线程(如一次性开辟50个线程),我们用户直接给线程池提交某个任务(比如爬取1000个URL),由线程池安排这50个线程去共同完成这个任务。

线程池代码如下,进程池的使用和线程池一样,只需要把 ThreadPoolExecutor 换成 ProcessPoolExecutor就行

# ThreadPoolExecutor 线程池, ProcessPoolExecutor 进程池
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutordef task(name):print('执行任务', name)if __name__ == '__main__':# 创建有3个进程的进程池with ThreadPoolExecutor(3) as t:for i in range(6):  # 现在要完成6个任务,将这6个任务分配给3个线程# 向线程池t提交任务task, 任务task需要传惨直接在后面写上,如name=xxxt.submit(task, name=f'task{i}')# 只有上述线程池代码执行完成后,才会接着往下执行print('任务全部执行完毕...')

执行效果如下图:

 进程池的应用——爬取北京新发地的价格数据

import requests
from concurrent.futures import ThreadPoolExecutordef download_one_page(cur_page):url = 'http://www.xinfadi.com.cn/getPriceData.html'data = {'limit': 20,'current': cur_page,  # 第多少页的数据'pubDateStartTime': '','pubDateEndTime': '','prodPcatid': '','prodCatid': '','prodName': ''}resp = requests.post(url, data=data)print(resp.json())if __name__ == '__main__':# 可以分别对比for循环和用线程池两种爬取方式,会看到线程池的速度要快很多# for i in range(1, 101): #     download_one_page(i)with ThreadPoolExecutor(10) as t:  # 创建有10个进程的进程池for i in range(1, 101):  # 数据太多了,这里只爬取前100页t.submit(download_one_page, cur_page=i)print('爬取北京新发地前100页数据完成!')

 

这篇关于Python爬虫中的多线程、线程池的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/537180

相关文章

使用Python将PDF表格自动提取并写入Word文档表格

《使用Python将PDF表格自动提取并写入Word文档表格》在实际办公与数据处理场景中,PDF文件里的表格往往无法直接复制到Word中,本文将介绍如何使用Python从PDF文件中提取表格数据,并将... 目录引言1. 加载 PDF 文件并准备 Word 文档2. 提取 PDF 表格并创建 Word 表格

使用Python实现局域网远程监控电脑屏幕的方法

《使用Python实现局域网远程监控电脑屏幕的方法》文章介绍了两种使用Python在局域网内实现远程监控电脑屏幕的方法,方法一使用mss和socket,方法二使用PyAutoGUI和Flask,每种方... 目录方法一:使用mss和socket实现屏幕共享服务端(被监控端)客户端(监控端)方法二:使用PyA

Python列表的创建与删除的操作指南

《Python列表的创建与删除的操作指南》列表(list)是Python中最常用、最灵活的内置数据结构之一,它支持动态扩容、混合类型、嵌套结构,几乎无处不在,但你真的会创建和删除列表吗,本文给大家介绍... 目录一、前言二、列表的创建方式1. 字面量语法(最常用)2. 使用list()构造器3. 列表推导式

Python使用Matplotlib和Seaborn绘制常用图表的技巧

《Python使用Matplotlib和Seaborn绘制常用图表的技巧》Python作为数据科学领域的明星语言,拥有强大且丰富的可视化库,其中最著名的莫过于Matplotlib和Seaborn,本篇... 目录1. 引言:数据可视化的力量2. 前置知识与环境准备2.1. 必备知识2.2. 安装所需库2.3

Python数据验证神器Pydantic库的使用和实践中的避坑指南

《Python数据验证神器Pydantic库的使用和实践中的避坑指南》Pydantic是一个用于数据验证和设置的库,可以显著简化API接口开发,文章通过一个实际案例,展示了Pydantic如何在生产环... 目录1️⃣ 崩溃时刻:当你的API接口又双叒崩了!2️⃣ 神兵天降:3行代码解决验证难题3️⃣ 深度

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Python实现快速扫描目标主机的开放端口和服务

《Python实现快速扫描目标主机的开放端口和服务》这篇文章主要为大家详细介绍了如何使用Python编写一个功能强大的端口扫描器脚本,实现快速扫描目标主机的开放端口和服务,感兴趣的小伙伴可以了解下... 目录功能介绍场景应用1. 网络安全审计2. 系统管理维护3. 网络故障排查4. 合规性检查报错处理1.

JAVA线程的周期及调度机制详解

《JAVA线程的周期及调度机制详解》Java线程的生命周期包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED,线程调度依赖操作系统,采用抢占... 目录Java线程的生命周期线程状态转换示例代码JAVA线程调度机制优先级设置示例注意事项JAVA线程

Python轻松实现Word到Markdown的转换

《Python轻松实现Word到Markdown的转换》在文档管理、内容发布等场景中,将Word转换为Markdown格式是常见需求,本文将介绍如何使用FreeSpire.DocforPython实现... 目录一、工具简介二、核心转换实现1. 基础单文件转换2. 批量转换Word文件三、工具特性分析优点局