基于Python編寫微信清理工具的示例代碼
前幾天網(wǎng)上找了一款 PC 端微信自動清理工具,用了一下,電腦釋放了 30GB 的存儲空間,而且不會刪除文字的聊天記錄,很好用,感覺很多人都用得到,就在此分享一下,而且是用 Python 寫的,喜歡 Python 的小伙伴可以探究一下。
主要功能
它可以自動刪除 PC 端微信自動下載的大量文件、視頻、圖片等數(shù)據(jù)內(nèi)容,釋放幾十 G 的空間占用,而且不會刪除文字的聊天記錄,可以放心使用。
工作以后,微信的群聊實在太多了,動不動就被拉入一個群中,然后群聊里大部分都是與自己無關(guān)的各大群聊中的文件、視頻、圖片等內(nèi)容,會非常占用存儲空間。
- 自動識別微信賬號,支持用戶選擇自定義路徑;
- 同時管理多個賬號,保留配置參數(shù),打開即用;
- 自由設(shè)置想要刪除的文件類型,包括圖片類緩存、文件、圖片、視頻;
- 自由設(shè)置需要刪除的文件的距離時間,默認 365 天;
- 刪除后的文件放置在回收站中,檢查后自行清空,防止刪錯文件;
- 支持刪除進度的顯示;
工具的主界面如下

運行環(huán)境
Windows,后續(xù)可能會支持 Mac。
核心代碼
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsDropShadowEffect, QListWidgetItem, QListView, QWidget, \
QLabel, QHBoxLayout, QFileDialog
from PyQt5.QtCore import Qt, QPropertyAnimation, QEasingCurve, QThread, pyqtSignal, QMutex, QSize, QEvent, QPoint
from PyQt5.QtGui import QMouseEvent, QCursor, QColor
from PyQt5.uic import loadUi
from pathlib import Path, PureWindowsPath
from dateutil import relativedelta
import utils.resources
import os, datetime, time, re, math, shutil, json
from utils.deleteThread import *
from utils.multiDeleteThread import multiDeleteThread
from utils.selectVersion import *
from utils.selectVersion import check_dir, existing_user_config
working_dir = os.path.split(os.path.realpath(__file__))[0]
# 主窗口
class Window(QMainWindow):
def mousePressEvent(self, event):
# 重寫一堆方法使其支持拖動
if event.button() == Qt.LeftButton:
self.m_drag = True
self.m_DragPosition = event.globalPos() - self.pos()
event.accept()
# self.setCursor(QCursor(Qt.OpenHandCursor))
def mouseMoveEvent(self, QMouseEvent):
try:
if Qt.LeftButton and self.m_drag:
self.move(QMouseEvent.globalPos() - self.m_DragPosition)
QMouseEvent.accept()
except:
pass
def mouseReleaseEvent(self, QMouseEvent):
self.m_drag = False
# self.setCursor(QCursor(Qt.ArrowCursor))
def _frame(self):
# 邊框
self.setWindowFlags(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground, True)
# 陰影
effect = QGraphicsDropShadowEffect(blurRadius=12, xOffset=0, yOffset=0)
effect.setColor(QColor(25, 25, 25, 170))
self.mainFrame.setGraphicsEffect(effect)
def doFadeIn(self):
# 動畫
self.animation = QPropertyAnimation(self, b'windowOpacity')
# 持續(xù)時間250ms
self.animation.setDuration(250)
try:
# 嘗試先取消動畫完成后關(guān)閉窗口的信號
self.animation.finished.disconnect(self.close)
except:
pass
self.animation.stop()
# 透明度范圍從0逐漸增加到1
self.animation.setEasingCurve(QEasingCurve.InOutCubic)
self.animation.setStartValue(0)
self.animation.setEndValue(1)
self.animation.start()
def doFadeOut(self):
self.animation.stop()
# 動畫完成則關(guān)閉窗口
self.animation.finished.connect(self.close)
# 透明度范圍從1逐漸減少到0s
self.animation.setEasingCurve(QEasingCurve.InOutCubic)
self.animation.setStartValue(1)
self.animation.setEndValue(0)
self.animation.start()
def setWarninginfo(self, text):
self.lab_info.setStyleSheet("""
.QLabel {
border:1px solid #ffccc7;
border-radius:3px;
line-height: 140px;
padding: 5px;
color: #434343;
background: #fff2f0;
}
""")
self.lab_info.setText(text)
def setSuccessinfo(self, text):
self.lab_info.setStyleSheet("""
.QLabel {
border:1px solid #b7eb8f;
border-radius:3px;
line-height: 140px;
padding: 5px;
color: #434343;
background: #f6ffed;
}
""")
self.lab_info.setText(text)
class ConfigWindow(Window):
Signal_OneParameter = pyqtSignal(int)
config = {}
def _connect(self):
self.combo_user.currentIndexChanged.connect(self.refresh_ui)
self.btn_close.clicked.connect(self.save_config)
self.btn_file.clicked.connect(self.open_file)
def open_file(self):
openfile_path = QFileDialog.getExistingDirectory(self, '選擇微信數(shù)據(jù)目錄', '')
if not openfile_path or openfile_path == '':
return False
if check_dir(openfile_path) == 0:
self.setSuccessinfo('讀取路徑成功!')
list_ = os.listdir(openfile_path)
user_list = [
elem for elem in list_
if elem != 'All Users' and elem != 'Applet'
]
# 如果已有用戶配置,那么寫入新的用戶配置,否則默認寫入新配置
dir_list = []
user_config = []
existing_user_config_dic = existing_user_config()
for user_wx_id in user_list:
dir_list.append(os.path.join(openfile_path, user_wx_id))
if user_wx_id in existing_user_config_dic:
user_config.append(existing_user_config_dic[user_wx_id])
else:
user_config.append({
"wechat_id": user_wx_id,
"clean_days": "365",
"is_clean": False,
"clean_pic_cache": True,
"clean_file": False,
"clean_pic": True,
"clean_video": True,
"is_timer": True,
"timer": "0h"
})
config = {"data_dir": dir_list, "users": user_config}
with open(
working_dir + "/config.json", "w", encoding="utf-8") as f:
json.dump(config, f)
self.load_config()
else:
self.setWarninginfo('請選擇正確的文件夾!一般是WeChat Files文件夾。')
def save_config(self):
self.update_config()
self.doFadeOut()
def check_wechat_exists(self):
self.selectVersion = selectVersion()
self.version_scan = self.selectVersion.getAllPath()[0]
self.users_scan = self.selectVersion.getAllPath()[1]
if len(self.version_scan) == 0:
return False
else:
return True
def load_config(self):
fd = open(working_dir + "/config.json", encoding="utf-8")
self.config = json.load(fd)
self.combo_user.clear()
for value in self.config["users"]:
self.combo_user.addItem(value["wechat_id"])
self.line_gobackdays.setText(
str(self.config["users"][0]["clean_days"]))
self.check_is_clean.setChecked(self.config["users"][0]["is_clean"])
self.check_picdown.setChecked(self.config["users"][0]["clean_pic"])
self.check_files.setChecked(self.config["users"][0]["clean_file"])
self.check_video.setChecked(self.config["users"][0]["clean_video"])
self.check_picscache.setChecked(
self.config["users"][0]["clean_pic_cache"])
self.setSuccessinfo("加載配置文件成功")
def refresh_ui(self):
self.config = open(working_dir + "/config.json", encoding="utf-8")
self.config = json.load(self.config)
for value in self.config["users"]:
if value["wechat_id"] == self.combo_user.currentText():
self.line_gobackdays.setText(str(value["clean_days"]))
self.check_is_clean.setChecked(value["is_clean"])
self.check_picdown.setChecked(value["clean_pic"])
self.check_files.setChecked(value["clean_file"])
self.check_video.setChecked(value["clean_video"])
self.check_picscache.setChecked(value["clean_pic_cache"])
def create_config(self):
true = True
if not os.path.exists(working_dir + "/config.json"):
if not self.check_wechat_exists():
self.setWarninginfo("默認位置沒有微信,請自定義位置")
return
self.config = {"data_dir": self.version_scan, "users": []}
for value in self.users_scan:
self.config["users"].append({
"wechat_id": value,
"clean_days": 365,
"is_clean": False,
"clean_pic_cache": true,
"clean_file": False,
"clean_pic": true,
"clean_video": true,
"is_timer": true,
"timer": "0h"
})
with open(
working_dir + "/config.json", "w", encoding="utf-8") as f:
json.dump(self.config, f)
self.load_config()
self.setSuccessinfo("加載配置文件成功")
else:
self.setSuccessinfo("加載配置文件成功")
self.load_config()
def update_config(self):
if not len(self.config):
return
else:
for value in self.config["users"]:
if value["wechat_id"] == self.combo_user.currentText():
try:
days = int(self.line_gobackdays.text())
if days < 0:
value["clean_days"] = "0"
else:
value["clean_days"] = self.line_gobackdays.text()
except ValueError:
value["clean_days"] = "0"
value["is_clean"] = self.check_is_clean.isChecked()
value["clean_pic"] = self.check_picdown.isChecked()
value["clean_file"] = self.check_files.isChecked()
value["clean_video"] = self.check_video.isChecked()
value["clean_pic_cache"] = self.check_picscache.isChecked()
with open(working_dir + "/config.json", "w", encoding="utf-8") as f:
json.dump(self.config, f)
self.setSuccessinfo("更新配置文件成功")
self.Signal_OneParameter.emit(1)
def __init__(self):
super().__init__()
loadUi(working_dir + "/images/config.ui", self)
self._frame()
self._connect()
self.doFadeIn()
self.create_config()
self.show()
class MainWindow(Window):
def deal_emit_slot(self, set_status):
if set_status and not self.config_exists:
self.setSuccessinfo("已經(jīng)準(zhǔn)備好,可以開始了!")
self.config_exists = True
def closeEvent(self, event):
sys.exit(0)
def eventFilter(self, object, event):
if event.type() == QEvent.MouseButtonPress:
if object == self.lab_close:
self.doFadeOut()
return True
elif object == self.lab_clean:
try:
self.setSuccessinfo("正在清理中...")
self.justdoit()
except:
self.setWarninginfo("清理失敗,請檢查配置文件后重試")
return True
elif object == self.lab_config:
cw = ConfigWindow()
cw.Signal_OneParameter.connect(self.deal_emit_slot)
return True
return False
def _eventfilter(self):
# 事件過濾
self.lab_close.installEventFilter(self)
self.lab_clean.installEventFilter(self)
self.lab_config.installEventFilter(self)
def get_fileNum(self, path, day, picCacheCheck, fileCheck, picCheck,
videoCheck, file_list, dir_list):
dir_name = PureWindowsPath(path)
# Convert path to the right format for the current operating system
correct_path = Path(dir_name)
now = datetime.datetime.now()
if picCacheCheck:
path_one = correct_path / 'Attachment'
path_two = correct_path / 'FileStorage/Cache'
self.getPathFileNum(now, day, path_one, path_two, file_list,
dir_list)
if fileCheck:
path_one = correct_path / 'Files'
path_two = correct_path / 'FileStorage/File'
self.getPathFileNum(now, day, path_one, path_two, file_list,
dir_list)
if picCheck:
path_one = correct_path / 'Image/Image'
path_two = correct_path / 'FileStorage/Image'
self.getPathFileNum(now, day, path_one, path_two, file_list,
dir_list)
if videoCheck:
path_one = correct_path / 'Video'
path_two = correct_path / 'FileStorage/Video'
self.getPathFileNum(now, day, path_one, path_two, file_list,
dir_list)
def pathFileDeal(self, now, day, path, file_list, dir_list):
if os.path.exists(path):
filelist = [
f for f in os.listdir(path)
if os.path.isfile(os.path.join(path, f))
]
for i in range(0, len(filelist)):
file_path = os.path.join(path, filelist[i])
if os.path.isdir(file_path):
continue
timestamp = datetime.datetime.fromtimestamp(
os.path.getmtime(file_path))
diff = (now - timestamp).days
if diff >= day:
file_list.append(file_path)
def getPathFileNum(self, now, day, path_one, path_two, file_list,
dir_list):
# caculate path_one
self.pathFileDeal(now, day, path_one, file_list, dir_list)
td = datetime.datetime.now() - datetime.timedelta(days=day)
td_year = td.year
td_month = td.month
# caculate path_two
if os.path.exists(path_two):
osdir = os.listdir(path_two)
dirlist = []
for i in range(0, len(osdir)):
file_path = os.path.join(path_two, osdir[i])
if os.path.isdir(file_path):
dirlist.append(osdir[i])
for i in range(0, len(dirlist)):
file_path = os.path.join(path_two, dirlist[i])
if os.path.isfile(file_path):
continue
if re.match('\d{4}(\-)\d{2}', dirlist[i]) != None:
cyear = int(dirlist[i].split('-', 1)[0])
cmonth = int(dirlist[i].split('-', 1)[1])
if self.__before_deadline(cyear, cmonth, td_year,
td_month):
dir_list.append(file_path)
else:
if cmonth == td_month:
self.pathFileDeal(now, day, file_path, file_list,
dir_list)
def __before_deadline(self, cyear, cmonth, td_year, td_month):
if cyear < td_year:
return True
elif cyear > td_year:
return False
elif cyear == td_year:
return cmonth < td_month
def callback(self, v):
value = v / int((self.total_file + self.total_dir)) * 100
self.bar_progress.setValue(value)
if value == 100:
out = "本次共清理文件" + str(self.total_file) + "個,文件夾" + str(
self.total_dir) + "個。請前往回收站檢查并清空。"
self.setSuccessinfo(out)
return
def justdoit(self): # 這個Api設(shè)計的太腦殘了,其實dir可以直接放在user里的... 有時間改吧
fd = open(working_dir + "/config.json", encoding="utf-8")
self.config = json.load(fd)
i = 0
need_clean = False
thread_list = []
total_file = 0
total_dir = 0
share_thread_arr = [0]
for value in self.config["users"]:
file_list = []
dir_list = []
if value["is_clean"]:
self.get_fileNum(self.config["data_dir"][i],
int(value["clean_days"]),
value["clean_pic_cache"], value["clean_file"],
value["clean_pic"], value["clean_video"],
file_list, dir_list)
if len(file_list) + len(dir_list) != 0:
need_clean = True
total_file += len(file_list)
total_dir += len(dir_list)
thread_list.append(
multiDeleteThread(file_list, dir_list, share_thread_arr))
thread_list[-1].delete_process_signal.connect(self.callback)
i = i + 1
if not need_clean:
self.setWarninginfo("沒有需要清理的文件")
else:
self.total_file = total_file
self.total_dir = total_dir
for thread in thread_list:
thread.run()
def __init__(self):
super().__init__()
loadUi(working_dir + "/images/main.ui", self)
self._frame()
self._eventfilter()
self.doFadeIn()
self.config_exists = True
# 判斷配置文件是否存在
if not os.path.exists(working_dir + "/config.json"):
self.setWarninginfo("配置文件不存在!請單擊“設(shè)置”創(chuàng)建配置文件")
self.config_exists = False
self.show()
if __name__ == '__main__':
app = QApplication([])
win = MainWindow()
app.exec_()
完整代碼
源代碼獲取地址 提取碼:vuud
到此這篇關(guān)于基于Python編寫微信清理工具的示例代碼的文章就介紹到這了,更多相關(guān)Python微信清理工具內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
TensorFlow卷積神經(jīng)網(wǎng)絡(luò)之使用訓(xùn)練好的模型識別貓狗圖片
今天小編就為大家分享一篇關(guān)于TensorFlow卷積神經(jīng)網(wǎng)絡(luò)之使用訓(xùn)練好的模型識別貓狗圖片,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03
Dockerfile構(gòu)建一個Python Flask 鏡像
這篇文章主要介紹了Dockerfile構(gòu)建一個Python Flask 鏡像,對正在學(xué)習(xí)的你有一定的參考價值,需要的小伙伴可以參考一下2022-01-01
Python?內(nèi)置模塊?argparse快速入門教程
argparse模塊是Python內(nèi)置的用于命令項選項與參數(shù)解析的模塊,argparse模塊可以讓人輕松編寫用戶友好的命令行接口,能夠幫助程序員為模型定義參數(shù),這篇文章主要介紹了快速入門Python內(nèi)置模塊argparse,需要的朋友可以參考下2023-06-06
python的xpath獲取div標(biāo)簽內(nèi)html內(nèi)容,實現(xiàn)innerhtml功能的方法
今天小編就為大家分享一篇python的xpath獲取div標(biāo)簽內(nèi)html內(nèi)容,實現(xiàn)innerhtml功能的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01
keras的load_model實現(xiàn)加載含有參數(shù)的自定義模型
這篇文章主要介紹了keras的load_model實現(xiàn)加載含有參數(shù)的自定義模型,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06
Python 字符串操作實現(xiàn)代碼(截取/替換/查找/分割)
這篇文章主要介紹了Python 字符串截取/替換/查找/分割等實現(xiàn)方法,需要的朋友可以參考下2013-06-06
python深度學(xué)習(xí)tensorflow1.0參數(shù)初始化initializer
這篇文章主要為大家介紹了python深度學(xué)習(xí)tensorflow1.0參數(shù)初始化initializer示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06

