探索Python爬虫的无限可能性:基础与应用

本文最后更新于:5 个月前

破冰

一些废话

🥣 这学期学校开设了 Python 课程,我索性把课堂作业啥的都放在这个栏目下了,那么这篇博客就用来记录日常的 Python 学习经验

🍝 这篇博客原标题是:

  • 探索Python爬虫的无限可能性:基础与应用
  • 介绍Python爬虫的进阶技术,包括动态页面爬取、数据解析、反爬虫策略等,并通过实战案例展示其实际应用

感觉这篇博文改名也没多大必要,不过还是把原标题放这留个纪念(2023/11/09晚)

正文

Python爬虫学习

  • 一直以来都想学习爬虫技术, 却一直下不了决心, 不知道从何学起, 也没有精力去学
  • 过年前后, 看过了黑马的Python基础课, 掌握了基本的语法, 现在伙伴匹配系统基础功能也完成了, 可以拿时间来系统且快速地学习Python爬虫了, 主要是我觉得一个Java程序猿怎么能不懂爬虫呢

爬虫的分类

  • API爬虫和网页爬虫(2023/08/29晚)
1
2
3
4
5
6
7
8
9
10
11
12
13
当从更专业的角度讨论API爬虫和网页爬虫时,我们可以更详细地了解它们的定义、特点和用途。

API爬虫是指通过调用网络应用程序接口(API)来获取数据的爬虫。API是一种允许不同应用程序之间进行通信和交互的接口。API通常提供了固定的数据格式和结构,比如JSON或XML,这使得数据更易于获取和解析。API爬虫通过发送HTTP请求到API的URL路径,来获取数据。一旦数据返回,爬虫可以解析它,并对数据进行处理和分析。

API爬虫的优点在于,它可以更方便地获取和处理结构化的数据,因为API通常提供了规范化的数据格式。此外,由于API接口具有稳定性和可靠性,爬取数据的稳定性和准确性也相对较高。因此,API 爬虫通常被用于从特定网站、应用程序或服务中获取数据。

网页爬虫则是通过发送HTTP请求到网页的URL路径,获取返回的HTML页面源代码,并进行解析、提取和处理信息的工具。网页爬虫通常用于从网页中获取结构化和非结构化的数据。它们可以通过解析HTML文档中的标签、属性和内容,来获取所需的数据。网页爬虫的输出可以是包含所提取数据的结构化文本文件或数据库。

网页爬虫的优点在于其灵活性和适用范围广泛。它们可以适应不同类型的网页,不仅限于特定的API接口。网页爬虫可以用于各种不同的应用,如搜索引擎索引、产品信息收集、舆情分析等。

总之,API爬虫和网页爬虫是两种不同的爬虫类型,它们在采集数据的方式、解析数据的方法和适用范围上存在差异。根据需求和情境,选择合适的爬虫类型可以提高爬取数据的效率和准确性。

希望这个更专业的解释对你有帮助!如果还有其他问题,请随时提问。

Day1

  • 今天是学习的第一天, 学习爬虫过程中也了解到了不少计网的知识, 这令我很惊喜了
  • 简要总结一下学到些什么吧, 详细的知识点可以拿到老师的, 待会儿可以贴上来, 我就记重点了
  • requests, http/https协议, 爬虫, XML, XHR, AJAX, axios 这是我今天了解到的概念
  • 爬取步骤
1
2
3
4
5
6
-- url请求路径
-- UA伪装
-- 请求参数
-- 发送请求
-- 接收响应值
-- 持久化数据
  • 今天爬取到了搜狗网页 百度翻译 iTab标签页 还是有很多注意点的, 把代码扔这吧
  • 搜狗网页采集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests

if __name__ == '__main__':
# UA伪装: 爬虫对应的身份标识伪装成某一浏览器, 避免请求被拒绝
headers = {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/113.0.0.0'
}
# 请求路径
url = 'https://www.sogou.com/web?'
# 请求参数
content = input('输入搜索词:')
params = {
'query': content
}
# 发送请求
response = requests.get(url=url, params=params, headers=headers)
# 获取响应值
page_text = response.text
# 持久化存储
fileName = content + '.html'
with open(fileName, 'w', encoding='utf-8') as fp:
fp.write(page_text)
print(fileName, '抓取成功!')

  • 百度翻译爬取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import json

import requests

if __name__ == '__main__':
# url路径
url = "https://fanyi.baidu.com/sug"
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/113.0.0.0'
}
# 请求参数
content = input('输入要翻译的词:')
data = {
'kw': content
}
# 发送请求
response = requests.post(url=url, data=data, headers=headers)
# 获取响应
dic_obj = response.json()
# 持久化存储
filename = content + '.json'
fp = open(filename, 'w', encoding='utf-8')
json.dump(dic_obj, fp, ensure_ascii=False)
print('over!!!')

  • iTab标签页爬取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import json

import requests
if __name__ == '__main__':
# url路径
url = "https://api.codelife.cc/api/getWeather"
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/113.0.0.0'
}
# 请求参数
params = {
'lang': ' cn',
'location': '101101101'
}
# 发送请求
response = requests.get(url, params=params, headers=headers)
# 获取响应
dic_obj = response.json()
# 持久化存储
fp = open('iTab标签页.json', 'w', encoding='utf-8')
json.dump(dic_obj, fp, ensure_ascii=False)
print('over!!!')

Day2

  • 距离上次学习都他妈过去5天了
  • 交一下昨天的作业吧 比较复杂的爬取
  • 化妆品生产许可信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import requests
import json

id_list = [] # 获取每个企业的id
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74'
}
# url请求路径
url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
# 发送请求
# 拿取 1-19 页
for page in range(1, 20):
page = str(page)
# 请求参数
data = {
'on': 'true',
'page': page,
'pageSize': '15',
'productName': '',
'conditionType': '1',
'applyname': '',
'applysn': ''
}
# 以json的格式返回response的内容(如果有的话)
response = requests.post(url=url, headers=headers, data=data).json()
for dic in response['list']: # response是一个字典,list是一个键,而我们需要这个键对应的值,所以就有了`response['list'],
# 稍微复杂的是dic是一个数组,这个数组里包括了一部字典,
id_list.append(dic['ID']) # dic是一个字典,我们同样用‘ID’这个键访问它对应的值,并把这个值添加到id_list[]里

# 从这里开始整个程序分为两部分,上面是获取所有的id,下面是对所有的id信息进行请求,然后是保存。
all_data = [] # 这个列表用来存放最终的所有公司的具体信息
# url请求路径
post_url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
# 发送请求
for id in id_list: # 把id封装到字典当作作为参数以备调用
# 请求参数
data2 = { # 遍历所有的id
'id': id
}
result = requests.post(url=post_url, headers=headers, data=data2).json()
all_data.append(result) # 通过a数据解析append方法把所有的请求结果上传到all_data[]中
print(result) # 直接输出请求返回的结果

# 持久化存储
file = open('./化妆品生产许可信息.json', 'w', encoding='utf-8')
json.dump(all_data, fp=file, ensure_ascii=False)

  • 今天又学习了图片的爬取 再补个作业
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import os.path
import re
import requests

if __name__ == '__main__':
# 创建文件夹
if not os.path.exists('./imgs'):
os.mkdir('./imgs')
# 请求路径
url = 'https://www.woyaogexing.com/tupian/index_4.html'
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, '
'like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/113.0.0.0'
}
# 爬取对应的网页内容
page_text = requests.get(url=url, headers=headers).text
# print(page_text)
# 爬取对应网页上的图片(正则表达式)
# 编写正则表达式
ex = '<div class="txList ">.*?<img.*?src="(.*?)".*?>.*?</div>'
# 获取所需字符串
img_src_list = re.findall(ex, page_text, re.S)
print(img_src_list)
for img_src in img_src_list:
# 拼接完整的图片路径
img_src = 'http:' + img_src
print(img_src)
# 请求图片
img = requests.get(url=img_src, headers=headers).content
# 图片名
img_name = img_src.split('/')[-1]
# 图片保存路径
img_path = './imgs/' + img_name
print('img_path = ' + img_path)
# 保存图片
with open(img_path, 'wb') as fp:
fp.write(img)
print(img_name + '下载完成!')
print('over!!!')

  • 这里是使用了正则表达式来处理要爬取的部分数据 我要爬取的图片就是这样的:

image-20230526151919628

  • 照猫画虎写了个贪婪匹配(学过都忘了,非常滴不熟练)
  • 可以爬取多个页面了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import os.path
import re

import requests

if __name__ == '__main__':
# 创建文件夹
if not os.path.exists('./imgs'):
os.mkdir('./imgs')
# 请求路径
url = 'https://www.woyaogexing.com/tupian/index_%d.html'
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, '
'like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/113.0.0.0'
}
for page_num in range(1, 11):
new_url = format(url % page_num)
# 爬取对应的网页内容
page_text = requests.get(url=new_url, headers=headers).text
# print(page_text)
# 爬取对应网页上的图片(正则表达式)
# 编写正则表达式
ex = '<div class="txList ">.*?<img.*?src="(.*?)".*?>.*?</div>'
# 获取所需字符串
img_src_list = re.findall(ex, page_text, re.S)
print(img_src_list)
for img_src in img_src_list:
# 拼接完整的图片路径
img_src = 'http:' + img_src
print(img_src)
# 请求图片
img = requests.get(url=img_src, headers=headers).content
# 图片名
img_name = img_src.split('/')[-1]
# 图片保存路径
img_path = './imgs/' + img_name
print('img_path = ' + img_path)
# 保存图片
with open(img_path, 'wb') as fp:
fp.write(img)
print(img_name + '下载完成!')
print('over!!!')

Day3

  • 数据分析,爬取页面中标签中的数据
  • 这边做完几个课堂练习后,自行找了个网页,练习了下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import os.path

import requests
from lxml import etree

if __name__ == '__main__':
# url路径
url = 'https://bobopic.com/4kbizhi.html'
# UA伪装
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/72.0.3626.121 Safari/537.36'
}
# 请求页面
page_text = requests.get(url=url, headers=headers).text
# 封装页面对象
tree = etree.HTML(page_text)
# 获取a标签
a_list = tree.xpath('//div[@class="related"]/ul/li/a')
# 遍历a_list
for a in a_list:
# 拿取每个a的href
a_href = a.xpath('./@href')[0]
# 请求新页面
new_page_text = requests.get(url=a_href + '', headers=headers).content
# 封装页面对象
tree = etree.HTML(new_page_text)
img_list = tree.xpath('//div[@class="entry-content u-clearfix"]//img')
if not os.path.exists('./二次元风景4k'):
os.mkdir('./二次元风景4k')
# 获取新页面下的图片
for img in img_list:
# 图片名
img_name = img.xpath('./@alt')[0] + '.jpg'
# 图片路径
img_src = img.xpath('./@src')[0] + ''
# 爬取图片
img_data = requests.get(url=img_src, headers=headers).content
# 图片存放路径
img_path = '二次元风景4k/' + img_name
# 存放图片
with open('二次元风景4k/' + img_name, 'wb') as fp:
fp.write(img_data)
print(img_name + '下载成功!')

  • 以alt内容作为图片名,里面含有”|”符号,不能在文件名中出现,搞了老半天(2023/05/30)

图表绘制

  • 简单记录一下使用 Python 实现图表绘制 (2023/10/16晚)

中国版图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from pyecharts.charts import Map
from pyecharts import options as opts

# 创建地图对象
china_map = Map()

# 准备数据
province_data = [
("广东省", 100),
("北京市", 506),
("上海市", 88),
("山西省", 1000),
# 其他省份数据...
]

# 设置地图数据
china_map.add("中国地图", province_data, "china")

# 设置全局选项
china_map.set_global_opts(
visualmap_opts=opts.VisualMapOpts(
is_show=True,
is_piecewise=True,
pieces=[
{"min": 1, "max": 99, "label": "1-99", "color": "#CCFFFF"},
{"min": 100, "max": 500, "label": "100-500", "color": "#99CCFF"},
{"min": 501, "max": 1500, "label": "501-1500", "color": "#66CCFF"}
]
)
)

# 绘图并保存
china_map.render("china_map.html")

image-20231016195435508

其他

折线图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
"""
演示可视化需求1:折线图开发
"""
import json
from pyecharts.charts import Line
from pyecharts.options import TitleOpts, LabelOpts

# 处理数据
f_us = open("D:/Project/python/other/美国.txt", "r", encoding="UTF-8")
us_data = f_us.read() # 美国的全部内容

f_jp = open("D:/Project/python/other/日本.txt", "r", encoding="UTF-8")
jp_data = f_jp.read() # 日本的全部内容

f_in = open("D:/Project/python/other/印度.txt", "r", encoding="UTF-8")
in_data = f_in.read() # 印度的全部内容

# 去掉不合JSON规范的开头
us_data = us_data.replace("jsonp_1629344292311_69436(", "")
jp_data = jp_data.replace("jsonp_1629350871167_29498(", "")
in_data = in_data.replace("jsonp_1629350745930_63180(", "")

# 去掉不合JSON规范的结尾
us_data = us_data[:-2]
jp_data = jp_data[:-2]
in_data = in_data[:-2]

# JSON转Python字典
us_dict = json.loads(us_data)
jp_dict = json.loads(jp_data)
in_dict = json.loads(in_data)

# 获取trend key
us_trend_data = us_dict['data'][0]['trend']
jp_trend_data = jp_dict['data'][0]['trend']
in_trend_data = in_dict['data'][0]['trend']

# 获取日期数据,用于x轴,取2020年(到 314 下标结束)
us_x_data = us_trend_data['updateDate'][:314]
jp_x_data = jp_trend_data['updateDate'][:314]
in_x_data = in_trend_data['updateDate'][:314]

# 获取确认数据,用于y轴,取2020年(到 314 下标结束)
us_y_data = us_trend_data['list'][0]['data'][:314]
jp_y_data = jp_trend_data['list'][0]['data'][:314]
in_y_data = in_trend_data['list'][0]['data'][:314]

# 生成图表
line = Line() # 构建折线图对象
# 添加x轴数据
line.add_xaxis(us_x_data) # x轴是公用的,所以使用一个国家的数据即可
# 添加y轴数据
line.add_yaxis("美国确诊人数", us_y_data, label_opts=LabelOpts(is_show=False)) # 添加美国的y轴数据
line.add_yaxis("日本确诊人数", jp_y_data, label_opts=LabelOpts(is_show=False)) # 添加日本的y轴数据
line.add_yaxis("印度确诊人数", in_y_data, label_opts=LabelOpts(is_show=False)) # 添加印度的y轴数据

# 设置全局选项
line.set_global_opts(
# 标题设置
title_opts=TitleOpts(title="2020年美日印三国确诊人数对比折线图", pos_left="center", pos_bottom="1%")
)

# 调用render方法,生成图表
line.render()
# 关闭文件对象
f_us.close()
f_jp.close()
f_in.close()

image-20231016194821867

饼图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pyecharts.charts import Pie
from pyecharts import options as opts

# 准备饼图数据
data = [
("A", 30),
("B", 20),
("C", 50)
]

# 创建饼图对象
pie = Pie(init_opts=opts.InitOpts(width="500px", height="300px"))

# 添加数据
pie.add("", data)

# 设置全局选项
pie.set_global_opts(title_opts=opts.TitleOpts(title="饼图示例"))

# 绘图并保存
pie.render("pie_chart.html")

image-20231016195023196

柱状图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pyecharts.charts import Bar
from pyecharts import options as opts

# 准备柱状图数据
x_data = ["A", "B", "C", "D"]
y_data = [10, 20, 30, 40]

# 创建柱状图对象
bar = Bar(init_opts=opts.InitOpts(width="500px", height="300px"))

# 添加x轴数据
bar.add_xaxis(x_data)

# 添加y轴数据
bar.add_yaxis("柱状图", y_data)

# 设置全局选项
bar.set_global_opts(title_opts=opts.TitleOpts(title="柱状图示例"))

# 绘图并保存
bar.render("bar_chart.html")

image-20231016194851306

Python 期末小组作业(旧)

  • 人工智能、Python课程的期末小组作业目前有想法,这俩门课程研究一个课题吧

课题名称

  • 智能生成图表

效果展示

  • PPT,展示生成的图表

开发工具

  • PyCharm

课题分析

  • 生成图表三步骤:(2023/10/12早)

    • 数据(构造数据,自行搜集资料构造数据)
      • 数据样式选择(方便接下来导入数据,作数据分析)
        • JSON 字符串,使用 Python 基础语法解构 JSON 字符串,获取数据
        • Excel 图表,使用 Python 自带工具类,导入 Excel 图表,解构图表获取数据
    • 导入数据:使用上述两种方式将数据导入完成后,智能分析,生成图表
    • 智能分析
  • 代码讲解:

    • 整体架构:上述三步骤
    • 构造数据简单讲解(JSON 字符串样式)
    • 列表、元组语法简单讲解
    • 亮点(重点):如何解构 JSON 字符串/ Excel 图表获得数据
    • 生成图表简单讲解
  • 闲话

Python 期末小组作业(新)

实现思路

构思一下整个 Python 课设的实现思路:

  • 实验目的

    • 掌握 Python 基础语法:函数定义与调用、条件分支、对象创建

    • 掌握在 Python 中使用第三方库对 Excel 表格分析思路与具体实现

    • 掌握使用 Python 实现数据库连接,并实现对数据基本的增、删、改、查操作 (2023/11/09晚)

  • 功能实现

    • 数据存储:使用 Python 分析 Excel 表格中的数据,存储到数据库中
    • 连接本地数据库(如SQLite、MySQL等)。(2023/11/25早)
    • 创建数据表,根据Excel表格的列名和数据类型定义表结构。
    • 读取Excel表格中的数据,可以使用pandas库进行操作。
    • 将读取到的数据插入到数据库中,可以使用pandas的to_sql方法。
    • 数据更新:更新数据库中的数据记录
    • 模拟请求,生成需要更新的数据记录。
    • 使用Python连接到数据库,执行更新操作。
    • 更新数据库中的数据记录,可以使用SQL语句或ORM框架(如SQLAlchemy)进行操作。
    • 数据写回:从数据库中查询记录,生成统计图表展示数据,生成 Excel 表格,适当优化表格样式后,保存表格到本地
    • 从数据库中查询记录,可以使用SQL语句或ORM框架进行查询。
    • 对查询结果进行处理,例如计算统计指标、生成图表等。
    • 将处理后的数据写入Excel表格,可以使用pandas的DataFrame对象进行操作。
    • 适当优化表格样式,可以使用pandas的样式函数进行设置。
    • 保存Excel表格到本地文件系统。
  • 开发文档

    • 图文并举,描述上述功能的具体实现

    • 实现数据存储:连接本地数据库,新增数据实体,分析 Excel 表格数据,写入数据库

    • 实现数据更新:模拟请求,更新数据库数据,掌握使用 Python 实现本地数据库的更新

    • 实现数据写回:掌握如何在 Python 中实现数据库记录的查询(查询全表)

  • 操作手册

    • 连个可视化页面都没有,何谈操作手册?
    • 主要是给开发者练手用的,本来就不是一个完整的应用
  • 学习价值和应用场景

    • 做这个大作业的好处有很多,以下是一些可能的学习价值和应用场景:

      1. 学习价值方面:
        • 提高编程能力:通过完成这个大作业,你可以锻炼自己的编程技巧和解决问题的能力。你将学习如何设计和实现一个完整的应用程序,并处理各种挑战和错误。
        • 加深对Python的理解:这个大作业将帮助你更深入地理解Python语言的各个方面,包括语法、数据类型、函数、类等。你将有机会应用这些知识来解决实际问题。
        • 掌握数据处理和分析技能:通过使用Excel表格和数据库进行数据处理和分析,你将学习如何处理大量的数据,并进行统计和可视化展示。这对于数据分析和决策支持方面的工作非常有用。
        • 培养团队合作能力:如果你选择与同学一起完成这个大作业,你将学会与他人合作、分工协作,共同解决问题。这有助于培养你的团队合作能力和沟通能力。
      2. 具体应用场景:
        • 数据存储和管理:这个大作业可以帮助你学习如何使用Python连接数据库,并将数据存储到数据库中。这对于需要管理大量数据的应用程序非常有用,例如电子商务网站、社交媒体平台等。
        • 数据分析和报告生成:通过使用Excel表格和数据库进行数据分析,你可以生成各种报告和图表,用于业务分析和决策支持。这对于企业、研究机构和政府部门都非常有用。
        • 数据更新和维护:这个大作业可以帮助你学习如何使用Python更新数据库中的数据记录。这对于需要频繁更新和维护数据的应用程序非常重要,例如客户关系管理系统、库存管理系统等。
        • 自动化数据处理流程:通过编写Python脚本来自动执行数据处理任务,你可以节省时间和精力,并提高工作效率。这对于需要处理大量重复性任务的工作非常有帮助,例如数据清洗、数据转换等。
      • 总之,这个大作业不仅能够提升你的编程能力和数据处理技能,还能为你未来的学习和职业发展打下坚实的基础。无论是在学术研究、商业应用还是工程开发领域,掌握这些技能都将使你更具竞争力。

开发日志

2023/11/25

  • 今天开始着手开发这个大作业了,,祝我一切顺利
  • 简单回忆学习了下如何在不同包之间实现函数调用,感觉还是先实现功能吧,把框架搭好,按步骤一步一步慢慢实现(2023/11/25早)

2023/11/26

  • 今早完成了数据库连接、查询数据库记录并用实体类接收、分析生成图表:

image-20231126115535582

数据处理

Python 处理 Excel 表格

🔥 推荐阅读:

Python操作Excel教程(全网最全,只看这一篇就够)_python excel-CSDN博客

如何使用python处理excel表格 - 知乎 (zhihu.com)

代码展示

  • 今天中午第一次尝试使用 Python 处理 Excel 表格,找到了一段简短的实例代码: (2023/11/06晚)
1
2
3
4
5
6
7
8
# 导入 pandas
import pandas as pd

# 读取Excel文件
df = pd.read_excel('职工表.xlsx')

# 打印数据框的内容
print(df)
  • 直接运行,结果如下:

image-20231106134654086

报错处理

  • 本来直接安装下载缺失的第三方库后,直接运行代码就可以了,但是一直不生效(这个报错没截到图片(2023/11/06晚)
1
pip install pandas
1
pip install openpyxl
  • 它一直提示 “缺失第三方库 openpyxl”,但是我已经安装了

  • 通过网络搜索询问 AI 等方式,我先后尝试了多种方法,均无果:

    • 查看 openpyxl 是否在当前环境中安装:
    1
    pip show openpyxl
    • 展示已安装的 第三方库
    1
    pip list
  • 我清楚这是编译器卡了的缘故,重启 Pycharm 也没效果
  • 狗血的是,我在代码中显示引入了 openpyxl 库,结果就成功运行了(这段删了也没事,主要是右键编译器自动导入成功了

1
2
3
4
5
6
7
8
import openpyxl
import pandas as pd

# 读取Excel文件
df = pd.read_excel('职工表.xlsx')

# 打印数据框的内容
print(df)
  • 看来我主动在终端安装下载第三方库不管用啊,还得显示导入 (2023/11/06晚)

openpyxl 处理数据 demo

  • 示例代码如下:

实现了对 Excel 表格的读取、数据新增、删除、、修改等功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def openpyxl_demo():
# 加载工作簿
wb = load_workbook(filename='职工表.xlsx')

# 获取工作表
ws = wb.active

# 获取行和列的数量
max_row = ws.max_row
max_col = ws.max_column

# 遍历工作表中的每一个单元格,并打印其值
for row in range(1, max_row):
for col in range(1, max_col):
cell = ws.cell(row=row, column=col)
print(cell.value, end=' ')
print('\n')

ws.append([8, "邓几把", 24, "流氓"])
ws.append([8, "邓几把", 24, "流氓"])
ws.append([8, "邓几把", 24, "流氓"])

wb.save('职工表.xlsx')
  • 注:操作对应 Excel 表时,确保文件未打开,否则会出现 无权限修改该文件 的报错:(2023/11/07午)

image-20231107134830545

Python 连接数据库

  • 作为 Python 大作业的一部分,今晚测试一下使用 Python 连接本地数据库的 demo 代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import mysql.connector

# 创建数据库连接
cnx = mysql.connector.connect(
host="localhost", # 数据库主机地址
port="3306", # 数据库端口号
user="****", # 数据库用户名
password="****", # 数据库密码
database="****" # 数据库名称
)

cursor = cnx.cursor()

# 执行查询语句
cursor.execute("SELECT * FROM article")

# 获取查询结果
results = cursor.fetchall()

# 处理查询结果
for row in results:
print(row)
  • 执行结果如下,成功执行 SQL,查询到数据库中的数据:(2023/11/15晚)

image-20231115220719115

人脸识别

  • 这个挺有意思的,记录一下(2023/11/25午)

image-20231125144542784

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 导入必要的包
import cv2

# 读入要检测的图像
# face_image = cv2.imread("pic1.JPG")
face_image = cv2.imread("pic2.JPG")

# 加载人脸检测器
face_model = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

# 人脸检测
faces = face_model.detectMultiScale(face_image, 1.3)
print(faces)

# 绘制矩形框
for (x, y, w, h) in faces:
cv2.rectangle(face_image, (x, y), (x + w, y + h), (0, 255, 0), 2)

# 显示图像
cv2.imshow("detection result", face_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

开发个人工具包

  • 上午突然冒出来的想法,原本打算随便操作数据库,作数据查询、插入、修改等,再使用第三方库做做可视化分析,生成个图表啥的
  • 但是突然觉着太 low 了,最主要的还是没啥实际用途,权当巩固复习 Python 基础语法了
  • 那我干脆一不做二不休,开发一个:个人工具包,包含日常学习办公常用的功能,目前的想法:(2023/11/27午)

image-20231127131901683

基本了解到了如何开发一个 MC mod 了,有机会试试 (2023/11/13午)

Alex3236博客-Alex3236专栏文章-文集-哔哩哔哩视频 (bilibili.com)

Python 程序打包为 .exe

🔥 推荐阅读:【5分钟搞定】如何将py打包成exe可执行文件 - 掘金 (juejin.cn)

  • 安装 pyinstaller(2023/11/27早)
1
pip install pyinstaller
  • 执行打包:
1
pyinstaller ***.py
  • 妈的,安装了半小时 pyinstaller,修改了 python 路径,咯噔了半天,又可以了:

image-20231127125712770

  • 打包成功!我们尝试执行一下:

image-20231127130742344

  • 在 dist 目录下找到可执行文件 .exe双击执行

image-20231127130849980

image-20231127130933730

  • 注意,我们只需要将 dist 目录下的文件压缩打包后,就可以发给好朋友使用了

Python 实现界面开发

Python 程序员过中秋Python+pygame 制作拼图中秋赏月小游戏(附源码:5源码) - 掘金 (juejin.cn)

  • 详细的练习代码已经上传至 Gitee 了,最后照着示例,实现了一个简单的笔记本案例:
  • 如下:

image-20231127202157653

  • 成功打包该笔记本软件,马上扔给好兄弟玩玩(2023/11/27晚)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
欢迎使用 notepad 笔记本软件
作者: @Memory

*
( `
)\))( ( ) ( (
((_)()\ ))\ ( ( )( )\ )
(_()((_)/((_) )\ ' )\(()\(()/(
| \/ (_)) _((_)) ((_)((_))(_))
| |\/| / -_) ' \() _ \ '_| || |
|_| |_\___|_|_|_|\___/_| \_, |
|__/

您可以用它来编辑文本文件,也可以快捷地统计文本字数
新的功能还在逐步开发完善中,敬请期待~
您不需要理会这个窗口,祝您使用愉快

image-20231127211054419

新建窗口

  • 点击按钮,新建窗口(2023/11/28午)
1
2
3
4
5
6
7
8
9
10
11
# 定义一个函数,用于创建新的窗口
def create_new_window(root):
new_window = tk.Toplevel(root) # 创建新的窗口实例
new_window.title("新窗口") # 设置新窗口的标题
new_window.geometry("400x300+500+250")
label = tk.Label(new_window, text="这是新窗口", font=("Arial", 18)) # 在新窗口中添加标签
label.pack(pady=50) # 添加标签并设置垂直空间

# 在新窗口中添加标签
label = tk.Label(new_window, text="这是新窗口", font=("Arial", 18))
label.pack(pady=50)
1
2
3
# 在下层Frame中添加按钮
button = tk.Button(bottom_frame, text="开始使用", font=("Arial", 10), command=lambda: create_new_window(root))
button.pack()
  • Button 按钮绑定了一个点击事件 create_new_window,创建了一个新窗口

一个用法震撼我两遍

  • 注意到上面 Button 按钮绑定事件的方法没:
1
button = tk.Button(bottom_frame, text="开始使用", font=("Arial", 10), command=lambda: create_new_window(root))
  • 我们实现窗口上实时显示时间:
1
2
3
4
5
6
def update_time(label, window):
# 获取当前时间并格式化为字符串
current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 更新Label的文本为当前时间
label['text'] = current_time
window.after(1000, lambda: update_time(label, window)) # 开始时调用一次update_time函数,之后每秒更新一次时间
1
2
3
4
5
6
7
8
9
10
11
12
new_window = tk.Toplevel(root)  # 创建新的窗口实例
new_window.title("新窗口") # 设置新窗口的标题
new_window.geometry("400x300+500+250")
label = tk.Label(new_window, text="这是新窗口", font=("Arial", 18)) # 在新窗口中添加标签
label.pack(pady=50) # 添加标签并设置垂直空间

# 在新窗口中添加标签
label = tk.Label(new_window, text="这是新窗口", font=("Arial", 18))
label.pack(pady=50)

# 创建定时器,每隔1秒更新时间
new_window.after(1000, lambda: update_time(label, new_window)) # 开始时调用一次update_time函数,之后每秒更新一次时间
  • 效果如下:

image-20231128120751320

  • 对于该函数用法的解释(文心一言):(2023/11/28午)
1
root.after(1000, update_time(label, root))

你在这里直接调用了update_time(label, root),这样的话,update_time函数会在主线程中立即执行,而不是在定时器触发后执行。因此,update_time函数只会被调用一次,而不是每秒一次。

正确的方法是将函数本身传递给after方法,而不是函数的执行结果。你可以使用一个lambda函数来解决这个问题,如下所示:

1
root.after(1000, lambda: update_time(label, root))

lambda: update_time(label, root) 是一个匿名函数,它会在指定的时间后执行update_time(label, root),这样就可以保证update_time`函数每秒被调用一次了。

点击执行一个可执行文件

  • 为什么会有这样的想法呢?我打算开发一个小游戏模块,这个模块有哪些小游戏呢?展示一下目前的想法吧:

拼图游戏、黄金矿工、贪吃蛇、扫雷、羊了个羊

  • 黄金矿工的话,之前用 Java 实现过,整体做工比较精良,我可不想再用 Python 写一遍
  • 目前的想法就是在该工具包下内嵌一个黄金矿工小游戏,直接点击按钮就能执行(2023/11/28午)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def execute_executable():
# 指定可执行文件的完整路径
executable_path = "E:\Singal_Games\GoldMiner\GoldMiner.exe"
# executable_path = "D:\Project\python\程序打包\界面编程\GoldMiner\GoldMiner.exe"

# 执行可执行文件
process = subprocess.Popen(executable_path)


# 创建主窗口
root = tk.Tk()
root.title("执行可执行文件")

# 创建按钮并绑定事件
button = tk.Button(root, text="执行可执行文件", command=execute_executable)
button.pack(pady=20)

# 启动主窗口的事件循环
root.mainloop()

英汉互译

  • 简单的英汉互译测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from translate import Translator

text = '''
在Python编程领域,我们常常需要处理多语言文本,而翻译工具就成为了必不可少的利器。今天,我们将介绍一个强大的Python翻译库——translate。
'''

text2 = '''
go and fuck yourself
'''

translator = Translator(from_lang="zh", to_lang="en")
translator2 = Translator(from_lang="en", to_lang="zh")

translated_text = translator.translate(text)
translated_text2 = translator2.translate(text2)

print(translated_text)
print(translated_text2)

image-20231128144220210

  • 成功实现英汉互译小程序,成功打包,发给好兄弟试试:(2023/11/28午)

image-20231128144602464

开发者文档

  • 这么新颖的项目,怎么能没有开发者文档呢?只是让用户使用一个个干煸的窗口怎么行?

  • 正好最近在研究 Memory API接口开放平台的开发者文档的编写 + 部署,历时三天可算搞完了

  • Memory API接口开放平台、Vuepress 文档站点、Python 小程序,三个方向看起来八竿子打不到一块,今天让我融会贯通了:

    • Python 小程序提供的功能,也是Memory API接口开放平台所需要的,这极大地拓展了我对开放接口的想法

    • Memory API 接口开放平台需要一个开发者文档,用以详细介绍如何调用接口,包含接口信息、返回值、错误码等。那 Python小程序也很需要这些

    • VuePress 搭建文档站点并成功部署,这是很久以前的想法了,历时五个月,今天终于实现了

      Mem-API 开发者文档 (gitee.io)

    • 他奶奶的我可太兴奋了,待会儿就把这个过程记录下来(2023/11/28午)

  • 那我们实现一个简单的网页跳转:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def open_docs():
webbrowser.open('https://deng-2022.gitee.io/vuepress-docs3/')

# 创建一个新窗口
window = tk.Tk()
window.title("开发者文档跳转按钮")

# 创建一个标签
label = tk.Label(window, text="点击下面的按钮跳转到开发者文档!")
label.pack()

# 创建一个跳转按钮
button = tk.Button(window, text="跳转到开发者文档", command=open_docs)
button.pack()

# 运行主循环
window.mainloop()
  • 妈的,创建圆形按钮失败,tkinter 不支持

讯飞星火 API接口开放平台

推荐阅读:使用讯飞星火API,轻松打造个性化AI聊天机器人程序-物联沃-IOTWORD物联网

  • 看下这篇文章就够了,也打包成功了,运行效果如下:(2023/11/28晚)

image-20231128235619438

  • 这样写的话,打包后运行 .exe 文件,窗口闪退了,尝试了一晚上还是没解决:

控制台-讯飞开放平台 (xfyun.cn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# -*- coding:utf-8 -*-
import os

os.environ['SPARK_APP_ID'] = "*****" # 星火大模型的APPID
os.environ['SPARK_API_SECRET'] = "******" # 星火大模型的APISecret
os.environ['SPARK_API_KEY'] = "******" # 星火大模型的APIKey
os.environ['SPARK_API_MODEL'] = "v2.0"
os.environ['SPARK_CHAT_MAX_TOKENS'] = "4096"
os.environ['SPARK_CHAT_TEMPERATURE'] = "0.5"
os.environ['SPARK_CHAT_TOP_K'] = "4"

from sparkapi.core.api import SparkAPI
from sparkapi.core.config import SparkConfig

if __name__ == '__main__':
Input = input("开始对话吧!(单击Enter键开始)")
config = SparkConfig().model_dump()
api = SparkAPI(**config)

api.chat()

res = api.get_completion('hello')
print(''.join(res))

messages = [
{'role': 'user', 'content': 'hello'},
{'role': 'assistant', 'content': 'Hello! How can I assist'},
{'role': 'user', 'content': 'write me a Python script of BubbleSort'},
]
res = api.get_completion_from_messages(messages)
print(''.join(res))

图片爬取

  • 实现了一个简单的图片爬取软件,一键获取100+张图片!
1

  • 妈的,好像被限制了还是咋的,本来打包就一直失败,现在本地都不能爬取了

天气预报

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root = tk.Tk()  # 创建主窗口
root.title("Weather Information") # 设置窗口标题
root.geometry("600x300") # 设置窗口大小(宽x高)
root.resizable(False, False) # 禁止调整窗口大小

text_area = scrolledtext.ScrolledText(root, height=10) # 创建滚动文本框,用于显示天气信息
text_area.pack(fill=tk.BOTH, expand=True) # 添加滚动文本框到主窗口,并自动调整大小以适应窗口大小变化

# 创建关键词标签和输入框
keyword_label = tk.Label(root, text="城市名:")
keyword_label.pack(pady=10)
keyword_entry = tk.Entry(root)
keyword_entry.pack(pady=10)

# 创建按钮并设置事件处理程序
button1 = tk.Button(root, text="天气查询", font=("Arial", 12),
command=lambda: show_weather_info(keyword_entry.get()))
button1.pack(side=tk.TOP, padx=10)

root.mainloop() # 启动主窗口的消息循环,显示窗口并等待用户操作
  • 这里的 keyword_entry.get() 拿一个变量 city 接收的话,会报 Null 的错误,所以不用参数接收,直接传参了
  • 效果如下:(2023/12/01午)

image-20231201142459038

image-20231201145026652

封装方法为一个类

  • 没错,面向对象编程的核心就是将具体功能封装到类中,这样能更好地组织和管理代码,同时也方便其他开发人员使用和共享这些功能。
  • 这里以简易笔记本的实现为例,源代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# 一个简单的笔记本
import tkinter as tk
import tkinter.filedialog as filedialog


# 打开文件
def open_file():
file_path = filedialog.askopenfilename(
filetypes=[
("Text files", "*.txt"),
("All files", "*.*")
]
)

if not file_path:
window.title("无标题 - 记事本")
return

txt_edit.delete("1.0", tk.END)

with open(
file_path,
mode="r",
encoding="utf-8") as input_file:
txt_edit.insert(
tk.END,
input_file.read()
)
window.title(f"{file_path} - 记事本")


# 另存为
def save_as_file():
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[
("Text files", "*.txt"),
("All files", "*.*")
]
)
if not file_path:
return
with open(
file_path,
mode="w",
encoding="utf-8") as output_file:
output_file.write(txt_edit.get("1.0", tk.END))
window.title(f"{file_path} - 记事本")


# 统计字数
def count_chars_in_text():
text = txt_edit.get("1.0", tk.END) # 获取 Text 组件中的所有文本
char_count = len(text) - 1 # 计算文本的长度
print(f"字符数: {char_count}")
var_lbl.set(f"字符数: {char_count}") # 显示文本


window = tk.Tk()
window.title("记事本")

window.rowconfigure(
0,
minsize=100,
weight=1
)
window.columnconfigure(
1,
minsize=100,
weight=1
)

# 左侧操作区
btn_frame = tk.Frame(
master=window,
bd=2
)
btn_frame.grid(
row=0,
column=0,
sticky="ns"
)

# 打开文本文件
btn_open = tk.Button(
master=btn_frame,
text="打开",
command=open_file
)
btn_open.grid(
row=0,
column=0,
sticky="ew",
padx=5,
pady=5
)

# 另存为
btn_save_as = tk.Button(
master=btn_frame,
text="另存",
command=save_as_file
)
btn_save_as.grid(
row=1,
column=0,
sticky="ew",
)

# 统计字数
btn_count = tk.Button(
master=window,
text="统计字数",
command=count_chars_in_text
)
btn_count.grid(row=1, column=1, sticky="ew")

# 字符数
var_lbl = tk.StringVar()
var_lbl.set("") # 初始化一个空字符串

lbl = tk.Label(
master=btn_count,
textvariable=var_lbl
)

lbl.grid(row=1, column=1, sticky="ew")

# 读写区
txt_edit = tk.Text(master=window)
txt_edit.grid(
row=0,
column=1,
sticky="nsew"
)

# 当文本内容改变时,调用count_chars_in_text函数
txt_edit.config(font=("Arial", 12), undo=True, wrap="word")
txt_edit.bind("<<Modified>>", count_chars_in_text) # 当文本内容改变时触发事件并调用count_chars_in_text函数


print("欢迎使用 notepad 笔记本软件")
print("作者: @Memory")
print("""
*
( `
)\))( ( ) ( (
((_)()\ ))\ ( ( )( )\ )
(_()((_)/((_) )\ ' )\(()\(()/(
| \/ (_)) _((_)) ((_)((_))(_))
| |\/| / -_) ' \() _ \ '_| || |
|_| |_\___|_|_|_|\___/_| \_, |
|__/
""")
print("您可以用它来编辑文本文件,也可以快捷地统计文本字数")
print("新的功能还在逐步开发完善中,敬请期待~")
print("您不需要理会这个窗口,祝您使用愉快")

window.mainloop()
  • 封装后:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import tkinter as tk
import tkinter.filedialog as filedialog


class MemoryNotepad:
def __init__(self, window):
self.window = window
self.window.title("记事本")
# 读写区
self.txt_edit = tk.Text(master=window)
self.txt_edit.grid(
row=0,
column=1,
sticky="nsew"
)
self.btn_frame = tk.Frame(window, bd=2)
self.btn_frame.grid(row=0, column=0, sticky="ns")
self.create_buttons()
# 字符数
self.var_lbl = tk.StringVar()
self.var_lbl.set("") # 初始化一个空字符串

def create_buttons(self):
btn_open = tk.Button(master=self.btn_frame, text="打开", command=self.open_file)
btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
btn_save_as = tk.Button(master=self.btn_frame, text="另存", command=self.save_as_file)
btn_save_as.grid(row=1, column=0, sticky="ew")

def open_file(self):
file_path = filedialog.askopenfilename(
filetypes=[
("Text files", "*.txt"),
("All files", "*.*")
]
)

if not file_path:
window.title("无标题 - 记事本")
return

self.txt_edit.delete("1.0", tk.END)

with open(
file_path,
mode="r",
encoding="utf-8") as input_file:
self.txt_edit.insert(
tk.END,
input_file.read()
)
window.title(f"{file_path} - 记事本")

def save_as_file(self):
file_path = filedialog.asksaveasfilename(defaultextension=".txt",
filetypes=[("Text files", "*.txt"), ("All files", "*.*")])
if not file_path:
return
with open(file_path, mode="w", encoding="utf-8") as output_file:
output_file.write(self.txt_edit.get("1.0", tk.END))
self.window.title(f"{file_path} - 记事本")

# 统计字数
def count_chars_in_text(self):
text = self.txt_edit.get("1.0", tk.END) # 获取 Text 组件中的所有文本
char_count = len(text) - 1 # 计算文本的长度
print(f"字符数: {char_count}")
self.var_lbl.set(f"字符数: {char_count}") # 显示文本

def init(self):
self.window.rowconfigure(
0,
minsize=100,
weight=1
)
self.window.columnconfigure(
1,
minsize=100,
weight=1
)

# 左侧操作区
btn_frame = tk.Frame(
master=self.window,
bd=2
)
btn_frame.grid(
row=0,
column=0,
sticky="ns"
)

# 打开文本文件
btn_open = tk.Button(
master=btn_frame,
text="打开",
command=self.open_file
)
btn_open.grid(
row=0,
column=0,
sticky="ew",
padx=5,
pady=5
)

# 另存为
btn_save_as = tk.Button(
master=btn_frame,
text="另存",
command=self.save_as_file
)
btn_save_as.grid(
row=1,
column=0,
sticky="ew",
)

# 统计字数
btn_count = tk.Button(
master=self.window,
text="统计字数",
command=self.count_chars_in_text
)
btn_count.grid(row=1, column=1, sticky="ew")

lbl = tk.Label(
master=btn_count,
textvariable=self.var_lbl
)

lbl.grid(row=1, column=1, sticky="ew")

# 当文本内容改变时,调用count_chars_in_text函数
self.txt_edit.config(font=("Arial", 12), undo=True, wrap="word")
self.txt_edit.bind("<<Modified>>", self.count_chars_in_text) # 当文本内容改变时触发事件并调用count_chars_in_text函数

print("欢迎使用 Memory Notepad 笔记本软件")
print("作者: @Memory")
print("""
*
( `
)\))( ( ) ( (
((_)()\ ))\ ( ( )( )\ )
(_()((_)/((_) )\ ' )\(()\(()/(
| \/ (_)) _((_)) ((_)((_))(_))
| |\/| / -_) ' \() _ \ '_| || |
|_| |_\___|_|_|_|\___/_| \_, |
|__/
""")
print("您可以用它来编辑文本文件,也可以快捷地统计文本字数")
print("新的功能还在逐步开发完善中,敬请期待~")
print("您不需要理会这个窗口,祝您使用愉快")

self.window.mainloop()


if __name__ == '__main__':
window = tk.Tk()
memoryNotepad = MemoryNotepad(window)
memoryNotepad.init()

  • 整个改造过程中,最重要的是把需要初始化的资源 / 变量都放在构造函数中完成,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def __init__(self, window):
self.window = window
self.window.title("记事本")
# 读写区
self.txt_edit = tk.Text(master=window)
self.txt_edit.grid(
row=0,
column=1,
sticky="nsew"
)
self.btn_frame = tk.Frame(window, bd=2)
self.btn_frame.grid(row=0, column=0, sticky="ns")
self.create_buttons()
# 字符数
self.var_lbl = tk.StringVar()
self.var_lbl.set("") # 初始化一个空字符串
  • 虽然之前学过 Python 基础语法,现在写还是有些生疏,慢慢习惯吧,还是很简单的(2023/12/04晚)
  • 遗留下了问题:

image-20231204222014864

  • 如上,在主函数引入 MemoryNotepad 类,点击 “开始使用”后,直接跳出 “记事本” 窗口,明天再研究吧

  • 继续(2023/12/08午)

  • 怪了,明明写了有参构造器,new 一个对象还报错:

image-20231208165645504

1
2
3
4
5
6
class MemoryTranslator:
def __int__(self, root):
self.root = root
# Create the main window with two text boxes and a button
self.root.title("Translation App")
self.root.geometry("600x300")
  • 哦他奶奶的, __init__ 写成 __int__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
self.window = tk.Tk()
self.window.title("记事本")

# 创建操作区(左层)
self.operate_frame = tk.Frame(self.window)
# top_frame.grid()
self.operate_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

# 创建读写区(右层)
self.io_frame = tk.Frame(self.window)
self.io_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

# 创建计数区(下层)
self.count_frame = tk.Frame(self.window)
self.count_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

# 文字编辑
self.txt_edit = tk.Text(master=self.io_frame)
self.txt_edit.pack(side=tk.RIGHT)

self.window.rowconfigure(0, minsize=100, weight=1)
self.window.columnconfigure(1, minsize=100, weight=1)

# 打开按钮
btn_open = tk.Button(master=self.operate_frame, text="打开", command=self.open_file)
btn_open.pack()

# 另存为按钮
btn_save_as = tk.Button(master=self.operate_frame, text="另存", command=self.save_as_file)
btn_save_as.pack()

# 统计字数按钮
btn_count = tk.Button(master=self.window, text="统计字数", command=self.count_chars_in_text)
btn_count.pack()

# 字符数标签
self.var_lbl = tk.StringVar()
self.var_lbl.set("") # 初始化一个空字符串
lbl = tk.Label(master=btn_count, textvariable=self.var_lbl)
lbl.pack()

# 当文本内容改变时,调用count_chars_in_text函数
self.txt_edit.config(font=("Arial", 12), undo=True, wrap="word")
self.txt_edit.bind("<<Modified>>", self.count_chars_in_text) # 当文本内容改变时触发事件并调用count_chars_in_text函数

一个折磨人的BUG

  • 就很简单的实现:按钮绑定函数,点击按钮,执行该函数(2023/12/08晚)
  • 现在的问题很诡异,实例化一个对象,直接创建窗口;点击按钮,不触发生成窗口函数
  • 看看这段奇怪的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MemoryNotepad:
def __init__(self):
self.window1 = tk.Tk()
self.window1.title("记事本")
# self.window.mainloop()


class MemoryTools:
def __init__(self):
self.window2 = tk.Tk()
self.window2.title("MemoryTools")

def create_window(self):
mn = MemoryNotepad()
self.window2.mainloop()


if __name__ == '__main__':
mt = MemoryTools()
mt.create_window()

image-20231208211539507

BUG 解决

  • 整整一周过去了,这个烦人的 BUG 终于解决了
  • 这其实是 tkinter 的渲染问题,在类 MemoryNotepad__init__ 函数中写如下代码:
1
2
3
4
class MemoryNotepad:
def __init__(self):
self.window = tk.Tk()
self.window.title("MemoryNotepad")
  • 在类 MemoryTools__init__ 函数中写如下代码:
1
2
3
4
5
6
7
8
class MemoryTools:
def __init__(self):
self.window = tk.Tk()
self.window.title("MemoryTools")

def create_window(self):
mn = MemoryNotepad()
self.window.mainloop()
  • 接着实例化一个 MemoryTools 对象,并调用 create_window 方法:
1
2
3
if __name__ == '__main__':
mt = MemoryTools()
mt.create_window()
  • 可以看到,在 create_window 方法中,实例化了一个 MemoryNotepad 对象,并执行生成了一个窗口
  • titleMemoryTools 的窗口被被执行生成是很正确的,但是 titleMemoryNotepad 的窗口也被执行生成了:

image-20231210114439198

  • 这就非常奇怪了,明明没有显示地执行生成titleMemoryNotepad 的窗口,它却自动生成窗口了,这个机制很奇怪
  • 引发了很棘手的问题:实现点击 button 按钮后,执行生成新窗口实现不了,因为实例化新窗口之后,就自动生成新窗口了
  • 几经折腾,总算找到了解决的办法:
  • 问题的根源在于我尝试在 __init__ 函数中,初始化一个窗口对象 window,只要把 window 的初始化,推迟到初始化函数中就行了,无需在 __init__ 中初始化,因为这个机制只会影响到 __init__ 内的 window 窗口对象的生成
  • 于是改写后的 MemoryNotepad 如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MemoryNotepad:
def __init__(self):
self.window = None
# 左侧操作区
self.operate_frame = None
# 打开按钮
self.btn_open = None
# 另存为按钮
self.btn_save_as = None
# 统计字数按钮
self.btn_count = None
# 文字编辑区
self.txt_edit = None
# 初始化字符数
self.var_lbl = None
# 字符数标签
self.lbl = None
1
2
3
4
5
6
7
8
9
10
  def init(self):
self.window = tk.Tk()
self.window.title("记事本")
# 左侧操作区
self.operate_frame = tk.Frame(master=self.window, bd=2)
self.operate_frame.grid(row=0, column=0, sticky="ns")
# 窗口大小不可变
self.window.rowconfigure(0, minsize=100, weight=1)
self.window.columnconfigure(1, minsize=100, weight=1)
...............................
  • 成功实现了点击 简易笔记本 按钮后,弹出窗口:

image-20231210114706146

整合所有功能

  • 整合 AI 对话模块时,出错了,由于 python 环境不同,同样的代码还运行不起来,改下 MemoryToolspython 环境变量即可:

image-20231210205845871

  • 改完记得要重启啊,不重启没效果的,运行成功:

image-20231210205939054

  • 终于把 AIChat 类改装好了,改装过程中,最需要注意的就是成员函数的参数形式了:

如果成员函数内部需要引用到成员变量,就把该成员函数的第一个参数指定为 self

1
2
3
4
5
6
7
8
9
10
11
def main(self, appid, api_key, api_secret, Spark_url, domain, question):
# print("星火:")
wsParam = Ws_Param(appid, api_key, api_secret, Spark_url)
# websocket.enableTrace(False)
wsUrl = wsParam.create_url()
ws = websocket.WebSocketApp(wsUrl, on_message=self.on_message, on_error=self.on_error, on_close=self.on_close,
on_open=self.on_open)
ws.appid = appid
ws.question = question
ws.domain = domain
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})

反之,就在成员函数定义上方添加这样的注解:@staticmethod

1
2
3
4
5
6
7
8
@staticmethod
def getlength(text):
length = 0
for content in text:
temp = content["content"]
leng = len(temp)
length += leng
return length

开发者文档插入图片

  • 上传至 Gitee 图床,在开发者文档中不显示,只好是放进文件夹中了,像这样:(2023/12/14晚)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
## 效果演示

- 打开软件

<img :src="$withBase('/img/article/image-20231214000036004.png')" class="no-zoom" style="margin: 10px;">

- 你可以点击 "打开" 按钮,选取并打开本地的文本文件

<img :src="$withBase('/img/article/image-20231214001301049.png')" class="no-zoom" style="margin: 10px;">

<img :src="$withBase('/img/article/image-20231214001336235.png')" class="no-zoom" style="margin: 10px;">

<img :src="$withBase('/img/article/image-20231214001417352.png')" class="no-zoom" style="margin: 10px;">

- 点击 "另存" 按钮,可将右侧文本框中的文本内容保存至本地

<img :src="$withBase('/img/article/image-20231214001732669.png')" class="no-zoom" style="margin: 10px;">

<img :src="$withBase('/img/article/image-20231214001743210.png')" class="no-zoom" style="margin: 10px;">

- 附带功能:快捷统计文本字数

<img :src="$withBase('/img/article/image-20231214002003689.png')" class="no-zoom" style="margin: 10px;">

image-20231214213905452

窗口填充图片

  • 在窗口中填充一张图片作为背景:
  • 导入相关库:
1
2
import tkinter as tk
from PIL import Image, ImageTk
  • 准备图片,并确保导入路径正确:
1
2
3
4
5
6
7
8
9
10
11
12
13
def __init__(self):
# 创建主窗口
self.window = tk.Tk()
self.window.geometry("300x225")
self.window.title("开始菜单")

# 加载背景图片
image = Image.open("D:\\Project\\星球项目\\MemoryTools\\imgs\\mountain2.jpg")
bg_image = ImageTk.PhotoImage(image)

# 创建标签并设置背景图片
bg_label = tk.Label(self.window, image=bg_image)
bg_label.pack(fill=tk.BOTH, expand=True)

image-20231218082934745

  • 然而这样是会报错的:

image-20231218083051347

  • 原因如下:

图片格式不受支持:TkinterPhotoImage 类只支持 GIFPPM/PGM 格式的图片。对于 JPEGPNG 等其他格式的图片,你需要使用 PIL(Python Imaging Library)库来进行处理。你可以尝试将你的 JPEG 图片转换为 GIF PPM/PGM 格式,或者你可以使用 PIL 库来加载 JPEG 图片。

  • 我们尝试使用 ImageImageTk 解决问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import tkinter as tk
from PIL import Image, ImageTk

window = tk.Tk()
# window.geometry("300x225")
window.title("开始菜单")

# 加载背景图片
image = Image.open("D:\\Project\\星球项目\\MemoryTools\\imgs\\mountain2.jpg")
bg_image = ImageTk.PhotoImage(image)

# 创建标签并设置背景图片
bg_label = tk.Label(window, image=bg_image)
bg_label.pack(fill=tk.BOTH, expand=True)

window.mainloop()
  • 设置背景图片成功,需要注意设置原背景图的尺寸(2023/12/18早)

image-20231218083431150

  • 尝试改造 MemoryTools 的开始菜单页面:
  • 填充背景图片,并适当调整窗口大小图片大小(2023/12/18早)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def __init__(self):
# 创建主窗口
self.window = tk.Tk()
self.window.geometry("620x350")
self.window.title("Memory Tools")
# 设置窗口大小禁止改变
self.window.resizable(False, False)

# 加载背景图片
self.image = Image.open("D:\\Project\\星球项目\\MemoryTools\\imgs\\mountain2.jpg")
# 调整图片尺寸
self.image = self.image.resize((620, 350)) # 这里的100是目标宽度和高度,你可以根据需要修改
# 转 JEPG 格式为 PPM/PGM 格式
self.bg_image = ImageTk.PhotoImage(self.image)
  • 添加一个标签组件 label 和一个按钮组件 button ,调整它们的显示方式,使其自由浮动于该图片之上
1
2
3
4
5
# 在上层Frame中添加一个标签
self.label = tk.Label(self.window, text="欢迎使用 Memery Tools!", font=("Arial", 16))

# 使用place()方法将标签放置在窗口的左上角,并设置宽度和高度为窗口的相对大小
self.label.place(x=200, y=60)
1
2
3
4
5
6
# 在下层Frame中添加按钮
self.button = tk.Button(self.window, text="开始使用", font=("Arial", 12), width=10, height=2,
command=lambda: self.create_menu_window(self.window))

# self.button.pack()
self.button.place(x=270, y=250) # 设置按钮的位置,这里的坐标是相对于窗口的左上角
  • 改造成功!效果如下:

image-20231218085414790

  • 上午我们实现了开始菜单页面背景图填充,晚上我们继续完成工具栏页面背景图填充

image-20231219000023787

项目打包后图片路径丢失

🍖 推荐阅读:

  • 我在项目中引用了图片:(2023/12/20晚)
1
2
3
4
5
6
7
8
9
10
# 加载背景图片
self.image = Image.open("imgs/mountain2.jpg")
# 调整图片尺寸
self.image = self.image.resize((620, 350)) # 这里的100是目标宽度和高度,你可以根据需要修改
# 转 JEPG 格式为 PPM/PGM 格式
self.bg_image = ImageTk.PhotoImage(self.image)

# 创建标签并设置背景图片
self.bg_label = tk.Label(self.window, image=self.bg_image)
self.bg_label.pack(fill=tk.BOTH, expand=True)
  • 这里的图片路径很关键,如果是绝对路径,pyinstaller 打包后,直接双击.exe文件会直接报错
  • 我只好按图片路径,在打包完成后,手动添加图片资源路径:

image-20231220135409789

点击按钮改变标签内容

  • 点击按钮,实现改变指定标签 lable 的内容
  • 这里实际的业务要求是标签根据文本框中的内容是否发生改变,记录此时的字符并绑定该值到标签 lable
  • 这是之前的实现方式:
1
2
3
4
5
6
7
8
9
10
# 统计字符数量
def count_chars_in_text(self):
# 获取 Text 组件中的所有文本
text = self.txt_edit.get("1.0", tk.END)
# 计算文本的长度
char_count = len(text) - 1

print(f"字符数: {char_count}")
# 显示文本
self.var_lbl.set(f"字符数: {char_count}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 统计字数按钮
self.btn_count = tk.Button(master=self.window, text="统计字数", command=self.count_chars_in_text)
self.btn_count.grid(row=1, column=1, sticky="ew")

# 初始化字符数
self.var_lbl = tk.StringVar()
# 初始化一个空字符串
self.var_lbl.set("")

# 字符数标签
self.lbl = tk.Label(master=self.btn_count, textvariable=self.var_lbl)
self.lbl.grid(row=1, column=1, sticky="ew")

# 监听文本内容改变
self.txt_edit.config(font=("Arial", 12), undo=True, wrap="word")
# 当文本内容改变时触发事件并调用count_chars_in_text函数
self.txt_edit.bind("<<Modified>>", self.count_chars_in_text)
  • 但是这里有个奇怪的问题:label 标签加上 textvariable 属性,在外部调用该 label 所在函数,label 标签不显示

  • 我使用了新的方案解决这个问题:不使用 textvariable 属性,使用简单的 text 属性解决这个问题:

1
2
3
4
5
6
7
8
9
10
11
12
# 统计字符数量
def count_chars_in_text(self):
# 获取 Text 组件中的所有文本
text = self.txt_edit.get("1.0", tk.END)
# 计算文本的长度
char_count = len(text) - 1

print(f"字符数: {char_count}")
# 修改字符串变量的值
self.var_lbl = f"字符数: {char_count}"
self.update_label() # 更新Label的文本为新的值
# self.var_lbl.set(f"字符数: {char_count}")
1
2
3
4
5
6
# 更新 Label 的文本的值
def update_label(self):
# 更新Label的文本为当前的字符串变量的值
self.lbl.config(text=self.var_lbl)
# 更新显示
self.lbl.update()
1
2
3
4
5
6
7
8
9
10
11
12
# 字符数标签
self.lbl = tk.Label(master=self.btn_count)
self.lbl.grid(row=1, column=1, sticky="ew")

# 创建一个可变的字符串变量
self.var_lbl = "初始文本"
self.update_label()

# 监听文本内容改变
self.txt_edit.config(font=("Arial", 12), undo=True, wrap="word")
# 当文本内容改变时触发事件并调用count_chars_in_text函数
self.txt_edit.bind("<<Modified>>", self.count_chars_in_text)
  • 不同的实现方法,完全一样的效果(2023/12/20晚)

日常报错

修改 pip 命令镜像源

1
pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple/

总结


探索Python爬虫的无限可能性:基础与应用
http://example.com/2023/05/21/探索 Python 爬虫的无限可能性:基础与应用/
作者
Memory
发布于
2023年5月21日
更新于
2023年12月20日
许可协议