āš™ļø Panduan Lengkap: Skrip Otomatis Perbaikan HTML

Sebuah rangkuman perjalanan dari error hingga solusi final dengan Python & BeautifulSoup.

Dalam proses mengelola banyak file HTML, seringkali kita menemukan error-error kecil yang berulang. Memperbaikinya satu per satu tentu membuang waktu. Solusinya? Membuat skrip dapat! Halaman ini merangkum proses pembuatan skrip Python dari awal, termasuk cara mengatasi berbagai kendala yang muncul di lingkungan Linux modern.


Tahap 1: Kendala Instalasi 🚧

Langkah pertama dalam membuat skrip ini adalah menginstal library yang dibutuhkan: BeautifulSoup4 dan lxml. Namun, di distro Linux modern, kita seringkali disambut dengan error:

pip install beautifulsoup4 lxml
error: externally-managed-environment

Kenapa Error Ini Muncul?

Error ini adalah sebuah fitur keamanan. Sistem operasi Linux (seperti Ubuntu/Debian) ingin melindungi paket Python internalnya agar tidak rusak oleh instalasi pip yang dilakukan pengguna. Jadi, ia "mengunci" lingkungan Python sistem.

Solusi Terbaik: Virtual Environment (venv)

Cara paling profesional adalah dengan membuat lingkungan kerja yang terisolasi. Dengan begitu, kita bebas menginstal paket apa pun tanpa mengganggu sistem utama.

# 1. Buat folder lingkungan virtual bernama 'venv'
python3 -m venv venv
# 2. Aktifkan lingkungan tersebut
source venv/bin/activate

Tahap 2: Error Shell yang Tidak Kompatibel 🐟

Setelah membuat venv, beberapa pengguna (terutama pengguna fish shell) akan menemukan error baru saat mencoba mengaktifkannya:

source venv/bin/activate
venv/bin/activate: 'case' builtin not inside of switch block

Kenapa Error Ini Muncul?

Skrip activate standar ditulis untuk shell bash. Shell lain seperti fish memiliki sintaks yang berbeda dan tidak bisa menjalankannya. Untungnya, venv sudah menyediakan solusinya.

Solusi: Gunakan Skrip Aktivasi yang Tepat

Cukup gunakan file aktivasi yang berakhiran .fish.

# Perintah aktivasi yang benar untuk fish shell
source venv/bin/activate.fish

Setelah perintah ini, terminal Anda akan menampilkan (venv) di depannya, menandakan Anda sudah berada di lingkungan virtual dan siap untuk melanjutkan instalasi.


Tahap 3: Skrip Final Perbaikan HTML šŸ

Setelah berhasil mengatasi masalah lingkungan, inilah skrip final fix_html.py yang kita kembangkan. Skrip ini tidak hanya memperbaiki error struktural, tetapi juga membersihkan spasi berlebih dan membuat file log yang rapi.

Fitur Utama Skrip:
1. Memperbaiki tag yang rusak.
2. Memindahkan konten yang salah tempat ke dalam `<body>`.
3. Membersihkan tag `<script>` JSON-LD.
4. Menghapus `<div>` kosong.
5. Membersihkan spasi dan baris baru di dalam tag (misal: `<strong>`).
6. Membuat file log `perbaikan_log.txt` dengan ringkasan.
import os
import re
from bs4 import BeautifulSoup, Comment, NavigableString
LOG_FILE = "perbaikan_log.txt"
def strip_whitespace_in_tags(soup):
 tags_to_exclude = {'pre', 'code', 'style', 'script'}
 for tag in soup.find_all(lambda t: t.name not in tags_to_exclude):
 for content in list(tag.contents):
 if isinstance(content, NavigableString):
 cleaned_string = str(content).replace('\xa0', ' ').strip()
 if cleaned_string != str(content):
 if cleaned_string:
 content.replace_with(cleaned_string)
 else:
 content.extract()
def fix_html_file(file_path):
 log_messages = []
 has_changes = False
 try:
 with open(file_path, 'r', encoding='utf-8') as f:
 content = f.read()
 if "times;" in content:
 content = content.replace("times;", "Ɨ")
 log_messages.append(" - Memperbaiki <span> penutup yang rusak.")
 has_changes = True
 end_marker = '</html>'
 if end_marker in content and content[content.rfind(end_marker) + len(end_marker):].strip():
 log_messages.append(" - Menemukan konten setelah </html> yang akan dipindahkan.")
 has_changes = True
 soup = BeautifulSoup(content, 'lxml')
 if not soup.body: return log_messages
 json_scripts = soup.find_all('script', type='application/ld+json')
 json_cleaned_count = 0
 for script in json_scripts:
 if script.string:
 original_string = script.string
 cleaned_json = re.sub(r'<[^>]+>|', '', original_string, flags=re.DOTALL)
 if cleaned_json.strip() != original_string.strip():
 script.string = cleaned_json.strip()
 json_cleaned_count += 1
 if json_cleaned_count > 0:
 log_messages.append(f" - Membersihkan {json_cleaned_count} blok skrip JSON-LD.")
 has_changes = True
 empty_divs_removed = 0
 for div in soup.find_all('div'):
 if not any(isinstance(c, Comment) or (isinstance(c, str) and c.strip()) or hasattr(c, 'name') for c in div.contents):
 div.decompose()
 empty_divs_removed += 1
 if empty_divs_removed > 0:
 log_messages.append(f" - Menghapus {empty_divs_removed} tag <div> kosong.")
 has_changes = True
 strip_whitespace_in_tags(soup)
 log_messages.append(" - Membersihkan spasi berlebih di dalam tag HTML.")
 has_changes = True
 if has_changes:
 output_path = file_path.replace('.html', '_fixed.html')
 with open(output_path, 'w', encoding='utf-8') as f:
 f.write(soup.prettify(formatter="html5"))
 print(f" [āœ“] Perbaikan & pembersihan diterapkan: {os.path.basename(output_path)}")
 else:
 print(f" [-] Tidak ada perbaikan yang diperlukan.")
 return log_messages
 except Exception as e:
 print(f" [āœ—] Gagal memproses {file_path}: {e}")
 return [f" - ERROR: {e}"]
def main():
 all_logs = {}
 if os.path.exists(LOG_FILE): os.remove(LOG_FILE)
 html_files_to_process = sorted([f for f in os.listdir('.') if f.endswith('.html') and not f.endswith('_fixed.html')])
 if not html_files_to_process:
 print("šŸ¤·ā™‚ļø Tidak ada file .html yang ditemukan.")
 return
 print(f"Ditemukan {len(html_files_to_process)} file untuk diproses...\n")
 for filename in html_files_to_process:
 print(f"[*] Memproses file: {filename}...")
 log_messages = fix_html_file(os.path.join('.', filename))
 if log_messages: all_logs[filename] = log_messages
 print("\nšŸŽ‰ Proses selesai.")
 if all_logs:
 with open(LOG_FILE, 'w', encoding='utf-8') as log_file:
 log_file.write(f"================ SUMMARY ================\n")
 log_file.write(f"Total file yang diproses dan dibersihkan: {len(all_logs)}\n")
 log_file.write(f"=======================================\n\n")
 for filename in sorted(all_logs.keys()):
 log_file.write(f"Log untuk file: {filename}\n")
 for msg in all_logs[filename]: log_file.write(f"{msg}\n")
 log_file.write("-" * 40 + "\n")
 print(f"šŸ“ Catatan log dengan summary telah dibuat di: {LOG_FILE}")
if __name__ == "__main__":
 main()

Tahap 4: Cara Penggunaan & Hasil šŸš€

Langkah-langkah Eksekusi

  1. Simpan kode di atas sebagai fix_html.py.
  2. Letakkan di folder yang sama dengan file-file HTML Anda.
  3. Buka terminal di folder tersebut.
  4. Aktifkan lingkungan virtual (source venv/bin/activate.fish).
  5. Jalankan skrip: python fix_html.py.

Hasil Akhir

Skrip akan menghasilkan dua hal:

  • File *_fixed.html untuk setiap file yang diperbaiki.
  • Satu file log perbaikan_log.txt yang berisi ringkasan dan detail semua perbaikan.
Contoh Log:
================ SUMMARY ================
Total file yang diproses dan dibersihkan: 1
=======================================
Log untuk file: contoh.html
 - Memperbaiki <span> penutup pencarian yang rusak.
 - Menemukan konten setelah </html> yang akan dipindahkan.
 - Membersihkan spasi berlebih di dalam tag HTML.
----------------------------------------

Semoga Berhasil.