Coding-With-Bashir commited on
Commit
92b3512
·
verified ·
1 Parent(s): 90807ca

Upload .\scripts\collect_wikipedia.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. .//scripts//collect_wikipedia.py +151 -0
.//scripts//collect_wikipedia.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Standalone Wikipedia collector - no torch/transformers dependency."""
2
+
3
+ import bz2
4
+ import json
5
+ import logging
6
+ import re
7
+ import time
8
+ import xml.etree.ElementTree as ET
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ import requests
13
+ from tqdm import tqdm
14
+
15
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
16
+ logger = logging.getLogger(__name__)
17
+
18
+ WIKI_API = "https://rw.wikipedia.org/w/api.php"
19
+ SESSION = requests.Session()
20
+ SESSION.headers.update({"User-Agent": "BwengeAi/0.1 (Kinyarwanda AI Research; contact: bwengeai@research.rw)"})
21
+
22
+ REQUEST_DELAY = 0.5 # seconds between requests
23
+
24
+ OUTPUT_DIR = Path("C:/Users/admin/BwengeAi/data/raw/wikipedia")
25
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
26
+
27
+
28
+ def api_request(params: dict, max_retries: int = 5) -> dict:
29
+ """Make an API request with retry and rate limiting."""
30
+ for attempt in range(max_retries):
31
+ time.sleep(REQUEST_DELAY)
32
+ try:
33
+ r = SESSION.get(WIKI_API, params=params, timeout=30)
34
+ if r.status_code == 429:
35
+ wait = min(30, 2 ** (attempt + 1))
36
+ logger.warning(f"Rate limited, waiting {wait}s...")
37
+ time.sleep(wait)
38
+ continue
39
+ r.raise_for_status()
40
+ return r.json()
41
+ except requests.exceptions.HTTPError as e:
42
+ if "429" in str(e):
43
+ wait = min(30, 2 ** (attempt + 1))
44
+ logger.warning(f"Rate limited (attempt {attempt + 1}), waiting {wait}s...")
45
+ time.sleep(wait)
46
+ else:
47
+ raise
48
+ raise Exception(f"Failed after {max_retries} retries")
49
+
50
+
51
+ def get_site_stats() -> dict:
52
+ params = {"action": "query", "meta": "siteinfo", "siprop": "statistics", "format": "json"}
53
+ data = api_request(params)
54
+ return data.get("query", {}).get("statistics", {})
55
+
56
+
57
+ def fetch_article_list() -> list[str]:
58
+ articles = []
59
+ params = {"action": "query", "list": "allpages", "apnamespace": 0, "aplimit": "max", "format": "json"}
60
+ while True:
61
+ data = api_request(params)
62
+ pages = data.get("query", {}).get("allpages", [])
63
+ articles.extend(p["title"] for p in pages)
64
+ logger.info(f" Fetched {len(articles)} article titles so far...")
65
+ if "continue" in data:
66
+ params["apcontinue"] = data["continue"]["apcontinue"]
67
+ else:
68
+ break
69
+ logger.info(f"Found {len(articles)} articles")
70
+ return articles
71
+
72
+
73
+ def clean_html(html: str) -> str:
74
+ from bs4 import BeautifulSoup
75
+ soup = BeautifulSoup(html, "lxml")
76
+ for tag in soup(["script", "style", "sup", "sub"]):
77
+ tag.decompose()
78
+ text = soup.get_text(separator=" ", strip=True)
79
+ lines = [l.strip() for l in text.split() if l.strip() and len(l.strip()) > 2]
80
+ return " ".join(lines)
81
+
82
+
83
+ def clean_mediawiki(text: str) -> str:
84
+ text = re.sub(r"\[\[([^|\]]*\|)?([^\]]*)\]\]", r"\2", text)
85
+ text = re.sub(r"\{\{[^}]*\}\}", "", text)
86
+ text = re.sub(r"'''?", "", text)
87
+ text = re.sub(r"<[^>]+>", "", text)
88
+ text = re.sub(r"==+\s*[^=]*\s*==+", "", text)
89
+ text = re.sub(r"\s+", " ", text)
90
+ return text.strip()
91
+
92
+
93
+ def fetch_article_content(title: str) -> dict | None:
94
+ params = {"action": "parse", "page": title, "prop": "wikitext|text|categories|links", "format": "json"}
95
+ try:
96
+ data = api_request(params)
97
+ parse = data.get("parse", {})
98
+ text = parse.get("text", {}).get("*", "")
99
+ clean = clean_html(text)
100
+ return {
101
+ "title": title,
102
+ "text": clean,
103
+ "wikitext": parse.get("wikitext", {}).get("*", ""),
104
+ "categories": [c["*"] for c in parse.get("categories", [])],
105
+ "links": [l["*"] for l in parse.get("links", [])],
106
+ "length": len(clean),
107
+ }
108
+ except Exception as e:
109
+ logger.warning(f"Failed to fetch '{title}': {e}")
110
+ return None
111
+
112
+
113
+ def main():
114
+ logger.info("=" * 60)
115
+ logger.info("BwengeAi - Kinyarwanda Wikipedia Collection")
116
+ logger.info("=" * 60)
117
+
118
+ stats = get_site_stats()
119
+ logger.info(f"Wikipedia stats: {stats}")
120
+
121
+ articles = fetch_article_list()
122
+ logger.info(f"Collecting {len(articles)} articles...")
123
+
124
+ results = []
125
+ output_path = OUTPUT_DIR / "wikipedia_articles.jsonl"
126
+
127
+ with open(output_path, "w", encoding="utf-8") as f:
128
+ for title in tqdm(articles, desc="Fetching articles"):
129
+ article = fetch_article_content(title)
130
+ if article and article["text"] and len(article["text"]) > 50:
131
+ f.write(json.dumps(article, ensure_ascii=False) + "\n")
132
+ results.append({"title": article["title"], "length": article["length"]})
133
+
134
+ summary = {
135
+ "source": "kinyarwanda_wikipedia",
136
+ "total_articles": len(articles),
137
+ "collected": len(results),
138
+ "output_path": str(output_path),
139
+ "total_characters": sum(r["length"] for r in results),
140
+ }
141
+
142
+ summary_path = OUTPUT_DIR / "wikipedia_summary.json"
143
+ with open(summary_path, "w", encoding="utf-8") as f:
144
+ json.dump(summary, f, indent=2, ensure_ascii=False)
145
+
146
+ logger.info(f"Done! Collected {len(results)} articles ({summary['total_characters']} characters)")
147
+ logger.info(f"Saved to: {output_path}")
148
+
149
+
150
+ if __name__ == "__main__":
151
+ main()