import
ast
import
subprocess
import
threading
import
time
from
tkinter
import
*
from
tkinter
import
filedialog
from
tkinter.ttk
import
*
import
os
import
hashlib
from
xml.etree.ElementTree
import
parse
import
jieba
import
sqlite3
import
tkinter.messagebox as GUI
from
PIL
import
Image, ImageTk
from
wordcloud
import
WordCloud
from
snapshot_selenium
import
snapshot
from
pyecharts
import
options as opts
from
pyecharts.charts
import
Bar
from
pyecharts.render
import
make_snapshot
import
matplotlib.pyplot as plt
import
ttkbootstrap as ttk
from
ttkbootstrap.constants
import
*
jieba.set_dictionary(
"dict.txt"
)
jieba.initialize()
global
dbpath
global
xmlpath
dbpath
=
""
xmlpath
=
""
chatroomkey
=
""
class
db1():
def
__init__(
self
, db_path
=
"plaintext.db"
):
self
.db
=
sqlite3.connect(db_path)
self
.cursor
=
self
.db.cursor()
self
.top_data
=
[]
self
.top_num
=
5
def
execute1(
self
, sql, param
=
None
):
count
=
0
try
:
if
param
is
None
:
self
.cursor.execute(sql)
self
.db.commit()
else
:
if
type
(param)
is
list
:
self
.cursor.executemany(sql, param)
else
:
self
.cursor.execute(sql, param)
count
=
self
.db.total_changes
self
.db.commit()
except
Exception as e:
print
(e)
return
False
, e
return
True
if
count >
0
else
False
def
query1(
self
, sql, param
=
None
):
if
param
is
None
:
self
.cursor.execute(sql)
else
:
self
.cursor.execute(sql, param)
return
self
.cursor.fetchall()
def
get_chartroom_id(
self
, chatroomname):
res
=
self
.query1(
"select username from rcontact where nickname like '%"
+
chatroomname
+
"%'"
)
print
(
"select username from rcontact where nickname='"
+
chatroomname
+
result
=
self
.query1(sql)
if
result[
0
][
0
]
=
=
"":
sql
=
"SELECT nickname FROM rcontact where username='"
+
id
+
"'"
result
=
self
.query1(sql)
return
result[
0
][
0
]
def
draw_image(
self
):
usernames
=
[]
counts
=
[]
for
user
in
self
.top_data:
usernames.append(
str
(user.get(
'username'
)).strip()[
0
:
8
])
counts.append(user.get(
'count'
))
def
bar_chart()
-
> Bar:
c
=
(
Bar()
.add_xaxis(usernames)
.add_yaxis(chatroomkey, counts)
.reversal_axis()
.set_series_opts(label_opts
=
opts.LabelOpts(position
=
"right"
))
.set_global_opts(title_opts
=
opts.TitleOpts(title
=
"最活跃的%d个小伙伴"
%
self
.top_num))
)
return
c
make_snapshot(snapshot, bar_chart().render(),
"bar.png"
)
class
WinGUI(Tk):
def
__init__(
self
):
super
().__init__()
self
.__win()
self
.tk_input_lauxqn9y
=
self
.__tk_input_lauxqn9y()
self
.tk_button_lauxqrfi
=
self
.__tk_button_lauxqrfi()
self
.tk_input_lauxrzf1
=
self
.__tk_input_lauxrzf1()
self
.tk_button_lauxs4bi
=
self
.__tk_button_lauxs4bi()
self
.tk_label_lauxutbi
=
self
.__tk_label_lauxutbi()
self
.tk_label_lauxxqk6
=
self
.__tk_label_lauxxqk6()
self
.tk_input_lauxz6au
=
self
.__tk_input_lauxz6au()
self
.tk_label_lauxznxk
=
self
.__tk_label_lauxznxk()
self
.tk_label_lauy095q
=
self
.__tk_label_lauy095q()
self
.tk_input_lauy1c0v
=
self
.__tk_input_lauy1c0v()
self
.tk_button_lauy9mi9
=
self
.__tk_button_lauy9mi9()
self
.tk_label_lauyak54
=
self
.__tk_label_lauyak54()
self
.tk_label_lauybx1s
=
self
.__tk_label_lauybx1s()
self
.tk_label_lauyc5pl
=
self
.__tk_label_lauyc5pl()
self
.tk_label_lb0d40rz
=
self
.__tk_label_lb0d40rz()
self
.tk_input_lb0d550v
=
self
.__tk_input_lb0d550v()
self
.tk_button_lb1m63sc
=
self
.__tk_button_lb1m63sc()
self
.tk_list_box_lb2z9la5
=
self
.__tk_list_box_lb2z9la5()
if
os.path.exists(
"mima.txt"
):
with
open
(
"mima.txt"
,
'r'
, encoding
=
'gb2312'
) as f:
firstline
=
f.readline()
if
firstline
=
=
"":
self
.tk_button_lb1m63sc.config(state
=
DISABLED)
else
:
self
.tk_button_lb1m63sc.config(state
=
DISABLED)
def
__win(
self
):
self
.title(
"五哈半自动微信分析"
)
self
.iconbitmap()
width
=
600
height
=
700
screenwidth
=
self
.winfo_screenwidth()
screenheight
=
self
.winfo_screenheight()
geometry
=
'%dx%d+%d+%d'
%
(width, height, (screenwidth
-
width)
/
2
, (screenheight
-
height)
/
2
)
self
.geometry(geometry)
self
.iconbitmap(
"favicon.ico"
)
self
.resizable(width
=
False
, height
=
False
)
def
select_file(
self
, strpath, strname):
strpath
=
filedialog.StringVar()
selected_file_path
=
filedialog.askopenfilename()
if
strname
=
=
"db"
:
self
.tk_input_lauxqn9y.delete(
0
,
"end"
)
self
.tk_input_lauxqn9y.insert(
0
, selected_file_path)
global
dbpath
dbpath
=
selected_file_path
else
:
self
.tk_input_lauxrzf1.delete(
0
,
"end"
)
self
.tk_input_lauxrzf1.insert(
0
, selected_file_path)
global
xmlpath
xmlpath
=
selected_file_path
global
image1
image1
=
selected_file_path
def
__tk_input_lauxqn9y(
self
):
ipt
=
Entry(
self
,textvariable
=
"")
ipt.place(x
=
30
, y
=
10
, width
=
401
, height
=
24
)
return
ipt
def
__tk_button_lauxqrfi(
self
):
btn
=
Button(
self
, text
=
"选择EnMicroMsg.db"
,
command
=
lambda
:
self
.thread_it(
self
.select_file(
'select_path'
,
'db'
)))
btn.place(x
=
450
, y
=
10
, width
=
140
, height
=
30
)
btn.place(x
=
450
, y
=
10
, width
=
140
, height
=
30
)
return
btn
def
__tk_input_lauxrzf1(
self
):
ipt
=
Entry(
self
,textvariable
=
"")
ipt.place(x
=
30
, y
=
90
, width
=
402
, height
=
24
)
return
ipt
def
__tk_button_lauxs4bi(
self
):
btn
=
Button(
self
, text
=
"选择"
, command
=
lambda
:
self
.thread_it(
self
.select_file(
'select_path'
,
'xml'
)))
btn.place(x
=
460
, y
=
90
, width
=
125
, height
=
30
)
return
btn
def
__tk_label_lauxutbi(
self
):
label
=
Label(
self
, text
=
"文件夹路径:在安卓手机的/data/data/com.tencent.mm/MicroMsg/一长串/EnMicroMsg.db"
)
label.place(x
=
30
, y
=
50
, width
=
552
, height
=
24
)
return
label
def
__tk_label_lauxxqk6(
self
):
label
=
Label(
self
, text
=
"文件夹路径:在安卓手机的/data/data/com.tencent.mm/shared_prefs/auth_info_key_prefs.xml中"
)
label.place(x
=
30
, y
=
130
, width
=
555
, height
=
24
)
return
label
def
__tk_input_lauxz6au(
self
):
ipt
=
Entry(
self
)
ipt.place(x
=
180
, y
=
200
, width
=
404
, height
=
24
)
return
ipt
def
__tk_label_lauxznxk(
self
):
label
=
Label(
self
, text
=
"请输入要解析的群名:"
)
label.place(x
=
30
, y
=
200
, width
=
136
, height
=
24
)
return
label
def
__tk_label_lauy095q(
self
):
label
=
Label(
self
, text
=
"请输入关键字(为空为活跃度排名):"
)
label.place(x
=
30
, y
=
240
, width
=
215
, height
=
24
)
return
label
def
__tk_input_lauy1c0v(
self
):
ipt
=
Entry(
self
)
ipt.place(x
=
262
, y
=
240
, width
=
321
, height
=
24
)
return
ipt
def
__tk_button_lauy9mi9(
self
):
btn
=
Button(
self
, text
=
"开始解析"
, command
=
lambda
:
self
.thread_it(
self
.startanalysis))
btn.place(x
=
350
, y
=
300
, width
=
121
, height
=
46
)
return
btn
def
__tk_button_lb1m63sc(
self
):
btn
=
Button(
self
, text
=
"复制上一次配置"
, command
=
lambda
:
self
.gethistroy())
btn.place(x
=
150
, y
=
300
, width
=
132
, height
=
45
)
return
btn
def
__tk_label_lauyak54(
self
):
label
=
Label(
self
, text
=
"1、生成的词云在本程序的同级目录。2、生成的活跃度排名在本程序的同级目录。"
)
label.place(x
=
30
, y
=
270
, width
=
551
, height
=
23
)
return
label
def
__tk_label_lauybx1s(
self
):
label
=
Label(
self
, image
=
"
") # text="
词云",
label.place(x
=
40
, y
=
540
, width
=
248
, height
=
147
)
return
label
def
__tk_label_lauyc5pl(
self
):
label
=
Label(
self
, image
=
"
") # , text="
活跃度排名"
label.place(x
=
319
, y
=
540
, width
=
248
, height
=
147
)
return
label
def
__tk_label_lb0d40rz(
self
):
label
=
Label(
self
, text
=
"手机IMEI码:"
)
label.place(x
=
30
, y
=
163
, width
=
137
, height
=
24
)
return
label
def
__tk_input_lb0d550v(
self
):
ipt
=
Entry(
self
, textvariable
=
"")
ipt.place(x
=
180
, y
=
163
, width
=
404
, height
=
24
)
ipt.insert(
0
,
'1234567890ABCDEF'
)
return
ipt
def
__tk_list_box_lb2z9la5(
self
):
lb
=
Listbox(
self
)
lb.insert(END,
"运行状态"
)
lb.place(x
=
31
, y
=
353
, width
=
547
, height
=
171
)
return
lb
def
gethistroy(
self
):
list
=
[]
with
open
(
'mima.txt'
,
'r'
, encoding
=
'gb2312'
) as
file
:
for
strline
in
file
:
strline
=
strline.replace(
"\\\\","
\\")
list
.append(strline)
self
.tk_input_lauxqn9y.delete(
0
,
"end"
)
self
.tk_input_lauxrzf1.delete(
0
,
"end"
)
self
.tk_input_lauxz6au.delete(
0
,
"end"
)
self
.tk_input_lauy1c0v.delete(
0
,
"end"
)
self
.tk_input_lauxqn9y.insert(
0
,
list
[
1
])
self
.tk_input_lauxrzf1.insert(
0
,
list
[
2
])
self
.tk_input_lauxz6au.insert(
0
,
list
[
3
])
self
.tk_input_lauy1c0v.insert(
0
,
list
[
4
])
def
resize(
self
, w, h, w_box, h_box, pil_image):
f1
=
1.0
*
w_box
/
w
f2
=
1.0
*
h_box
/
h
factor
=
min
([f1, f2])
width
=
int
(w
*
factor)
height
=
int
(h
*
factor)
return
pil_image.resize((width, height), Image.ANTIALIAS)
def
thread_it(
self
, func,
*
args):
print
(
"----------------------启动多线程--------------"
)
self
.myThread
=
threading.Thread(target
=
func, args
=
args)
self
.myThread.setDaemon(
True
)
self
.myThread.start()
def
shownewwindows(
self
,imagepath):
top
=
Toplevel(
self
)
top.title(imagepath)
top.geometry(
"850x500"
)
top.iconbitmap(
"favicon.ico"
)
v1
=
StringVar()
label1
=
Label(top)
label1.place(width
=
843
, height
=
500
)
img_open1
=
Image.
open
(imagepath)
w, h
=
img_open1.size
w_box
=
850
h_box
=
500
img_open1
=
self
.resize(w, h, w_box, h_box, img_open1)
img_png1
=
ImageTk.PhotoImage(img_open1)
label1.configure(image
=
img_png1)
label1.image
=
img_png1
def
startanalysis(
self
):
xmlpath
=
self
.tk_input_lauxrzf1.get().replace(
"\n"
, "")
dbpath
=
self
.tk_input_lauxqn9y.get().replace(
"\n"
, "")
if
(
self
.tk_input_lauxrzf1.get()!
=
"
" and self.tk_input_lauxqn9y.get()!="
" and self.tk_input_lb0d550v.get()!="
" and self.tk_input_lauxz6au.get()!="
"):
try
:
self
.tk_button_lauy9mi9.config(state
=
DISABLED)
self
.tk_button_lb1m63sc.config(state
=
DISABLED)
self
.tk_button_lauxqrfi.config(state
=
DISABLED)
self
.tk_button_lauxs4bi.config(state
=
DISABLED)
self
.tk_list_box_lb2z9la5.insert(
0
,
"dbpath路径获取成功:"
+
str
(dbpath))
self
.tk_list_box_lb2z9la5.insert(
0
,
"xmlpath路径获取成功:"
+
str
(dbpath))
strimei
=
self
.tk_input_lb0d550v.get()
f
=
open
(xmlpath)
et
=
parse(f)
root
=
et.getroot()
strunid
=
""
for
e
in
root.iterfind(
'int'
):
if
e.get(
'name'
)
=
=
"_auth_uin"
:
strunid
=
e.get(
'value'
)
self
.tk_list_box_lb2z9la5.insert(
0
,
"_auth_uin值获取成功:"
+
str
(strunid))
md5
=
hashlib.md5((strimei
+
strunid).encode()).hexdigest()
pwd
=
md5[
0
:
7
]
self
.tk_list_box_lb2z9la5.insert(
0
,
"数据库密码值计算成功:"
+
str
(pwd))
file
=
open
(
'mima.txt'
,
'w'
, encoding
=
'gb2312'
)
self
.tk_list_box_lb2z9la5.insert(
0
,
"配置文件打开成功!"
)
strwechatname
=
self
.tk_input_lauxz6au.get().replace(
"\n"
, "")
strwechatkey
=
self
.tk_input_lauy1c0v.get().replace(
"\n"
, "")
strvbs
=
pwd
+
"\n"
+
dbpath
+
"\n"
+
xmlpath
+
"\n"
+
strwechatname
+
"\n"
+
strwechatkey
+
"\n!@no~~"
strvbs
=
str
(strvbs)
file
.write(strvbs)
file
.close()
if
os.path.exists(
"plaintext.db"
):
GUI.showinfo(title
=
'提示'
, message
=
'检测到已存在脱密数据库plaintext.db,如需要重新生成请删除这个db文件!'
)
else
:
self
.tk_list_box_lb2z9la5.insert(
0
,
"开始生成脱密数据库!"
)
os.system(
"test.vbs"
)
last_line
=
"!@no~~"
while
last_line !
=
"yes"
:
with
open
(
"mima.txt"
,
'r'
, encoding
=
'gb2312'
) as f:
lines
=
f.readlines()
last_line
=
lines[
-
1
]
print
(last_line)
self
.tk_list_box_lb2z9la5.insert(
0
,
"等待数据库脱密完成~"
)
time.sleep(
2
)
self
.tk_list_box_lb2z9la5.insert(
0
,
"数据库脱密完成!"
)
self
.tk_list_box_lb2z9la5.insert(
0
,
"开始解析数据库!"
)
wechat
=
db1()
chatroomname
=
self
.tk_input_lauxz6au.get().replace(
"\n"
, "")
chartroom_id
=
wechat.get_chartroom_id(chatroomname)
allchatroommesg
=
wechat.getallchatroommesg(chartroom_id)
words
=
wechat.getchatroommesg(allchatroommesg)
words1
=
wechat.getchatroommesg1(allchatroommesg)
strwords
=
str
(words)
temp
=
jieba.cut(strwords, cut_all
=
True
)
temp1
=
""
for
i
in
temp:
temp1
+
=
str
(i)
+
" "
self
.tk_list_box_lb2z9la5.insert(
0
,
"开始生成词云!"
)
wechat.generate_wordcloud(temp1)
wechat.insertop(allchatroommesg)
global
chatroomkey
chatroomkey
=
self
.tk_input_lauy1c0v.get()
self
.tk_list_box_lb2z9la5.insert(
0
,
"开始生成柱状图!"
)
wechat.get_top_partner()
wechat.draw_image()
img_open1
=
Image.
open
(
"群聊.png"
)
self
.tk_list_box_lb2z9la5.insert(
0
,
"柱状图生成完毕!"
)
w, h
=
img_open1.size
w_box
=
248
h_box
=
147
img_open1
=
self
.resize(w, h, w_box, h_box, img_open1)
img_png1
=
ImageTk.PhotoImage(img_open1)
self
.tk_label_lauybx1s.configure(image
=
img_png1)
self
.tk_label_lauybx1s.image
=
img_png1
img_open2
=
Image.
open
(
"bar.png"
)
img_open2
=
self
.resize(w, h, w_box, h_box, img_open2)
img_png2
=
ImageTk.PhotoImage(img_open2)
self
.tk_label_lauyc5pl.configure(image
=
img_png2)
self
.tk_label_lauyc5pl.image
=
img_png2
self
.tk_list_box_lb2z9la5.insert(
0
,
"程序运行完毕!"
)
self
.tk_list_box_lb2z9la5.insert(
0
,
"点击图片可以进行放大!"
)
self
.tk_button_lauy9mi9.config(state
=
ACTIVE)
self
.tk_button_lb1m63sc.config(state
=
ACTIVE)
self
.tk_button_lauxqrfi.config(state
=
ACTIVE)
self
.tk_button_lauxs4bi.config(state
=
ACTIVE)
except
Exception as err:
self
.tk_button_lauy9mi9.config(state
=
ACTIVE)
self
.tk_button_lb1m63sc.config(state
=
ACTIVE)
self
.tk_button_lauxqrfi.config(state
=
ACTIVE)
self
.tk_button_lauxs4bi.config(state
=
ACTIVE)
print
(
'An exception happened: '
+
str
(err))
print
(err.__traceback__.tb_frame.f_globals[
"__file__"
])
print
(err.__traceback__.tb_lineno)
self
.tk_list_box_lb2z9la5.insert(
0
,
"程序出现异常-异常文件:"
+
str
(err.__traceback__.tb_frame.f_globals[
"__file__"
]))
self
.tk_list_box_lb2z9la5.insert(
0
,
"程序出现异常行数:"
+
str
(err.__traceback__.tb_lineno))
self
.tk_list_box_lb2z9la5.insert(
0
,
"程序出现异常:"
+
str
(err))
else
:
GUI.showinfo(title
=
'提示'
, message
=
'请把必填项填写完整后再次提交!'
)
class
Win(WinGUI):
def
__init__(
self
):
super
().__init__()
self
.__event_bind()
def
词云show(
self
, evt):
win.shownewwindows(
"群聊.png"
)
def
排名show(
self
, evt):
win.shownewwindows(
"bar.png"
)
def
__event_bind(
self
):
self
.tk_label_lauybx1s.bind(
'<Button>'
,
self
.词云show)
self
.tk_label_lauyc5pl.bind(
'<Button>'
,
self
.排名show)
if
__name__
=
=
"__main__"
:
win
=
Win()
win.mainloop()