#!/usr/bin/env python3 """ e5_format_html.py — Форматирователь HTML: разбивает на строки по тегам, БЕЗ добавления отступов. Просто каждый тег на новой строке. Использование: python e5_format_html.py input.html → выводит в stdout python e5_format_html.py input.html output.html → сохраняет в файл python e5_format_html.py --dir ./folder → все .html в папке (на месте) """ import os import re import sys # Теги, после открывающего/закрывающего которых ставим перенос BLOCK_TAGS = { "html", "head", "body", "div", "p", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "table", "tbody", "thead", "tfoot", "tr", "td", "th", "form", "fieldset", "section", "article", "nav", "aside", "header", "footer", "main", "details", "summary", "blockquote", "pre", "script", "style", "link", "meta", "title", } # Теги, содержимое которых не трогаем PRESERVE_TAGS = {"pre", "textarea", "script", "style"} def format_html(html): # Защищаем содержимое pre/textarea/script/style preserved = [] def save_block(m): preserved.append(m.group(0)) return f"\x00BLOCK{len(preserved) - 1}\x00" html = re.sub( r"(<(?:pre|textarea|script|style)\b[^>]*>)(.*?)()", save_block, html, flags=re.DOTALL | re.IGNORECASE, ) # Убираем существующие переносы и лишние пробелы html = re.sub(r"[\r\n\t]+", " ", html) html = re.sub(r" {2,}", " ", html) # Перенос ПЕРЕД открывающими блочными тегами tags_pattern = "|".join(BLOCK_TAGS) html = re.sub( rf"(?<=>)\s*(<(?:{tags_pattern})\b)", r"\n\1", html, flags=re.IGNORECASE ) # Перенос ПОСЛЕ закрывающих блочных тегов html = re.sub(rf"()\s*", r"\1\n", html, flags=re.IGNORECASE) # Перенос ПОСЛЕ самозакрывающихся тегов (meta, link, br, hr, img) html = re.sub(r"(<(?:meta|link)\b[^>]*>)\s*", r"\1\n", html, flags=re.IGNORECASE) # Перенос ПОСЛЕ
и
(но br внутри кода не трогаем — они в preserved) html = re.sub(r"(<[Hh][Rr]\s*/?>)\s*", r"\1\n", html) # Убираем пустые строки (больше 2 переносов подряд) html = re.sub(r"\n{3,}", "\n\n", html) # Убираем пробелы в начале строк (но НЕ добавляем отступы) lines = html.split("\n") lines = [line.strip() for line in lines] html = "\n".join(lines) # Убираем пустые строки в начале и конце html = html.strip() # Восстанавливаем защищённые блоки for i, block in enumerate(preserved): html = html.replace(f"\x00BLOCK{i}\x00", block) return html if __name__ == "__main__": if len(sys.argv) < 2: print("Использование:") print(" python html_format.py input.html [output.html]") print(" python html_format.py --dir ./folder") sys.exit(1) if sys.argv[1] == "--dir": folder = sys.argv[2] if len(sys.argv) > 2 else "." count = 0 for root, _, files in os.walk(folder): for fname in files: if not fname.lower().endswith(".html"): continue fpath = os.path.join(root, fname) with open(fpath, "r", encoding="utf-8-sig") as f: original = f.read() result = format_html(original) with open(fpath, "w", encoding="utf-8-sig") as f: f.write(result) count += 1 print(f" {os.path.relpath(fpath, folder)}") print(f"\nОтформатировано: {count} файлов") else: input_file = sys.argv[1] with open(input_file, "r", encoding="utf-8-sig") as f: html = f.read() result = format_html(html) if len(sys.argv) > 2: with open(sys.argv[2], "w", encoding="utf-8-sig") as f: f.write(result) print(f"Сохранено: {sys.argv[2]}") else: print(result)