IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python Spider “Pyqt5 GUI-Version 最终打包为exe“ -> 正文阅读

[Python知识库]Python Spider “Pyqt5 GUI-Version 最终打包为exe“

目录

前言

目标网站: www.trxs.cc

程序分为program 与 setup_ui两个部分

先附成品

一. setup_ui部分

前置条件① - 顶层窗口初始化 以及全局配置 将包含外部逻辑程序的类实例化

前置条件② - 在此调用相关部件的方法

控件① 使窗口处于居中位置

Ⅰ. 调用的第三方库

?Ⅱ. 类方法代码(每段功能注释写的十分清楚)

Ⅲ. 结果 - 居中出现

控件② 文本输入框

Ⅰ.?调用的第三方库

Ⅱ. 类方法代码(每段功能注释写的十分清楚)

Ⅲ. 结果 - 文本输入框

控件③ 警告框: 网址信息错误

Ⅰ.?调用的第三方库

Ⅱ. 类方法代码(每段功能注释写的十分清楚)

Ⅲ. 结果 -? 警告框: 网址信息错误

控件④ 显示详细进度信息 本质是标签框控件 并且配置滚动条QScrollArea(Scroll)

Ⅰ.?调用的第三方库:

Ⅱ. 类方法代码(每段功能注释写的十分清楚)

Ⅲ. 结果 - 滚动条

控件⑤ 小插件 - 获取Label文本? 并且在原有文本的基础上进行添加? 两个函数调用的封装? 传入要添加的文字参数即可

Ⅰ.?调用的第三方库

Ⅱ. 类方法代码(每段功能注释写的十分清楚)

Ⅲ. 结果 - 修改Label文本

控件⑥ 获取文本输入框输入的值

Ⅰ.?调用的第三方库

Ⅱ. 类方法代码(每段功能注释写的十分清楚)

控件⑦ 设置一个下载按钮 并且触发多个事件

Ⅰ.?调用的第三方库

Ⅱ. 类方法代码(每段功能注释写的十分清楚)

Ⅲ. 结果 - 下载按钮

setup_ui 部分 - 全部代码

二. Program(逻辑) 部分

Ⅰ. class Spider类

引入的第三方库

① 初始化部分? 以及定义全局变量

② 核验UI界面文本输入框传来的URL

③ 利用正则校验获得的网页源码是否为假数据

④ 以正则获取网页编码charset, 以自动为爬取源码解码

⑤?请求方法, 并且自动为网页源码编码

⑥ 数据处理方法

Ⅱ.? class CoroutineRequest 类 (协程请求逻辑爬虫对象)

调用的第三方库

① 初始化部分 以及定义全局变量

② 因为协程方法与方法之间依赖调用比较强 在这里就不分开了

三. 打包

在终端中输入下面命令

?会出现下面问题

① 打包后资源不能正常显示 如界面背景图片

结语


前言:

目标网站: www.trxs.cc

秉着GUI制作逻辑和UI隔离的原则和初衷, 程序分为program 与 setup_ui两个部分.

先附上成品:

一. setup_ui部分

在此将整个UI窗口作为一个对象, 在Class里加入它的各种行为的方法

前置条件① - 顶层窗口初始化 以及全局配置 将包含外部逻辑程序的类实例化 - 将程序逻辑与UI部分隔离分开

# 顶层窗口初始化 以及全局配置
    def __init__(self):
        super().__init__()
        # 将包含外部逻辑程序的类实例化 - 将程序逻辑与UI部分隔离分开
        self.object_ = Spider()
        self.coroutineRequest = CoroutineRequest()
        # 设置主窗口标题
        self.setWindowTitle("单本书籍爬取")
        # 设置主窗口图标
        self.setWindowIcon(QIcon(r'D:\firefox downloads/bitbug_favicon.ico'))
        self.resize(700, 600)

        palette = QPalette()
        palette.setBrush(QPalette.Background, QBrush(QPixmap(":/images/主窗口背景.png")))
        self.setPalette(palette)

        # 屏蔽顶部窗口框架中的最大化按钮, 已完善下方 禁止拉伸窗口的意图
        self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint)     # 只有返回0才能解释 | ,&, and, or结果都不一样
        # 禁止拉伸窗口, 固定 窗口大小
        self.setFixedSize(self.width(), self.height())
        self.setup_ui()

前置条件② - 在此调用相关部件的方法

# 在此调用相关部件的方法
    def setup_ui(self):    # 设置ui, 调用类方法
        self.center()
        self.progress_bar()
        self.text_input_box()
        self.progress_bar()
        self.information_label()
        self.get_text_box_input()

控件① 使窗口处于居中位置

Ⅰ. 调用的第三方库:

from PyQt5.QtWidgets import Qwidegt, QDesktopWidegt

?Ⅱ. 类方法代码(每段功能注释写的十分清楚) :

# 使窗口初始处于居中位置
    def center(self):
        # 获得主窗口大小
        MainWindowSize = self.frameGeometry()
        # 获得显示器分辨率大小, 并获取中间点位置
        GetComputerSize = QDesktopWidget().availableGeometry().center()
        # 将软件窗口框架的中心点放在显示器的中心点, 使框架居中放置
        MainWindowSize.moveCenter(GetComputerSize)
        # 将软件窗口左上角放在框架左上角
        self.move(MainWindowSize.topLeft())

Ⅲ. 结果 - 居中出现

控件② 文本输入框

Ⅰ.?调用的第三方库:

from PyQt5.QtWidgets import QWidget, QlineEdit

Ⅱ. 类方法代码(每段功能注释写的十分清楚) :

# 文本输入框
    def text_input_box(self):
        # 初始化文本框
        self.le = QLineEdit(self)
        # 设置文本输入框初始文本
        self.le.setText("https://www.INPUT URL")
        # 设置文本输入框样式- 文字大小, 背景颜色(rgb最后一个0设置透明度), 圆角
        self.le.setStyleSheet("font-size:25px;background-color:rgb(255,255,0,160);border-radius:18px;")
        # 监测回车事件, 绑定与下载按钮函数一样的方法
        self.le.returnPressed.connect(self.Bind_get_text)
        # 设置输入框大小
        self.le.resize(500, 50)
        # 设置输入框位置
        self.le.move(100, 50)

Ⅲ. 结果 - 文本输入框

控件③ 警告框: 网址信息错误

Ⅰ.?调用的第三方库:

from PyQt5.QtWidgets import QMessageBox

Ⅱ. 类方法代码(每段功能注释写的十分清楚) :

# 警告框: 网址信息错误
    def warning_box(self):
        # warning(父对象, 警告框标题, 警告框内容, 警告框选项[这里只有Yes], 默认选项[就是弹出警告框您按回车默认选择的那个])
        QMessageBox.warning(self, '错误', "输入网址格式错误", QMessageBox.Yes, QMessageBox.Yes)

Ⅲ. 结果 -? 警告框: 网址信息错误

控件④ 伴随进度条 显示详细进度信息 本质是标签框控件 并且配置滚动条QScrollArea(Scroll)

Ⅰ.?调用的第三方库:

from PyQt5.QtWidgets import QFrame,  QLineEdit, QScrollArea, QLabel

Ⅱ. 类方法代码(每段功能注释写的十分清楚) :

# 伴随进度条,显示详细进度信息, 本质是标签框控件 # 并且配置滚动条QScrollArea(Scroll)
    def information_label(self):
        self.InformationLabel = QLabel(self)     # 定义label标签, 初始化
        self.InformationLabel.setGeometry(QRect(150, 240, 400, 200))    # 设置位置 # 最后一个参数调整行距(其实也可以看作是高度,发现*2时与按钮等高
        self.InformationLabel.setText("<div style='font-size:20px;'>单本书籍爬取</div>")    # 设置文本显示内容
        # 设置对象名(不是设置显示容)
        self.InformationLabel.setObjectName("label")
        # 边框     边框长度与label控件一致(如果要修改长度,只需要修改上方的setGeometry的第三个参数
        # 设置边框样式
        self.InformationLabel.setFrameShape(QFrame.Box)
        # 设置阴影 据说只有加了这步才能设置边框颜色。///可选样式有Raised、Sunken、Plain(这个无法设置颜色)等
        self.InformationLabel.setFrameShadow(QFrame.Raised)
        # 设置边框颜色
        self.InformationLabel.setStyleSheet('border-width: 2px; border-style: solid; border-color:rgb(245 ,245, 245, 150); background-color:rgb(255, 228, 225, 150);border-radius:15px;')
        # 设置文本靠左对齐
        self.InformationLabel.setAlignment(Qt.AlignHCenter)
        # # 设置文本缩进值
        # self.InformationLabel.setIndent(-8)
        # 允许Label换行
        self.InformationLabel.setWordWrap(True)

        # 配置标签Label滚动显示
        # 初始化滚动区域
        Scroll_ = QScrollArea(self)
        # 将InformationLabel(Label)设置为QScrollArea(Scroll)的子控件, 如此才能当label内容超过滚动区域时下滑
        Scroll_.setWidget(self.InformationLabel)
        # 配置滚动区域参数, 这里和label一样大,不然呵呵
        Scroll_.move(150, 240)
        Scroll_.resize(400, 200)
        # 这个是确保滚动区域能显示出label内容, 无论label,和Scroll大小如何, Scroll会自动调整大小,以显示label # 伴随效果, 当label不超过滚动区域时, 不显示粗鄙难看的滚动条.
        Scroll_.setWidgetResizable(True)
        # 设置滚动条样式, 包括但不限于 滚动条圆角, 颜色, 鼠标触到改变颜色, 划过部分颜色, 未滑过颜色; 滑轨圆角, 颜色; 滚动条背景;
        Scroll_.setStyleSheet('''
        QAbstractScrollArea
        {
            background-color: rgb(0, 0, 0, 0);
            border-color: rgb(0, 0, 0, 0);
            border-radius: 8px;
        }
        QScrollBar:vertical  
        {
            width:15px;
            border-radius:10px; 
            opacity: 0.0;  
            padding-top:14px;  
            padding-bottom:14px;  
        }
        QScrollBar::handle:vertical
        {
            background:#00FFFF;
            border-radius:7px;
            margin-left:0px;
            margin-right:0px;
        }
        QScrollBar::handle:vertical:hover
        {
            background:#FFFF00;
            border-radius:10px;
        }
        QScrollBar::add-line:vertical
        {
            image:url('');
        }
        QScrollBar::sub-line:vertical
        {
            image:url('');
        }
        QScrollBar::add-page:vertical
        {
            background:#E6E6FA;
        }
        QScrollBar::sub-page:vertical
        {
            background:#79CDCD;
        }
        ''')

Ⅲ. 结果 - 滚动条

控件⑤ 小插件 - 获取Label文本? 并且在原有文本的基础上进行添加? 两个函数调用的封装? 传入要添加的文字参数即可

Ⅰ.?调用的第三方库:

None

Ⅱ. 类方法代码(每段功能注释写的十分清楚) :

# 小插件 - 获取Label文本, 并且在原有文本的基础上进行添加, 两个函数调用的封装, 传入要添加的文字参数即可
    def get_label_text(self, content):
        # 获取Label文本, 调用InformationLabel控件的提供的相关方法
        self.LabelText = self.InformationLabel.text()
        # 设置文本, 因为Label文本修改只能全部变, 所以将获取到的原文本与要添加的文本拼接, 其中用上HTML标签, 并且设置样式 - 文字大小
        self.InformationLabel.setText(f'{self.LabelText}<div style="font-size:17px;">{content}</div>')

Ⅲ. 结果 - 修改Label文本

控件⑥ 获取文本输入框输入的值

Ⅰ.?调用的第三方库:

# 这是自编的逻辑层代码库, 详见下面 逻辑层内容中方法
from GUI单本书籍爬取.program.SpideMain import Spider, CoroutineRequest

Ⅱ. 类方法代码(每段功能注释写的十分清楚) :

# 获取文本输入框输入的值
    def Bind_get_text(self):
        # 调用网址格式检验 方法 格式正确返回 1 错误返回 -1
        statu = Spider.check_url_front(self.object_, self.le.text())

        if statu == 1:
            # 获取label文本, # 修改Lable文本, 与原有文本(通过上行获取) 换行叠加, 详见get_label_text方法
            self.get_label_text('网址 格式 核验正确')
            # 异常处理, 防止请求的网址因为 404(不存在)问题 使程序无错误崩溃退出
            try:
                self.get_label_text('尝试请求...')

                if Spider.request(self.object_, self.le.text()) == -1:
                    self.get_label_text('?请求状态码错误?')
                else:
                    self.get_label_text('请求成功, 检查数据是否为假...')
                    if self.object_.RespOne.text:
                        if self.object_.check_html_whether_false(self.object_.RespOne.text):
                            self.get_label_text('数据为真, 分析外部页面数据...')
                            # 获取书名
                            self.coroutineRequest.get_novel_name(self.object_.RespOne.text)
                            self.get_label_text('书籍名称获取完毕')

                            # 此处调用数据处理解析的Spider(self.object_)方法
                            self.object_.data_handle_outside(self.object_.RespOne.text)
                            self.get_label_text('分析完毕, 请求内部页面...')
                            # 调用coroutine类中的协程请求方法

                            self.coroutineRequest.my_coroutine_request(self.object_.AnalysisTwo)

                            self.get_label_text(f'《{self.coroutineRequest.novel_name[0]}》 下载完毕')

                            self.get_label_text(f'内容页面网络请求总时间:{self.coroutineRequest.for_request_time}')
                        else:
                            # 调用UI类中的方法, 在label显示相关错误报告
                            self.get_label_text('? 网站获取数据为假 ?')
            except IndexError as ErrorOne:
                # 异常处理, 在label中显示 "请求状态码错误"
                self.get_label_text(f'? {ErrorOne}错误 ?')
            # except:
            #     self.get_label_text('? 未知错误 ?')

        elif statu == -1:
            # 弹出警告框, 详见 warn_box()方法
            self.warning_box()

控件⑦ 设置一个下载按钮 并且触发多个事件

Ⅰ.?调用的第三方库:

from PyQt5.QtWidgets import QPushButton

Ⅱ. 类方法代码(每段功能注释写的十分清楚) :

# 设置一个下载按钮,并且触发多个事件
    def get_text_box_input(self):
        # 设置开始下载按钮, 并在下方绑定事件
        but = QPushButton("  开始下载  ", self)
        but.resize(100, 50)
        but.move(300, 150)

        but.setStyleSheet("QPushButton{border-image:url(:/images/背景.png);font-size:18px;border-radius:16px;}")
        # 按钮 信号绑定Bin_get_text()方法 - 详见方法内部
        # but - 信号源; clicked - 触发事件;
        but.clicked.connect(self.Bind_get_text)

Ⅲ. 结果 - 下载按钮

setup_ui 部分 - 全部代码

# -*- coding: UTF-8 -*-
"""
@Author: 王散 Creative
@Time: 2022/5/18 21:51
@IDE_Name/Software: PyCharm
@File: main
"""
from PyQt5.QtWidgets import (QWidget, QFrame, QApplication, QDesktopWidget, QLineEdit, QScrollArea,
                             QPushButton, QMessageBox, QLabel)
from PyQt5.QtGui import QIcon, QPalette, QBrush, QPixmap
from PyQt5.QtCore import Qt, QRect
from sys import exit, argv
from GUI单本书籍爬取.program.SpideMain import Spider, CoroutineRequest
from GUI单本书籍爬取.setup_ui import images


class Windows(QWidget, Spider):
    # 顶层窗口初始化 以及全局配置
    def __init__(self):
        super().__init__()
        # 将包含外部逻辑程序的类实例化 - 将程序逻辑与UI部分隔离分开
        self.object_ = Spider()
        self.coroutineRequest = CoroutineRequest()
        # 设置主窗口标题
        self.setWindowTitle("单本书籍爬取")
        # 设置主窗口图标
        self.setWindowIcon(QIcon(r'D:\firefox downloads/bitbug_favicon.ico'))
        self.resize(700, 600)

        palette = QPalette()
        palette.setBrush(QPalette.Background, QBrush(QPixmap(":/images/主窗口背景.png")))
        self.setPalette(palette)

        # 屏蔽顶部窗口框架中的最大化按钮, 已完善下方 禁止拉伸窗口的意图
        self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint)     # 只有返回0才能解释 | ,&, and, or结果都不一样
        # 禁止拉伸窗口, 固定 窗口大小
        self.setFixedSize(self.width(), self.height())
        self.setup_ui()

    # 在此调用相关部件的方法
    def setup_ui(self):    # 设置ui, 调用类方法
        self.center()
        self.progress_bar()
        self.text_input_box()
        self.progress_bar()
        self.information_label()
        self.get_text_box_input()

    # 使窗口初始处于居中位置
    def center(self):
        # 获得主窗口大小
        MainWindowSize = self.frameGeometry()
        # 获得显示器分辨率大小, 并获取中间点位置
        GetComputerSize = QDesktopWidget().availableGeometry().center()
        # 将软件窗口框架的中心点放在显示器的中心点, 使框架居中放置
        MainWindowSize.moveCenter(GetComputerSize)
        # 将软件窗口左上角放在框架左上角
        self.move(MainWindowSize.topLeft())

    # 文本输入框
    def text_input_box(self):
        # 初始化文本框
        self.le = QLineEdit(self)
        # 设置文本输入框初始文本
        self.le.setText("https://www.INPUT URL")
        # 设置文本输入框样式- 文字大小, 背景颜色(rgb最后一个0设置透明度), 圆角
        self.le.setStyleSheet("font-size:25px;background-color:rgb(255,255,0,160);border-radius:18px;")
        # 监测回车事件, 绑定与下载按钮函数一样的方法
        self.le.returnPressed.connect(self.Bind_get_text)
        # 设置输入框大小
        self.le.resize(500, 50)
        # 设置输入框位置
        self.le.move(100, 50)

    # 警告框: 网址信息错误
    def warning_box(self):
        # warning(父对象, 警告框标题, 警告框内容, 警告框选项[这里只有Yes], 默认选项[就是弹出警告框您按回车默认选择的那个])
        QMessageBox.warning(self, '错误', "输入网址格式错误", QMessageBox.Yes, QMessageBox.Yes)

    # 伴随进度条,显示详细进度信息, 本质是标签框控件 # 并且配置滚动条QScrollArea(Scroll)
    def information_label(self):
        self.InformationLabel = QLabel(self)     # 定义label标签, 初始化
        self.InformationLabel.setGeometry(QRect(150, 240, 400, 200))    # 设置位置 # 最后一个参数调整行距(其实也可以看作是高度,发现*2时与按钮等高
        self.InformationLabel.setText("<div style='font-size:20px;'>单本书籍爬取</div>")    # 设置文本显示内容
        # 设置对象名(不是设置显示容)
        self.InformationLabel.setObjectName("label")
        # 边框     边框长度与label控件一致(如果要修改长度,只需要修改上方的setGeometry的第三个参数
        # 设置边框样式
        self.InformationLabel.setFrameShape(QFrame.Box)
        # 设置阴影 据说只有加了这步才能设置边框颜色。///可选样式有Raised、Sunken、Plain(这个无法设置颜色)等
        self.InformationLabel.setFrameShadow(QFrame.Raised)
        # 设置边框颜色
        self.InformationLabel.setStyleSheet('border-width: 2px; border-style: solid; border-color:rgb(245 ,245, 245, 150); background-color:rgb(255, 228, 225, 150);border-radius:15px;')
        # 设置文本靠左对齐
        self.InformationLabel.setAlignment(Qt.AlignHCenter)
        # # 设置文本缩进值
        # self.InformationLabel.setIndent(-8)
        # 允许Label换行
        self.InformationLabel.setWordWrap(True)

        # 配置标签Label滚动显示
        # 初始化滚动区域
        Scroll_ = QScrollArea(self)
        # 将InformationLabel(Label)设置为QScrollArea(Scroll)的子控件, 如此才能当label内容超过滚动区域时下滑
        Scroll_.setWidget(self.InformationLabel)
        # 配置滚动区域参数, 这里和label一样大,不然呵呵
        Scroll_.move(150, 240)
        Scroll_.resize(400, 200)
        # 这个是确保滚动区域能显示出label内容, 无论label,和Scroll大小如何, Scroll会自动调整大小,以显示label # 伴随效果, 当label不超过滚动区域时, 不显示粗鄙难看的滚动条.
        Scroll_.setWidgetResizable(True)
        # 设置滚动条样式, 包括但不限于 滚动条圆角, 颜色, 鼠标触到改变颜色, 划过部分颜色, 未滑过颜色; 滑轨圆角, 颜色; 滚动条背景;
        Scroll_.setStyleSheet('''
        QAbstractScrollArea
        {
            background-color: rgb(0, 0, 0, 0);
            border-color: rgb(0, 0, 0, 0);
            border-radius: 8px;
        }
        QScrollBar:vertical  
        {
            width:15px;
            border-radius:10px; 
            opacity: 0.0;  
            padding-top:14px;  
            padding-bottom:14px;  
        }
        QScrollBar::handle:vertical
        {
            background:#00FFFF;
            border-radius:7px;
            margin-left:0px;
            margin-right:0px;
        }
        QScrollBar::handle:vertical:hover
        {
            background:#FFFF00;
            border-radius:10px;
        }
        QScrollBar::add-line:vertical
        {
            image:url('');
        }
        QScrollBar::sub-line:vertical
        {
            image:url('');
        }
        QScrollBar::add-page:vertical
        {
            background:#E6E6FA;
        }
        QScrollBar::sub-page:vertical
        {
            background:#79CDCD;
        }
        ''')

    # 配置进度条, 包含但不只限于进度条相关参数, 进度条父对象
    def progress_bar(self):
        pass
        # self.progress = QProgressBar(parent=self)
        # self.progress.setGeometry(30, 40, 200, 25)
        # self.timer = QBasicTimer()
        # self.step = 0

    # 小插件 - 获取Label文本, 并且在原有文本的基础上进行添加, 两个函数调用的封装, 传入要添加的文字参数即可
    def get_label_text(self, content):
        # 获取Label文本, 调用InformationLabel控件的提供的相关方法
        self.LabelText = self.InformationLabel.text()
        # 设置文本, 因为Label文本修改只能全部变, 所以将获取到的原文本与要添加的文本拼接, 其中用上HTML标签, 并且设置样式 - 文字大小
        self.InformationLabel.setText(f'{self.LabelText}<div style="font-size:17px;">{content}</div>')

    # 获取文本输入框输入的值
    def Bind_get_text(self):
        # 调用网址格式检验 方法 格式正确返回 1 错误返回 -1
        statu = Spider.check_url_front(self.object_, self.le.text())

        if statu == 1:
            # 获取label文本, # 修改Lable文本, 与原有文本(通过上行获取) 换行叠加, 详见get_label_text方法
            self.get_label_text('网址 格式 核验正确')
            # 异常处理, 防止请求的网址因为 404(不存在)问题 使程序无错误崩溃退出
            try:
                self.get_label_text('尝试请求...')

                if Spider.request(self.object_, self.le.text()) == -1:
                    self.get_label_text('?请求状态码错误?')
                else:
                    self.get_label_text('请求成功, 检查数据是否为假...')
                    if self.object_.RespOne.text:
                        if self.object_.check_html_whether_false(self.object_.RespOne.text):
                            self.get_label_text('数据为真, 分析外部页面数据...')
                            # 获取书名
                            self.coroutineRequest.get_novel_name(self.object_.RespOne.text)
                            self.get_label_text('书籍名称获取完毕')

                            # 此处调用数据处理解析的Spider(self.object_)方法
                            self.object_.data_handle_outside(self.object_.RespOne.text)
                            self.get_label_text('分析完毕, 请求内部页面...')
                            # 调用coroutine类中的协程请求方法

                            self.coroutineRequest.my_coroutine_request(self.object_.AnalysisTwo)

                            self.get_label_text(f'《{self.coroutineRequest.novel_name[0]}》 下载完毕')

                            self.get_label_text(f'内容页面网络请求总时间:{self.coroutineRequest.for_request_time}')
                        else:
                            # 调用UI类中的方法, 在label显示相关错误报告
                            self.get_label_text('? 网站获取数据为假 ?')
            except IndexError as ErrorOne:
                # 异常处理, 在label中显示 "请求状态码错误"
                self.get_label_text(f'? {ErrorOne}错误 ?')
            # except:
            #     self.get_label_text('? 未知错误 ?')

        elif statu == -1:
            # 弹出警告框, 详见 warn_box()方法
            self.warning_box()

    # 设置一个下载按钮,并且触发多个事件
    def get_text_box_input(self):
        # 设置开始下载按钮, 并在下方绑定事件
        but = QPushButton("  开始下载  ", self)
        but.resize(100, 50)
        but.move(300, 150)

        but.setStyleSheet("QPushButton{border-image:url(:/images/背景.png);font-size:18px;border-radius:16px;}")
        # 按钮 信号绑定Bin_get_text()方法 - 详见方法内部
        # but - 信号源; clicked - 触发事件;
        but.clicked.connect(self.Bind_get_text)

# 对象测试, 外部调用对象不会执行
if __name__ == '__main__':
    # 监视变量及信号变换
    app = QApplication(argv)
    # 实例化窗口对象
    window = Windows()
    # 使窗口显示
    window.show()
    # 进入事件循环
    exit(app.exec_())

二. Program(逻辑) 部分

在此将整个program逻辑分成两个对象class CoroutineRequest(协程请求逻辑爬虫对象) 和 class Spider(单一页面爬虫请求逻辑对象)

在Class里加入它的各种行为的方法

Ⅰ. class Spider类

引入的第三方库

from requests import get
from re import compile, S

① 初始化部分? 以及定义全局变量

    def __init__(self):
        self.AnalysisTwo = None
        self.RespOne = None
        self.url = ''
        self.WebCoding = []
        self.novel_name = []

② 核验UI界面文本输入框传来的URL, 错误返回 -1, 正确返回 1

# 核验UI界面文本输入框传来的URL, 错误返回 -1, 正确返回 1
    def check_url_front(self, url):
        if 'http://www' in url:
            print("网址格式正确")
            return 1
        elif 'https://www' in url:
            print("网址格式正确")
            return 1
        elif 'https://blog' in url:
            print("网址格式正确")
            return 1
        elif None:  # 在此更新网页格式核验规则
            pass
        else:
            print("网址格式错误")
            return -1

③ 利用正则校验获得的网页源码是否为假数据

# 利用正则校验获得的网页源码是否为假数据
    def check_html_whether_false(self, WebSourceCode):
        RuleOne = compile(r'<head>(.*?)</head>', S)
        RuleTwo = compile(r'<body.*?>(.*?)</body>', S)
        AnalysisOne = RuleOne.findall(WebSourceCode)
        AnalysisTwo = RuleTwo.findall(WebSourceCode)
        try:
            if AnalysisOne[0]:
                print("head数据存在")
                if AnalysisTwo[0]:
                    print("body数据存在")
            return 1
        except:
            return -1

?④ 以正则获取网页编码charset, 以自动为爬取源码解码 # 需要重构, 有bug #重构完毕, 新版具有通用性, 简洁许多

# 以正则获取网页编码charset, 以自动为爬取源码解码 # 需要重构, 有bug #重构完毕, 新版具有通用性, 简洁许多
    def get_web_coding(self, WebSourceCode):
        MyCharsets = ['GBK18030', 'GBK', 'GB2312', 'UTF-8']
        Rule = compile(r'<head>.*?charset=(.*?)</head>', S)
        self.WebCoding = Rule.findall(WebSourceCode)
        for item in MyCharsets:
            if item in self.WebCoding[0]:
                self.WebCoding[0] = item
            elif item.lower() in self.WebCoding[0]:
                self.WebCoding[0] = item
        return self.WebCoding
        '''有BUG的老版代码'''
        # try:
        #     self.WebCoding[0]
        # except IndexError:
        #     self.which_ = 1
        #     Rule = compile(r'charset=(.*?)">', S)
        #     self.WebCoding = Rule.findall(WebSourceCode)
        #     try:
        #         self.WebCoding[0]
        #     except IndexError:
        #         self.which_ = '百度正则, CSDN正则'
        #         Rule = compile(r'charset="(.*?)">', S)
        #         self.WebCoding = Rule.findall(WebSourceCode)
        #         try:
        #             self.WebCoding[0]
        #         except IndexError:
        #             print("正则规则仍然无法匹配, 请您更新正则规则")
        #             pass    # 在此更新正则规则

⑤?请求方法, 并且自动为网页源码编码

# 请求方法, 并且自动为网页源码编码
    def request(self, url):
        header = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0"
        }
        self.RespOne = get(url=url)
        if self.RespOne.status_code >= 380:
            print("请求状态码错误")
            return -1
        else:
            self.RespOne = get(url=url, headers=header)
            self.get_web_coding(self.RespOne.text)
            self.RespOne.encoding = self.WebCoding[0]

⑥ 数据处理方法

    # 数据处理方法
    def data_handle_outside(self, WebSourceCode):
        Frequency = 0
        RuleOne = compile(r'<ul class="clearfix">(.*?)</ul>', S)
        RuleTwo = compile(r'href="(.*?)"', S)
        AnalysisOne = RuleOne.findall(WebSourceCode)
        self.AnalysisTwo = RuleTwo.findall(AnalysisOne[0])
        for item in self.AnalysisTwo:
            self.AnalysisTwo[Frequency] = 'http://www.trxs.cc' + item
            Frequency += 1

Ⅱ.? class CoroutineRequest 类 (协程请求逻辑爬虫对象)

调用的第三方库

from aiohttp import ClientSession, ClientTimeout, TCPConnector
from asyncio import Semaphore, ensure_future, get_event_loop, wait, set_event_loop_policy, WindowsSelectorEventLoopPolicy
from time import time

① 初始化部分 以及定义全局变量

    def __init__(self):
        self.spider_ = Spider()

        self.tasksOne = []
        self.tasksTwo = []
        self.tasksThree = []
        self.tasksFour = []
        self.tasksFive = []
        self.tasksSix = []

        self.WebCoding = []
        self.web_code = ''
        self.loop = get_event_loop()
        self.loop_two = get_event_loop()
        self.Document = ''  # 按钮选择保存的文件夹
        self.header = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                          "Chrome/101.0.4951.67 Safari/537.36",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,"
                      "application/signed-exchange;v=b3;q=0.9"
        }

② 因为协程方法与方法之间依赖调用比较强 在这里就不分开了

    def get_novel_name(self, WebSourceCode):
        self.web_code = WebSourceCode
        RuleThree = compile(r'<h1>(.*?)</h1>', S)
        self.novel_name = RuleThree.findall(WebSourceCode)

    # 协程请求方法, 参数: 不限制
    async def my_coroutine_http(self, *content):
        timeout_all = ClientTimeout(total=2000)
        timeout = ClientTimeout(total=150)
        async with content[1]:
            async with ClientSession(headers=self.header, timeout=timeout_all, connector=TCPConnector(ssl=False)) as session:
                async with session.get(url=content[0], timeout=timeout) as Response:
                    try:
                        return await Response.text(encoding='gb18030')
                    except:
                        try:
                            print('Error One')
                            return await Response.text(encoding='utf-8')
                        except:
                            try:
                                print('Error Two')
                                return await Response.text(encoding='GBK')
                            except:
                                print('Error Three')
                                pass

    # ayncio 构造请求task, 调用协程请求方法, 参数: 目前: 列表 将来: 元组
    def my_coroutine_request(self, urls):   # 该方法成功启用大并发协程, 是个非常好的例子, 多次观摩
        # self.WebCoding = self.spider_.get_web_coding(self.web_code)
        self.tasksOne = []
        self.tasksTwo = []
        semaphoreOne = Semaphore(300, loop=self.loop)  # 该变量不能放在aiohttp方法中定义, 不然asyncio事件循环会一直重置这个变量, 他会一直是300,不会递减 这样并发量, 特别多
        start = time()
        Frequency = 0
        for url in urls:
            if 0 <= Frequency < 500:    # 有规律可循 #
                self.tasksOne.append(ensure_future(self.my_coroutine_http(url, semaphoreOne)))
            elif 500 <= Frequency < 1000:
                self.tasksTwo.append(ensure_future(self.my_coroutine_http(url, semaphoreOne)))
            elif 1000 <= Frequency < 1500:
                self.tasksThree.append(ensure_future(self.my_coroutine_http(url, semaphoreOne)))
            elif 1500 <= Frequency < 2000:
                self.tasksFour.append(ensure_future(self.my_coroutine_http(url, semaphoreOne)))
            elif 2000 <= Frequency < 2500:
                self.tasksFive.append(ensure_future(self.my_coroutine_http(url, semaphoreOne)))
            elif 2500 <= Frequency:
                self.tasksSix.append(ensure_future(self.my_coroutine_http(url, semaphoreOne)))
            else:
                pass
            Frequency += 1

        loop = get_event_loop()
        loop.run_until_complete(wait(self.tasksOne))
        print("二阶段")
        if self.tasksTwo:
            loop.run_until_complete(wait(self.tasksTwo))
        elif self.tasksThree:
            loop.run_until_complete(wait(self.tasksThree))
        elif self.tasksFour:
            loop.run_until_complete(wait(self.tasksFour))
        elif self.tasksFive:
            loop.run_until_complete(wait(self.tasksFive))
        elif self.tasksSix:
            loop.run_until_complete(wait(self.tasksSix))
        else:
            pass

        end = time()

        for task_one in self.tasksOne:
            self.data_handle(task_one.result())
        if self.tasksTwo:
            for task_two in self.tasksTwo:
                self.data_handle(task_two.result())
        if self.tasksThree:
            for task_three in self.tasksThree:
                self.data_handle(task_three.result())
        if self.tasksFour:
            for task_four in self.tasksFour:
                self.data_handle(task_four.result())
        if self.tasksFive:
            for task_five in self.tasksFive:
                self.data_handle(task_five.result())
        if self.tasksSix:
            for task_six in self.tasksSix:
                self.data_handle(task_six.result())
        self.for_request_time = end - start
        print("内容页面网络请求总时间: ", self.for_request_time)

    def data_handle(self, WebSourceCode):
        try:
            RuleOne = compile(r'<div class="read_chapterDetail">(.*?)</div>', S)
            RuleTwo = compile(r'<p>(.*?)</p>', S)
            AnalysisOne = RuleOne.findall(WebSourceCode)
            with open(rf'D:\python_write_file\FirstPythonGUI\GUI单本书籍爬取\novel/{self.novel_name[0]}.txt', 'a',
                      encoding='gb18030') as novel_file:
                if AnalysisOne:
                    AnalysisTwo = RuleTwo.findall(AnalysisOne[0])
                    print(AnalysisTwo)
                    for itemOne in AnalysisTwo:
                        novel_file.write(itemOne)
                        novel_file.write('\n')
                else:
                    pass
        except TypeError as ErrorOne:
            print(ErrorOne)
            try:
                print(RuleTwo)
            except:
                print("无")
            print(WebSourceCode)

三. 打包

在终端中输入下面命令

 pyinstaller -F 程序入口文件绝对地址(有空格的话加双引号) -w -i 程序打包的图标绝对地址(双引号) 

?会出现下面问题

① 打包后资源不能正常显示 如界面背景图片

解决办法: 用python自带的工具编写qrc文件将image图片转为py文件 然后在UI文件导入即可

?建立qrc文件, 文件内容如下:

    <!DOCTYPE RCC>
    <RCC version="1.0">
        <qresource>
            <file alias="图片地址">图片地址</file>
            <file alias="图片地址">图片地址</file>
        </qresource>
    </RCC>

最后在终端中输入如下

pyrcc5 qrc文件地址 -o 名称自己定.py

然后会出现转换后资源的py文件,在UI文件import即可

结语

1.程序各处异常处理没细化, 没广泛,所以程序的健壮性不是很强, 自用还不错, But如果是用户使用,那么就难登大雅之堂了

2.进度条没有加上

3.个人觉得不是很美观

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-06-01 15:11:50  更:2022-06-01 15:12:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 15:36:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计