├── README.md ├── example.py ├── .gitignore ├── LICENSE └── CurrencyExchange.py /README.md: -------------------------------------------------------------------------------- 1 | currency_exchange_rates 2 | ======================= 3 | 4 | Bank of Canada currency exchange rates. 5 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | from CurrencyExchange import BOCCurrencyExchange 2 | 3 | if __name__=="__main__": 4 | boc_currency = BOCCurrencyExchange() 5 | print boc_currency.get_rates() -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nav Aulakh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /CurrencyExchange.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import re 3 | import os 4 | import urllib 5 | import datetime 6 | from HTMLParser import HTMLParser 7 | 8 | 9 | class BOCCurrencyExchange(object): 10 | main_page_url = 'http://www.bankofcanada.ca/rates/exchange/daily-converter/#cfee' 11 | rates_page_url = 'http://www.bankofcanada.ca/stats/assets/js/daily_curr_data.js' 12 | 13 | def __init__(self): 14 | self.main_page_content = '' 15 | self.rates_page_content = '' 16 | self.fetch_data() 17 | 18 | def fetch_data(self): 19 | main_file = '{}_boc_main.dat'.format(datetime.datetime.now().today().date()) 20 | rates_file = '{}_boc_rates.dat'.format(datetime.datetime.now().today().date()) 21 | 22 | if os.path.isfile(main_file): 23 | with open(main_file, 'r') as fsock: 24 | self.main_page_content = fsock.read() 25 | else: 26 | main_page_content = urllib.urlopen(self.main_page_url).read() 27 | with open(main_file, 'w') as fsock: 28 | fsock.write(main_page_content) 29 | self.main_page_content = main_page_content 30 | 31 | if os.path.isfile(rates_file): 32 | with open(rates_file, 'r') as fsock: 33 | self.rates_page_content = fsock.read() 34 | else: 35 | rates_page_content = urllib.urlopen(self.rates_page_url).read() 36 | with open(rates_file, 'w') as fsock: 37 | fsock.write(rates_page_content) 38 | self.rates_page_content = rates_page_content 39 | 40 | def get_code_name_mapping(self): 41 | content = self.main_page_content 42 | 43 | class CodeParser(HTMLParser): 44 | mapping = dict() 45 | current_currency = None 46 | current_name = None 47 | 48 | def handle_starttag(self, tag, attrs): 49 | if tag == "option": 50 | for attr in attrs: 51 | if attr[0] == "value" and attr[1].startswith('LOOKUPS'): 52 | self.current_currency = attr[1] 53 | else: 54 | self.current_currency = None 55 | 56 | def handle_data(self, data): 57 | if self.current_currency: 58 | self.current_name = data.strip() 59 | 60 | def handle_endtag(self, tag): 61 | if self.current_currency and self.current_name: 62 | self.mapping[self.current_currency] = self.current_name 63 | 64 | def get_mapping(self): 65 | self.feed(content) 66 | return self.mapping 67 | 68 | parser = CodeParser() 69 | code_name_mapping = parser.get_mapping() 70 | 71 | return code_name_mapping 72 | 73 | def get_code_rate_mapping(self): 74 | code_pattern = r'\["(.*)"\]' 75 | rate_pattern = r'rate:"(.*)",\sdate_en' 76 | code_rate_mapping = dict() 77 | currency_rate_mapping = dict() 78 | 79 | for line in self.rates_page_content.split('\n'): 80 | code = re.findall(code_pattern, line) 81 | rate = re.findall(rate_pattern, line) 82 | if code and rate: 83 | code_rate_mapping[code[0]] = float(rate[0]) 84 | 85 | return code_rate_mapping 86 | 87 | def get_rates(self): 88 | rates = dict() 89 | code_name_mapping = self.get_code_name_mapping() 90 | for code, rate in self.get_code_rate_mapping().items(): 91 | rates[code_name_mapping[code]] = rate 92 | 93 | return rates --------------------------------------------------------------------------------