| |
| """ |
| SEO STUB GENERATOR cho V.AISTUDIO Static Space |
| Tạo 1979 file HTML cho mỗi sản phẩm để fix 404 và hỗ tr�n SEO URL sharing. |
| |
| Cài đặt: pip install huggingface_hub requests |
| Chạy: python3 generate_seo_stubs.py |
| |
| Yêu cầu: BIẾT_TOKEN trong environment hoặc ~/.huggingface/token |
| """ |
| import json |
| import re |
| import unicodedata |
| import time |
| import urllib.request |
|
|
| try: |
| from huggingface_hub import HfApi |
| except ImportError: |
| import subprocess |
| subprocess.check_call(["pip", "install", "-q", "huggingface_hub"]) |
| from huggingface_hub import HfApi |
|
|
| SPACE_ID = "bep40/V.AISTUDIO" |
| BASE = "https://bep40-v-aistudio.static.hf.space" |
|
|
| def slugify(name): |
| s = unicodedata.normalize('NFKD', name) |
| s = s.encode('ascii', 'ignore').decode('ascii').lower() |
| s = re.sub(r'[^a-z0-9]+', '-', s).strip('-') |
| s = re.sub(r'-+', '-', s) |
| return s |
|
|
| def html_stub(slug, name, brand, price, img, desc): |
| title = f"{name} | {brand} - V.AISTUDIO" if brand else f"{name} - V.AISTUDIO" |
| desc = re.sub(r'<[^>]+>', '', str(desc))[:160] |
| img = img or f"{BASE}/logo/logo_600.png" |
| return f'''<!DOCTYPE html> |
| <html lang="vi"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>{title}</title> |
| <meta name="description" content="{desc}"> |
| <meta name="robots" content="noindex,follow"> |
| <link rel="canonical" href="{BASE}/san-pham/{slug}/"> |
| <meta property="og:type" content="product"> |
| <meta property="og:title" content="{title}"> |
| <meta property="og:description" content="{desc}"> |
| <meta property="og:image" content="{img}"> |
| <meta property="og:url" content="{BASE}/san-pham/{slug}/"> |
| <meta property="og:site_name" content="V.AISTUDIO"> |
| <meta name="twitter:card" content="summary_large_image"> |
| <script>window.location.replace("/?product={slug}");</script> |
| </head> |
| <body><h1><a href="/?product={slug}">{title}</a></h1></body> |
| </html>''' |
|
|
| def main(): |
| api = HfApi() |
| |
| |
| print("Downloading index.html...") |
| with urllib.request.urlopen(f"https://huggingface.co/spaces/{SPACE_ID}/resolve/main/index.html") as r: |
| html = r.read().decode() |
| |
| |
| m = re.search(r'var products\s*=\s*(\[[\s\S]*?\]);', html) |
| if not m: |
| print("ERROR: Khong tim thay products array") |
| return |
| |
| |
| products_json = m.group(1) |
| products_json = re.sub(r'\\x([0-9a-f]{2})', lambda x: chr(int(x.group(1),16)), products_json) |
| products_json = re.sub(r',\s*([}\]])', r'\1', products_json) |
| products = json.loads(products_json) |
| print(f"Found {len(products)} products") |
| |
| |
| uploaded = 0 |
| seen = {} |
| for p in products: |
| name = p.get('name', '') |
| slug = p.get('slug') or slugify(name) |
| if slug in seen: |
| seen[slug] += 1 |
| slug = f"{slug}-{seen[slug]}" |
| seen[slug] = 0 |
| |
| stub = html_stub( |
| slug, |
| name[:200], |
| p.get('brand', ''), |
| p.get('price', ''), |
| p.get('image', ''), |
| p.get('description', '') |
| ) |
| |
| try: |
| api.upload_file( |
| path_or_fileobj=stub.encode(), |
| path_in_repo=f"san-pham/{slug}/index.html", |
| repo_id=SPACE_ID, |
| repo_type="space", |
| commit_message=f"SEO stub: {name[:40]}" |
| ) |
| uploaded += 1 |
| except Exception as e: |
| print(f" Error: {e}") |
| |
| if uploaded % 100 == 0: |
| print(f" {uploaded}...") |
| time.sleep(0.5) |
| |
| print(f"Done! {uploaded} stubs uploaded to {SPACE_ID}") |
| |
| print("\nKiem tra: curl -sI https://bep40-v-aistudio.static.hf.space/san-pham/<slug>/") |
|
|
| if __name__ == "__main__": |
| main() |