[Python] 纯文本查看 复制代码
import re
import sys
import requests
from bs4 import BeautifulSoup
import pyperclip
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QLabel, QLineEdit, QPushButton,
QRadioButton, QButtonGroup, QMessageBox, QFrame,
QTextEdit, QProgressBar)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QFont, QIcon
from PyQt5.QtWebEngineWidgets import QWebEngineView
class ExtractThread(QThread):
"""提取线程"""
finished = pyqtSignal(str, str) # (status, content/message)
def __init__(self, editor_type, tid):
super().__init__()
self.editor_type = editor_type
self.tid = tid
def run(self):
try:
if self.editor_type == '135':
content = self.get_135_html(self.tid)
else:
content = self.get_yiban_html(self.tid)
# 复制到剪贴板
pyperclip.copy(content)
self.finished.emit('success', content)
except Exception as e:
self.finished.emit('error', str(e))
def get_135_html(self, tid):
"""从135编辑器获取模板HTML"""
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.289 Safari/537.36'
}
url = f'https://www.135editor.com/editor_styles/{tid}.html'
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status()
com = re.compile('<div class=\"l-img\">.*?</div>', re.S)
res = com.findall(resp.text)
if res:
return res[0]
else:
raise ValueError('模板ID错误,无法获取数据')
def get_yiban_html(self, tid):
"""从壹伴助手获取模板HTML"""
try:
return self.yiban_template_api(tid)
except Exception as e:
print(f'模板接口失败尝试样式接口: {e}')
try:
return self.yiban_style_api(tid)
except Exception as e2:
raise ValueError('ID错误,无法获取数据')
def yiban_template_api(self, tid):
"""壹伴模板API"""
url = f'https://yiban.io/api/style_template/system/one?style_template_id={tid}'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'Referer': 'https://yiban.io/',
'Origin': 'https://yiban.io'
}
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status()
data = resp.json()
if not data.get('success'):
raise ValueError(data.get('status_message', '接口返回错误'))
html = data['style_template']['total']
if not html:
raise ValueError('模板内容为空')
return html
def yiban_style_api(self, sid):
"""壹伴样式API"""
url = f'https://yiban.io/api/article_editor/material/one?material_id={sid}'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'Referer': 'https://yiban.io/',
'Origin': 'https://yiban.io'
}
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status()
data = resp.json()
if not data.get('success'):
raise ValueError(data.get('status_message', '接口返回错误'))
html = data['material']['detail']
soup = BeautifulSoup(html, 'html.parser')
sections = soup.find_all('section')
for s in sections:
if s.get('data-role') == 'paragraph' and (not s.get_text(strip=True)):
continue
if s.get('style') or s.find('img') or s.get_text(strip=True) or s.get('data-id'):
return str(s)
return html
class TemplateExtractor(QMainWindow):
"""模板提取助手主窗口"""
def __init__(self):
super().__init__()
self.current_html = ""
self.init_ui()
self.set_default_values()
def init_ui(self):
"""初始化UI"""
self.setWindowTitle('模板提取助手')
self.resize(1400, 800)
# 设置窗口样式
self.setStyleSheet("""
QMainWindow {
background-color: #f5f7fa;
}
QLabel {
color: #333333;
font-family: "Microsoft YaHei", "微软雅黑";
}
QRadioButton {
font-family: "Microsoft YaHei", "微软雅黑";
font-size: 12px;
color: #333333;
spacing: 8px;
}
QRadioButton::indicator {
width: 15px;
height: 15px;
}
QLineEdit {
font-family: "Microsoft YaHei", "微软雅黑";
font-size: 12px;
padding: 5px;
border: 1px solid #dcdcdc;
border-radius: 3px;
background-color: white;
}
QLineEdit:focus {
border-color: #4CAF50;
}
QPushButton {
font-family: "Microsoft YaHei", "微软雅黑";
font-size: 12px;
font-weight: bold;
padding: 8px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
QPushButton:hover {
opacity: 0.9;
}
QPushButton:pressed {
opacity: 0.8;
}
QPushButton:disabled {
opacity: 0.5;
cursor: not-allowed;
}
QTextEdit {
border: 1px solid #dcdcdc;
border-radius: 4px;
font-family: "Consolas", "Monaco", monospace;
font-size: 11px;
}
""")
# 中央窗口部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
# 主布局(水平布局:左侧控制区+代码区,右侧预览区)
main_layout = QHBoxLayout(central_widget)
main_layout.setSpacing(10)
main_layout.setContentsMargins(10, 10, 10, 10)
# ========== 左侧区域(控制区 + 代码区) ==========
left_widget = QWidget()
left_layout = QVBoxLayout(left_widget)
left_layout.setSpacing(10)
left_layout.setContentsMargins(0, 0, 0, 0)
# 控制区域
control_frame = QFrame()
control_frame.setStyleSheet("""
QFrame {
background-color: white;
border-radius: 8px;
padding: 10px;
}
""")
control_layout = QVBoxLayout(control_frame)
control_layout.setSpacing(8) # 减少间距
# 标题
title_label = QLabel('📝 模板提取助手')
title_font = QFont('Microsoft YaHei', 16, QFont.Bold)
title_label.setFont(title_font)
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet('color: #333333; margin-bottom: 5px;')
control_layout.addWidget(title_label)
# 编辑器选择区域
selection_layout = QHBoxLayout()
selection_layout.setSpacing(20)
self.radio_135 = QRadioButton('🔹 135编辑器')
self.radio_yiban = QRadioButton('🔸 壹伴助手')
self.radio_135.setChecked(True)
# 设置单选按钮组
self.button_group = QButtonGroup()
self.button_group.addButton(self.radio_135, 1)
self.button_group.addButton(self.radio_yiban, 2)
# 连接信号
self.radio_135.toggled.connect(self.on_editor_change)
self.radio_yiban.toggled.connect(self.on_editor_change)
selection_layout.addWidget(self.radio_135)
selection_layout.addWidget(self.radio_yiban)
selection_layout.addStretch()
control_layout.addLayout(selection_layout)
# 输入和操作区域
action_layout = QHBoxLayout()
action_layout.setSpacing(10)
action_layout = QHBoxLayout()
action_layout.setSpacing(10)
tip_label = QLabel('模板ID:')
tip_label.setStyleSheet('font-size: 12px; font-weight: bold;')
action_layout.addWidget(tip_label)
self.id_input = QLineEdit()
self.id_input.setFixedWidth(150)
self.id_input.setPlaceholderText('请输入模板ID')
self.id_input.returnPressed.connect(self.start_extract)
action_layout.addWidget(self.id_input)
# 左侧弹簧
action_layout.addStretch()
# 提取按钮
self.extract_btn = QPushButton('开始提取')
self.extract_btn.setFixedSize(120, 35)
self.extract_btn.setStyleSheet("""
QPushButton {
background-color: #4CAF50;
color: white;
}
QPushButton:hover {
background-color: #45a049;
}
""")
self.extract_btn.clicked.connect(self.start_extract)
action_layout.addWidget(self.extract_btn)
# 复制按钮
self.copy_btn = QPushButton('复制代码')
self.copy_btn.setFixedSize(100, 35)
self.copy_btn.setEnabled(False)
self.copy_btn.setStyleSheet("""
QPushButton {
background-color: #2196F3;
color: white;
}
QPushButton:hover {
background-color: #1976D2;
}
QPushButton:disabled {
background-color: #cccccc;
}
""")
self.copy_btn.clicked.connect(self.copy_html)
action_layout.addWidget(self.copy_btn)
# 使用教程按钮
self.tutorial_btn = QPushButton('使用教程')
self.tutorial_btn.setFixedSize(100, 35)
self.tutorial_btn.setStyleSheet("""
QPushButton {
background-color: #FF9800;
color: white;
}
QPushButton:hover {
background-color: #F57C00;
}
""")
self.tutorial_btn.clicked.connect(self.show_tutorial)
action_layout.addWidget(self.tutorial_btn)
# 右侧弹簧
action_layout.addStretch()
# 状态标签
self.status_label = QLabel('')
self.status_label.setStyleSheet('color: #888888; font-size: 10px;')
action_layout.addWidget(self.status_label)
control_layout.addLayout(action_layout)
# 进度条
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
self.progress_bar.setFixedHeight(3)
self.progress_bar.setStyleSheet("""
QProgressBar {
border: none;
background-color: #e0e0e0;
border-radius: 2px;
}
QProgressBar::chunk {
background-color: #4CAF50;
border-radius: 2px;
}
""")
control_layout.addWidget(self.progress_bar)
left_layout.addWidget(control_frame)
# HTML代码显示区域(纯文本,自适应伸缩)
code_frame = QFrame()
code_frame.setStyleSheet("""
QFrame {
background-color: white;
border-radius: 8px;
padding: 5px;
}
""")
code_layout = QVBoxLayout(code_frame)
code_layout.setSpacing(5)
code_layout.setContentsMargins(10, 5, 10, 10)
code_label = QLabel('📄 HTML代码')
code_label.setStyleSheet('font-weight: bold; font-size: 12px; padding: 0px;')
code_layout.addWidget(code_label)
# 使用QTextEdit显示纯文本HTML代码(不会渲染)
self.code_editor = QTextEdit()
self.code_editor.setFont(QFont('Consolas', 10))
self.code_editor.setPlainText('') # 设置为纯文本模式
self.code_editor.setStyleSheet("""
QTextEdit {
background-color: #1e1e1e;
color: #d4d4d4;
border: none;
font-family: "Consolas", "Monaco", monospace;
font-size: 11px;
}
""")
code_layout.addWidget(self.code_editor)
left_layout.addWidget(code_frame, 1) # 设置拉伸因子为1,让代码区自适应伸缩
# ========== 右侧区域(实时预览) ==========
right_widget = QWidget()
right_widget.setFixedWidth(800) # 添加这一行,固定宽度800
right_layout = QVBoxLayout(right_widget)
right_layout.setSpacing(5)
right_layout.setContentsMargins(0, 0, 0, 0)
preview_frame = QFrame()
preview_frame.setStyleSheet("""
QFrame {
background-color: white;
border-radius: 8px;
padding: 5px;
}
""")
preview_layout = QVBoxLayout(preview_frame)
preview_layout.setSpacing(5) # 减少标签和内容之间的间距
preview_layout.setContentsMargins(10, 5, 10, 10)
preview_label = QLabel('🌐 实时预览')
preview_label.setStyleSheet('font-weight: bold; font-size: 12px; padding: 0px;')
preview_layout.addWidget(preview_label)
# 预览区域自适应高度
self.web_view = QWebEngineView()
self.web_view.setStyleSheet("border: none; border-radius: 4px;")
preview_layout.addWidget(self.web_view, 1) # 设置拉伸因子,让预览区自适应伸缩
right_layout.addWidget(preview_frame, 1) # 整个右侧区域自适应伸缩
# 将左右区域添加到主布局
main_layout.addWidget(left_widget, 1)
main_layout.addWidget(right_widget) # 右侧占1份宽度
def show_tutorial(self):
"""显示使用教程"""
tutorial_text = """使用教程
公众号模板提取器使用说明:
1. 选择编辑器类型(135编辑器 或 壹伴助手)
2. 输入对应的模板ID
3. 点击"开始提取"按钮
4. 提取成功后,HTML代码会自动复制到剪贴板
5. 打开微信公众号编辑器
6. 切换到HTML代码模式
7. 粘贴代码即可看到模板效果
温馨提示:
• 135编辑器默认ID:137387
• 壹伴助手默认ID:24190
• 提取的代码已自动复制,直接粘贴使用"""
QMessageBox.information(self, '使用教程', tutorial_text)
def set_default_values(self):
"""设置默认值"""
self.id_input.setText('137387')
def on_editor_change(self):
"""切换编辑器类型时更新界面"""
if self.radio_135.isChecked():
self.setWindowTitle('135收费模板提取助手 - 带预览')
self.id_input.setText('137387')
self.extract_btn.setStyleSheet("""
QPushButton {
background-color: #4CAF50;
color: white;
}
QPushButton:hover {
background-color: #45a049;
}
""")
else:
self.setWindowTitle('壹伴样式提取助手 - 带预览')
self.id_input.setText('24190')
self.extract_btn.setStyleSheet("""
QPushButton {
background-color: #FF9800;
color: white;
}
QPushButton:hover {
background-color: #F57C00;
}
""")
self.id_input.setFocus()
def start_extract(self):
"""启动提取任务"""
tid = self.id_input.text().strip()
if not tid:
QMessageBox.warning(self, '⚠️ 提示', '请输入模板ID')
return
# 获取编辑器类型
editor_type = '135' if self.radio_135.isChecked() else 'yiban'
# 禁用按钮并更新状态
self.extract_btn.setEnabled(False)
self.extract_btn.setText('提取中...')
self.copy_btn.setEnabled(False)
self.status_label.setText('正在获取模板,请稍候...')
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0) # 无限进度
# 清空之前的内容
self.code_editor.clear()
self.web_view.setHtml(
'<html><body style="background-color:#f5f7fa; display:flex; align-items:center; justify-content:center; height:100vh; margin:0;"><p style="color:#999; font-family:Arial;">加载中...</p></body></html>')
# 创建并启动提取线程
self.extract_thread = ExtractThread(editor_type, tid)
self.extract_thread.finished.connect(self.on_extract_finished)
self.extract_thread.start()
def on_extract_finished(self, status, content):
"""提取完成回调"""
# 恢复按钮状态
self.extract_btn.setEnabled(True)
self.extract_btn.setText('开始提取')
self.status_label.setText('')
self.progress_bar.setVisible(False)
# 显示结果
if status == 'success':
self.current_html = content
# 在代码编辑器中显示纯HTML代码(使用setPlainText确保显示原始代码)
self.code_editor.setPlainText(content)
# 在Web视图中预览渲染效果
self.web_view.setHtml(content)
# 启用复制按钮
self.copy_btn.setEnabled(True)
# 显示成功消息
QMessageBox.information(self, '✅ 成功', '模板提取成功!\n已复制到剪贴板,并显示在下方预览区。')
else:
self.web_view.setHtml(
f'<html><body style="background-color:#f5f7fa; display:flex; align-items:center; justify-content:center; height:100vh; margin:0;"><p style="color:#f44336; font-family:Arial;">提取失败:{content}</p></body></html>')
QMessageBox.critical(self, '❌ 错误', f'提取失败:{content}')
def copy_html(self):
"""复制HTML代码到剪贴板"""
if self.current_html:
pyperclip.copy(self.current_html)
QMessageBox.information(self, '✅ 成功', 'HTML代码已复制到剪贴板!')
else:
QMessageBox.warning(self, '⚠️ 提示', '没有可复制的HTML代码')
def main():
"""主函数"""
app = QApplication(sys.argv)
# 设置应用程序图标
try:
app.setWindowIcon(QIcon('icon.ico'))
except:
pass
window = TemplateExtractor()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()