generate_frontend_sourcecode.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 前端源代码拼接脚本
  5. 将 output_sourcecode/front/ 目录下所有HTML文件内容拼接生成统一的前端源代码文档
  6. CSS处理策略:
  7. - 彻底移除 <style> 标签及其内容
  8. - 移除 CSS 外部链接 (rel="stylesheet")
  9. - 移除内联样式属性 (style="...")
  10. - 保留HTML结构和JavaScript逻辑
  11. - 保留class属性(可能对JavaScript功能重要)
  12. 这样可以显著减少文档长度,突出核心程序逻辑,更适合软著申请材料要求
  13. """
  14. import os
  15. import re
  16. import math
  17. def remove_css_content(html_content):
  18. """
  19. 彻底移除HTML中的CSS样式内容,只保留HTML结构和JavaScript逻辑
  20. """
  21. # 移除 <style> 标签及其内容
  22. html_content = re.sub(r'<style[^>]*>.*?</style>',
  23. '\n <!-- CSS样式已省略,完整CSS请查看原始HTML文件 -->\n',
  24. html_content, flags=re.DOTALL)
  25. # 移除CSS外部链接(保留JavaScript和字体链接)
  26. html_content = re.sub(r'<link[^>]*rel=["\']stylesheet["\'][^>]*>',
  27. ' <!-- CSS外部链接已省略 -->',
  28. html_content, flags=re.IGNORECASE)
  29. # 移除内联样式属性
  30. html_content = re.sub(r'\s+style=["\'][^"\']*["\']', '', html_content)
  31. # 移除CSS相关的class属性(可选,保留功能性class)
  32. # 这里我们保留class属性,因为它们可能对JavaScript功能重要
  33. return html_content
  34. def estimate_tokens(text):
  35. """
  36. 估算文本的token数量 (粗略估算:1 token ≈ 4 个字符)
  37. """
  38. return len(text) // 4
  39. def split_content_by_token_limit(html_files, front_dir, max_tokens=30000):
  40. """
  41. 根据token限制智能分批HTML文件
  42. """
  43. batches = []
  44. current_batch = []
  45. current_tokens = 0
  46. for html_file in html_files:
  47. file_path = os.path.join(front_dir, html_file)
  48. try:
  49. with open(file_path, 'r', encoding='utf-8') as f:
  50. content = f.read()
  51. # 移除CSS后估算token数
  52. clean_content = remove_css_content(content)
  53. file_tokens = estimate_tokens(clean_content)
  54. # 如果单个文件就超过限制,需要进一步处理
  55. if file_tokens > max_tokens:
  56. # 如果当前批次不为空,先保存
  57. if current_batch:
  58. batches.append(current_batch)
  59. current_batch = []
  60. current_tokens = 0
  61. # 将大文件单独作为一个批次(或进一步拆分)
  62. batches.append([html_file])
  63. print(f"⚠️ 文件 {html_file} 较大 (~{file_tokens} tokens),单独处理")
  64. continue
  65. # 检查加入当前文件后是否超限
  66. if current_tokens + file_tokens > max_tokens and current_batch:
  67. # 保存当前批次,开始新批次
  68. batches.append(current_batch)
  69. current_batch = [html_file]
  70. current_tokens = file_tokens
  71. else:
  72. # 加入当前批次
  73. current_batch.append(html_file)
  74. current_tokens += file_tokens
  75. except Exception as e:
  76. print(f"⚠️ 读取文件 {html_file} 时出错: {e}")
  77. current_batch.append(html_file) # 仍然加入批次,稍后处理
  78. # 保存最后一个批次
  79. if current_batch:
  80. batches.append(current_batch)
  81. return batches
  82. def compress_html_content(html_content, compression_level=1):
  83. """
  84. 进一步压缩HTML内容以减少token数量
  85. compression_level:
  86. 1 - 轻度压缩:移除多余空白,保留结构
  87. 2 - 中度压缩:移除注释,简化标签
  88. 3 - 重度压缩:只保留核心结构和JavaScript
  89. """
  90. if compression_level >= 1:
  91. # 移除多余的空白和换行
  92. html_content = re.sub(r'\n\s*\n', '\n', html_content) # 移除空行
  93. html_content = re.sub(r'^\s+', '', html_content, flags=re.MULTILINE) # 移除行首空白
  94. if compression_level >= 2:
  95. # 移除HTML注释
  96. html_content = re.sub(r'<!--[^>]*-->', '', html_content, flags=re.DOTALL)
  97. # 移除多余的标签属性(保留重要的id, class, onclick等)
  98. # 这里可以根据需要进一步定制
  99. if compression_level >= 3:
  100. # 重度压缩:只保留核心结构
  101. # 移除大部分非功能性内容,只保留关键的DOM结构和JavaScript
  102. html_content = re.sub(r'<meta[^>]*>', '', html_content, flags=re.IGNORECASE)
  103. html_content = re.sub(r'<link[^>]*>', '', html_content, flags=re.IGNORECASE)
  104. return html_content
  105. def extract_html_files(front_dir):
  106. """
  107. 提取前端目录中的所有HTML文件
  108. """
  109. html_files = []
  110. if os.path.exists(front_dir):
  111. for file in os.listdir(front_dir):
  112. if file.endswith('.html'):
  113. html_files.append(file)
  114. # 按文件名排序
  115. html_files.sort()
  116. return html_files
  117. def generate_frontend_sourcecode():
  118. """
  119. 生成前端源代码文档
  120. """
  121. # 定义路径 (脚本移动到子目录后需要调整相对路径)
  122. base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # 回到项目根目录
  123. front_dir = os.path.join(base_dir, 'output_sourcecode', 'front')
  124. output_dir = os.path.join(base_dir, 'output_docs')
  125. output_file = os.path.join(output_dir, '前端源代码.txt')
  126. # 确保输出目录存在
  127. os.makedirs(output_dir, exist_ok=True)
  128. # 获取所有HTML文件
  129. html_files = extract_html_files(front_dir)
  130. if not html_files:
  131. print(f"错误:在 {front_dir} 目录下没有找到HTML文件")
  132. return
  133. print(f"找到 {len(html_files)} 个HTML文件:")
  134. for file in html_files:
  135. print(f" - {file}")
  136. # 智能分批处理以避免token超限
  137. print("\n🔍 分析文件大小并智能分批...")
  138. batches = split_content_by_token_limit(html_files, front_dir, max_tokens=25000)
  139. print(f"📊 将生成 {len(batches)} 个文档文件:")
  140. for i, batch in enumerate(batches, 1):
  141. print(f" 批次 {i}: {len(batch)} 个文件")
  142. # 开始生成文档
  143. print("\n🚀 开始生成前端源代码文档...")
  144. generated_files = []
  145. for batch_idx, batch in enumerate(batches, 1):
  146. # 为每个批次生成单独的文件
  147. if len(batches) == 1:
  148. batch_output_file = output_file
  149. else:
  150. batch_output_file = output_file.replace('.txt', f'_part{batch_idx}.txt')
  151. generated_files.append(batch_output_file)
  152. print(f"\n📝 生成批次 {batch_idx}/{len(batches)} ({len(batch)} 个文件)")
  153. with open(batch_output_file, 'w', encoding='utf-8') as f:
  154. # 写入批次说明头部
  155. if len(batches) > 1:
  156. f.write(f"前端源代码文档 - 第 {batch_idx} 部分\n")
  157. f.write(f"包含文件: {', '.join(batch)}\n")
  158. f.write(f"生成时间: {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
  159. f.write(f"{'='*80}\n\n")
  160. total_tokens = 0
  161. # 处理当前批次的每个HTML文件
  162. for i, html_file in enumerate(batch, 1):
  163. file_path = os.path.join(front_dir, html_file)
  164. print(f" 处理文件 {i}/{len(batch)}: {html_file}")
  165. try:
  166. with open(file_path, 'r', encoding='utf-8') as html_f:
  167. html_content = html_f.read()
  168. # 移除CSS内容
  169. html_content = remove_css_content(html_content)
  170. # 如果内容仍然过大,进行压缩
  171. file_tokens = estimate_tokens(html_content)
  172. if file_tokens > 15000: # 单文件超过15K tokens时压缩
  173. print(f" ⚠️ 文件较大,应用压缩 (~{file_tokens} tokens)")
  174. html_content = compress_html_content(html_content, compression_level=2)
  175. file_tokens = estimate_tokens(html_content)
  176. print(f" ✅ 压缩后 ~{file_tokens} tokens")
  177. total_tokens += file_tokens
  178. # 写入文件分隔标识和源代码
  179. f.write(f"=== {html_file} ===\n")
  180. f.write(html_content)
  181. f.write("\n\n")
  182. except Exception as e:
  183. print(f" ❌ 处理文件 {html_file} 时出错: {e}")
  184. f.write(f"=== {html_file} ===\n")
  185. f.write(f"错误:无法读取文件内容 - {e}\n\n")
  186. print(f" 📊 批次 {batch_idx} 预估token数: ~{total_tokens}")
  187. print(f"\n✅ 前端源代码文档生成完成!")
  188. if len(generated_files) == 1:
  189. print(f"📁 输出文件: {generated_files[0]}")
  190. else:
  191. print(f"📁 输出文件 ({len(generated_files)} 个):")
  192. for i, file_path in enumerate(generated_files, 1):
  193. print(f" {i}. {file_path}")
  194. # 显示文件大小统计
  195. total_size = 0
  196. print(f"\n📊 文件大小统计:")
  197. try:
  198. for i, file_path in enumerate(generated_files, 1):
  199. file_size = os.path.getsize(file_path)
  200. total_size += file_size
  201. if file_size > 1024 * 1024:
  202. size_str = f"{file_size / (1024 * 1024):.2f} MB"
  203. elif file_size > 1024:
  204. size_str = f"{file_size / 1024:.2f} KB"
  205. else:
  206. size_str = f"{file_size} bytes"
  207. if len(generated_files) > 1:
  208. print(f" Part {i}: {size_str}")
  209. else:
  210. print(f" 总大小: {size_str}")
  211. if len(generated_files) > 1:
  212. if total_size > 1024 * 1024:
  213. total_str = f"{total_size / (1024 * 1024):.2f} MB"
  214. elif total_size > 1024:
  215. total_str = f"{total_size / 1024:.2f} KB"
  216. else:
  217. total_str = f"{total_size} bytes"
  218. print(f" 📊 总计: {total_str}")
  219. except Exception as e:
  220. print(f" ⚠️ 无法获取文件大小: {e}")
  221. # 智能建议
  222. if len(generated_files) > 1:
  223. print(f"\n💡 建议:")
  224. print(f" • 生成了 {len(generated_files)} 个分段文件以避免token超限")
  225. print(f" • 在AI对话中可以分批次粘贴每个文件内容")
  226. print(f" • 或者选择最重要的几个页面单独处理")
  227. def main():
  228. """
  229. 主函数
  230. """
  231. print("=" * 60)
  232. print("前端源代码拼接脚本")
  233. print("=" * 60)
  234. try:
  235. generate_frontend_sourcecode()
  236. except Exception as e:
  237. print(f"❌ 脚本执行失败: {e}")
  238. return 1
  239. return 0
  240. if __name__ == "__main__":
  241. exit(main())