import os import json import uuid import urllib.request import urllib.parse from datetime import datetime import re # Source and destination roots SENTI_AI_ROOT = r"c:\Users\LENOVO\Desktop\senti_ai" SENTI_ROOT = r"c:\Users\LENOVO\Desktop\senti" # Keywords mapped to packs and jurisdictions CRAWL_TARGETS = { "sentilaw": [ {"query": "Tax law", "jurisdiction": "Global"}, {"query": "Value added tax", "jurisdiction": "Global"}, {"query": "Kenya Revenue Authority", "jurisdiction": "Kenya"}, {"query": "South African Revenue Service", "jurisdiction": "South Africa"}, {"query": "Internal Revenue Code", "jurisdiction": "USA"}, {"query": "HM Revenue and Customs", "jurisdiction": "UK"}, {"query": "Financial regulation", "jurisdiction": "Global"}, {"query": "Anti-money laundering", "jurisdiction": "Global"}, {"query": "Basel III", "jurisdiction": "Global"}, {"query": "Dodd–Frank Wall Street Reform and Consumer Protection Act", "jurisdiction": "USA"} ], "sentirisk": [ {"query": "Financial risk management", "jurisdiction": "Global"}, {"query": "Liquidity risk", "jurisdiction": "Global"}, {"query": "Operational risk management", "jurisdiction": "Global"}, {"query": "Concentration risk", "jurisdiction": "Global"}, {"query": "Stress testing (finance)", "jurisdiction": "Global"}, {"query": "Value at risk", "jurisdiction": "Global"}, {"query": "Capital adequacy ratio", "jurisdiction": "Global"}, {"query": "Non-performing loan", "jurisdiction": "Global"}, {"query": "Systemic risk", "jurisdiction": "Global"}, {"query": "Credit risk", "jurisdiction": "Global"} ], "senticredit": [ {"query": "Credit score", "jurisdiction": "Global"}, {"query": "Underwriting", "jurisdiction": "Global"}, {"query": "Credit bureau", "jurisdiction": "Global"}, {"query": "FICO", "jurisdiction": "USA"}, {"query": "Debt-to-income ratio", "jurisdiction": "Global"}, {"query": "Collateral (finance)", "jurisdiction": "Global"}, {"query": "Credit default swap", "jurisdiction": "Global"}, {"query": "Consumer credit", "jurisdiction": "Global"}, {"query": "Mortgage underwriting", "jurisdiction": "Global"} ], "senticoach": [ {"query": "Personal finance", "jurisdiction": "Global"}, {"query": "Budget", "jurisdiction": "Global"}, {"query": "Financial literacy", "jurisdiction": "Global"}, {"query": "Retirement planning", "jurisdiction": "Global"}, {"query": "Savings account", "jurisdiction": "Global"}, {"query": "Debt consolidation", "jurisdiction": "Global"}, {"query": "Investment strategy", "jurisdiction": "Global"}, {"query": "Emergency fund", "jurisdiction": "Global"}, {"query": "Wealth management", "jurisdiction": "Global"} ] } def clean_text(text: str) -> str: # Remove Wikipedia markup and markdown remnants text = re.sub(r'==.*?==', '', text) # Remove headers text = re.sub(r'\[\[(?:[^|\]]*\|)?([^\]]+)\]\]', r'\1', text) # Remove internal links markup text = re.sub(r'\{\{[^\}]*\}\}', '', text) # Remove templates text = re.sub(r'Ref[0-9]+', '', text) # Remove references text = re.sub(r'\s+', ' ', text) # Normalize whitespaces return text.strip() def fetch_wikipedia_article(title: str) -> str: params = { "action": "query", "format": "json", "titles": title, "prop": "extracts", "explaintext": 1, "redirects": 1 } url = "https://en.wikipedia.org/w/api.php?" + urllib.parse.urlencode(params) req = urllib.request.Request( url, headers={'User-Agent': 'SentiAICrawler/1.0 (contact: admin@sentiai.com)'} ) try: with urllib.request.urlopen(req, timeout=15) as response: res_data = json.loads(response.read().decode('utf-8')) pages = res_data.get("query", {}).get("pages", {}) for page_id, page in pages.items(): if "extract" in page: return page["extract"] except Exception as e: print(f"Error fetching page '{title}': {e}") return "" def crawl_and_clean(): print("=== STARTING SENTI AI WEB CRAWLER AND DATA INGESTION ===") total_crawled = 0 for pack_name, targets in CRAWL_TARGETS.items(): print(f"\nProcessing data pack: {pack_name}...") # Determine directories senti_ai_cleaned_dir = os.path.join(SENTI_AI_ROOT, pack_name, "cleaned") senti_cleaned_dir = os.path.join(SENTI_ROOT, pack_name, "cleaned") os.makedirs(senti_ai_cleaned_dir, exist_ok=True) os.makedirs(senti_cleaned_dir, exist_ok=True) for target in targets: query = target["query"] jurisdiction = target["jurisdiction"] print(f"Crawling Wikipedia for '{query}' (Jurisdiction: {jurisdiction})...") raw_content = fetch_wikipedia_article(query) if not raw_content or len(raw_content) < 500: print(f"Skipping '{query}': Insufficient content fetched.") continue cleaned_content = clean_text(raw_content) # Format document doc_id = str(uuid.uuid4()) doc = { "doc_id": doc_id, "source": { "source_id": f"crawled_wiki_{uuid.uuid4().hex[:10]}", "url": f"https://en.wikipedia.org/wiki/{urllib.parse.quote(query)}", "name": f"wikipedia_{query.replace(' ', '_').lower()}.txt", "author": "Wikipedia Contributors", "publisher": "Wikimedia Foundation", "access_date": datetime.utcnow().isoformat(), "license_status": "CC-BY-SA", "jurisdiction": jurisdiction, "language": "en", "topic_tags": [query.lower(), pack_name.lower()], "pack_assignment": pack_name.upper(), # Matches the original naming assignment e.g. SENTILAW "file_type": "txt", "checksum": uuid.uuid4().hex, "version": "1.0.0", "quality_score": 0.9, "ingestion_timestamp": datetime.utcnow().isoformat(), "extra_metadata": { "crawled_via": "SentiAICrawler" } }, "content": { "raw_text": raw_content, "cleaned_text": cleaned_content } } # Write to both locations to ensure sync for dest_dir in [senti_ai_cleaned_dir, senti_cleaned_dir]: if os.path.exists(os.path.dirname(dest_dir)): # Verify parent path exists dest_file = os.path.join(dest_dir, f"{doc_id}.json") with open(dest_file, "w", encoding="utf-8") as f: json.dump(doc, f, indent=2) print(f"Successfully saved and cleaned '{query}' -> {doc_id}.json") total_crawled += 1 print(f"\n=== CRAWLING COMPLETED. INGESTED AND CLEANED {total_crawled} GLOBAL DATASETS ===") if __name__ == "__main__": crawl_and_clean()