Coding-With-Bashir commited on
Commit
c02894a
·
verified ·
1 Parent(s): 68a8e59

Upload .\src\data_collection\rbc_collector.py with huggingface_hub

Browse files
.//src//data_collection//rbc_collector.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Rwanda Biomedical Centre content collector."""
2
+
3
+ import json
4
+ import logging
5
+ import time
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import requests
10
+ from bs4 import BeautifulSoup
11
+
12
+ from data_collection.web_utils import check_robots_txt
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class RBCCollector:
18
+ """Collects health-related content from RBC."""
19
+
20
+ def __init__(self, output_dir: str, config: dict[str, Any]):
21
+ self.output_dir = Path(output_dir)
22
+ self.output_dir.mkdir(parents=True, exist_ok=True)
23
+ self.config = config
24
+ self.session = requests.Session()
25
+ self.session.headers.update({
26
+ "User-Agent": "BwengeAi/0.1 (Kinyarwanda AI Research)"
27
+ })
28
+
29
+ rbc_config = config.get("rbc", {})
30
+ self.base_url = rbc_config.get("base_url", "https://rbc.gov.rw")
31
+ self.rate_limit = rbc_config.get("rate_limit", 2.0)
32
+
33
+ def fetch_page(self, url: str) -> BeautifulSoup | None:
34
+ """Fetch a page and return parsed HTML."""
35
+ try:
36
+ if not check_robots_txt(url, user_agent="BwengeAi/0.1", delay=self.rate_limit):
37
+ return None
38
+ time.sleep(self.rate_limit)
39
+ response = self.session.get(url, timeout=30)
40
+ response.raise_for_status()
41
+ return BeautifulSoup(response.text, "lxml")
42
+ except Exception as e:
43
+ logger.error(f"Failed to fetch {url}: {e}")
44
+ return None
45
+
46
+ def get_article_links(self, max_pages: int = 5) -> list[str]:
47
+ """Get article links from the site."""
48
+ links = []
49
+
50
+ news_url = f"{self.base_url}/news"
51
+ soup = self.fetch_page(news_url)
52
+ if soup:
53
+ for a_tag in soup.find_all("a", href=True):
54
+ href = a_tag["href"]
55
+ if "/news/" in href or "/article/" in href or "/publication/" in href:
56
+ if not href.startswith("http"):
57
+ href = f"{self.base_url}{href}"
58
+ if href not in links:
59
+ links.append(href)
60
+
61
+ return links[:max_pages * 20]
62
+
63
+ def scrape_article(self, url: str) -> dict[str, Any] | None:
64
+ """Scrape a single article."""
65
+ soup = self.fetch_page(url)
66
+ if not soup:
67
+ return None
68
+
69
+ article = {"url": url, "source": "rbc"}
70
+
71
+ title_tag = soup.find("h1") or soup.find("title")
72
+ if title_tag:
73
+ article["title"] = title_tag.get_text(strip=True)
74
+
75
+ for tag in soup(["script", "style", "nav", "footer", "header", "aside"]):
76
+ tag.decompose()
77
+
78
+ content_div = soup.find("article") or soup.find("div", class_=lambda x: x and "content" in str(x).lower())
79
+ if content_div:
80
+ paragraphs = content_div.find_all("p")
81
+ else:
82
+ paragraphs = soup.find_all("p")
83
+
84
+ texts = [p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True) and len(p.get_text(strip=True)) > 20]
85
+ article["text"] = "\n\n".join(texts)
86
+
87
+ if not article["text"] or len(article["text"]) < 100:
88
+ return None
89
+
90
+ article["length"] = len(article["text"])
91
+ return article
92
+
93
+ def collect_all(self) -> list[dict[str, Any]]:
94
+ """Collect all articles from RBC."""
95
+ logger.info("Starting RBC collection...")
96
+
97
+ links = self.get_article_links()
98
+ logger.info(f"Found {len(links)} article links")
99
+
100
+ results = []
101
+ for url in links:
102
+ article = self.scrape_article(url)
103
+ if article:
104
+ results.append(article)
105
+
106
+ output_path = self.output_dir / "rbc_articles.jsonl"
107
+ with open(output_path, "w", encoding="utf-8") as f:
108
+ for article in results:
109
+ f.write(json.dumps(article, ensure_ascii=False) + "\n")
110
+
111
+ summary = {
112
+ "source": "rbc",
113
+ "total_links": len(links),
114
+ "collected": len(results),
115
+ "output_path": str(output_path),
116
+ "total_characters": sum(a.get("length", 0) for a in results),
117
+ }
118
+
119
+ summary_path = self.output_dir / "rbc_summary.json"
120
+ with open(summary_path, "w", encoding="utf-8") as f:
121
+ json.dump(summary, f, indent=2, ensure_ascii=False)
122
+
123
+ logger.info(f"Collected {len(results)} articles from RBC")
124
+ return results