Python 第四五节
2025-03-30
一、文件IO
1、数据来源
(1)模型训练
需要大量的数据
通用的共享的开源素材,直接下载使用(开源数据集)
垂直领域的素材(自己公司所有),别人可以买
(2)保存数据
如果有多条数据,需要保存到JSON文件里面
特点:源数据格式是什么样的,需要先进行数据处理
(3)文件保存到Excel
使用
openpyxl
库 → 合并单元格、单元格美化安装
pip install openpyxl
步骤
打开或创建一个Excel文件
选择工作表
给单元格设置内容
import openpyxl
# 打开或创建一个工作簿
wb = openpyxl.Workbook()
# 激活表格
ws = wb.active
# 创建表头列表
headers = ['序号', '姓名', '年龄', '性别', '电话']
# 将表头写入表格
ws.append(headers)
# 创建数据列表
data = [
{
'姓名': '张三',
'年龄': 18,
'性别': '男',
'电话': '1234567890'
}, {
'姓名': '李四',
'年龄': 20,
'性别': '女',
'电话': '0987654321'
}, {
'姓名': '王五',
'年龄': 22,
'性别': '男',
'电话': '1111111111'
}
]
# 将数据写入表格
for index, row in enumerate(data, start=1):
ws.append([index, row['姓名'], row['年龄'], row['性别'], row['电话']])
# 保存工作簿
wb.save('./0330/demo.xlsx')
2、文件解析
(1)JSON文件解析
import json
import os
# 读取JSON文件
file_path = './0330/user_list.json'
if os.path.exists(file_path):
# 打开JSON文件
with open(file_path, 'r', encoding='utf-8') as f:
# 使用JSON库读取
user_list = json.load(f)
# 遍历JSON进行输出
for user in user_list:
print(f'姓名: {user["name"]}, 年龄: {user["age"]}, 地址: {user["addr"]}')
else:
print('文件不存在')
(2)Excel文件解析
import os
import openpyxl
file_path = './0330/demo.xlsx'
# 读取Excel文件
if os.path.exists(file_path):
# 打开Excel文件
workbook = openpyxl.load_workbook(file_path)
# 选择工作表
sheet = workbook['Sheet']
# 批量读取单元格的值
for row in sheet.iter_rows(min_row=2, values_only=True):
# row是一个元组,包含每一行的单元格值
print(f'序号: {row[0]}, 姓名: {row[1]}, 年龄: {row[2]}, 性别: {row[3]}, 电话: {row[4]}')
# 关闭工作簿
workbook.close()
else:
print('文件不存在')
二、爬虫
向某个网站发起HTTP请求,得到响应(一个网页的源码→HTML),解析HTML,找到你先要的东西,然后做点处理
1、网络请求
使用
request
库
请求方式:get、post、delete、put等
(1)发起HTTP请求
①get
import requests
response = requests.get("https://www.nipic.com/show/45718130.html/")
print(response.text)
②post
import requests
import json
data = {
'username': 'zhangsan',
'password': '76209400'
}
headers = {
'Content-Type': 'application/json'
}
# 自己电脑上的JAVA项目
response = requests.post("http://localhost:8888/public_rental_housing_ssm_war_exploded/admin/login/",
data=json.dumps(data),headers=headers) # 将字典数据转为JSON,且指定请求头媒体类型为JSON
print(response.text)
(2)response
text → 获取响应的文本内容
content → 获取响应的字节内容
2、实战
(1)爬取百度二维码图片
所用图片:https://pss.bdstatic.com/static/superman/img/qrcode_download-02b84e1f66.png
步骤:
使用网络请求访问该网址→HTML代码→requests的get方法
取出里面的内容→图片的内容
保存图片→open以wb方式打开
import requests
from urllib.parse import urlparse
# 图片URL
url = 'https://pss.bdstatic.com/static/superman/img/qrcode_download-02b84e1f66.png'
# 扩展:从URL中提取文件名
filename = urlparse(url).path.split('/')[-1]
# 发送GET请求下载图片
response = requests.get(url)
# 检查请求是否成功
if response.status_code == 200:
# 以二进制写入模式打开文件,使用原始文件名
with open('./0330/' + filename, 'wb') as f:
# 将内容写入文件
f.write(response.content)
print(f'图片下载成功!保存为: {filename}')
else:
print(f'下载失败,状态码:{response.status_code}')
(2)爬取昵图网图片
返回的HTML代码中会包含很多图片(img标签),获取每个标签(图片)对应的图片,然后分别用request请求一下
网址:
https://www.nipic.com/show/45718130.html/
步骤:
使用网络请求访问该网址→html代码→requests的get方法
解析HTML代码,找到img标签→获取src属性→多个
使用
beautifulSoup4
和lxml
两个库
通过request请求去访问src里面指定的路径,得到图片的二进制流
保存图片
import requests
from bs4 import BeautifulSoup
import os
from urllib.parse import urljoin
import time
def download_images(url):
# 创建保存图片的目录
save_dir = 'downloaded_images'
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 设置请求头,模拟浏览器
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
try:
# 获取网页内容
response = requests.get(url, headers=headers)
response.raise_for_status() # 检查请求是否成功
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(response.content, 'lxml')
# 查找所有图片标签
img_tags = soup.find_all('img')
# 遍历并下载图片
for img in img_tags:
# 获取图片URL
img_url = img.get('src')
if not img_url:
continue
# 确保URL是完整的
img_url = urljoin(url, img_url)
try:
# 下载图片
img_response = requests.get(img_url, headers=headers)
img_response.raise_for_status()
# 从URL中获取文件名
filename = os.path.basename(img_url)
filepath = os.path.join(save_dir, filename)
# 保存图片
with open(filepath, 'wb') as f:
f.write(img_response.content)
print(f'成功下载图片: {filename}')
# 添加延迟,避免请求过快
time.sleep(1)
except Exception as e:
print(f'下载图片失败: {img_url}')
print(f'错误信息: {str(e)}')
except Exception as e:
print(f'获取网页失败: {url}')
print(f'错误信息: {str(e)}')
# 运行爬虫
url = 'https://www.nipic.com/show/45718130.html'
download_images(url)
三、PyQt图形界面编程
1、使用PyQt界面
安装
pip install PyQt5
创建一个界面应用户程序
创建一个窗口
设置窗口标题
setWindowTitle()
设置窗户口图标
setWindowIcon()
设置窗口大小
resize()
调整窗口位置以及大小
setGeometry()
创建各种组件:文本框,密码框,单选,按钮等
2、文本框
(1)创建文本框
文本框主要用于显示文字,使用
QLabel()
方案一:
QLabel('example')
创建的时候顺便写好文字
方案二:
创建的时候不写文字
QLabel()
通过调用
setText()
(2)指定文本框的父级元素
也就是显示在哪个窗口
需要指定显示在那个窗口,不指定也就不显示在任何窗口上
一样通过
setGeometry()
设置文本位置和大小方案一:
使用
setParent()
方案二:
在创建的时候就顺便指定了
QLable(exanple_window)
3、图片控件
借助
QLabel()
来实现的,然后使用QPixmap()
来指定图片
4、单行文本控件
使用
QLineEdit()
输出模式
QLineEdit.password
→ 密文模式QLineEdit.passwordEchoOnEdit
→ 编辑时可见,不编辑时不可见QLineEdit.NoEcho
→ 不显示QLineEdit.Normal
→ 普通明文模式
5、多行文本控件
使用
QTextEdit()
如何创建
如何设置文本内容
如何获取文本内容
setPlainText()
setHtml()
→ 可以包含HTML标签,如使用<br>
、<strong></strong>
、<p></p>
、<h1></h1>
说明也可以显示多媒体元素(图片
<img></img>
、视频<video></video>
、音频<audio></audio>
)
清空文本内容:
clear()
6、按钮
普通按钮
使用
QPushButton()
,使用clicked
来实现按下后操作
单选按钮
使用
QRadioButton()
,使用toggled
来处理切换操作,按钮组
复选按钮
使用
QCheckBox()
,使用isChecked
判断某个复选框被选中
7、对话框
(2)QMessageBox()
信息提示框,包含
warning
,information
,critical
,question
等修改按钮文字:
自带的默认按钮:Ok,Yes,No,Cancel
自定义对话框
# 默认方法
tip_box = QMessageBox()
tip_box.question(
example_window,
'example_title',
'example_message',
buttons= QMessages.Ok | QMessageBox.Cancel
)
# 自定义对话框
tip_box_1 = QMessageBox()
# 自定义标题和文字
tip_box_1.setWindowTitle('example_title')
tip_box_1.setText('example_text')
# 自己创建两个按钮,作为对话框的按钮来用
yes_button = QPushButton('是')
no_button = QPushButton('否')
# 将按钮添加到对话框
tip_box_1.addButton(yes_btn,QMessageBox.AcceptRole) # 作为接受按钮
tip_box_1.addButton(no_btn,QMessageBox.RejectRole) # 作为拒绝按钮
# 执行显示修改后的对话框
tip_box_1.exec()
(3)QInputDialog()
带输入框的对话框
q_dialog = QInputDialog()
q_dialog.getText(example_window, 'example_title')
8、布局
(1)类型
水平布局
QHBoxLayout
垂直布局
QVBoxLayout
表单布局
QFormLayout
网络布局
QGridLayout
嵌套布局
(2)示例
创建一个挂件
QWidget
,并设置该挂件的布局方式为垂直布局
# left_view.py
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton
class LeftView(QWidget):
def __init__(self):
super().__init__()
self.btn1 = QPushButton("打开文件")
self.btn2 = QPushButton("打开文件夹")
v_layout = QVBoxLayout()
self.setLayout(v_layout)
# 添加按钮
v_layout.addWidget(self.btn1)
v_layout.addWidget(self.btn2)
# main.py
from left_view import LeftView
from PyQt5.QtWidgets import QApplication
import sys
if __name__ == "__main__":
app = QApplication(sys.argv)
left_view = LeftView()
left_view.show()
sys.exit(app.exec_())
三、课后复习
1、找一个网页实现爬虫
顺便回顾递归算法,怎么实现递归
import requests
import os
from bs4 import BeautifulSoup
import time
from urllib.parse import urljoin
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session():
"""创建一个带重试机制的会话"""
session = requests.Session()
# 配置重试策略
retries = Retry(
total=5, # 总共重试5次
backoff_factor=1, # 重试之间的等待时间
status_forcelist=[500, 502, 503, 504] # 需要重试的HTTP状态码
)
# 将重试策略应用到会话
adapter = HTTPAdapter(max_retries=retries)
session.mount('http://', adapter)
session.mount('https://', adapter)
# 设置请求头,模拟浏览器
session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
})
return session
def get_original_image_url(session, detail_url):
"""获取原图URL"""
try:
response = session.get(detail_url, verify=False)
if response.status_code == 200:
soup = BeautifulSoup(response.text, 'html.parser')
# 查找原图链接,通常在id为wallpaper的img标签中
img_tag = soup.find('img', id='wallpaper')
if img_tag and img_tag.get('src'):
return img_tag['src']
except Exception as e:
print(f"获取原图链接失败: {detail_url}")
print(f"错误信息: {str(e)}")
return None
def download_wallpaper(session, url, save_dir):
"""下载单张壁纸"""
try:
response = session.get(url, verify=False)
if response.status_code == 200:
# 使用原图URL中的文件名
filename = url.split('/')[-1]
# 检查文件类型
file_ext = os.path.splitext(filename)[1].lower()
if file_ext not in ['.jpg', '.jpeg', '.png', '.webp']:
print(f"不支持的文件类型: {filename}")
return False
filepath = os.path.join(save_dir, filename)
if os.path.exists(filepath):
print(f"文件已存在: {filename}")
else:
with open(filepath, 'wb') as f:
f.write(response.content)
print(f"成功下载: {filename}")
return True
except Exception as e:
print(f"下载失败: {url}")
print(f"错误信息: {str(e)}")
return False
def crawl_page(session, url, save_dir, page=1, max_pages=10, retry_count=0, max_retries=3):
"""递归爬取壁纸页面"""
if retry_count >= max_retries:
print(f"已达到最大重试次数 {max_retries},停止爬取")
return
if page > max_pages:
print("达到最大页数,重新从第一页开始")
# 重新从第一页开始,增加重试计数
crawl_page(session, url, save_dir, 1, max_pages, retry_count + 1, max_retries)
return
print(f"正在爬取第 {page} 页...")
try:
page_url = f"{url}&page={page}"
response = session.get(page_url, verify=False)
soup = BeautifulSoup(response.text, 'html.parser')
# 找到所有壁纸预览图的父级元素
wallpaper_links = soup.find_all('a', class_='preview')
if not wallpaper_links:
print(f"第 {page} 页没有找到壁纸,继续下一页")
# 继续爬取下一页,而不是重新开始
crawl_page(session, url, save_dir, page, max_pages, retry_count, max_retries)
return
for link in wallpaper_links:
# 获取详情页URL
detail_url = link.get('href')
if detail_url:
print(f"正在处理: {detail_url}")
# 获取原图URL
original_url = get_original_image_url(session, detail_url)
if original_url:
# 下载原图
download_wallpaper(session, original_url, save_dir)
# 添加延时,避免请求过于频繁
time.sleep(3)
# 递归爬取下一页
crawl_page(session, url, save_dir, page + 1, max_pages, retry_count, max_retries)
except Exception as e:
print(f"爬取页面失败: {page}")
print(f"错误信息: {str(e)}")
def main():
# 禁用SSL验证警告
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# 设置壁纸网站URL
base_url = "https://wallhaven.cc/search?categories=110&purity=100&atleast=1920x1080&topRange=1M&sorting=favorites&order=desc&ai_art_filter=1"
# 设置保存目录
save_dir = "wallpapers"
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 创建会话
session = create_session()
# 开始爬取
crawl_page(session, base_url, save_dir)
if __name__ == "__main__":
main()
2、绘制一个登录注册页面
并实现登录注册请求,和相关提示
from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QLineEdit,
QPushButton, QRadioButton, QMessageBox)
from PyQt5.QtGui import QIcon, QPixmap
import sys
import json
import requests
class LoginWindow(QWidget):
def __init__(self):
super().__init__()
self.login_widgets = [] # 存储登录相关组件
self.register_widgets = [] # 存储注册相关组件
self.initUI()
def initUI(self):
# 初始化窗口基本设置
self.setWindowTitle('传一卓跃')
self.setWindowIcon(QIcon('./0330/icon.jpg'))
# 设置背景图
self.pic_label = QLabel(self)
pic = QPixmap('./downloaded_images/18295875_213833592103_2.jpg')
self.pic_label.setPixmap(pic)
self.resize(pic.width(), pic.height())
# 初始化登录界面
self.init_login_ui()
def init_login_ui(self):
# 欢迎文字
welcome_label = QLabel('欢迎登录', self)
welcome_label.setStyleSheet('color: white; font-size: 30px;')
welcome_label.setGeometry(30, 30, 300, 50)
# 账号输入区域
acc_label = QLabel('账号:', self)
acc_label.setGeometry(30, 75, 170, 50)
acc_label.setStyleSheet('color: white; font-size: 20px;')
self.username_input = QLineEdit(self)
self.username_input.setPlaceholderText('请输入账号')
self.username_input.setGeometry(30, 120, 170, 30)
self.username_input.setStyleSheet('border: 1px solid blue; padding: 5px;')
# 密码输入区域
pwd_label = QLabel('密码:', self)
pwd_label.setGeometry(30, 150, 170, 50)
pwd_label.setStyleSheet('color: white; font-size: 20px;')
self.password_input = QLineEdit(self)
self.password_input.setPlaceholderText('请输入密码')
self.password_input.setGeometry(30, 195, 170, 30)
self.password_input.setStyleSheet('border: 1px solid blue; padding: 5px;')
self.password_input.setEchoMode(QLineEdit.Password)
# 用户类型选择
self.radio_admin = QRadioButton('管理员', self)
self.radio_admin.setGeometry(30, 230, 170, 50)
self.radio_admin.setStyleSheet('color: white; font-size: 20px;')
self.radio_admin.setChecked(True)
self.radio_user = QRadioButton('普通用户', self)
self.radio_user.setGeometry(30, 270, 170, 50)
self.radio_user.setStyleSheet('color: white; font-size: 20px;')
# 登录按钮
self.login_button = QPushButton('登录', self)
self.login_button.setGeometry(30, 320, 170, 50)
self.login_button.setStyleSheet('background-color: blue; color: white; font-size: 20px;')
self.login_button.clicked.connect(self.handle_login)
# 注册按钮
self.register_button = QPushButton('注册', self)
self.register_button.setGeometry(30, 380, 170, 50)
self.register_button.setStyleSheet('background-color: green; color: white; font-size: 20px;')
self.register_button.clicked.connect(self.show_register_ui)
# 保存登录相关组件引用
self.login_widgets = [welcome_label, acc_label, self.username_input,
pwd_label, self.password_input, self.radio_admin,
self.radio_user, self.login_button, self.register_button]
def init_register_ui(self):
# 注册标题
reg_title = QLabel('用户注册', self)
reg_title.setStyleSheet('color: white; font-size: 30px;')
reg_title.setGeometry(30, 30, 300, 50)
# 注册表单字段
fields = ['账号', '密码', '姓名', '手机', '证件号码', '联系地址']
self.reg_inputs = {}
for i, field in enumerate(fields):
# 创建标签
label = QLabel(f'{field}:', self)
label.setGeometry(30, 75 + i*70, 170, 50)
label.setStyleSheet('color: white; font-size: 20px;')
# 创建输入框
input_box = QLineEdit(self)
input_box.setPlaceholderText(f'请输入{field}')
input_box.setGeometry(30, 120 + i*70, 170, 30)
input_box.setStyleSheet('border: 1px solid blue; padding: 5px;')
if field == '密码':
input_box.setEchoMode(QLineEdit.Password)
self.reg_inputs[field] = input_box
self.register_widgets.extend([label, input_box])
# 注册和返回按钮
y_pos = 120 + len(fields)*70
submit_btn = QPushButton('提交注册', self)
submit_btn.setGeometry(30, y_pos, 170, 50)
submit_btn.setStyleSheet('background-color: green; color: white; font-size: 20px;')
submit_btn.clicked.connect(self.handle_register)
back_btn = QPushButton('返回登录', self)
back_btn.setGeometry(30, y_pos + 60, 170, 50)
back_btn.setStyleSheet('background-color: gray; color: white; font-size: 20px;')
back_btn.clicked.connect(self.show_login_ui)
self.register_widgets.extend([reg_title, submit_btn, back_btn])
# 初始隐藏所有注册组件
for widget in self.register_widgets:
widget.hide()
def show_register_ui(self):
# 隐藏登录组件
for widget in self.login_widgets:
widget.hide()
# 确保注册组件已初始化
if not self.register_widgets:
self.init_register_ui()
# 显示注册组件
for widget in self.register_widgets:
widget.show()
def show_login_ui(self):
# 隐藏注册组件
for widget in self.register_widgets:
widget.hide()
# 显示登录组件
for widget in self.login_widgets:
widget.show()
def handle_login(self):
username = self.username_input.text()
password = self.password_input.text()
if not username or not password:
QMessageBox.warning(self, '提示', '请输入账号和密码!')
return
# 根据用户类型选择不同的登录接口
main_url = 'http://localhost:8888/public_rental_housing_ssm_war_exploded/'
api_url = 'admin/login' if self.radio_admin.isChecked() else 'tenant/login'
# 登录信息
login_data = {'account': username, 'password': password}
try:
# 调用登录接口
response = requests.post(main_url + api_url,
json=login_data, # 使用json参数自动处理JSON序列化
headers={'Content-Type': 'application/json'})
# 将响应解析为JSON
result = response.json()
# 检查响应状态
if result.get('code') == 200: # 使用get方法安全获取code
QMessageBox.information(self, '成功', '登录成功!')
else:
QMessageBox.warning(self, '错误', result.get('message', '未知错误'))
except requests.exceptions.RequestException as e:
QMessageBox.critical(self, '错误', f'网络请求失败:{str(e)}')
except json.JSONDecodeError as e:
QMessageBox.critical(self, '错误', f'响应数据格式错误:{str(e)}')
except Exception as e:
QMessageBox.critical(self, '错误', f'系统错误:{str(e)}')
def handle_register(self):
# 收集注册信息
register_data = {
'account': self.reg_inputs['账号'].text(),
'password': self.reg_inputs['密码'].text(),
'name': self.reg_inputs['姓名'].text(),
'phone': self.reg_inputs['手机'].text(),
'idCard': self.reg_inputs['证件号码'].text(),
'address': self.reg_inputs['联系地址'].text()
}
# 验证必填字段
if not all(register_data.values()):
QMessageBox.warning(self, '提示', '请填写所有必填信息!')
return
try:
# 调用注册接口
response = requests.post(
'http://localhost:8888/public_rental_housing_ssm_war_exploded/tenant/reg',
json=register_data, # 使用json参数自动处理JSON序列化
headers={'Content-Type': 'application/json'}
)
# 将响应解析为JSON
result = response.json()
# 检查响应状态
if result.get('code') == 200:
QMessageBox.information(self, '成功', '注册成功!')
self.show_login_ui() # 返回登录界面
else:
QMessageBox.warning(self, '错误', result.get('message', '注册失败'))
except requests.exceptions.RequestException as e:
QMessageBox.critical(self, '错误', f'网络请求失败:{str(e)}')
except json.JSONDecodeError as e:
QMessageBox.critical(self, '错误', f'响应数据格式错误:{str(e)}')
except Exception as e:
QMessageBox.critical(self, '错误', f'系统错误:{str(e)}')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = LoginWindow()
window.show()
sys.exit(app.exec_())
3、PyQt小扩展
修改QInputDialog的按钮为中文
使用文件选择框,获取用户选择文件(文件路径)
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QInputDialog, QFileDialog
class Example(QWidget):
def __init__(self):
super().__init__()
self.file_btn = None
self.input_btn = None
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
# 创建按钮
self.input_btn = QPushButton('打开输入对话框', self)
self.file_btn = QPushButton('选择文件', self)
# 连接按钮点击事件
self.input_btn.clicked.connect(self.show_input_dialog)
self.file_btn.clicked.connect(self.show_file_dialog)
# 添加按钮到布局
layout.addWidget(self.input_btn)
layout.addWidget(self.file_btn)
self.setLayout(layout)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('PyQt 示例')
self.show()
def show_input_dialog(self):
# 创建输入对话框
dialog = QInputDialog(self)
dialog.setWindowTitle('输入对话框')
dialog.setLabelText('请输入内容:')
# 修改按钮文本为中文
dialog.setOkButtonText('确定')
dialog.setCancelButtonText('取消')
# 显示对话框
ok = dialog.exec_()
text = dialog.textValue()
if ok:
print(f'输入的内容: {text}')
def show_file_dialog(self):
# 创建文件选择对话框
options = QFileDialog.Options()
file_name, _ = QFileDialog.getOpenFileName(
self,
"选择文件",
"",
"所有文件 (*);;文本文件 (*.txt)",
options=options
)
if file_name:
print(f'选择的文件路径: {file_name}')
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())