Download crawler/parser.py from Bhargav19/all-model-scrapper: direct link, hf CLI and curl.
- Browser
- Download file 3.63 kB
-
https://huggingface.co/Bhargav19/all-model-scrapper/resolve/main/crawler/parser.py
- Command line
-
hf download hf://Bhargav19/all-model-scrapper/crawler/parser.py
-
curl -L -o parser.py https://huggingface.co/Bhargav19/all-model-scrapper/resolve/main/crawler/parser.py
3.63 kB
| import re | |
| from urllib.parse import urljoin, urlparse | |
| from bs4 import BeautifulSoup | |
| from typing import Dict, Any, List, Set | |
| class PageParser: | |
| def __init__(self, domain: str, scheme: str = "https"): | |
| self.domain = domain.lower() | |
| self.scheme = scheme | |
| def clean_text(self, text: str) -> str: | |
| """Cleans formatting, extra spaces, and redundant newlines from text.""" | |
| if not text: | |
| return "" | |
| # Remove script and style elements content during parsing | |
| text = re.sub(r'\s+', ' ', text) | |
| return text.strip() | |
| def parse(self, html_content: str, current_url: str) -> Dict[str, Any]: | |
| """Extracts structured text data and meta information from the HTML.""" | |
| # Use lxml parser for fast and memory efficient parsing | |
| soup = BeautifulSoup(html_content, "lxml") | |
| # Strip script, style, and iframe components before extracting text | |
| for element in soup(["script", "style", "iframe", "noscript"]): | |
| element.decompose() | |
| # Page metadata | |
| title = soup.title.string.strip() if soup.title else "" | |
| meta_desc = "" | |
| meta_desc_tag = soup.find("meta", attrs={"name": "description"}) or soup.find("meta", attrs={"property": "og:description"}) | |
| if meta_desc_tag: | |
| meta_desc = meta_desc_tag.get("content", "").strip() | |
| meta_keys = "" | |
| meta_keys_tag = soup.find("meta", attrs={"name": "keywords"}) | |
| if meta_keys_tag: | |
| meta_keys = meta_keys_tag.get("content", "").strip() | |
| # Header structures | |
| h1s = [self.clean_text(h.get_text()) for h in soup.find_all("h1") if h.get_text()] | |
| h1_headers = " | ".join(h1s) | |
| # Body main text extraction | |
| # Focus on paragraphs, main, article sections if possible, fallback to clean body text | |
| main_content = soup.find("main") or soup.find("article") or soup.find("body") | |
| if main_content: | |
| text_content = self.clean_text(main_content.get_text(separator=" ")) | |
| else: | |
| text_content = self.clean_text(soup.get_text(separator=" ")) | |
| return { | |
| "title": title, | |
| "meta_description": meta_desc, | |
| "meta_keywords": meta_keys, | |
| "h1_headers": h1_headers, | |
| "text_content": text_content, | |
| } | |
| def extract_links(self, html_content: str, current_url: str) -> Set[str]: | |
| """Discovers and normalizes all internal/same-domain links from page anchor tags.""" | |
| soup = BeautifulSoup(html_content, "lxml") | |
| links = set() | |
| for anchor in soup.find_all("a", href=True): | |
| href = anchor["href"].strip() | |
| # Skip empty, mailto, javascript, and anchor links | |
| if not href or href.startswith(("#", "mailto:", "javascript:", "tel:")): | |
| continue | |
| # Resolve relative URLs | |
| absolute_url = urljoin(current_url, href) | |
| # Parse resolved URL | |
| parsed = urlparse(absolute_url) | |
| # Verify same domain | |
| if parsed.netloc.lower() == self.domain: | |
| # Normalize url by stripping fragment (#) and query params sorting if needed | |
| normalized_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}" | |
| # Append trailing slash consistency or query parameters if it contains pathing parameters | |
| if parsed.query: | |
| normalized_url += f"?{parsed.query}" | |
| links.add(normalized_url) | |
| return links | |