Initial commit
This commit is contained in:
142
export_pages.py
Normal file
142
export_pages.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""
|
||||
Exporte chaque page Django en HTML autonome (CSS + JS inline).
|
||||
Usage : python3 export_pages.py
|
||||
"""
|
||||
import os, re, base64, mimetypes
|
||||
from pathlib import Path
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from urllib.parse import urljoin, urlparse
|
||||
|
||||
BASE_URL = 'http://127.0.0.1:8000'
|
||||
OUT_DIR = Path(__file__).parent / 'figma_export'
|
||||
OUT_DIR.mkdir(exist_ok=True)
|
||||
|
||||
PAGES = [
|
||||
('home', '/'),
|
||||
('a-propos', '/a-propos/'),
|
||||
('kiriq', '/produits/kiriq/'),
|
||||
('monitor', '/produits/monitor/'),
|
||||
('joolid', '/produits/joolid/'),
|
||||
('carrieres','/carrieres/'),
|
||||
]
|
||||
|
||||
session = requests.Session()
|
||||
|
||||
def fetch(url):
|
||||
r = session.get(urljoin(BASE_URL, url), timeout=10)
|
||||
r.raise_for_status()
|
||||
return r
|
||||
|
||||
def to_data_uri(url_or_path):
|
||||
"""Convertit une URL de ressource en data URI base64."""
|
||||
try:
|
||||
r = fetch(url_or_path)
|
||||
mime = r.headers.get('Content-Type', 'application/octet-stream').split(';')[0]
|
||||
b64 = base64.b64encode(r.content).decode()
|
||||
return f"data:{mime};base64,{b64}"
|
||||
except Exception as e:
|
||||
print(f" ⚠️ Impossible de charger {url_or_path}: {e}")
|
||||
return url_or_path
|
||||
|
||||
def inline_css(soup, base_url):
|
||||
"""Remplace les <link rel=stylesheet> par des <style> inline."""
|
||||
for link in soup.find_all('link', rel='stylesheet'):
|
||||
href = link.get('href', '')
|
||||
if not href:
|
||||
continue
|
||||
try:
|
||||
css_text = fetch(href).text
|
||||
# Inline les url() dans le CSS (fonts, images)
|
||||
def replace_url(m):
|
||||
raw = m.group(1).strip('"\'')
|
||||
if raw.startswith('data:') or raw.startswith('http'):
|
||||
return m.group(0)
|
||||
full = urljoin(BASE_URL + href, raw)
|
||||
path = urlparse(full).path
|
||||
return f"url('{to_data_uri(path)}')"
|
||||
css_text = re.sub(r'url\(([^)]+)\)', replace_url, css_text)
|
||||
style_tag = soup.new_tag('style')
|
||||
style_tag.string = css_text
|
||||
link.replace_with(style_tag)
|
||||
print(f" ✅ CSS inline : {href}")
|
||||
except Exception as e:
|
||||
print(f" ⚠️ CSS ignoré {href}: {e}")
|
||||
|
||||
def inline_images(soup):
|
||||
"""Remplace les src d'images par des data URI."""
|
||||
for img in soup.find_all('img'):
|
||||
src = img.get('src', '')
|
||||
if src and not src.startswith('data:') and not src.startswith('http'):
|
||||
img['src'] = to_data_uri(src)
|
||||
print(f" 🖼️ Image inline : {src}")
|
||||
|
||||
def inline_js(soup):
|
||||
"""Remplace les <script src=...> par du JS inline."""
|
||||
for script in soup.find_all('script', src=True):
|
||||
src = script.get('src', '')
|
||||
if not src or src.startswith('http'):
|
||||
continue
|
||||
try:
|
||||
js_text = fetch(src).text
|
||||
new_script = soup.new_tag('script')
|
||||
new_script.string = js_text
|
||||
script.replace_with(new_script)
|
||||
print(f" ⚡ JS inline : {src}")
|
||||
except Exception as e:
|
||||
print(f" ⚠️ JS ignoré {src}: {e}")
|
||||
|
||||
def fix_google_fonts(soup):
|
||||
"""Garde les liens Google Fonts (ils fonctionnent sans serveur)."""
|
||||
# On ne touche pas aux fonts Google — elles chargent via CDN
|
||||
pass
|
||||
|
||||
def export_page(name, path):
|
||||
print(f"\n📄 Export : {name} ({path})")
|
||||
try:
|
||||
html = fetch(path).text
|
||||
except Exception as e:
|
||||
print(f" ❌ Erreur : {e}")
|
||||
return
|
||||
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
|
||||
# Supprimer les balises CSRF et admin (inutiles en statique)
|
||||
for tag in soup.find_all('input', {'name': 'csrfmiddlewaretoken'}):
|
||||
tag.decompose()
|
||||
|
||||
fix_google_fonts(soup)
|
||||
inline_css(soup, path)
|
||||
inline_images(soup)
|
||||
inline_js(soup)
|
||||
|
||||
# Fixer les liens internes pour qu'ils pointent vers les fichiers exportés
|
||||
page_map = {
|
||||
'/': 'home.html',
|
||||
'/a-propos/': 'a-propos.html',
|
||||
'/produits/kiriq/': 'kiriq.html',
|
||||
'/produits/monitor/': 'monitor.html',
|
||||
'/produits/joolid/': 'joolid.html',
|
||||
'/carrieres/': 'carrieres.html',
|
||||
}
|
||||
for a in soup.find_all('a', href=True):
|
||||
href = a['href']
|
||||
# Supprimer les ancres avec fragment pour les pages principales
|
||||
clean = href.split('#')[0]
|
||||
if clean in page_map:
|
||||
a['href'] = page_map[clean] + (('#' + href.split('#')[1]) if '#' in href else '')
|
||||
|
||||
out_file = OUT_DIR / f"{name}.html"
|
||||
out_file.write_text(str(soup), encoding='utf-8')
|
||||
size_kb = out_file.stat().st_size / 1024
|
||||
print(f" ✅ Sauvegardé : {out_file.name} ({size_kb:.0f} KB)")
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("🚀 Export HTML autonome — Jool International\n")
|
||||
for name, path in PAGES:
|
||||
export_page(name, path)
|
||||
print(f"\n🎉 Terminé ! Fichiers dans : {OUT_DIR}")
|
||||
print("\n📌 Étapes suivantes :")
|
||||
print(" 1. Ouvrez Figma Desktop")
|
||||
print(" 2. Installez le plugin 'html.to.design' depuis la communauté Figma")
|
||||
print(" 3. Dans le plugin : Local File → sélectionnez chaque .html dans figma_export/")
|
||||
Reference in New Issue
Block a user