Spaces:
Running on Zero
Running on Zero
Download app/article.py from prathvi9607/loki: direct link, hf CLI and curl.
- Browser
- Download file 2.64 kB
-
https://huggingface.co/spaces/prathvi9607/loki/resolve/main/app/article.py
- Command line
-
hf download hf://spaces/prathvi9607/loki/app/article.py
-
curl -L -o article.py https://huggingface.co/spaces/prathvi9607/loki/resolve/main/app/article.py
2.64 kB
| import ipaddress | |
| import socket | |
| from urllib.parse import urljoin, urlsplit | |
| import nltk | |
| import requests | |
| from newspaper import Article | |
| TIMEOUT = 10 | |
| MAX_BYTES = 5_000_000 | |
| MAX_REDIRECTS = 5 | |
| try: | |
| nltk.data.find("tokenizers/punkt") # needed by Article.nlp() | |
| except LookupError: | |
| nltk.download("punkt", quiet=True) | |
| class ArticleError(ValueError): | |
| pass | |
| def check_url(url: str) -> str: | |
| """Reject non-http(s) URLs and hosts resolving to non-public addresses. Returns the hostname.""" | |
| parts = urlsplit(url) | |
| if parts.scheme not in ("http", "https") or not parts.hostname: | |
| raise ArticleError("Only http and https URLs are supported") | |
| try: | |
| addresses = {info[4][0] for info in socket.getaddrinfo(parts.hostname, parts.port)} | |
| except socket.gaierror: | |
| raise ArticleError(f"Cannot resolve host {parts.hostname}") from None | |
| for address in addresses: | |
| ip = ipaddress.ip_address(address.split("%")[0]) | |
| if not ip.is_global or ip.is_multicast: | |
| raise ArticleError("URL points to a non-public address") | |
| return parts.hostname | |
| def _download(url: str) -> bytes: | |
| # Redirects are followed by hand so every hop passes the SSRF check. | |
| # shortcut: DNS is resolved again by requests, so a rebinding host could slip through; pin the IP if this faces hostile users. | |
| for _ in range(MAX_REDIRECTS + 1): | |
| check_url(url) | |
| with requests.get( | |
| url, timeout=TIMEOUT, allow_redirects=False, stream=True, headers={"User-Agent": "Mozilla/5.0"} | |
| ) as response: | |
| if response.is_redirect: | |
| url = urljoin(url, response.headers["Location"]) | |
| continue | |
| response.raise_for_status() | |
| body = b"" | |
| for chunk in response.iter_content(65536): | |
| body += chunk | |
| if len(body) > MAX_BYTES: | |
| raise ArticleError("Page is too large") | |
| return body | |
| raise ArticleError("Too many redirects") | |
| def extract(url: str) -> dict: | |
| try: | |
| html = _download(url) | |
| except requests.RequestException as e: | |
| raise ArticleError(f"Could not download article: {e}") from e | |
| article = Article(url) | |
| article.download(input_html=html) | |
| article.parse() | |
| article.nlp() | |
| return { | |
| "title": article.title or None, | |
| "authors": article.authors, | |
| "publish_date": article.publish_date.isoformat() if article.publish_date else None, | |
| "top_image": article.top_image or None, | |
| "text": article.text, | |
| "summary": article.summary or None, | |
| "keywords": article.keywords, | |
| } | |