
WebCodeCompressor
网页代码压缩工具
1
背景介绍
WebCodeCompressor
网页代码压缩
WebCodeCompressor
作者最初是使用Axure进行原型设计时,发现导出的HTML文件包结构较为复杂,研究了一段时间想看看能不能直接把这个文件包变成前端,最终当然也是以失败告终,不过看到其中有的代码文件存在压缩加密,就想到能不能把自己的代码也做相似的压缩加密,就算做不到加密,只是去注释去缩进来将其变成一长行还是可行的。
这个想法拖了好久才开始去实现,而开发过程中突然发现Minify这个插件,做的正是我想做的事情,而且做的更加成熟,于是开发到一半就停工了,开始用现成的Minify插件,同时发现VUE框架下的前端打包时也是用Minify来压缩代码的(虽然两个可能只是同名并非一个东西)。
但后来我尝试用Minify压缩一个内联CSS和JS的HTML文件时,发现其压缩后运行出错,主要问题就在其中JS部分的注释,经常导致各种一团乱,但是内联CSS和JS的HTML文件对于一个简洁的网页小工具来说是很好的选择,作者也喜欢开发小工具时把所有的代码放在一个文件里面,完整又统一。所以我再次开始了网页代码压缩工具的开发。
遗憾的是,代码从开始的400多行一直升到900多行,不断地查漏补缺,最终也没能完美地解决内联CSS和JS的HTML文件的压缩问题,情况太多总是会出错,作者思量再三之后觉得不如用户遇到错误直接手动删除该处注释,调整一下再重新压缩。
最后,这个网页代码压缩小工具基本能达到和Minify插件一样的压缩效果(可能Minify插件还有更多功能是我的小工具所未实现的)。
附:刚发现微信公众号推送里粘贴进来的代码缩进也有各种问题,一个一个改太费劲了,作者在此就不改了(我记得本身从推送里复制代码就有缩进错误,既然出入都有错误就不如不改),读者可复制代码后让AI修复缩进错误,或直接私信联系作者要代码源文件。
2
软件图示
WebCodeCompressor
WebCodeCompressor

3
软件源代码
WebCodeCompressor
import sysimport osimport reimport jsonfrom PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,QHBoxLayout, QPushButton, QLabel, QTextEdit,QFileDialog, QMessageBox, QFrame, QListWidget,QListWidgetItem, QProgressBar, QSplitter)from PyQt5.QtCore import Qtfrom PyQt5.QtGui import QFontclassWebCodeCompressorApp(QMainWindow):def__init__(self):super().__init__()self.files = []self.output_directory = Noneself.initUI()definitUI(self):"""初始化用户界面"""self.setWindowTitle('网页代码压缩工具')self.setGeometry(100, 100, 1000, 700)self.setup_style()self.create_widgets()self.setup_connections()defsetup_style(self):"""设置界面样式"""self.setStyleSheet("""QMainWindow {background-color: #1a1d23;}QWidget {color: #e0e0e0;font-family: 'Segoe UI', Arial, sans-serif;}QPushButton {background-color: #2d3748;border: 2px solid #4a5568;color: #e0e0e0;padding: 10px 20px;border-radius: 4px;font-weight: bold;font-size: 12px;}QPushButton:hover {background-color: #4a5568;border-color: #718096;}QPushButton:pressed {background-color: #1a202c;border-color: #2d3748;}QPushButton:disabled {background-color: #2d3748;color: #718096;}QListWidget {background-color: #2d3748;border: 2px solid #4a5568;border-radius: 4px;padding: 5px;font-size: 12px;}QListWidget::item {padding: 8px;border-bottom: 1px solid #4a5568;}QListWidget::item:selected {background-color: #4a5568;color: #ffffff;}QTextEdit {background-color: #2d3748;border: 2px solid #4a5568;border-radius: 4px;padding: 12px;font-family: 'Consolas', 'Monaco', monospace;font-size: 12px;}QLabel {padding: 5px;color: #cbd5e0;}QProgressBar {border: 2px solid #4a5568;border-radius: 4px;text-align: center;background-color: #2d3748;color: #e0e0e0;height: 20px;}QProgressBar::chunk {background-color: #4299e1;border-radius: 2px;}QFrame {background-color: #2d3748;border-radius: 4px;border: 2px solid #4a5568;}QSplitter::handle {background-color: #4a5568;width: 4px;}""")defcreate_widgets(self):"""创建界面控件"""central_widget = QWidget()self.setCentralWidget(central_widget)layout = QVBoxLayout(central_widget)# 标题title = QLabel("网页代码压缩工具")title.setAlignment(Qt.AlignCenter)title_font = QFont('Segoe UI', 24, QFont.Bold)title.setFont(title_font)title.setStyleSheet("color: #4299e1; padding: 20px; border-bottom: 2px solid #4a5568;")layout.addWidget(title)# 创建分割器splitter = QSplitter(Qt.Horizontal)# 左侧面板left_frame = QFrame()left_frame.setMaximumWidth(350)left_layout = QVBoxLayout(left_frame)# 文件列表区域file_label = QLabel("已选文件:")file_label.setFont(QFont('Segoe UI', 12, QFont.Bold))left_layout.addWidget(file_label)self.file_list = QListWidget()self.file_list.setMinimumHeight(200)self.file_list.setStyleSheet("font-size: 20px; padding: 5px;")left_layout.addWidget(self.file_list)# 按钮区域button_layout = QHBoxLayout()self.add_button = QPushButton("📁 添加文件")self.add_button.clicked.connect(self.add_files)self.add_button.setStyleSheet("""QPushButton {background-color: #4299e1;border-color: #3182ce;font-size: 20px;padding: 12px;}QPushButton:hover {background-color: #3182ce;border-color: #2c5282;}""")button_layout.addWidget(self.add_button)self.remove_button = QPushButton("🗑️ 移除文件")self.remove_button.clicked.connect(self.remove_file)self.remove_button.setStyleSheet("""QPushButton {background-color: #4299e1;border-color: #3182ce;font-size: 20px;padding: 12px;}QPushButton:hover {background-color: #3182ce;border-color: #2c5282;}""")button_layout.addWidget(self.remove_button)left_layout.addLayout(button_layout)# 输出路径选择output_layout = QVBoxLayout()output_label = QLabel("输出路径:")output_label.setFont(QFont('Segoe UI', 12, QFont.Bold))output_layout.addWidget(output_label)self.output_path_button = QPushButton("📂 选择输出目录")self.output_path_button.clicked.connect(self.select_output_directory)self.output_path_button.setStyleSheet("""QPushButton {background-color: #4299e1;border-color: #3182ce;font-size: 24px;padding: 12px;}QPushButton:hover {background-color: #3182ce;border-color: #2c5282;}""")output_layout.addWidget(self.output_path_button)self.output_path_label = QLabel("未选择目录")self.output_path_label.setStyleSheet("""color: #a0aec0;font-size: 20px;padding: 5px;background-color: #1a202c;border-radius: 3px;""")self.output_path_label.setWordWrap(True)output_layout.addWidget(self.output_path_label)left_layout.addLayout(output_layout)# 压缩按钮self.process_button = QPushButton("⚡ 压缩文件")self.process_button.clicked.connect(self.process_files)self.process_button.setStyleSheet("""QPushButton {background-color: #4299e1;border-color: #3182ce;font-size: 24px;padding: 12px;}QPushButton:hover {background-color: #3182ce;border-color: #2c5282;}""")left_layout.addWidget(self.process_button)# 进度条self.progress_bar = QProgressBar()self.progress_bar.setVisible(False)left_layout.addWidget(self.progress_bar)left_layout.addStretch()# 右侧面板 - 预览right_frame = QFrame()right_layout = QVBoxLayout(right_frame)preview_label = QLabel("文件预览:")preview_label.setFont(QFont('Segoe UI', 12, QFont.Bold))right_layout.addWidget(preview_label)self.preview_edit = QTextEdit()self.preview_edit.setPlaceholderText("选择文件以预览...")self.preview_edit.setStyleSheet("font-size: 20px; padding: 5px;")self.preview_edit.setMinimumHeight(400)right_layout.addWidget(self.preview_edit)# 状态标签self.preview_status = QLabel("")self.preview_status.setStyleSheet("color: #a0aec0; font-size: 20px; padding: 5px;")right_layout.addWidget(self.preview_status)# 添加到分割器splitter.addWidget(left_frame)splitter.addWidget(right_frame)splitter.setSizes([350, 650])layout.addWidget(splitter)# 状态栏self.statusBar().setStyleSheet("background-color: #1a202c; color: #a0aec0;")self.statusBar().showMessage("就绪")defsetup_connections(self):"""设置信号连接"""self.file_list.currentItemChanged.connect(self.preview_file)defadd_files(self):"""添加文件到列表"""file_dialog = QFileDialog()file_paths, _ = file_dialog.getOpenFileNames(self, "选择文件", "","网页文件 (*.html *.htm *.css *.js *.json);;所有文件 (*.*)")new_files_count = 0for file_path in file_paths:if file_path notin self.files:self.files.append(file_path)item = QListWidgetItem(f"📄 {os.path.basename(file_path)}")item.setData(Qt.UserRole, file_path)item.setToolTip(file_path)self.file_list.addItem(item)new_files_count += 1if new_files_count > 0:self.statusBar().showMessage(f"已添加 {new_files_count} 个文件")else:self.statusBar().showMessage("没有添加新文件")defremove_file(self):"""从列表中移除文件"""current_row = self.file_list.currentRow()if current_row >= 0:item = self.file_list.takeItem(current_row)file_path = item.data(Qt.UserRole)if file_path in self.files:self.files.remove(file_path)self.statusBar().showMessage("已移除文件")self.preview_edit.clear()self.preview_status.setText("")else:QMessageBox.warning(self, "警告", "请先选择一个文件")defselect_output_directory(self):"""选择输出目录"""directory = QFileDialog.getExistingDirectory(self, "选择输出目录")if directory:self.output_directory = directorydisplay_path = directoryiflen(directory) > 50:display_path = "..." + directory[-47:]self.output_path_label.setText(display_path)self.output_path_label.setToolTip(directory)self.statusBar().showMessage(f"输出目录: {directory}")defpreview_file(self, current, previous):"""预览选中的文件"""if current:file_path = current.data(Qt.UserRole)try:withopen(file_path, 'r', encoding='utf-8') as file:content = file.read()self.preview_edit.setPlainText(content)file_size = os.path.getsize(file_path)file_ext = os.path.splitext(file_path)[1].lower()self.preview_status.setText(f"文件: {os.path.basename(file_path)} | 大小: {self.format_file_size(file_size)} | 类型: {file_ext}")except Exception as e:self.preview_edit.setPlainText(f"无法读取文件: {str(e)}")self.preview_status.setText(f"错误: {str(e)}")defformat_file_size(self, size):"""格式化文件大小显示"""for unit in ['B', 'KB', 'MB', 'GB']:if size < 1024.0:returnf"{size:.1f}{unit}"size /= 1024.0returnf"{size:.1f} TB"defprocess_files(self):"""处理所有选中的文件"""if self.output_directory isNone:QMessageBox.warning(self, "警告", "请先选择输出目录")returnifnot self.files:QMessageBox.warning(self, "警告", "请先添加文件")returnself.progress_bar.setVisible(True)self.progress_bar.setMaximum(len(self.files))self.progress_bar.setValue(0)success_count = 0error_files = []for i, file_path inenumerate(self.files):try:withopen(file_path, 'r', encoding='utf-8') as file:content = file.read()file_ext = os.path.splitext(file_path)[1].lower()processed_content = self.compress_content(content, file_ext)file_name = os.path.basename(file_path)output_path = os.path.join(self.output_directory, file_name)withopen(output_path, 'w', encoding='utf-8') as file:file.write(processed_content)success_count += 1except Exception as e:error_files.append(os.path.basename(file_path))self.progress_bar.setValue(i + 1)self.progress_bar.setVisible(False)message = f"成功处理 {success_count}/{len(self.files)} 个文件"if error_files:message += f"\n失败文件: {', '.join(error_files[:5])}"iflen(error_files) > 5:message += f" 等{len(error_files)}个文件"QMessageBox.information(self, "处理完成", message)self.statusBar().showMessage(f"完成: {success_count}/{len(self.files)} 个文件")defcompress_content(self, content, file_ext):"""压缩文件内容"""# 移除注释content = self.remove_comments(content, file_ext)# 移除缩进和空白content = self.remove_indentation(content, file_ext)# 根据文件类型进行特定压缩if file_ext == '.js':return self.compress_js(content)elif file_ext == '.css':return self.compress_css(content)elif file_ext in ['.html', '.htm']:# 检测是否包含内联CSS或JSif self.has_inline_css_js(content):return self.compress_htmlcssjs(content)else:return self.compress_html(content)elif file_ext == '.json':return self.compress_json(content)else:return self.basic_compress(content)defhas_inline_css_js(self, html_content):"""检测HTML是否包含内联CSS或JS"""# 检测<style>标签style_pattern = re.compile(r'<style[^>]*>[\s\S]*?</style>', re.IGNORECASE)has_style = Falsefor match in style_pattern.finditer(html_content):style_content = match.group(0)# 提取<style>标签内容tag_match = re.match(r'<style[^>]*>([\s\S]*?)</style>', style_content, re.IGNORECASE)if tag_match and tag_match.group(1).strip():has_style = Truebreak# 检测<script>标签script_pattern = re.compile(r'<script[^>]*>[\s\S]*?</script>', re.IGNORECASE)has_script_content = Falsefor match in script_pattern.finditer(html_content):script_tag = match.group(0)# 检查是否有src属性if re.search(r'src\s*=', script_tag, re.IGNORECASE):# 如果有src属性,检查标签内是否还有内容tag_match = re.match(r'<script[^>]*>([\s\S]*?)</script>', script_tag, re.IGNORECASE)if tag_match and tag_match.group(1).strip():# 既有src属性又有内容has_script_content = Truebreakelse:# 若没有src属性,检查标签内是否有内容tag_match = re.match(r'<script[^>]*>([\s\S]*?)</script>', script_tag, re.IGNORECASE)if tag_match and tag_match.group(1).strip():has_script_content = Truebreak# 检测内联事件处理has_inline_events = re.search(r'on\w+\s*=\s*["\'][^"\']*["\']', html_content, re.IGNORECASE) isnotNone# 检测内联样式has_inline_style = re.search(r'style\s*=\s*["\'][^"\']*["\']', html_content, re.IGNORECASE) isnotNone# 调试信息:在控制台输出检测结果print(f"HTML内联代码检测结果:")print(f"包含内联CSS(<style>标签): {has_style}")print(f"包含内联JS(<script>标签内容): {has_script_content}")print(f"包含内联事件(如onclick): {has_inline_events}")print(f"包含内联样式(style属性): {has_inline_style}")print(f"使用特殊压缩(有内联CSS或内联JS): {has_style or has_script_content}")print(f"使用普通HTML压缩: {not (has_style or has_script_content)}")return has_style or has_script_contentdefremove_comments(self, content, file_ext):"""根据文件类型移除注释"""if file_ext == '.js':# 移除多行注释 /* ... */content = re.sub(r'/\*[\s\S]*?\*/', '', content)# 移除单行注释 //content = re.sub(r'//.*', '', content)elif file_ext == '.css':# 移除CSS注释 /* ... */content = re.sub(r'/\*[\s\S]*?\*/', '', content)elif file_ext in ['.html', '.htm']:# 移除HTML注释 <!-- ... -->content = re.sub(r'<!--[\s\S]*?-->', '', content)elif file_ext == '.json':# JSON不支持注释,直接返回passreturn contentdefremove_indentation(self, content, file_ext):"""去除缩进和多余空白"""lines = content.split('\n')processed_lines = []for line in lines:line = line.strip()line = re.sub(r'\s+', ' ', line)if line:processed_lines.append(line)if file_ext == '.json':# JSON保持单行格式return' '.join(processed_lines)elif file_ext in ['.html', '.htm']:# HTML标签紧贴joined = ' '.join(processed_lines)return re.sub(r'>\s+<', '><', joined)else:# 其他文件用空格连接return' '.join(processed_lines)defbasic_compress(self, content):"""基本压缩"""content = re.sub(r'\s+', ' ', content)return content.strip()defcompress_js(self, content):"""压缩JavaScript代码"""content = re.sub(r'\s*([=+\-*/%&|^<>!?:;,{}()])\s*', r'\1', content)content = re.sub(r';(\w)', r'; \1', content)content = re.sub(r'(\w+)\s*\(', r'\1(', content)content = re.sub(r'\.\s+', '.', content)return content.strip()defcompress_css(self, content):"""压缩CSS代码"""content = re.sub(r'\s*([{}:;,])\s*', r'\1', content)content = re.sub(r';}', '}', content)content = re.sub(r':(\w)', r': \1', content)return content.strip()defcompress_html(self, content):"""压缩HTML代码"""content = re.sub(r'>\s+<', '><', content)content = re.sub(r'<(\w+)([^>]*?)\s*>', r'<\1\2>', content)content = re.sub(r'\s+=\s+', '=', content)return content.strip()defcompress_json(self, content):"""压缩JSON代码"""try:data = json.loads(content)return json.dumps(data, separators=(',', ':'))except:return self.basic_compress(content)defcompress_htmlcssjs(self, content):"""压缩内联CSS和JS的HTML文件"""original_len = len(content)# 除HTML注释content = re.sub(r'<!--[\s\S]*?-->', '', content)# 按行处理,保持基本结构lines = content.split('\n')processed_lines = []for line in lines:line = line.strip()ifnot line:continueline = re.sub(r'\s+', ' ', line)processed_lines.append(line)content = ' '.join(processed_lines)# 处理<style>标签中的CSSstyle_pattern = re.compile(r'(<style[^>]*>)(.*?)(</style>)', re.IGNORECASE | re.DOTALL)defprocess_style(match):tag_start, css_content, tag_end = match.groups()# 移除CSS注释css_content = re.sub(r'/\*[\s\S]*?\*/', '', css_content)# 安全压缩CSSlines = []for css_line in css_content.split(';'):css_line = css_line.strip()if css_line:css_line = re.sub(r'\s*{\s*', '{', css_line)css_line = re.sub(r'\s*}\s*', '}', css_line)css_line = re.sub(r'\s*:\s*', ':', css_line)css_line = re.sub(r'\s*,\s*', ',', css_line)css_line = re.sub(r'\s+', ' ', css_line)lines.append(css_line)compressed_css = ';'.join(lines)# 确保每个规则正确结束compressed_css = re.sub(r'([^;}])$', r'\1;', compressed_css)returnf'{tag_start}{compressed_css}{tag_end}'content = style_pattern.sub(process_style, content)# 找到所有<script>标签的位置script_tags = []script_positions = []# 查找所有的<script>开始标签pattern = re.compile(r'<script[^>]*>', re.IGNORECASE)for match in pattern.finditer(content):script_tags.append(match.group())script_positions.append(match.start())# 迭代方式处理脚本标签if script_positions:for i inrange(len(script_positions) - 1, -1, -1):start_pos = script_positions[i]tag_end_pos = content.find('>', start_pos)if tag_end_pos == -1:continuecontent_start = tag_end_pos + 1end_script_pos = content.find('</script>', content_start)if end_script_pos == -1:continue# 提取<script>开始标签和内容script_start_tag = content[start_pos:tag_end_pos + 1]script_content = content[content_start:end_script_pos]# 处理JS内容(区分外部脚本和内联脚本)if'src='in script_start_tag.lower():# 外部脚本,保持内容不变processed_content = script_contentelse:# 内联脚本,进行压缩processed_content = self.compress_javascript_content(script_content)# 重新构建整个<script>标签new_script_tag = f"{script_start_tag}{processed_content}</script>"# 替换原内容中的<script>标签部分content = content[:start_pos] + new_script_tag + content[end_script_pos + 9:] # 9是len('</script>')# 处理HTML标签结构defprocess_html_structure(html):"""处理HTML结构,确保标签正确"""# 处理自闭合标签self_closing_tags = ['meta', 'link', 'img', 'br', 'hr', 'input']for tag in self_closing_tags:pattern = re.compile(f'<{tag}([^>]*?)\\s*>', re.IGNORECASE)html = pattern.sub(f'<{tag}\\1>', html)# 处理普通标签html = re.sub(r'<(\w+)\s*>', r'<\1>', html)# 确保标签名和属性间有空格deffix_tag_spacing(match):full_match = match.group(0)if re.search(r'<\w+[a-zA-Z0-9]', full_match):tag_end_match = re.search(r'<\w+', full_match)if tag_end_match:tag_name_end = tag_end_match.end()fixed = full_match[:tag_name_end] + ' ' + full_match[tag_name_end:]return fixedreturn full_matchtag_pattern = re.compile(r'<\w+[^>]*>')html = tag_pattern.sub(fix_tag_spacing, html)# 压缩标签内的空白defcompress_tag_internals(match):tag_content = match.group(0)tag_content = re.sub(r'\s+=\s+', '=', tag_content)tag_content = re.sub(r'\s+', ' ', tag_content)return tag_contenthtml = tag_pattern.sub(compress_tag_internals, html)# 移除标签间的空白html = re.sub(r'>\s+<', '><', html)return htmlcontent = process_html_structure(content)# 修复CSS错误content = re.sub(r'};}', r'}}', content)content = re.sub(r'};', r'}', content)content = re.sub(r'(\w+):(\w+)', r'\1:\2', content)# 处理内联事件处理器defprocess_inline_events(match):attr_name = match.group(1)quote_char = match.group(2)js_code = match.group(3)js_code = re.sub(r'\s+', ' ', js_code.strip())returnf'{attr_name}={quote_char}{js_code}{quote_char}'inline_event_pattern = re.compile(r'(on\w+)=(["\'])(.*?)\2', re.IGNORECASE | re.DOTALL)content = inline_event_pattern.sub(process_inline_events, content)# 处理内联样式defprocess_inline_styles(match):attr_name = match.group(1)quote_char = match.group(2)css_code = match.group(3)css_code = re.sub(r'\s*:\s*', ':', css_code)css_code = re.sub(r'\s*;\s*', ';', css_code)css_code = re.sub(r'\s+', ' ', css_code.strip())returnf'{attr_name}={quote_char}{css_code}{quote_char}'inline_style_pattern = re.compile(r'(style)=(["\'])(.*?)\2', re.IGNORECASE | re.DOTALL)content = inline_style_pattern.sub(process_inline_styles, content)# 确保DOCTYPE正确if'<!DOCTYPE'in content and'html>'in content:doctype_end = content.find('html>') + 5doctype_part = content[:doctype_end]rest = content[doctype_end:]# 确保DOCTYPE后没有紧贴其他标签if rest and rest[0] != ' ':content = doctype_part + ' ' + rest# 修复可能的多余空格content = re.sub(r'\s+', ' ', content).strip()# 检查是否有空的<script></script>标签empty_scripts = re.findall(r'<script[^>]*>\s*</script>', content, re.IGNORECASE)if empty_scripts:print(f"警告:发现 {len(empty_scripts)} 个空的<script>标签")print(f"压缩完成:")print(f" 原始长度: {original_len} 字符")print(f" 压缩后长度: {len(content)} 字符")if original_len > 0:compression_rate = (1 - len(content) / original_len) * 100print(f" 压缩率: {compression_rate:.1f}%")return contentdefcompress_javascript_content(self, js_content):"""压缩HTML文件内联JavaScript内容"""# 如果内容为空或只有空白,返回原样ifnot js_content or js_content.strip() == '':return js_contentoriginal_js = js_content# 移除多行注释 /* ... */js_content = re.sub(r'/\*[\s\S]*?\*/', '', js_content)# 移除单行注释 //lines = js_content.split('\n')processed_lines = []for line in lines:# 处理单行注释in_string = Falsestring_char = Noneescaped = Falseresult_chars = []i = 0while i < len(line):char = line[i]# 处理转义字符if escaped:result_chars.append(char)escaped = Falsei += 1continue# 处理字符串if in_string:if char == '\\':escaped = Trueelif char == string_char:in_string = Falseresult_chars.append(char)i += 1continue# 检查字符串开始if char in ['"', "'"]:in_string = Truestring_char = charresult_chars.append(char)i += 1continue# 检查单行注释if char == '/'and i + 1 < len(line) and line[i + 1] == '/':# 找到注释开始,跳过行剩余部分break# 正常字符result_chars.append(char)i += 1processed_line = ''.join(result_chars).strip()if processed_line:processed_lines.append(processed_line)js_content = '\n'.join(processed_lines)# 如果处理后内容为空,返回原始内容ifnot js_content.strip():print("警告:JS内容处理后为空,返回原始内容")return original_js# 将多行压缩为一行,但保留基本结构js_content = ' '.join(line.strip() for line in js_content.split('\n') if line.strip())# 压缩运算符周围的空格js_content = re.sub(r'\s*([=!<>+\-*/%&|^])\s*=\s*', r'\1=', js_content)js_content = re.sub(r'=\s*=\s*=', r'===', js_content)js_content = re.sub(r'!\s*=\s*=', r'!==', js_content)# 压缩函数调用js_content = re.sub(r'(\w)\s*\(\s*', r'\1(', js_content)js_content = re.sub(r'\s*\)\s*', r')', js_content)# 压缩大括号和分号js_content = re.sub(r'\s*{\s*', r'{', js_content)js_content = re.sub(r'\s*}\s*', r'}', js_content)js_content = re.sub(r'\s*;\s*', r';', js_content)js_content = re.sub(r'\s*,\s*', r',', js_content)js_content = re.sub(r'\s*:\s*', r':', js_content)# 压缩点号周围的空格js_content = re.sub(r'\s*\.\s*', r'.', js_content)# 压缩多余空格,但保留基本的空格js_content = re.sub(r'\s+', ' ', js_content).strip()# 确保不以分号结尾js_content = re.sub(r';\s*$', ';', js_content)return js_contentif __name__ == '__main__':app = QApplication(sys.argv)app.setStyle('Fusion')window = WebCodeCompressorApp()window.show()sys.exit(app.exec_())
源代码:基于PyQt5开发、利用pyinstaller打包
排版|秀米、玉箫制作
代码|玉箫制作
文字|玉箫制作
图片|玉箫制作
代码均为原创,仅供参考
部分图像素材源于网络
如有侵权请联系删除!