当前位置: 首页 > news >正文

使用 Python 和 PyQt5 构建多线程图片下载器

在这篇文章中,我们将探讨如何使用 Python 和 PyQt5 构建一个多线程图片下载器,该下载器可以从 wallhaven.cc 网站批量下载壁纸图片。我将通过代码示例逐步讲解项目的各个部分,帮助你理解其工作原理,并提供一些优化建议。

文章代码是结合AI的建议不断的修改,还有不足之处,如有bug也可以进行反馈!!!
完整代码下载在文末!!!

1. 项目概述

这个项目的核心功能是通过并发的方式快速下载大量图片。用户可以通过一个图形界面(GUI)选择下载条件(如图片类别、纯度、页数等),然后程序会自动从指定的网页抓取图片并下载到本地。

2. 项目代码结构

首先,我们来看看项目的代码结构。

import os
import sys
import time
import requests
from PyQt5 import QtCore, QtGui, QtWidgets
import threading
from PyQt5.QtWidgets import QMessageBox, QFileDialog
from bs4 import BeautifulSoupheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, Gecko) Chrome/81.0.4389.90 Safari/531.36'
}
cent = 0
lock = threading.Lock()

在代码开头,我们导入了必要的模块:

  • os, sys, time: 用于文件操作、系统操作和时间处理。
  • requests: 处理 HTTP 请求。
  • threading: 实现多线程。
  • PyQt5: 用于构建图形界面。
  • BeautifulSoup: 用于解析 HTML 内容。

headers 是请求头,伪装成浏览器以避免被网站拒绝。cent 是全局计数器,用于记录已下载的图片数量。

3. 图片下载逻辑

接下来,我们定义了一个类 eachPageThread,用于处理每个页面的图片下载任务。

class eachPageThread(threading.Thread):def __init__(self, url, file_name, form):threading.Thread.__init__(self)self.form = formself.eachPageUrl = urlself.file_path = file_namedef run(self):try:eachPageCollection = []content = open_url(self.eachPageUrl)soup = BeautifulSoup(content, 'lxml')images = soup.find('section', class_="thumb-listing-page")for li in images.find_all('li'):string = str(li.a['href'])eachPageCollection.append(string)threadingSet = []for eachImage in eachPageCollection:name = eachImage.split('/')[-1]eachImageUrl = f'https://w.wallhaven.cc/full/{name[0:2]}/wallhaven-{name}.jpg'html = requests.head(eachImageUrl)res = html.status_codeImagePosixFlag = 0if res == 404:eachImageUrl = eachImageUrl[:-3] + 'png'ImagePosixFlag = 1t = threading.Thread(target=downloadEachImage, args=(eachImageUrl, name, self.file_path, ImagePosixFlag))threadingSet.append(t)t.start()ui.set_down_nums(f'已经下载 {cent} 张')for eachThread in threadingSet:eachThread.join()except Exception as e:print(f"Error in eachPageThread: {e}")
  • eachPageThread:这是一个继承自 threading.Thread 的类,用于处理每个页面的图片下载。
  • run 方法:抓取页面中的所有图片链接,并启动多个线程下载这些图片。图片下载完成后,使用全局计数器 cent 更新下载数量。

图片下载的实际执行在 downloadEachImage 函数中进行:

def downloadEachImage(url, name, file_path, flag):global centfix_file_name = f'{file_path}/{name}.jpg' if flag == 0 else f'{file_path}/{name}.png'if not os.path.exists(fix_file_name):print(f"正在下载 {fix_file_name}")with open(fix_file_name, 'wb') as f:img = requests.get(url, headers=headers).contentf.write(img)with lock:cent += 1else:print(f"发现 {fix_file_name} 存在,未下载")
  • downloadEachImage 函数:下载单张图片,并确保下载后计数器 cent 的更新是线程安全的。通过 flag 标记来区分下载 .jpg.png 格式的图片。
4. 网络请求与 HTML 解析

为了抓取网页内容,我们定义了一个简单的函数 open_url

def open_url(url):response = requests.get(url, headers=headers)response.encoding = 'utf-8'return response.text

这个函数发送 HTTP 请求并返回页面的 HTML 内容,供 BeautifulSoup 进行解析。

5. GUI 界面实现

项目的图形界面由 Ui_Form 类构建,该类定义了窗口的布局和逻辑。

class Ui_Form(object):def setupUi(self, Form):# 配置窗口和控件Form.setObjectName("Form")Form.resize(794, 497)self.mesb = QMessageBoxself.mark = [1, 1, 0]self.mark_2 = [1, 1, 0]self.file = ''self.sorting = 'toplist'self.topRange = '1M'self.categories = '110'self.purity = '110'font = QtGui.QFont()font.setPointSize(12)# 添加按钮和输入框self.Button_start = QtWidgets.QPushButton(Form)self.Button_start.setGeometry(QtCore.QRect(510, 50, 169, 61))self.Button_start.setFont(font)self.Button_start.setObjectName("Button_start")self.Button_start.clicked.connect(lambda: self.start(Form))# 选择文件夹按钮self.Button_choose_file = QtWidgets.QPushButton(Form)self.Button_choose_file.setGeometry(QtCore.QRect(320, 240, 121, 61))self.Button_choose_file.setFont(font)self.Button_choose_file.setObjectName("Button_choose_file")self.Button_choose_file.clicked.connect(lambda: self.thread_it(self.get_filename(Form)))# 其他控件省略...
  • setupUi 方法:设置窗口和控件的属性及位置,包括按钮、输入框、标签等。
  • 按钮的功能:如 Button_start 按钮连接到 start 方法,用于触发图片下载。

在 GUI 中,用户可以输入下载页数、选择保存路径、设置图片类别和纯度,所有操作都通过按钮和输入框来完成。

def start(self, Form):if not self.mark_2[2]:self.mesb.warning(Form, '警告', '请选择文件目录', QMessageBox.Yes)returnif not self.Page_input.text().isdigit():self.mesb.warning(Form, '警告', '请输入有效的页数', QMessageBox.Yes)returnnums = self.spinBox_nums_common.value()self.mark[2] = 1page = int(self.Page_input.text())url_template = f"https://wallhaven.cc/search?categories={self.categories}&purity={self.purity}&sorting={self.sorting}&topRange={self.topRange}&page="for i in range(1, page + 1):print(f'正在爬取第 {i} 页')t = eachPageThread(f"{url_template}{i}", self.file, Form)t.start()t.join()self.mark[2] = 0self.mesb.warning(Form, '提示', '下载完成', QMessageBox.Yes)
  • start 方法:验证用户输入的页数和文件夹路径,构建下载 URL,并调用 eachPageThread 类进行下载。
6. 多线程下载与优化

多线程的实现是这个项目的关键之一。在 eachPageThread 类中,每个页面的下载任务都是在一个独立的线程中执行的,这大大提高了下载速度。

然而,多线程编程中经常会遇到资源争用问题,比如多个线程同时修改一个变量。在这个项目中,我们使用全局锁 lock 来保护计数器 cent,确保即使在高并发的情况下,程序也能稳定运行。

with lock:cent += 1

这种做法确保了 cent 的更新操作是线程安全的,避免了并发冲突。

7. 可能的改进与优化

虽然这个项目功能齐全,但仍有一些改进的空间:

  • 错误处理:目前的错误处理比较简单,可以进一步增强 open_url 和图片下载中的错误处理,防止程序因为网络问题或其他异常而崩溃。
  • 多线程优化:考虑使用线程池或 asyncio 来优化并发模型,这可以提高在高并发情况下的性能表现。
  • 用户体验:在 GUI 中添加更多的用户提示或进度条,以更好地显示下载进度和状态。
8. 总结

通过这个项目,我们可以看到 Python 在处理多线程任务以及构建 GUI 应用程序方面的强大能力。多线程的使用显著提高了任务处理的效率,而 PyQt5 则提供了一个用户友好的图形界面,使得复杂的操作变得简单直观。对于需要批量处理大量数据或文件下载的场景,这种多线程与 GUI 的组合无疑是非常有效的解决方案。

这个项目不仅展示了 Python 在实际项目中的应用,也为进一步的优化和扩展提供了可能。希望这篇文章能为你提供一些关于多线程编程和 PyQt5 开发的灵感和实用技巧。或 asyncio 来进一步优化并发模型,特别是在高并发的情况下。

  • 用户体验:添加更多的用户提示或进度条,进一步提高用户体验。
  • 代码结构优化:将下载逻辑、GUI 逻辑和线程管理逻辑分离到不同的模块,提高代码的可维护性。
8. 总结

通过这个项目,我们展示了如何利用 Python 的多线程能力和 PyQt5 的 GUI 构建一个高效的图片下载器。这个项目不仅展示了 Python 在处理并发任务上的强大能力,还展示了如何通过图形界面使复杂的操作变得更加直观和用户友好。

完整代码地址:https://github.com/stfghly/wallhaven-download

你可以根据自己的需求,进一步扩展和优化这个项目,打造一个更为强大的图片下载器。希望这篇文章能为你提供一些关于多线程编程和 PyQt5 开发的灵感!
在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 单HTML文件集成vue3+ElementPlus的使用
  • 构建高效NLP管道:PydanticOutputParser与Langchain的结合
  • 机器学习课程学习周报九
  • 【文档合集】软件类常用文档整理大全,软件工程,软件项目管理,技术标书方案,模
  • 【系统规划与管理师】【案例分析】【课后习题】第九章 IT服务营销
  • 数据库架构演变过程
  • vagrant 创建虚拟机
  • 使用notepad++将shell脚本转为UNIX格式方法(主要差别在换行符)
  • SpringBoot集成google登陆快速入门Demo
  • 运放篇——理想运放与实际运放
  • Docker 安装与配置 Docker Registry 指南
  • Linux——文件系统层次结构,绝对路径
  • 膨胀罐的安装注意事项
  • torch.unbind()拆分张量练习过程
  • 基础算法--递推算法[信奥一本通]
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • Create React App 使用
  • IDEA常用插件整理
  • JAVA SE 6 GC调优笔记
  • JavaScript新鲜事·第5期
  • JS函数式编程 数组部分风格 ES6版
  • Laravel5.4 Queues队列学习
  • Lsb图片隐写
  • mongodb--安装和初步使用教程
  • nodejs调试方法
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Sublime text 3 3103 注册码
  • Vue官网教程学习过程中值得记录的一些事情
  • zookeeper系列(七)实战分布式命名服务
  • 缓存与缓冲
  • 如何编写一个可升级的智能合约
  • 在weex里面使用chart图表
  • 数据库巡检项
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​MySQL主从复制一致性检测
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • #{}和${}的区别?
  • #Linux(帮助手册)
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • (~_~)
  • (C11) 泛型表达式
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (Java数据结构)ArrayList
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (不用互三)AI绘画工具应该如何选择
  • (二)PySpark3:SparkSQL编程
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (四)opengl函数加载和错误处理
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (原创)可支持最大高度的NestedScrollView
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • (转)为C# Windows服务添加安装程序
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**