90 lines
3.0 KiB
Python
90 lines
3.0 KiB
Python
#!/usr/bin/env python
|
|
import json
|
|
import re
|
|
from urllib.parse import urljoin
|
|
|
|
import requests
|
|
from bs4 import BeautifulSoup
|
|
|
|
COUNTRY_CODE_PATTERN = r"[A-Z]{2}"
|
|
EMPTY_RANGE = (0, 0)
|
|
URL = "https://www.swift.com/standards/data-standards/iban"
|
|
|
|
|
|
def get_raw():
|
|
soup = BeautifulSoup(requests.get(URL).content, "html.parser")
|
|
link = soup.find("a", attrs={"data-tracking-title": "IBAN Registry (TXT)"})
|
|
return requests.get(urljoin(URL, link["href"])).content.decode(encoding="latin1")
|
|
|
|
|
|
def parse_int(raw):
|
|
return int(re.search(r"\d+", raw).group())
|
|
|
|
|
|
def parse_range(raw):
|
|
pattern = r".*?(?P<from>\d+)\s*-\s*(?P<to>\d+)"
|
|
match = re.search(pattern, raw)
|
|
if not match:
|
|
return EMPTY_RANGE
|
|
return (int(match["from"]) - 1, int(match["to"]))
|
|
|
|
|
|
def parse(raw):
|
|
columns = {}
|
|
for line in raw.split("\r\n"):
|
|
header, *rows = line.split("\t")
|
|
if header == "IBAN prefix country code (ISO 3166)":
|
|
columns["country"] = [
|
|
re.search(COUNTRY_CODE_PATTERN, item).group() for item in rows
|
|
]
|
|
elif header == "Country code includes other countries/territories":
|
|
columns["other_countries"] = [
|
|
re.findall(COUNTRY_CODE_PATTERN, item) for item in rows
|
|
]
|
|
elif header == "BBAN structure":
|
|
columns["bban_spec"] = rows
|
|
elif header == "BBAN length":
|
|
columns["bban_length"] = [parse_int(item) for item in rows]
|
|
elif header == "Bank identifier position within the BBAN":
|
|
columns["bank_code_position"] = [parse_range(item) for item in rows]
|
|
elif header == "Branch identifier position within the BBAN":
|
|
columns["branch_code_position"] = [parse_range(item) for item in rows]
|
|
elif header == "IBAN structure":
|
|
columns["iban_spec"] = rows
|
|
elif header == "IBAN length":
|
|
columns["iban_length"] = [parse_int(item) for item in rows]
|
|
return [dict(zip(columns.keys(), row)) for row in zip(*columns.values())]
|
|
|
|
|
|
def process(records):
|
|
registry = {}
|
|
for record in records:
|
|
country_codes = [record["country"]]
|
|
country_codes.extend(record["other_countries"])
|
|
for code in country_codes:
|
|
registry[code] = {
|
|
"bban_spec": record["bban_spec"],
|
|
"iban_spec": record["iban_spec"],
|
|
"bban_length": record["bban_length"],
|
|
"iban_length": record["iban_length"],
|
|
"positions": process_positions(record),
|
|
}
|
|
return registry
|
|
|
|
|
|
def process_positions(record):
|
|
bank_code = record["bank_code_position"]
|
|
branch_code = record["branch_code_position"]
|
|
if branch_code == EMPTY_RANGE:
|
|
branch_code = (bank_code[1], bank_code[1])
|
|
return {
|
|
"account_code": (max(bank_code[1], branch_code[1]), record["bban_length"]),
|
|
"bank_code": bank_code,
|
|
"branch_code": branch_code,
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
with open("iban_registry.json", "w+") as fp:
|
|
json.dump(process(parse(get_raw())), fp, indent=2)
|