| |
| import json, re, unicodedata, os, time |
| from huggingface_hub import HfApi, hf_hub_download |
|
|
| def download_index(): |
| print("[1/4] Downloading index.html...") |
| path = hf_hub_download("bep40/V.AISTUDIO", "index.html", repo_type="space") |
| with open(path) as f: |
| html = f.read() |
| print(f" Downloaded {len(html):,} bytes") |
| return html |
|
|
| def extract_products(html): |
| print("[2/4] Extracting products...") |
| idx = html.find('var products = ') |
| if idx == -1: |
| idx = html.find('var products=') |
| if idx == -1: |
| idx = html.find('products=') |
| if idx == -1: |
| raise ValueError("Cannot find products") |
| start = html.find('[', idx) |
| bracket_count = 0 |
| end = start |
| in_string = False |
| for i in range(start, len(html)): |
| c = html[i] |
| if c == '"' and (i == 0 or html[i-1] != '\\'): |
| in_string = not in_string |
| elif not in_string: |
| if c == '[': bracket_count += 1 |
| elif c == ']': |
| bracket_count -= 1 |
| if bracket_count == 0: |
| end = i + 1 |
| break |
| raw = html[start:end] |
| products = json.loads(raw) |
| print(f" Found {len(products):,} products") |
| if products: |
| print(f" Sample: {json.dumps(products[0], ensure_ascii=False)[:200]}") |
| return products |
|
|
| def generate_stubs(products): |
| print("[3/4] Generating stubs...") |
| saved = 0 |
| seen = set() |
| api = HfApi() |
| |
| for p in products: |
| slug = p.get('slug', '') |
| if not slug: |
| slug = p.get('name', f'p-{saved}') |
| slug = unicodedata.normalize('NFKD', slug).lower() |
| slug = re.sub(r'[^a-z0-9]+', '-', slug).strip('-') |
| slug = re.sub(r'-+', '-', slug) |
| orig_slug = slug |
| counter = 0 |
| while slug in seen: |
| counter += 1 |
| slug = f'{orig_slug}-{counter}' |
| seen.add(slug) |
| |
| name = p.get('name', '') |
| brand = p.get('brand', '') |
| img = p.get('image', '') |
| desc = p.get('description', '')[:160] |
| desc = re.sub(r'<[^>]*>', '', desc) |
| desc = re.sub(r'\s+', ' ', desc).strip() |
| title = f'{name} | {brand} - V.AISTUDIO' if brand else f'{name} - V.AISTUDIO' |
| title_esc = title.replace('&', '&').replace('"', '"').replace('<', '<') |
| desc_esc = desc.replace('&', '&').replace('"', '"').replace('<', '<') |
| |
| stub = f'''<!DOCTYPE html> |
| <html lang="vi"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="robots" content="noindex,follow"> |
| <title>{title_esc}</title> |
| <meta name="description" content="{desc_esc}"> |
| <link rel="canonical" href="https://bep40-v-aistudio.static.hf.space/san-pham/{slug}/"> |
| <meta property="og:type" content="product"> |
| <meta property="og:title" content="{title_esc}"> |
| <meta property="og:description" content="{desc_esc}"> |
| <meta property="og:image" content="{img}"> |
| <meta property="og:url" content="https://bep40-v-aistudio.static.hf.space/san-pham/{slug}/"> |
| <meta name="twitter:card" content="summary_large_image"> |
| <script>window.location.replace("/?product={slug}");</script> |
| </head> |
| <body><h1><a href="/?product={slug}">{title_esc}</a></h1></body> |
| </html>''' |
| |
| |
| try: |
| api.upload_file( |
| path_or_fileobj=stub.encode('utf-8'), |
| path_in_repo=f"san-pham/{slug}/index.html", |
| repo_id="bep40/V.AISTUDIO", |
| repo_type="space", |
| commit_message=f"Add SEO stub for {name}" |
| ) |
| saved += 1 |
| if saved % 100 == 0: |
| time.sleep(1) |
| print(f" Uploaded {saved} stubs...") |
| except Exception as e: |
| print(f" ERROR {slug}: {e}") |
| break |
| |
| print(f" Total: {saved} stubs uploaded") |
| return saved |
|
|
| def main(): |
| html = download_index() |
| products = extract_products(html) |
| saved = generate_stubs(products) |
| print(f"\nDONE! {saved} stubs created") |
| print("Verify: https://bep40-v-aistudio.static.hf.space/san-pham/<slug>/") |
|
|
| if __name__ == "__main__": |
| main() |
|
|