PyQt制作【小红书图片抓取】神器

文章目录

  • 📢闲言碎语
  • 🐾窗口设计
  • 🐾功能设计
  • 📚资源领取

在这里插入图片描述

📢闲言碎语

最近写一个系统,被一个Bug折腾了两天,至今还未解决。由于解决Bug弄得我有点心力憔悴,于是想着写其他小项目玩玩(爬取小红书图片),放松放松,在构思这个小项目的时候想着弄得稍微复杂点,弄着弄着花了大概三个小时时间(屁股要坐烂了),以为要准备结束了,结果又遇到Bug……😵

前前后后解决大大小小的Bug又花了大概一个小时😊

在这里插入图片描述

好在最终将Bug解决了,项目也成功的启动咯~

在这里插入图片描述

具体实现,请往下看👇

🐾窗口设计

如下图所示,主窗口主要有窗口图标、自动的鼠标图标、“小红书图片抓取”标签、带有frame窗口的QLineEdit和两个QPushButton,虽然看起来简单,但其背后逻辑功能的实现也不是很难。

首先需要定义一个子类Frame,并通过继承QFrame类来进行一些窗口的基本设置,设置其窗口的样式和背景颜色。

class Frame(QFrame):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setFrameStyle(QFrame.Box | QFrame.Plain)
        self.setLineWidth(3)	# 外线宽
        self.setMidLineWidth(3)		# 中线宽 
        self.setStyleSheet("QFrame {border: 3px solid #ff2442;}")   # 设置边框颜色

接着设计主窗口,设置窗口和鼠标的图标,再设置基本的控件,其中setFrameWidget方法主要是将控件放在Frame窗口中,并通过传入的参数,计算控件的大小和移动的位置,让控件与Frame窗口贴合,达到一个美化的效果。

然后重写mousePressEventmouseMoveEventmouseReleaseEvent事件,实现鼠标在窗口中按下可移动的功能,其中还会带动processWindow(显示过程信息的窗口)的移动。

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        # 设置窗口
        self.setWindowTitle('小红书图片抓取')
        self.resize(400, 250)
        self.setWindowIcon(QIcon('window.png'))
        self.setStyleSheet('background-color: white;')

        # 设置鼠标
        self.setCursor(QCursor(QPixmap('mouse.png').scaled(30, 30), 0, 0))

        # 设置frame窗口宽度
        self.frameWidth = 3

        self.mouseFlag = False

        self.setup_ui()

    # 设置组件
    def setup_ui(self):
        # 设置窗口大标题
        label_title = QLabel(self)
        label_title.setText('小红书图片抓取')
        label_title.setStyleSheet('color: #ff2442')
        label_title.move(50, 10)
        label_title.setFont(QFont('华文行楷', 25))
        label_title.adjustSize()

        # 设置文本框接收
        self.lineEdit_filepath = self.setFrameWidget(QLineEdit, (130, 30), (70, 80))
        # 设置选择文件按钮
        self.button_selectFile = self.setFrameWidget(QPushButton, (100, 30), (220, 80))
        self.button_selectFile.setText("选择文件")
        self.button_selectFile.setFont(QFont('华文行楷', 13))
        self.button_selectFile.clicked.connect(lambda: self.selectFile(self.lineEdit_filepath))

        # 设置开始启动按钮
        self.button_start = self.setFrameWidget(QPushButton, (200, 70), (90, 150))
        self.button_start.setText('开始启动')
        self.button_start.setFont(QFont('华文行楷', 20))
        self.button_start.clicked.connect(self.start)
	
	    # 选择文件
    def selectFile(self, lineEdit):
        # 弹出对话框选择文件夹
        file_dialog = QFileDialog()
        file_dialog.setFileMode(QFileDialog.AnyFile)
        file_dialog.exec_()

        selected_files = file_dialog.selectedFiles()
        if selected_files:
            lineEdit.setText(selected_files[0])

    # 开始启动
    def start(self):
        self.setProcessWindow()
        # 实例化功能类
        appFunction = AppFunction(self.textEdit_process)
        # 获取小红书链接
        LinkUrls = appFunction.getLinkUrls(self.lineEdit_filepath.text())

        for linkurl in LinkUrls:
            t = threading.Thread(target=appFunction.run, args=(linkurl,))
            t.setDaemon(True)
            t.start()
            
    # 设置frame组件
    def setFrameWidget(self, widget, widget_size, widget_move, frameToplevel=None):
        if frameToplevel != None:
            frame = Frame(frameToplevel)
        else:
            frame = Frame(self)

        widget = widget(frame)
        frame.move(widget_move[0], widget_move[1])
        frame.resize(widget_size[0], widget_size[1])
        widget.move(self.frameWidth, self.frameWidth)
        widget.resize(widget_size[0] - self.frameWidth*2, widget_size[1] - self.frameWidth*2)

        if widget.inherits("QPushButton"):
            widget.setStyleSheet("background-color: #ff2442; color: white;")
        elif widget.inherits("QLineEdit") or widget.inherits("QTextEdit"):
            widget.setStyleSheet('border: none; font-weight: bold;')   # 设置为无边框

        return widget

    # 新窗口的设置
    def setProcessWindow(self):
        self.processWindow = ProcessWindow()
        self.processWindow.setGeometry(self.x()+500, self.y(), 250, 290)

        # 设置记录过程的textEdit
        self.textEdit_process = QTextEdit(self.processWindow)
        self.textEdit_process.resize(250, 290)
        self.textEdit_process.setStyleSheet('border: none; color: white;')    # 设置为无边框
        self.textEdit_process.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)     # 隐藏垂直滚动条
        self.processWindow.show()

    # 窗口移动
    def mousePressEvent(self, evt):
        if evt.button() == Qt.LeftButton:
            self.mouseFlag = True
            self.mouse_x, self.mouse_y = evt.globalPos().x(), evt.globalPos().y()
            self.origin_x, self.origin_y = self.x(), self.y()


    def mouseMoveEvent(self, evt):
        if self.mouseFlag == True:
            move_x = self.origin_x + (evt.globalPos().x() - self.mouse_x)
            move_y = self.origin_y + (evt.globalPos().y() - self.mouse_y)
            self.move(move_x, move_y)
            try:
                self.processWindow.move(move_x+500, move_y)     # 过程信息展示窗口移动
            except:
                pass

    def mouseReleaseEvent(self, evt):
        self.mouseFlag = False

    # 窗口关闭
    def closeEvent(self, evt):
        try:
            self.processWindow.close()
        except:
            pass

在设计显示过程信息的窗口时将窗口设置为半透明、背景颜色为黑色,并在其中添加QTextEdit输入框并设置其无边框和垂直滚动条为不可见,最终在整个程序执行时,会将不同过程不同颜色字体的信息放到输入框中显示,达到一个电影中常见的黑客操作信息时的炫酷效果。

    # 新窗口的设置
    def setProcessWindow(self):
        self.processWindow = ProcessWindow()
        self.processWindow.setGeometry(self.x()+500, self.y(), 250, 290)

        # 设置记录过程的textEdit
        self.textEdit_process = QTextEdit(self.processWindow)
        self.textEdit_process.resize(250, 290)
        self.textEdit_process.setStyleSheet('border: none; color: white;')    # 设置为无边框
        self.textEdit_process.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)     # 隐藏垂直滚动条
        self.processWindow.show()
        
# ====================================== 新窗口-过程信息展示区 ======================================
class ProcessWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowFlag(Qt.FramelessWindowHint)  # 将窗口设置为无边框无标题
        self.setWindowOpacity(0.5)  # 将窗口设置为半透明
        self.setStyleSheet('background-color: black;')

🐾功能设计

功能设计主要实现读取excel中的链接、访问链接、在显示过程信息的窗口的输入框中插入信息、获取图片URL、爬取并下载图片。

# ====================================== 爬取图片能执行 ======================================
class AppFunction:
    def __init__(self, textEdit):
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
        }

        self.pattern = 'url\("(.*)"\);'

        self.nowPath = os.getcwd()

        self.textEdit = textEdit

        self.cursor = self.textEdit.textCursor()    # 获取光标位置
        # 红色
        self.red = QColor(Qt.yellow)
        # 绿色
        self.green = QColor(Qt.green)
        # 驱动路径
        driver_path = 'D:\chromedriver-win32\chromedriver.exe'
        # 固定搭配直接用就行了
        self.service = Service(executable_path=driver_path)
        # 无头模式
        self.option = webdriver.ChromeOptions()
        self.option.add_experimental_option('excludeSwitches', ['enable-automation'])
        self.option.add_argument('--disable-blink-features=AutomationControlled')
        # self.option.add_argument('--headless')

    # 执行方法
    def run(self, url):
        self.getPhotoUrl(url)

    # 插入文本
    def insertText(self, text, color):
        format = QTextCharFormat()
        format.setForeground(color)
        self.cursor.insertText(text, format)    # 在QTextEdit中插入文字
        self.textEdit.ensureCursorVisible()     # 在插入的时候随着光标向下滚动,即达到文字自动滚动效果

    # 获取图片URL
    def getPhotoUrl(self, url):
        url = url
        driver = webdriver.Chrome(service=self.service, options=self.option)
        self.insertText('\n' + url + ' -- 开始抓取图片URL', self.red)
        driver.get(url)

        # 等待图片URL出现
        WebDriverWait(driver, 1000).until(
            EC.presence_of_element_located((By.XPATH, '//div[@class="swiper-wrapper"]/div'))
        )
        # 获取图片URL
        temp_PhotoUrls = [i.get_attribute('style') for i in driver.find_elements(By.XPATH, '//div[@class="swiper-wrapper"]/div')]
        # 获取小红书标题
        title = driver.find_element(By.XPATH, '//div[@id="detail-title"]').text

        # 正则方法清洗图片url
        PhotoUrls = list(set([re.findall(self.pattern, i)[0] for i in temp_PhotoUrls]))

        # 关闭浏览器
        driver.close()
        self.insertText('\n' + url + ' -- 图片URL抓取完成', self.green)

        self.getPhoto(PhotoUrls, title)

    # 获取图片
    def getPhoto(self, PhotoUrls, title):
        path = os.path.join(self.nowPath, title)
        if not os.path.exists(path):  # 创建文件夹
            os.makedirs(path)

        for i in range(len(PhotoUrls)):
            self.insertText('\n' + PhotoUrls[i] + ' -- 开始抓取图片', self.red)

            res = requests.get(PhotoUrls[i], headers=self.headers)
            with open(f'./{title}/{i}.webp', 'wb') as f:
                f.write(res.content)
                self.insertText('\n' + f'{self.nowPath}\\{title}\\{i}.png' + ' -- 下载完成', self.green)
        self.insertText(f'\n{title}图片下载完成!\n'.center(71, '='), self.green)

    # 读取文件,获取网页链接
    def getLinkUrls(self, path):
        f = openpyxl.load_workbook(path)
        sheet = f['Sheet1']
        LinkUrls = [i.value for i in sheet['A']]
        return LinkUrls

📚资源领取

关注微信公众号👉【Python小作坊】,回复💬“小红书图片爬取”,即可免费领取(含源代码)~
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python 使用OS模块调用 cmd

嗨喽,大家好呀~这里是爱看美女的茜茜呐 在os模块中提供了两种调用 cmd 的方法,os.popen() 和 os.system() os.system(cmd) 是在执行command命令时需要打开一个终端,并且无法保存command命令的执行结果。 os.popen(cmd,mode) 打开一个与comma…

JuCheap开发的微信小程序商城(NetCore商城)

一、目的 最近工作需要,在学习微信小程序的开发,用周末空闲时间开发了一个微信小程序商城。 二、功能 2.1 管理后台 管理后台是基于JuCheap开发的,使用Net6Vue3ElementPlus开发,具体功能包含如下: 2.1.1 店铺模块…

环形链表解析(c语言)c语言版本!自我解析(看了必会)

目录 1.判断一个表是否是环形链表! 代码如下 解析如下 2.快指针的步数和慢指针的步数有什么影响(无图解析) 3.怎么找到环形链表的入环点 代码如下 解析如下 1.判断一个表是否是环形链表! 代码如下 bool hasCycle(struct L…

[ARM入门]ARM模式及其切换、异常

ARM技术特征 ARM处理器有如下特点 体积小、功耗低、成本低、性能高支持Thumb(16位)/ARM(32位)双指令集,能很好地兼容8位/16位器件大量使用寄存器,指令执行速度更快大多数数据操作都在寄存器中完成寻址方式…

【Java】Java8 Function 和 Consumer 接口的使用场景

文章目录 前言1. Function 示例2. Function 介绍3. Consumer 示例4. Consumer 介绍5. Function 和 Consumer 接口的使用场景后记 前言 在 《精通Java8》一书中有讲过 Java8的函数式接口可以简化设计模式的实施,这里记录一下Function 和 Consumer 的使用场景。 1. …

Docker从零开始学习,及常用命令大全(附带代码讲解)

Docker从零开始,及常用命令大全(附带代码讲解) docker是一种开源的应用容器引擎,可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。…

【集简云调度影刀RPA】

集简云调度影刀 集简云的http请求,都是用webhook。 1、获取token的时候,在url中必须这么填,在数据或者headers里面填写keyID和密码不管用。 2.调起应用的时候,需要选择webhook中的post,自定义的请求,才能…

二叉树的遍历(先序,中序,后序,层序)

目录 1.先序遍历1.代码实现 2.中序遍历1.代码实现 3.后序遍历1.代码实现 4.遍历算法的应用5.层序遍历1.算法思想2.代码实现 6.由遍历序列构造二叉树 1.先序遍历 根左右。 1.代码实现 若二叉树为空,则什么也不做; 若二叉树非空: ①访问根结点; ②先序遍历左子树; ③先…

如何在Linux服务器上后台持久运行Gunicorn

如何在Linux服务器上后台持久运行Gunicorn **问题概述****解决方案一:使用nohup命令****解决方案二:使用systemd服务****创建systemd服务文件****修改systemd服务文件以使用虚拟环境**日志管理**激活并启动服务:**如何设置用户和组**确认用户…

FPGA高端项目:图像采集+GTX+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持

目录 1、前言免责声明本项目特点 2、相关方案推荐我这里已有的 GT 高速接口解决方案我这里已有的以太网方案 3、设计思路框架设计框图视频源选择OV5640摄像头配置及采集动态彩条视频数据组包GTX 全网最细解读GTX 基本结构GTX 发送和接收处理流程GTX 的参考时钟GTX 发送接口GTX …

在 WSL 上启用 NVIDIA CUDA

环境要求 Windows 11 或 Windows 10 版本 21H2特定版本的GPU驱动: 安装支持 NVIDIA CUDA 的 WSL 驱动程序: https://www.nvidia.com/download/index.aspx具体安装哪个版本,查阅:https://docs.nvidia.com/cuda/wsl-user-guide/in…

基于Python+Django的图书管理系统

项目介绍 图书是人类文明传播的一个重要方式,很多历史悠久的文明都是通过图书来进行传递的,虽然随着时代的进步电子信息技术发展很快,但是纸质图书的地位仍然是非常稳固的,为了能够让知识拥有更加快捷方便的传递方式我们开发了本…

asp.net员工管理系统VS开发sqlserver数据库web结构c#编程包括出差、请假、考勤

一、源码特点 asp.net员工管理系统是一套完善的web设计管理系统(主要包括出差、请假、考勤基础业务管理),系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010 ,数据库为sqlserver2008&a…

postman设置动态token, 每次登录更新token

postman设置动态token, 每次登录更新token 文章目录 postman设置动态token, 每次登录更新token问题1. 设置全局变量2. 新建登录接口3. 设置脚本4. 切换环境5. 配置动态token 问题 token过期时间一般比较短, 每次使用postman调用接口都token非常麻烦 实现token过期后, 调用一次…

Swift 常用类别整理

生成颜色,传入16进制数字生成对应颜色 个人不喜欢传字符串的写法,比如 "0x0080FF" 或者 "0080FF",原因如下: 传了字符串最后还是要解析成数字参与颜色运算的,需要额外做字符串转数字的操作&…

人工智能领域200例教程专栏—学习人工智能的指南宝典

🎉🎊🎉 你的技术旅程将在这里启航! 🚀 本专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 …

Kotlin基础——接口和类

接口 使用 : 表示继承关系&#xff0c;只能继承一个类&#xff0c;但可以实现多个接口override修饰符表示重写可以有默认方法&#xff0c;若父类的默认方法冲突&#xff0c;则需要子类重写&#xff0c;使用super<XXX>.xxx()调用某一父类方法 interface Focusable {fun …

Rt-Thread 移植6--多线程(KF32)

6.1 就绪列表 6.1.1 线程就绪优先级组 线程优先级表的索引对应的线程的优先级。 为了快速的找到线程在线程优先级表的插入和移出的位置&#xff0c;RT-Thread专门设计了一个线程就绪优先级组。线程就绪优先组是一个32位的整型数&#xff0c;每一个位对应一个优先级&#xff…

Spark算子

一、编写spark程序的准备工作&#xff08;程序入口 SparkContext&#xff09; 1.创建SparkConf val conf new SparkConf().setMaster("local[2]").setAppName("hello-app") 2.创建sparkContext val sc: SparkContext new SparkContext(conf) 二、基…