FlyRates / scratch /test_cbsl_backtrack.py
Sadeep Sachintha
feat: implement FX service with persistent caching and optimize concurrent rate fetching in main
72fdc64
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())