Python多线程爬虫编程中queue.Queue和queue.SimpleQueue的区别和应用

简介: 在Python中,queue模块提供了多种队列类,用于在多线程编程中安全地交换信息。其中,queue.Queue 和queue.SimpleQueue 是两个常用的先进先出(FIFO)的队列类,它们有以下区别和优缺点:queue.Queue 是一个更复杂的队列类实现涉及到多个锁和条件变量,因此可能会影响性能和内存效率。SimpleQueue 是一个更简单的队列类它只提供了put()和get()两个方法,并且不支持maxsize参数

247047476.jpg


在Python中,queue模块提供了多种队列类,用于在多线程编程中安全地交换信息。其中,queue.Queue 和queue.SimpleQueue 是两个常用的先进先出(FIFO)的队列类,它们有以下区别和优缺点:

  1. 1、queue.Queue 是一个更复杂的队列类,它提供了一些方法和功能,如限制队列大小、等待队列中的任务完成、检查队列是否为空或满等。这些功能可以方便地在多线程环境中同步生产者和消费者的行为,并且使得代码更易于设计、阅读和维护。
  • 2、queue.Queue 的缺点是它的实现涉及到多个锁和条件变量,因此可能会影响性能和内存效率。另外,它不能处理重入性的问题,即如果在同一线程中调用put()或get()方法时被打断,可能会导致死锁或数据丢失。
  • 3、queue.SimpleQueue 是一个更简单的队列类,它只提供了put()和get()两个方法,并且可以处理重入性的问题。因此,它有更好的性能和内存效率,并且可以在一些特殊情况下安全地调用put()或get()方法,如del方法、weakref回调或信号处理器。
  • 4、queue.SimpleQueue 的缺点是它只提供了put()和get()两个方法,并且不支持maxsize参数。因此,它不能限制队列大小,也不能等待队列中的任务完成。如果需要这些功能,可以使用queue.Queue 或者queue.JoinableQueue。

下面分别用Queue和queue.SimpleQueue,根据多线程网络请求的需求进行实现。

importqueueimportthreadingimportrequests# 定义一个队列对象,用于在多线程中传递数据q=queue.Queue()
# 定义一个函数,用于在子线程中发送请求,并使用代理IPdefsend_request():
# 从队列中获取数据,如果队列为空,则阻塞等待data=q.get()
# 获取代理IP地址、用户名和密码,以及目标URLproxy_ip=data["proxy_ip"]    
username=data["username"]
password=data["password"]
url=data["url"]
# 设置代理参数,包括代理IP、用户名和密码proxies= {
"http": f"http://{username}:{password}@{proxy_ip}",
"https": f"http://{username}:{password}@{proxy_ip}"    }
# 发送请求,并打印响应状态码和内容try:
response=requests.get(url, proxies=proxies)
print(f"Response status: {response.status_code}")
print(f"Response content: {response.text}")
exceptExceptionase:
print(f"Error: {e}")
# 通知队列任务已完成,并释放资源q.task_done()
# 在主线程中创建三个子线程对象,并传入send_request函数作为参数,并设置为守护线程(daemon)threads= []
foriinrange(3):
thread=threading.Thread(target=send_request, daemon=True)
threads.append(thread)
# 启动三个子线程,并将它们加入到主线程的等待列表中forthreadinthreads:
thread.start()
# 在主线程中向队列中放入数据,这里假设有三组代理IP和URL的组合data_list= [
    {"proxy_ip": "www.16yun.cn:3100", "username": "16YUN", "password": "16IP-ps1", "url": "http://example.com/1.html"},
    {"proxy_ip": "www.16yun.cn:3100", "username": "16YUN", "password": "16IP-ps2", "url": "http://example.com/2.html"},
    {"proxy_ip": "www.16yun.cn:3100", "username": "16YUN", "password": "16IP-ps3", "url": "http://example.com/3.html"}
]
fordataindata_list:
q.put(data)
# 等待队列中的所有任务完成,并阻塞主线程直到所有子线程结束q.join()

上面代码使用Queue实现了一个多线程的爬虫程序,通过3个线程采集不同的url,然后等待队列中的所有任务完成,并阻塞主线程直到所有子线程结束。

importqueueimportthreadingimportrequests# 定义一个SimpleQueue对象,用于在多线程中传递数据q=queue.SimpleQueue()
# 定义一个函数,用于在子线程中发送请求,并使用代理IPdefsend_request():
# 从队列中获取数据,如果队列为空,则阻塞等待data=q.get()
# 获取代理IP地址和目标URLproxy_ip=data["proxy_ip"]
url=data["url"]
# 获取用户名和密码username=data["username"]
password=data["password"]
# 设置代理参数,并添加认证信息proxies= {
"http": f"http://{proxy_ip}",
"https": f"http://{proxy_ip}"    }
auth=requests.auth.HTTPProxyAuth(username, password)
# 发送请求,并打印响应状态码和内容try:
response=requests.get(url, proxies=proxies, auth=auth)
print(f"Response status: {response.status_code}")
print(f"Response content: {response.text}")
exceptExceptionase:
print(f"Error: {e}")
# 在主线程中创建三个子线程对象,并传入send_request函数作为参数,并设置为守护线程(daemon)threads= []
foriinrange(3):
thread=threading.Thread(target=send_request, daemon=True)
threads.append(thread)
# 启动三个子线程,并将它们加入到主线程的等待列表中forthreadinthreads:
thread.start()
# 在主线程中向队列中放入数据,这里假设有三组代理IP和URL的组合,以及对应的用户名和密码data_list= [
    {"proxy_ip": "www.16yun.cn:3100", "url": "http://example.com/1.html", "username": "16YUN", "password": "16IP-ps1"},
    {"proxy_ip": "www.16yun.cn:3100", "url": "http://example.com/2.html", "username": "16YUN", "password": "16IP-ps2"},
    {"proxy_ip": "www.16yun.cn:3100", "url": "http://example.com/3.html", "username": "16YUN", "password": "16IP-ps3"}
]
fordataindata_list:
q.put(data)
# 等待所有子线程结束(由于是守护线程,所以当主线程结束时会自动结束)

上面代码使用SimpleQueue对象,实现了一个生产者-消费者模式的多线程爬虫程序程序。它的功能是在多个子线程中使用代理IP向目标URL发送HTTP请求,并打印响应状态码和内容,等待所有子线程结束。由于子线程设置为守护线程,所以当主线程结束时,子线程也会自动结束。

通过上述示例,可以分别根据目前的应用场景和需求选择适合的方式。

相关文章
|
3天前
|
存储 调度 C++
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
23 1
|
3天前
|
数据采集 存储 JSON
Python网络爬虫教程概览
【6月更文挑战第21天】Python网络爬虫教程概览:安装requests和BeautifulSoup库抓取网页;使用HTTP GET请求获取HTML,解析标题;利用CSS选择器提取数据;处理异步内容可选Selenium;遵循爬虫策略,处理异常,尊重法律与网站规定。
8 1
|
6天前
|
分布式计算 JavaScript 前端开发
多线程、多进程、协程的概念、区别与联系
多线程、多进程、协程的概念、区别与联系
18 1
|
1天前
|
机器学习/深度学习 人工智能 前端开发
Python中的模块化编程
【6月更文挑战第17天】Python模块化编程与软件架构设计的关键在于拆分任务到独立模块,提高代码的可维护性、可重用性和可扩展性。例如,学生管理系统可分解为录入、查询和删除模块。MVC和MVVM架构模式有助于组织代码,而微服务和函数式编程将在未来发展中扮演重要角色。通过示例代码,读者能学习如何实现这些概念,提升项目开发效率和质量。
146 56
|
1天前
|
数据采集 前端开发 JavaScript
python语言通过简单爬虫实例了解文本解析与读写
python|通过一个简单爬虫实例简单了解文本解析与读写
|
4天前
|
安全 Java Python
GIL是Python解释器的锁,确保单个进程中字节码执行的串行化,以保护内存管理,但限制了多线程并行性。
【6月更文挑战第20天】GIL是Python解释器的锁,确保单个进程中字节码执行的串行化,以保护内存管理,但限制了多线程并行性。线程池通过预创建线程池来管理资源,减少线程创建销毁开销,提高效率。示例展示了如何使用Python实现一个简单的线程池,用于执行多个耗时任务。
17 6
|
6天前
|
开发者 Python
【干货】Python编程惯例
【干货】Python编程惯例
11 1
|
5天前
|
数据采集 前端开发 JavaScript
Python爬虫技术:动态JavaScript加载音频的解析
Python爬虫技术:动态JavaScript加载音频的解析
|
8天前
|
数据采集 存储 数据挖掘
Python网络爬虫实战:抓取并分析网页数据
使用Python的`requests`和`BeautifulSoup`,本文演示了一个简单的网络爬虫,抓取天气网站数据并进行分析。步骤包括发送HTTP请求获取HTML,解析HTML提取温度和湿度信息,以及计算平均温度。注意事项涉及遵守robots.txt、控制请求频率及处理动态内容。此基础爬虫展示了数据自动收集和初步分析的基础流程。【6月更文挑战第14天】
80 9
|
20天前
|
数据采集 JSON API
自动化Reddit图片收集:Python爬虫技巧
自动化Reddit图片收集:Python爬虫技巧