Spaces:
Sleeping
Sleeping
| """Download the HuffPost News Category Dataset (full ~210k articles, ~87 MB). | |
| The full dataset is **not** committed to the repo. This script fetches it into | |
| ``data/News_Category_Dataset_v3.json``. | |
| How it works | |
| ------------ | |
| 1. **Public mirror (default, no account needed).** The dataset is pulled over | |
| plain HTTPS from a Hugging Face mirror, so it "just works" without any login | |
| or API token. Override the URL with the ``DATASET_URL`` environment variable. | |
| 2. **Kaggle API (fallback).** If the mirror is unreachable and you have Kaggle | |
| API credentials configured, it falls back to the Kaggle CLI. | |
| Note on Kaggle: the Kaggle API authenticates with an **API token** | |
| (``~/.kaggle/kaggle.json`` or the ``KAGGLE_USERNAME``/``KAGGLE_KEY`` environment | |
| variables) — being logged into the kaggle.com **website** in your browser does | |
| *not* count, which is why the API can report that you need to log in. | |
| A small ``data/sample_news.jsonl`` (~700 docs) is committed so the project runs | |
| out of the box without any download. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| import urllib.request | |
| from pathlib import Path | |
| DATA_DIR = Path(__file__).resolve().parents[1] / "data" | |
| TARGET = DATA_DIR / "News_Category_Dataset_v3.json" | |
| KAGGLE_SLUG = "rmisra/news-category-dataset" | |
| # Public, no-auth mirror of the exact same JSON-lines file (overridable). | |
| MIRROR_URL = os.getenv( | |
| "DATASET_URL", | |
| "https://huggingface.co/datasets/heegyu/news-category-dataset/resolve/main/data.json", | |
| ) | |
| def _looks_valid(path: Path) -> bool: | |
| """Cheap sanity check: first non-empty line is a news record.""" | |
| try: | |
| with path.open("r", encoding="utf-8") as fh: | |
| for line in fh: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| import json | |
| rec = json.loads(line) | |
| return "headline" in rec and "category" in rec | |
| except Exception: | |
| return False | |
| return False | |
| def _progress(done: int, total: int) -> None: | |
| if total > 0: | |
| pct = done * 100 // total | |
| bar = "#" * (pct // 4) | |
| print(f"\r [{bar:<25}] {pct:3d}% ({done/1e6:5.1f} / {total/1e6:.1f} MB)", | |
| end="", flush=True) | |
| else: | |
| print(f"\r {done/1e6:5.1f} MB", end="", flush=True) | |
| def _download_from_mirror() -> bool: | |
| """Stream the dataset from the public mirror to a temp file, then rename.""" | |
| tmp = TARGET.with_suffix(".json.part") | |
| print(f"Downloading dataset from public mirror:\n {MIRROR_URL}") | |
| try: | |
| req = urllib.request.Request(MIRROR_URL, headers={"User-Agent": "news-search/1.0"}) | |
| with urllib.request.urlopen(req) as resp: # noqa: S310 (trusted, configurable URL) | |
| total = int(resp.headers.get("Content-Length", 0)) | |
| done = 0 | |
| with tmp.open("wb") as out: | |
| while True: | |
| chunk = resp.read(1 << 20) # 1 MB | |
| if not chunk: | |
| break | |
| out.write(chunk) | |
| done += len(chunk) | |
| _progress(done, total) | |
| print() | |
| except Exception as exc: | |
| print(f"\n[warn] Mirror download failed: {exc}") | |
| tmp.unlink(missing_ok=True) | |
| return False | |
| if not _looks_valid(tmp): | |
| print("[warn] Downloaded file did not look like the expected dataset.") | |
| tmp.unlink(missing_ok=True) | |
| return False | |
| tmp.replace(TARGET) | |
| return True | |
| def _download_from_kaggle() -> bool: | |
| try: | |
| import kaggle # noqa: F401 | |
| except Exception: | |
| print( | |
| "[info] Kaggle fallback unavailable (no `kaggle` package or API token).\n" | |
| " The Kaggle API needs an API *token* at ~/.kaggle/kaggle.json or the\n" | |
| " KAGGLE_USERNAME / KAGGLE_KEY env vars — a website browser login is not enough." | |
| ) | |
| return False | |
| import subprocess | |
| print(f"Downloading {KAGGLE_SLUG} via Kaggle API...") | |
| try: | |
| subprocess.run( | |
| ["kaggle", "datasets", "download", "-d", KAGGLE_SLUG, | |
| "-p", str(DATA_DIR), "--unzip"], | |
| check=True, | |
| ) | |
| except Exception as exc: | |
| print(f"[warn] Kaggle download failed: {exc}") | |
| return False | |
| return TARGET.exists() | |
| def main() -> int: | |
| DATA_DIR.mkdir(parents=True, exist_ok=True) | |
| if TARGET.exists(): | |
| print(f"Dataset already present: {TARGET}") | |
| return 0 | |
| if _download_from_mirror() or _download_from_kaggle(): | |
| size_mb = TARGET.stat().st_size / 1e6 | |
| print(f"Done. Saved {TARGET} ({size_mb:.1f} MB).") | |
| return 0 | |
| print( | |
| "\n[error] Could not download the dataset automatically.\n" | |
| f" Download it manually from https://www.kaggle.com/datasets/{KAGGLE_SLUG}\n" | |
| f" and place News_Category_Dataset_v3.json in {DATA_DIR}/." | |
| ) | |
| return 1 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |