0%

记一次python大作业

bro这么久不更新,不练题,是去做什么去了,原来是去做beyond python大作业去了,罢了罢了,来看看成果吧

第一题,百度图片爬虫,这个喷不了,只要在框架上改改就可以(因为懒,一直没写多线程)

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
import requests
import re
import os
from bs4 import BeautifulSoup
from PIL import Image,ImageFont,ImageDraw
def get_url(page,key_word):
original_url="https://image.baidu.com/search/acjson"
body={'tn':'resultjson_com',
'word':key_word,
'ie':'utf-8',
'fp':'result',
'fr':'',
'ala':'0',
'applid':'',
'pn':30*page,
'rn':30,
'nojc':0,
'gsm':hex(page*30)[2:],
'newReq':1,
}
# 按照上次搜狗的规律,不加header头强打,发现触发百度的反爬机制,于是得在这里补上一个header头,cookie直接F12抄下来就可以了,ua头网上一大堆,再次强打仍被反爬,查询资料后加上host
header={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
"Cookie":"BAIDUID_BFESS=E806634726C41BE45E14B222E8C48E10:FG=1;H_WISE_SIDS=62325_62865_62968_63034_63161;",
"Host":"image.baidu.com",
"Referer": "https://image.baidu.com/",
}
r=requests.get(url=original_url,params=body,headers=header)
soup=BeautifulSoup(r.text,"html.parser").prettify()
#用美丽汤来美化一下爬取下来的内容,看一下图片标题和url对应的键是什么,直接正则搜图片的标题和对应的url
title=r'"titleShow":\s*"((?:\\"|[^"])*)"'
picUrl=r'"objurl":\s*"((?:\\"|[^"])*)"'
#这里我也修改过了,我原先写的正则表达式(例如:itle=r'"titleShow":\s*"([^"]+)"')不够健壮
#后面试运行的时候发现还是不够健壮,有的广告链居然也用objurl,这里为了不发生这种错误,我还是直接进行正则过滤,只匹配url末尾是jpg或png的内容,失败了,换回原来的正则表达式,看来这里的广告比我想象的要顽强
pic_url=re.findall(picUrl,soup)
title1=re.findall(title,soup)
dict={k:v for k,v in zip(pic_url,title1)}
return dict
def shuiyin(path,text,x1,y1,font_path=None,font_size=36,opacity=128,margin=10):
img=Image.open(path).convert("RGBA")#按照手册上的,先转换为RGBA模式
txt=Image.new('RGBA',img.size,(255,255,255,0))#然后创建与原图同尺寸的透明画布,再初始化
draw=ImageDraw.Draw(txt)
if font_path and os.path.exists(font_path):
font=ImageFont.truetype(font_path,font_size)#这里给用户一个自定义字体的选择,要是不自定义的话就直接进入else的try语句执行,用默认字体库
else:
try:
font=ImageFont.truetype(r"C:\Windows\Fonts\msyh.ttc",font_size)#这里用支持中文的微软雅黑,不然我的姓名班级写不上去
except IOError:
font=ImageFont.load_default()
draw.text((x1,y1),text,font=font,fill=(255,0,0,opacity))#红色波长最长,就用红色好了,这里可以自己选择想要的水印的位置的x,y坐标
watermarked=Image.alpha_composite(img,txt)#将水印和原图合成,变为带水印的图
watermarked=watermarked.convert('RGB')#这里从rgba变为rgb,去掉透明模式
watermarked.save(path)#直接覆盖原图,原图留着也没什么用
cnt=0
def single_download(url,title,dir_saving,watertxt,x1,y1,font_path=None):
global cnt
url=url.encode().decode("unicode_escape")
print(url)
title=title
dir_saving=dir_saving
header={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
}
try:
r=requests.get(url,headers=header)
if r.status_code==200:#一般来说是只有通的时候可以下载,但是我在实操的时候,也会遇到一些响应码不是200但仍然成功下载的(内容为乱码)
with open (f"{dir_saving}/{title}.png","wb") as f:
cnt=cnt+1
f.write(r.content)
print(f"第{cnt + 1}张图片正在下载")
shuiyin(f"{dir_saving}/{title}.png",watertxt,font_path,x1,y1)#这里直接添加水印
except Exception:
print("重试ing")
def down_load_image(keyword,page,dir_saving,watertxt,x1,y1,font_path=None):
if not os.path.exists(dir_saving):
os.makedirs(dir_saving)
dict=get_url(page,keyword)
for key,value in dict.items():
single_download(key,value,dir_saving,watertxt,font_path,x1,y1)#这个就是最终的结合体函数了,将以上那么多内容都囊括进来
if __name__=='__main__':
cnt=0
keyword=input("keyword:")
page=int(input("page:"))
dir_saving=input("dir you want to save:")
water_text = input("请输入水印文字: ")
font_path = input("请输入字体文件路径(可选,支持中文,不想用直接回车就按默认的输出):")
x1=int(input("水印的x坐标"))
y1=int(input("水印的y坐标"))
down_load_image(keyword,page,dir_saving,water_text,font_path,x1,y1)

第二题GUI库编写图形化界面实现猜字谜小游戏(这个是我最想喷的,密码的,那么多内容,硬写是吧,有点意思)

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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import tkinter as tk
from tkinter import messagebox, simpledialog
#这个是用于弹出提示框和输入框
import hashlib
import json
#数据储存使用json的格式更加方便(其实在百度那题我就想着不用美丽汤了,因为百度的返回格式为json,直接json读取就可以了,但是题目要求必须要用bs4)
import random
from collections import deque
#这里是因为排行榜只保留前5名使用deque(maxlen=5)可以自动在超出长度时丢弃最老元素,这里用库,否则自己写就会写爆炸(python大作业太多了)
#感谢baozongwi,感谢ziantt,感谢拉蒙特徐,看了他们的博客,查了很多资料,才有这份写完的作业,这题是真的难
riddles="riddles.json"
rank="leaderboard.json"
#定义两个json文件,这里也很容易理解这两个都是干什么的,一个是存谜语,另外一个存排行榜
class RiddleGame:
#我直接定义一个类,用于初始化这个猜灯谜游戏界面
def __init__(self,root):
self.root=root#传入的root保存为实例属性self.root
self.root.title("猜灯谜游戏")#设置标题,后续会用tkinter的title方法来进行修改
#初始化游戏数据,这里我要把谜题通过gui界面输入,这里就全部初始化为None
self.score=10
self.current_riddle=None
self.timer=None#题目里说的,这里以时间作为区分难度的关键
self.time_left=0#定义玩这个猜字谜的游戏的剩余时间,就是类的初始化
self.difficulty=1#定义难度的等级
#这里拉一下谜题库和排行榜库,后续会定义load_riddles和load_leaderboard
self.riddles=self.load_riddles()
self.leaderboard=self.load_leaderboard()
#创建界面,构造gui组件和界面显示
self.create_widgets()#后续也会一起定义的,这里先对功能有一个想法
self.update_display()

def create_widgets(self):
#这个函数用于界面的构建,接下来创建一个frame容器,可以拿来水平布局两个标签,一个为时间,一个为分数
self.info_frame=tk.Frame(self.root)
self.info_frame.pack(pady=10)#这里我就直接用默认的pack(最简单也最好用的布局方式)10像素垂直间距
self.score_label=tk.Label(self.info_frame,text="当前分数:10")#这里建立一个标签显示分数
self.score_label.pack(side=tk.LEFT,padx=20)#左右留20像素间距
self.time_label=tk.Label(self.info_frame,text="剩余时间:0")
self.time_label.pack(side=tk.LEFT,padx=20)
#跟上面一样,Label方法在主窗口创建一个用来显示灯谜文字的多行标签,宽度400像素(防止文字,就是题目太长,給爆了)
self.riddle_label=tk.Label(self.root,text="",wraplength=400)
self.riddle_label.pack(pady=20)
#答案输入,用一个单行文本框输入答案,而且只要按回车就可以回答答案(check_answer方法后续也会定义)
self.answer_entry=tk.Entry(self.root,width=30)
self.answer_entry.pack(pady=10)
self.answer_entry.bind("<Return>",self.check_answer)
#按钮区域
self.button_frame=tk.Frame(self.root)
self.button_frame.pack(pady=10)
self.start_btn=tk.Button(self.button_frame,text="开始游戏",command=self.start_game)
self.start_btn.pack(side=tk.LEFT,padx=5)
self.add_btn=tk.Button(self.button_frame,text="添加谜题",command=self.add_riddle)
self.add_btn.pack(side=tk.LEFT,padx=5)
self.leaderboard_btn=tk.Button(self.button_frame,text="排行榜",command=self.show_leaderboard)
self.leaderboard_btn.pack(side=tk.LEFT,padx=5)
self.quit_btn=tk.Button(self.button_frame,text="退出游戏",command=self.quit_game)
self.quit_btn.pack(side=tk.LEFT,padx=5)
#建立四个按钮,用于四种功能
#接下来定义一个载入函数,用于加载谜语文件
def load_riddles(self):
try:
with open(riddles,'r') as f:
return json.load(f)
except FileNotFoundError:
return []
#可以写入谜语
def save_riddles(self):
with open(riddles,'w') as f:
json.dump(self.riddles,f,ensure_ascii=False, indent=2)
#为了保持中文字符,此处要ensure_ascii=False,还有就是文档中讲的设置indent,据说是为了增加可读性
def load_leaderboard(self):
try:
with open(rank,'r') as f:
return deque(sorted(json.load(f),key=lambda x: x['score'], reverse=True),maxlen=5)
except FileNotFoundError:
return deque(maxlen=5)

def save_leaderboard(self):
with open(rank,'w') as f:
json.dump(list(self.leaderboard),f,indent=2)
#定一个启动游戏的的函数,最低难度为1,最高难度为3
def start_game(self):
self.difficulty=simpledialog.askinteger(
"选择难度",
"请选择难度(1-简单 2-中级 3-困难)",
minvalue=1,maxvalue=3
)
if self.difficulty:
self.prepare_new_question()
def prepare_new_question(self):
available=[r for r in self.riddles if r['difficulty']==self.difficulty]
if not available:
messagebox.showerror("错误","该难度下没有可用题目")
return
self.current_riddle=random.choice(available)
self.riddle_label.config(text=self.current_riddle['riddle'])
#在这里设置倒计时
self.time_left=30-(self.difficulty-1)*10
self.update_timer()
def update_timer(self):
self.time_label.config(text=f"剩余时间:{self.time_left}")
if self.time_left>0:
self.timer=self.root.after(1000,self.update_timer)
self.time_left-=1
else:
self.handle_timeout()
def handle_timeout(self):
messagebox.showwarning("时间到", "答题时间已用完!")
self.score-=self.difficulty
self.update_display()
self.check_game_over()
if self.score>=0:
self.prepare_new_question()
def check_answer(self,event=None):#哈希比较,我这里都是用sha256,目前安全系数很高
if self.time_left<=0 or not self.current_riddle:
return
answer=self.answer_entry.get().strip().lower()
self.answer_entry.delete(0,tk.END)
if self.timer:
self.root.after_cancel(self.timer)
self.timer=None
hashed=hashlib.sha256(answer.encode()).hexdigest()
if hashed==self.current_riddle['answer']:
self.score+=self.difficulty
messagebox.showinfo("正确", "回答正确!")
else:
self.score-=self.difficulty
messagebox.showerror("错误", f"回答错误!正确答案是:{self.current_riddle['answer_plain']}")
self.update_display()
self.check_game_over()
if self.score>=0:
self.prepare_new_question()
def add_riddle(self):
add_window=tk.Toplevel(self.root)
add_window.title("添加新谜题")
#在这里谜面输入
tk.Label(add_window, text="谜面:").grid(row=0,column=0,padx=5,pady=5)
riddle_entry = tk.Entry(add_window,width=30)
riddle_entry.grid(row=0,column=1,padx=5,pady=5)
#这里是答案输入
tk.Label(add_window, text="答案:").grid(row=1,column=0,padx=5,pady=5)
answer_entry=tk.Entry(add_window,width=30)
answer_entry.grid(row=1,column=1,padx=5,pady=5)
#选择难度
tk.Label(add_window,text="难度:").grid(row=2,column=0,padx=5,pady=5)
difficulty=tk.IntVar(value=1)
tk.Radiobutton(add_window,text="简单",variable=difficulty,value=1).grid(row=2,column=1,sticky="w")
tk.Radiobutton(add_window,text="中级",variable=difficulty,value=2).grid(row=3,column=1,sticky="w")
tk.Radiobutton(add_window,text="困难",variable=difficulty,value=3).grid(row=4,column=1,sticky="w")
def save():
answer_plain=answer_entry.get().strip().lower()
if not riddle_entry.get() or not answer_plain:
messagebox.showerror("错误","谜面和答案不能为空")
return
hashed=hashlib.sha256(answer_plain.encode()).hexdigest()
new_riddle={
'riddle':riddle_entry.get(),
'answer':hashed,
'difficulty':difficulty.get(),
'answer_plain':answer_plain
}
self.riddles.append(new_riddle)
self.save_riddles()
add_window.destroy()
messagebox.showinfo("成功","谜题添加成功!")
save_btn=tk.Button(add_window,text="保存",command=save)
save_btn.grid(row=5,columnspan=2,pady=10)

def show_leaderboard(self):
lb_text="排行榜\n"
if not self.leaderboard:
lb_text+="暂无记录"
else:
for i,entry in enumerate(sorted(self.leaderboard,key=lambda x:x['score'],reverse=True)):
lb_text+=f"第{i+1}名: {entry['name']}-{entry['score']}分\n"
messagebox.showinfo("排行榜",lb_text)

def check_game_over(self):
if self.score<0:
messagebox.showinfo("游戏结束","挑战失败!")
if self._check_leaderboard_qualify():
name=simpledialog.askstring("记录","恭喜进入前五!请输入姓名:")
if name:
self.leaderboard.append({'name':name,'score':self.score})
self.save_leaderboard()
self.root.destroy()

def _check_leaderboard_qualify(self):
if len(self.leaderboard)<5:
return True
return self.score>min(entry['score'] for entry in self.leaderboard)
#一个方法一个方法慢慢定义下去吧,遇到不会的就问问学长他们,好累,终于要写完了
def quit_game(self):
if self.score>0 and self._check_leaderboard_qualify():
name=simpledialog.askstring("记录","恭喜进入前五!请输入姓名:")
if name:
self.leaderboard.append({'name':name,'score':self.score})
self.save_leaderboard()
self.root.destroy()
def update_display(self):
self.score_label.config(text=f"当前分数:{self.score}")
self.time_label.config(text=f"剩余时间:{self.time_left}")
if __name__=="__main__":
root=tk.Tk()
game=RiddleGame(root)
root.mainloop()

恶心至极啊,根本没写过这种又臭又长的东西,简直是ex死我了,还没什么作用,这个年代了谁还用tkinter这种基础GUI库(你别杠说你为什么不去用其他库,一共就5天时间写完这么多大作业,我还要去上课,怎么可能学更复杂的)

第三题,西游记画词云图和人物关系图

闲着没事干是不是,画这种玩意,jieba库是给你这么玩的,能不能干点更有意义的事情
还好我借助了东南大学同学开源的人物名称字典,剩下的自己写一个白名单进行过滤即可
感谢东南大学的大力支持
https://github.com/GaoChDemo/xyjnpl/blob/master/source/nr_deal.txt
附上他们的链接
恶心啊

每一题恶心题被写,都有一个学生遭到迫害–拉蒙特徐

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
import jieba
import wordcloud
import networkx as nx
import matplotlib.pyplot as plt
from collections import defaultdict

def takeSecond(elem):
return elem[1]

def wordyun(dict):#这里需要用词频生成
w=wordcloud.WordCloud(
font_path="msyh.ttc",
width=1000,
height=500,
background_color="white",
)
#我这里先设置一下词云的基本参数,调用的和第一题一样都是支持中文的微软雅黑
#然后就是基本的参数,这些都看个人,爱使用哪个就可以用哪个,反正可以查一下库
w.generate_from_frequencies(dict)#原先我直接用generate,但是不行,后面查了,用词频作为输入对象,得用另外一种(文中这种)的方法
w.to_file("词云.jpg")
#看错了题目的要求,是要对人物进行分词,我上github找了现成的词典(xiyouji_dict),然后纳入jieba库里面,我这里再加一句载入jieba库的语句
#在后面我还会进行更改,只对人物名的分词进行统计,不会对例如金箍棒这样子的词进行统计
jieba.load_userdict("xiyouji_dict.txt")
#这里定义一个白名单,只有在字典中的人物名会被统计,其余都会被过滤
white_words=set()
with open("xiyouji_dict.txt", "r", encoding="utf-8") as f:
for line in f:
parts = line.strip().split()
if parts: #这里要确保非空行
white_words.add(parts[0])
def main():
path="西游记.txt"
with open(path,"r",encoding="gb18030") as f:
text=f.read()
words=jieba.lcut(text)
count={}
for word in words:
if len(word)==1:
continue#单个字的很明显不是什么人名,直接去掉即可
#上网查一个西游记里面的人物及其别名大全,直接写下来,合并成同一个词
elif word in ["大圣","老孙","行者","孙大圣","孙行者","猴王","悟空","齐天大圣","猴子","斗战胜佛","弼马温","美猴王"]:
uniword="孙悟空"
elif word in ["师父","三藏","圣僧","金禅子","玄奘","唐三藏","御弟"]:
uniword="唐僧"
elif word in ["呆子","八戒","老猪","净坛使者"]:
uniword="猪八戒"
elif word in ["妖精","妖魔","妖道"]:
uniword="妖怪"#这里你就想,西游记里妖怪这个词出现的次数肯定比黄风大王,白骨精那些都要多,统计妖怪即可
elif word in["沙和尚","悟净","卷帘大将"]:
uniword="沙僧"
elif word=="佛祖":
uniword="如来"
elif word in ["三太子","八部天龙马"]:
uniword="白马"
elif word=="唐王":
uniword="唐三藏"
elif word=="观音":
uniword="观音菩萨"
elif word=="天王":
uniword="李天王"
else:
uniword=word #一些主要人物考虑一下不同的称呼,其余的就算了,因为百眼魔君孙悟空叫他妖道,镇元子孙悟空也是这么叫的,这种根本统计不完,这里非特殊词保持原样
#接下来通过白名单来过滤那些非人名的词
white_words.update(["唐僧", "孙悟空", "猪八戒", "沙僧", "如来", "白马", "妖怪","唐三藏","观音菩萨","李天王"])#更新一下白名单
if uniword in white_words:
count[uniword]=count.get(uniword,0)+1
items=list(count.items())
items.sort(key=takeSecond,reverse=True)
wordyun(count)

for i in range(24):
item=items[i]
keyWord=item[0]
jishu=item[1]
print("{0:<10}{1:>5}".format(keyWord,jishu))

top24=[item[0] for item in items[:24]]#准备一下前24个人物,后续画关系网络的重点
perduan=[par for par in text.split("\n")]#按段落来区分,准备看人物关系了
'''
接下来按道理是要进行像上面那样,分类同义词以及进行分词,然后再判断是不是在top24里面,但是照抄没什么意思,我看直接画关系即可,就是不进行同义词这部分
'''
for para in perduan:
char=set()
for word in jieba.lcut(para):
if word in top24:
char.add(word)
#把每个人物出现的有限人物都存储下来
#接下来要确保每个人物在每一段中出现的有效次数只有一次,方便后续计算(不然的话,那很完蛋了,孙悟空在段落里出现一次,猪八戒出现三次,那究竟是要记一次还是三次,为了保险起见,还是只记一次)
ls=list(char)
newcount=defaultdict(int)
for i in range(len(ls)):
for j in range(i+1,len(ls)):
#这里要利用元组的特性,例如("孙悟空", "唐僧")和("唐僧", "孙悟空")会被排序后统一为同一个元组
double=tuple(sorted([char[i],char[j]]))
#接下来用字典记权重
newcount[double]+=1
#创建一个新的无向图,用networkx的Graph方法(注意:区分开头大小写,我就因此中招)
draw=nx.Graph()
#将前24位人物都初始化为网络节点
draw.add_nodes_from(top24)
for double,value in newcount.items():
if value>0:
#很容易理解的,权重为0那就是毫无关系,那就不能加边
draw.add_edge(double[0],double[1],weight=value)
#权重weight就是字典的值value
plt.figure(figsize=(15,15))
pos=nx.spring_layout(draw, k=0.8)
nx.draw(draw,pos,with_labels=True,
node_size=[count[name]*50 for name in draw.nodes()],
width=[d['weight']*0.3 for (u,v,d) in draw.edges(data=True)],
font_family='SimHei')
#用默认参数即可
plt.savefig('关系网络.png', dpi=300)
plt.close()

if __name__=="__main__":
main()

剩下两题等我写完一起骂

😋shi😋