import asyncio import aiohttp from html.parser import HTMLParser import socket from datetime import datetime, timedelta class CBSLTableParser(HTMLParser): def __init__(self): super().__init__() self.tables = [] self.current_table = [] self.current_row = [] self.current_cell = [] self.in_table = False self.in_row = False self.in_cell = False self.cell_type = None def handle_starttag(self, tag, attrs): if tag == 'table': self.in_table = True self.current_table = [] elif tag == 'tr' and self.in_table: self.in_row = True self.current_row = [] elif tag in ('td', 'th') and self.in_row: self.in_cell = True self.cell_type = tag self.current_cell = [] def handle_endtag(self, tag): if tag == 'table' and self.in_table: self.in_table = False self.tables.append(self.current_table) elif tag == 'tr' and self.in_row: self.in_row = False self.current_table.append(self.current_row) elif tag in ('td', 'th') and self.in_cell: self.in_cell = False text = "".join(self.current_cell).strip().replace('\n', ' ') self.current_row.append(text) def handle_data(self, data): if self.in_cell: self.current_cell.append(data) async def fetch_cbsl_rates_for_date(session, date_str): url = "https://www.cbsl.gov.lk/cbsl_custom/exrates/exrates_results.php" data = [ ("lookupPage", "lookup_daily_exchange_rates.php"), ("startRange", "2006-11-11"), ("rangeType", "dates"), ("txtStart", date_str), ("txtEnd", date_str), ("chk_cur[]", "USD~US Dollar"), ("chk_cur[]", "EUR~Euro"), ("chk_cur[]", "GBP~Sterling Pound"), ("chk_cur[]", "AUD~Australian Dollar"), ("chk_cur[]", "JPY~Japanese Yen"), ("chk_cur[]", "AED~UAE Dirham"), ("chk_cur[]", "SAR~Saudi Arabian Riyal"), ("chk_cur[]", "INR~Indian Rupee"), ("chk_cur[]", "CNY~Chinese Yuan (Renminbi)"), ("chk_cur[]", "QAR~Qatar Riyal"), ("submit_button", "Submit") ] headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Origin": "https://www.cbsl.gov.lk", "Referer": "https://www.cbsl.gov.lk/cbsl_custom/exrates/exrates.php" } async with session.post(url, data=data, timeout=10) as r: if r.status == 200: text = await r.text() parser = CBSLTableParser() parser.feed(text) return parser.tables return [] async def get_latest_cbsl_rates(): connector = aiohttp.TCPConnector(family=socket.AF_INET) headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } async with aiohttp.ClientSession(connector=connector, headers=headers) as session: # Start with today and go backwards up to 7 days today = datetime.now() for i in range(8): query_date = today - timedelta(days=i) date_str = query_date.strftime("%Y-%m-%d") print(f"Trying date: {date_str}...") tables = await fetch_cbsl_rates_for_date(session, date_str) # Check if we parsed tables and at least one table has data rows (length > 1) has_data = False rates_dict = {} if tables: for table in tables: cleaned_rows = [] for row in table: row_cleaned = [item.strip() for item in row if item.strip()] if row_cleaned: cleaned_rows.append(row_cleaned) if len(cleaned_rows) > 1: # We have a header row and a data row! # E.g. Header: ['Date', '1 USD -> LKR', '1 LKR -> USD'] # Data: ['2026-05-15', '324.7184', '0.0031'] header = cleaned_rows[0] data_row = cleaned_rows[1] # Extract currency code from header, e.g. "1 USD -> LKR" import re match = re.search(r'1\s+([A-Z]{3})\s+->', header[1]) if match and len(data_row) >= 2: cur_code = match.group(1) try: rate_val = float(data_row[1]) rates_dict[cur_code] = rate_val has_data = True except ValueError: pass if has_data: print(f"SUCCESS! Found valid rates for date {date_str}:") for cur, val in rates_dict.items(): print(f" {cur}: {val} LKR") return rates_dict else: print(f"No rates found for {date_str}.") print("Failed to find any CBSL rates in the last 7 days.") return {} async def main(): await get_latest_cbsl_rates() if __name__ == "__main__": asyncio.run(main())