├── .bandit.yml ├── .coveragerc ├── .flake8 ├── .git-blame-ignore-revs ├── .github └── workflows │ ├── main.yml │ └── publish.yml ├── .gitignore ├── .isort.cfg ├── .pre-commit-config.yaml ├── CHANGES.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── codecov.yml ├── price_parser ├── __init__.py ├── _currencies.py ├── parser.py └── py.typed ├── pyproject.toml ├── setup.py ├── tests └── test_price_parsing.py └── tox.ini /.bandit.yml: -------------------------------------------------------------------------------- 1 | skips: 2 | - B101 # assert_used, needed for mypy 3 | - B311 4 | - B320 5 | - B410 6 | exclude_dirs: ['tests'] -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = true 3 | 4 | [report] 5 | show_missing = true 6 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # applying pre-commit hooks to the project 2 | ca42e4247d872a174afc539902e156282940ad5d -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tests: 7 | runs-on: ubuntu-latest 8 | if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | include: 13 | - python-version: "3.9" 14 | env: 15 | TOXENV: py 16 | - python-version: "3.9" 17 | env: 18 | TOXENV: py 19 | - python-version: "3.10" 20 | env: 21 | TOXENV: py 22 | - python-version: "3.11" 23 | env: 24 | TOXENV: py 25 | - python-version: "3.12" 26 | env: 27 | TOXENV: py 28 | - python-version: "3.13" 29 | env: 30 | TOXENV: py 31 | - python-version: "3.13" 32 | env: 33 | TOXENV: mypy 34 | steps: 35 | - uses: actions/checkout@v3 36 | - name: Set up Python ${{ matrix.python-version }} 37 | uses: actions/setup-python@v4 38 | with: 39 | python-version: ${{ matrix.python-version }} 40 | 41 | - name: Run tests 42 | env: ${{ matrix.env }} 43 | run: | 44 | pip install -U tox 45 | tox 46 | 47 | - name: Upload coverage report 48 | uses: codecov/codecov-action@v5 49 | with: 50 | token: ${{ secrets.CODECOV_TOKEN }} 51 | 52 | pre-commit: 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v4 56 | - uses: pre-commit/action@v3.0.0 57 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | push: 4 | tags: 5 | - '[0-9]+.[0-9]+.[0-9]+' 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | environment: 10 | name: pypi 11 | url: https://pypi.org/p/${{ github.event.repository.name }} 12 | permissions: 13 | id-token: write 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-python@v5 17 | with: 18 | python-version: 3.13 19 | - run: | 20 | python -m pip install --upgrade build 21 | python -m build 22 | - name: Publish to PyPI 23 | uses: pypa/gh-action-pypi-publish@release/v1 24 | with: 25 | password: ${{ secrets.PYPI_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | *.pyc 3 | *.pyo 4 | /build/ 5 | /dist/ 6 | *.egg-info 7 | 8 | # Mac OS 9 | *.DS_Store 10 | 11 | # IDE 12 | /.idea/ 13 | 14 | .mypy_cache/ 15 | .cache/ 16 | .tox/ 17 | .pytest_cache/ 18 | .coverage 19 | 20 | # python virtual env 21 | .venv/ 22 | venv/ 23 | .env/ 24 | env/ 25 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | profile = black -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/PyCQA/bandit 3 | rev: 1.7.9 4 | hooks: 5 | - id: bandit 6 | args: [-r, -c, .bandit.yml] 7 | - repo: https://github.com/psf/black.git 8 | rev: 24.8.0 9 | hooks: 10 | - id: black 11 | - repo: https://github.com/PyCQA/flake8 12 | rev: 7.1.1 13 | hooks: 14 | - id: flake8 15 | - repo: https://github.com/pycqa/isort 16 | rev: 5.13.2 17 | hooks: 18 | - id: isort -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | Changes 2 | ======= 3 | 4 | 0.4.0 (2025-02-05) 5 | ------------------ 6 | 7 | * Dropped support for Python 3.8 and lower, added support for Python 3.11 and 8 | higher. 9 | 10 | * Added a ``digit_group_separator`` parameter to ``Price.fromstring()``. 11 | 12 | 0.3.4 (2020-11-25) 13 | ------------------ 14 | 15 | * Improved parsing of prices without digits before a decimal point ('.75'), 16 | https://github.com/scrapinghub/price-parser/pull/42 17 | * Fix parsing of price with non-breaking spaces 18 | https://github.com/scrapinghub/price-parser/pull/43 19 | 20 | 0.3.3 (2020-02-05) 21 | ------------------ 22 | 23 | * Fixed installation issue on some Windows machines. 24 | 25 | 0.3.2 (2020-01-28) 26 | ------------------ 27 | 28 | * Improved Korean and Japanese currency detection. 29 | * Declare Python 3.8 support. 30 | 31 | 0.3.1 (2019-10-21) 32 | ------------------ 33 | 34 | * Redundant $ signs are no longer returned as a part of currency, e.g. 35 | for ``SGD$ 100`` currency would be ``SGD``, not ``SGD$``. 36 | 37 | 0.3.0 (2019-10-19) 38 | ------------------ 39 | 40 | * New ``Price.fromstring`` argument ``decimal_separator`` allows to override 41 | decimal separator for the cases where it is known 42 | (i.e. disable decimal separator detection); 43 | * NTD and RBM unofficial currency names are added; 44 | * quantifiers in regular expressions are made non-greedy, which provides 45 | a small speedup; 46 | * test improvements. 47 | 48 | 0.2.4 (2019-07-03) 49 | ------------------ 50 | 51 | * Declare price-parser as providing type annotations (pep-561). This enables 52 | better type checking for projects using price-parser. 53 | * improved test coverage 54 | 55 | 0.2.3 (2019-06-18) 56 | ------------------ 57 | 58 | * Follow-up for 0.2.2 release: improved parsing of prices with 4+ digits 59 | after a decimal separator. 60 | 61 | 0.2.2 (2019-06-18) 62 | ------------------ 63 | 64 | * Fixed parsing of prices with 4+ digits after a decimal separator. 65 | 66 | 0.2.1 (2019-04-19) 67 | ------------------ 68 | 69 | * 23 additional currency symbols are added; 70 | * ``A$`` alias for Australian Dollar is added. 71 | 72 | 0.2 (2019-04-12) 73 | ---------------- 74 | 75 | Added support for currencies replaced by euro. 76 | 77 | 0.1.1 (2019-04-12) 78 | ------------------ 79 | 80 | Minor packaging fixes. 81 | 82 | 0.1 (2019-04-12) 83 | ---------------- 84 | 85 | Initial release. 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Scrapinghub 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of ScrapingHub nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include CHANGES.rst 2 | include LICENSE 3 | include README.rst 4 | 5 | recursive-include tests * 6 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | price-parser 3 | ============ 4 | 5 | .. image:: https://img.shields.io/pypi/v/price-parser.svg 6 | :target: https://pypi.python.org/pypi/price-parser 7 | :alt: PyPI Version 8 | 9 | .. image:: https://img.shields.io/pypi/pyversions/price-parser.svg 10 | :target: https://pypi.python.org/pypi/price-parser 11 | :alt: Supported Python Versions 12 | 13 | .. image:: https://github.com/scrapinghub/price-parser/actions/workflows/main.yml/badge.svg?branch=master 14 | :target: https://github.com/scrapinghub/price-parser/actions?workflow=Tests 15 | :alt: Build Status 16 | 17 | .. image:: https://codecov.io/github/scrapinghub/price-parser/coverage.svg?branch=master 18 | :target: https://codecov.io/gh/scrapinghub/price-parser 19 | :alt: Coverage report 20 | 21 | 22 | ``price-parser`` is a small library for extracting price and currency from 23 | raw text strings. 24 | 25 | Features: 26 | 27 | * robust price amount and currency symbol extraction 28 | * zero-effort handling of thousand and decimal separators 29 | 30 | The main use case is parsing prices extracted from web pages. 31 | For example, you can write a CSS/XPath selector which targets an element 32 | with a price, and then use this library for cleaning it up, 33 | instead of writing custom site-specific regex or Python code. 34 | 35 | License is BSD 3-clause. 36 | 37 | Installation 38 | ============ 39 | 40 | :: 41 | 42 | pip install price-parser 43 | 44 | price-parser requires Python 3.6+. 45 | 46 | Usage 47 | ===== 48 | 49 | Basic usage 50 | ----------- 51 | 52 | >>> from price_parser import Price 53 | >>> price = Price.fromstring("22,90 €") 54 | >>> price 55 | Price(amount=Decimal('22.90'), currency='€') 56 | >>> price.amount # numeric price amount 57 | Decimal('22.90') 58 | >>> price.currency # currency symbol, as appears in the string 59 | '€' 60 | >>> price.amount_text # price amount, as appears in the string 61 | '22,90' 62 | >>> price.amount_float # price amount as float, not Decimal 63 | 22.9 64 | 65 | If you prefer, ``Price.fromstring`` has an alias ``price_parser.parse_price``, 66 | they do the same: 67 | 68 | >>> from price_parser import parse_price 69 | >>> parse_price("22,90 €") 70 | Price(amount=Decimal('22.90'), currency='€') 71 | 72 | The library has extensive tests (900+ real-world examples of price strings). 73 | Some of the supported cases are described below. 74 | 75 | Supported cases 76 | --------------- 77 | 78 | Unclean price strings with various currencies are supported; 79 | thousand separators and decimal separators are handled: 80 | 81 | >>> Price.fromstring("Price: $119.00") 82 | Price(amount=Decimal('119.00'), currency='$') 83 | 84 | >>> Price.fromstring("15 130 Р") 85 | Price(amount=Decimal('15130'), currency='Р') 86 | 87 | >>> Price.fromstring("151,200 تومان") 88 | Price(amount=Decimal('151200'), currency='تومان') 89 | 90 | >>> Price.fromstring("Rp 1.550.000") 91 | Price(amount=Decimal('1550000'), currency='Rp') 92 | 93 | >>> Price.fromstring("Běžná cena 75 990,00 Kč") 94 | Price(amount=Decimal('75990.00'), currency='Kč') 95 | 96 | 97 | Euro sign is used as a decimal separator in a wild: 98 | 99 | >>> Price.fromstring("1,235€ 99") 100 | Price(amount=Decimal('1235.99'), currency='€') 101 | 102 | >>> Price.fromstring("99 € 95 €") 103 | Price(amount=Decimal('99'), currency='€') 104 | 105 | >>> Price.fromstring("35€ 999") 106 | Price(amount=Decimal('35'), currency='€') 107 | 108 | 109 | Some special cases are handled: 110 | 111 | >>> Price.fromstring("Free") 112 | Price(amount=Decimal('0'), currency=None) 113 | 114 | 115 | When price or currency can't be extracted, corresponding 116 | attribute values are set to None: 117 | 118 | >>> Price.fromstring("") 119 | Price(amount=None, currency=None) 120 | 121 | >>> Price.fromstring("Foo") 122 | Price(amount=None, currency=None) 123 | 124 | >>> Price.fromstring("50% OFF") 125 | Price(amount=None, currency=None) 126 | 127 | >>> Price.fromstring("50") 128 | Price(amount=Decimal('50'), currency=None) 129 | 130 | >>> Price.fromstring("R$") 131 | Price(amount=None, currency='R$') 132 | 133 | 134 | Currency hints 135 | -------------- 136 | 137 | ``currency_hint`` argument allows to pass a text string which may (or may not) 138 | contain currency information. This feature is most useful for automated price 139 | extraction. 140 | 141 | >>> Price.fromstring("34.99", currency_hint="руб. (шт)") 142 | Price(amount=Decimal('34.99'), currency='руб.') 143 | 144 | Note that currency mentioned in the main price string may be 145 | **preferred** over currency specified in ``currency_hint`` argument; 146 | it depends on currency symbols found there. If you know the correct currency, 147 | you can set it directly: 148 | 149 | >>> price = Price.fromstring("1 000") 150 | >>> price.currency = 'EUR' 151 | >>> price 152 | Price(amount=Decimal('1000'), currency='EUR') 153 | 154 | 155 | Decimal separator 156 | ----------------- 157 | 158 | If you know which symbol is used as a decimal separator in the input string, 159 | pass that symbol in the ``decimal_separator`` argument to prevent price-parser 160 | from guessing the wrong decimal separator symbol. 161 | 162 | >>> Price.fromstring("Price: $140.600", decimal_separator=".") 163 | Price(amount=Decimal('140.600'), currency='$') 164 | 165 | >>> Price.fromstring("Price: $140.600", decimal_separator=",") 166 | Price(amount=Decimal('140600'), currency='$') 167 | 168 | 169 | Contributing 170 | ============ 171 | 172 | * Source code: https://github.com/scrapinghub/price-parser 173 | * Issue tracker: https://github.com/scrapinghub/price-parser/issues 174 | 175 | Use tox_ to run tests with different Python versions:: 176 | 177 | tox 178 | 179 | The command above also runs type checks; we use mypy. 180 | 181 | .. _tox: https://tox.readthedocs.io 182 | 183 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: 2 | layout: "header, diff, tree" 3 | 4 | coverage: 5 | status: 6 | project: false 7 | -------------------------------------------------------------------------------- /price_parser/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .parser import Price, parse_price # noqa F401 3 | -------------------------------------------------------------------------------- /price_parser/_currencies.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Currency information. 4 | 5 | ``CURRENCIES`` data is from https://github.com/StorePilot/coinify, 6 | which is supposed to provide combined data from 7 | 8 | * https://gist.github.com/Fluidbyte/2973986 9 | * https://en.wikipedia.org/wiki/ISO_4217 10 | * http://www.iotafinance.com/en/ISO-4217-Currency-Codes.html 11 | * http://www.xe.com/symbols.php 12 | 13 | Field meaning: 14 | 15 | * s - currency main symbol 16 | * n - currency name (currently unused) 17 | * sn - currency native symbol 18 | * d - decimal digits (currently unused) 19 | * r - rounding (currently unused) 20 | * np - currency name, plural (currently unused) 21 | * sn2 - other currency symbols 22 | 23 | Some extra abbreviations are added to the list (they are set below 24 | ``CURRENCIES`` variable, scroll to the bottom). 25 | """ 26 | from itertools import chain 27 | from typing import Dict, List 28 | 29 | CURRENCIES: Dict[str, Dict] = { 30 | "AED": { 31 | "s": "AED", 32 | "n": "United Arab Emirates Dirham", 33 | "sn": "د.إ.‏", 34 | "d": 2, 35 | "r": 0, 36 | "np": "UAE dirhams", 37 | }, 38 | "AFN": { 39 | "s": "Af", 40 | "n": "Afghan Afghani", 41 | "sn": "؋", 42 | "d": 0, 43 | "r": 0, 44 | "np": "Afghan Afghanis", 45 | }, 46 | "ALL": { 47 | "s": "ALL", 48 | "n": "Albanian Lek", 49 | "sn": "Lek", 50 | "d": 0, 51 | "r": 0, 52 | "np": "Albanian lekë", 53 | }, 54 | "AMD": { 55 | "s": "AMD", 56 | "n": "Armenian Dram", 57 | "sn": "դր.", 58 | "d": 0, 59 | "r": 0, 60 | "np": "Armenian drams", 61 | }, 62 | "ANG": { 63 | "s": "ƒ", 64 | "n": "Netherlands Antilles Guilder", 65 | "sn": "ƒ", 66 | "d": 2, 67 | "r": 0, 68 | "np": "Netherlands Antilles Guilder", 69 | }, 70 | "AOA": { 71 | "s": "Kz", 72 | "n": "Angolan kwanza", 73 | "sn": "Kz", 74 | "d": 2, 75 | "r": 0, 76 | "np": "Angolan kwanza", 77 | }, 78 | "ARS": { 79 | "s": "AR$", 80 | "n": "Argentine Peso", 81 | "sn": "$", 82 | "d": 2, 83 | "r": 0, 84 | "np": "Argentine pesos", 85 | }, 86 | "AUD": { 87 | "s": "AU$", 88 | "n": "Australian Dollar", 89 | "sn": "$", 90 | "d": 2, 91 | "r": 0, 92 | "np": "Australian dollars", 93 | }, 94 | "AWG": { 95 | "s": "ƒ", 96 | "n": "Aruban Guilder", 97 | "sn": "ƒ", 98 | "d": 2, 99 | "r": 0, 100 | "np": "Aruban Guilders", 101 | }, 102 | "AFL": { 103 | "s": "Afl.", 104 | "n": "Aruban Florin", 105 | "sn": "Afl.", 106 | "d": 2, 107 | "r": 0, 108 | "np": "Aruban Florins", 109 | }, 110 | "AZN": { 111 | "s": "man.", 112 | "n": "Azerbaijani Manat", 113 | "sn": "ман.", 114 | "d": 2, 115 | "r": 0, 116 | "np": "Azerbaijani manats", 117 | }, 118 | "BAM": { 119 | "s": "KM", 120 | "n": "Bosnia-Herzegovina Convertible Mark", 121 | "sn": "KM", 122 | "d": 2, 123 | "r": 0, 124 | "np": "Bosnia-Herzegovina convertible marks", 125 | }, 126 | "BDT": { 127 | "s": "Tk", 128 | "n": "Bangladeshi Taka", 129 | "sn": "৳", 130 | "d": 2, 131 | "r": 0, 132 | "np": "Bangladeshi takas", 133 | }, 134 | "BBD": { 135 | "s": "Bds$", 136 | "n": "Barbados dollar", 137 | "sn": "$", 138 | "d": 2, 139 | "r": 0, 140 | "np": "Barbados dollar", 141 | }, 142 | "BGN": { 143 | "s": "BGN", 144 | "n": "Bulgarian Lev", 145 | "sn": "лв.", 146 | "d": 2, 147 | "r": 0, 148 | "np": "Bulgarian leva", 149 | }, 150 | "BHD": { 151 | "s": "BD", 152 | "n": "Bahraini Dinar", 153 | "sn": "د.ب.‏", 154 | "d": 3, 155 | "r": 0, 156 | "np": "Bahraini dinars", 157 | }, 158 | "BIF": { 159 | "s": "FBu", 160 | "n": "Burundian Franc", 161 | "sn": "FBu", 162 | "d": 0, 163 | "r": 0, 164 | "np": "Burundian francs", 165 | }, 166 | "BSD": { 167 | "s": "$", 168 | "n": "Bahamas Dollar", 169 | "sn": "$", 170 | "d": 2, 171 | "r": 0, 172 | "np": "Bahamas Dollar", 173 | }, 174 | "BMD": { 175 | "s": "$", 176 | "n": "Bermuda Dollar", 177 | "sn": "$", 178 | "d": 2, 179 | "r": 0, 180 | "np": "Bermuda Dollars", 181 | }, 182 | "BND": { 183 | "s": "BN$", 184 | "n": "Brunei Dollar", 185 | "sn": "$", 186 | "d": 2, 187 | "r": 0, 188 | "np": "Brunei dollars", 189 | }, 190 | "BOB": { 191 | "s": "Bs", 192 | "n": "Bolivian Boliviano", 193 | "sn": "Bs", 194 | "d": 2, 195 | "r": 0, 196 | "np": "Bolivian bolivianos", 197 | }, 198 | "BOV": { 199 | "s": "-", 200 | "n": "Bolivian Mvdol", 201 | "sn": "-", 202 | "d": 2, 203 | "r": 0, 204 | "np": "Bolivian Mvdol", 205 | }, 206 | "BRL": { 207 | "s": "R$", 208 | "n": "Brazilian Real", 209 | "sn": "R$", 210 | "d": 2, 211 | "r": 0, 212 | "np": "Brazilian reals", 213 | }, 214 | "BTN": { 215 | "s": "Nu.", 216 | "n": "Bhutanese ngultrum", 217 | "sn": "Nu.", 218 | "d": 2, 219 | "r": 0, 220 | "np": "Bhutanese ngultrum", 221 | }, 222 | "BWP": { 223 | "s": "BWP", 224 | "n": "Botswanan Pula", 225 | "sn": "P", 226 | "d": 2, 227 | "r": 0, 228 | "np": "Botswanan pulas", 229 | }, 230 | "BYN": { 231 | "s": "Br", 232 | "n": "Belarusian Ruble", 233 | "sn": "Br", 234 | "d": 0, 235 | "r": 0, 236 | "np": "Belarusian rubles", 237 | }, 238 | "BZD": { 239 | "s": "BZ$", 240 | "n": "Belize Dollar", 241 | "sn": "$", 242 | "d": 2, 243 | "r": 0, 244 | "np": "Belize dollars", 245 | }, 246 | "CAD": { 247 | "s": "CA$", 248 | "n": "Canadian Dollar", 249 | "sn": "$", 250 | "d": 2, 251 | "r": 0, 252 | "np": "Canadian dollars", 253 | }, 254 | "CDF": { 255 | "s": "CDF", 256 | "n": "Congolese Franc", 257 | "sn": "FrCD", 258 | "d": 2, 259 | "r": 0, 260 | "np": "Congolese francs", 261 | }, 262 | "CHE": { 263 | "s": "-", 264 | "n": "WIR Euro (complementary currency)", 265 | "sn": "-", 266 | "d": 2, 267 | "r": 0, 268 | "np": "WIR Euros (complementary currency)", 269 | }, 270 | "CHF": { 271 | "s": "CHF", 272 | "n": "Swiss Franc", 273 | "sn": "CHF", 274 | "d": 2, 275 | "r": 0.05, 276 | "np": "Swiss francs", 277 | }, 278 | "CHW": { 279 | "s": "-", 280 | "n": "WIR Franc (complementary currency)", 281 | "sn": "-", 282 | "d": 2, 283 | "r": 0, 284 | "np": "WIR Franc (complementary currency)", 285 | }, 286 | "CLF": { 287 | "s": "UF", 288 | "n": "Unidad de Fomento (funds code)", 289 | "sn": "UF", 290 | "d": 4, 291 | "r": 0, 292 | "np": "Unidad de Fomento (funds code)", 293 | }, 294 | "CLP": { 295 | "s": "CL$", 296 | "n": "Chilean Peso", 297 | "sn": "$", 298 | "d": 0, 299 | "r": 0, 300 | "np": "Chilean pesos", 301 | }, 302 | "CNY": { 303 | "s": "CN¥", 304 | "n": "Chinese Yuan", 305 | "sn": "CN¥", 306 | "d": 2, 307 | "r": 0, 308 | "np": "Chinese yuan", 309 | }, 310 | "COP": { 311 | "s": "CO$", 312 | "n": "Colombian Peso", 313 | "sn": "$", 314 | "d": 0, 315 | "r": 0, 316 | "np": "Colombian pesos", 317 | }, 318 | "COU": { 319 | "s": "-", 320 | "n": "Unidad de Valor Real (UVR) (funds code)", 321 | "sn": "-", 322 | "d": 2, 323 | "r": 0, 324 | "np": "Unidad de Valor Real (UVR) (funds code)", 325 | }, 326 | "CRC": { 327 | "s": "₡", 328 | "n": "Costa Rican Colón", 329 | "sn": "₡", 330 | "d": 0, 331 | "r": 0, 332 | "np": "Costa Rican colóns", 333 | }, 334 | "CUC": { 335 | "s": "CUC$", 336 | "n": "Cuban convertible peso", 337 | "sn": "$", 338 | "d": 2, 339 | "r": 0, 340 | "np": "Cuban convertible pesos", 341 | }, 342 | "CUP": { 343 | "s": "₱", 344 | "n": "Cuba Peso", 345 | "sn": "₱", 346 | "d": 0, 347 | "r": 0, 348 | "np": "Cuba Pesos", 349 | }, 350 | "CVE": { 351 | "s": "CV$", 352 | "n": "Cape Verdean Escudo", 353 | "sn": "CV$", 354 | "d": 0, 355 | "r": 0, 356 | "np": "Cape Verdean escudos", 357 | }, 358 | "CZK": { 359 | "s": "Kč", 360 | "n": "Czech Republic Koruna", 361 | "sn": "Kč", 362 | "d": 2, 363 | "r": 0, 364 | "np": "Czech Republic korunas", 365 | }, 366 | "DJF": { 367 | "s": "Fdj", 368 | "n": "Djiboutian Franc", 369 | "sn": "Fdj", 370 | "d": 0, 371 | "r": 0, 372 | "np": "Djiboutian francs", 373 | }, 374 | "DKK": { 375 | "s": "Dkr", 376 | "n": "Danish Krone", 377 | "sn": "kr", 378 | "d": 2, 379 | "r": 0, 380 | "np": "Danish kroner", 381 | }, 382 | "DOP": { 383 | "s": "RD$", 384 | "n": "Dominican Peso", 385 | "sn": "RD$", 386 | "d": 2, 387 | "r": 0, 388 | "np": "Dominican pesos", 389 | }, 390 | "DZD": { 391 | "s": "DA", 392 | "n": "Algerian Dinar", 393 | "sn": "د.ج.‏", 394 | "d": 2, 395 | "r": 0, 396 | "np": "Algerian dinars", 397 | }, 398 | "EEK": { 399 | "s": "Ekr", 400 | "n": "Estonian Kroon", 401 | "sn": "kr", 402 | "d": 2, 403 | "r": 0, 404 | "np": "Estonian kroons", 405 | }, 406 | "EGP": { 407 | "s": "EGP", 408 | "n": "Egyptian Pound", 409 | "sn": "ج.م.‏", 410 | "d": 2, 411 | "r": 0, 412 | "np": "Egyptian pounds", 413 | }, 414 | "ERN": { 415 | "s": "Nfk", 416 | "n": "Eritrean Nakfa", 417 | "sn": "Nfk", 418 | "d": 2, 419 | "r": 0, 420 | "np": "Eritrean nakfas", 421 | }, 422 | "ETB": { 423 | "s": "Br", 424 | "n": "Ethiopian Birr", 425 | "sn": "Br", 426 | "d": 2, 427 | "r": 0, 428 | "np": "Ethiopian birrs", 429 | }, 430 | "EUR": {"s": "€", "n": "Euro", "sn": "€", "d": 2, "r": 0, "np": "euros"}, 431 | "FJD": { 432 | "s": "$", 433 | "n": "Fiji Dollar", 434 | "sn": "$", 435 | "d": 2, 436 | "r": 0, 437 | "np": "Fiji Dollars", 438 | }, 439 | "FKP": { 440 | "s": "£", 441 | "n": "Falkland Islands (Malvinas) Pound", 442 | "sn": "£", 443 | "d": 2, 444 | "r": 0, 445 | "np": "Falkland Islands (Malvinas) Pound", 446 | }, 447 | "GBP": { 448 | "s": "£", 449 | "n": "British Pound Sterling", 450 | "sn": "£", 451 | "d": 2, 452 | "r": 0, 453 | "np": "British pounds sterling", 454 | }, 455 | "GEL": { 456 | "s": "GEL", 457 | "n": "Georgian Lari", 458 | "sn": "GEL", 459 | "d": 2, 460 | "r": 0, 461 | "np": "Georgian laris", 462 | }, 463 | "GGP": { 464 | "s": "£", 465 | "n": "Guernsey Pound", 466 | "sn": "£", 467 | "d": 2, 468 | "r": 0, 469 | "np": "Guernsey Pounds", 470 | }, 471 | "GHS": { 472 | "s": "GH₵", 473 | "n": "Ghanaian Cedi", 474 | "sn": "GH₵", 475 | "d": 2, 476 | "r": 0, 477 | "np": "Ghanaian cedis", 478 | }, 479 | "GIP": { 480 | "s": "£", 481 | "n": "Gibraltar Pound", 482 | "sn": "£", 483 | "d": 2, 484 | "r": 0, 485 | "np": "Gibraltar Pounds", 486 | }, 487 | "GMD": { 488 | "s": "D", 489 | "n": "Gambian dalasi", 490 | "sn": "D", 491 | "d": 2, 492 | "r": 0, 493 | "np": "Gambian dalasi", 494 | }, 495 | "GNF": { 496 | "s": "FG", 497 | "n": "Guinean Franc", 498 | "sn": "FG", 499 | "d": 0, 500 | "r": 0, 501 | "np": "Guinean francs", 502 | }, 503 | "GTQ": { 504 | "s": "GTQ", 505 | "n": "Guatemalan Quetzal", 506 | "sn": "Q", 507 | "d": 2, 508 | "r": 0, 509 | "np": "Guatemalan quetzals", 510 | }, 511 | "GYD": { 512 | "s": "$", 513 | "n": "Guyana Dollar", 514 | "sn": "$", 515 | "d": 2, 516 | "r": 0, 517 | "np": "Guyana Dollars", 518 | }, 519 | "HKD": { 520 | "s": "HK$", 521 | "n": "Hong Kong Dollar", 522 | "sn": "$", 523 | "d": 2, 524 | "r": 0, 525 | "np": "Hong Kong dollars", 526 | }, 527 | "HNL": { 528 | "s": "HNL", 529 | "n": "Honduran Lempira", 530 | "sn": "L", 531 | "d": 2, 532 | "r": 0, 533 | "np": "Honduran lempiras", 534 | }, 535 | "HRK": { 536 | "s": "kn", 537 | "n": "Croatian Kuna", 538 | "sn": "kn", 539 | "d": 2, 540 | "r": 0, 541 | "np": "Croatian kunas", 542 | }, 543 | "HTG": { 544 | "s": "G", 545 | "n": "Haitian gourde", 546 | "sn": "G", 547 | "d": 2, 548 | "r": 0, 549 | "np": "Haitian gourde", 550 | }, 551 | "HUF": { 552 | "s": "Ft", 553 | "n": "Hungarian Forint", 554 | "sn": "Ft", 555 | "d": 0, 556 | "r": 0, 557 | "np": "Hungarian forints", 558 | }, 559 | "IDR": { 560 | "s": "Rp", 561 | "n": "Indonesian Rupiah", 562 | "sn": "Rp", 563 | "d": 0, 564 | "r": 0, 565 | "np": "Indonesian rupiahs", 566 | }, 567 | "ILS": { 568 | "s": "₪", 569 | "n": "Israeli New Sheqel", 570 | "sn": "₪", 571 | "d": 2, 572 | "r": 0, 573 | "np": "Israeli new sheqels", 574 | }, 575 | "IMP": { 576 | "s": "£", 577 | "n": "Isle of Man Pound", 578 | "sn": "£", 579 | "d": 2, 580 | "r": 0, 581 | "np": "Isle of Man Pounds", 582 | }, 583 | "INR": { 584 | "s": "Rs", 585 | "n": "Indian Rupee", 586 | "sn": "টকা", 587 | "d": 2, 588 | "r": 0, 589 | "np": "Indian rupees", 590 | }, 591 | "IQD": { 592 | "s": "IQD", 593 | "n": "Iraqi Dinar", 594 | "sn": "د.ع.‏", 595 | "d": 3, 596 | "r": 0, 597 | "np": "Iraqi dinars", 598 | }, 599 | "IRR": { 600 | "s": "IRR", 601 | "n": "Iranian Rial", 602 | "sn": "﷼", 603 | "d": 0, 604 | "r": 0, 605 | "np": "Iranian rials", 606 | }, 607 | "ISK": { 608 | "s": "Ikr", 609 | "n": "Icelandic Króna", 610 | "sn": "kr", 611 | "d": 0, 612 | "r": 0, 613 | "np": "Icelandic krónur", 614 | }, 615 | "JEP": { 616 | "s": "£", 617 | "n": "Jersey Pound", 618 | "sn": "£", 619 | "d": 2, 620 | "r": 0, 621 | "np": "Jersey Pounds", 622 | }, 623 | "JMD": { 624 | "s": "J$", 625 | "n": "Jamaican Dollar", 626 | "sn": "$", 627 | "d": 2, 628 | "r": 0, 629 | "np": "Jamaican dollars", 630 | }, 631 | "JOD": { 632 | "s": "JD", 633 | "n": "Jordanian Dinar", 634 | "sn": "د.أ.‏", 635 | "d": 3, 636 | "r": 0, 637 | "np": "Jordanian dinars", 638 | }, 639 | "JPY": { 640 | "s": "¥", 641 | "n": "Japanese Yen", 642 | "sn": "¥", 643 | "sn2": ["円"], 644 | "d": 0, 645 | "r": 0, 646 | "np": "Japanese yen", 647 | }, 648 | "KES": { 649 | "s": "Ksh", 650 | "n": "Kenyan Shilling", 651 | "sn": "Ksh", 652 | "d": 2, 653 | "r": 0, 654 | "np": "Kenyan shillings", 655 | }, 656 | "KGS": { 657 | "s": "лв", 658 | "n": "Kyrgyzstan Som", 659 | "sn": "лв", 660 | "d": 2, 661 | "r": 0, 662 | "np": "Kyrgyzstan Som", 663 | }, 664 | "KHR": { 665 | "s": "KHR", 666 | "n": "Cambodian Riel", 667 | "sn": "៛", 668 | "d": 2, 669 | "r": 0, 670 | "np": "Cambodian riels", 671 | }, 672 | "KMF": { 673 | "s": "CF", 674 | "n": "Comorian Franc", 675 | "sn": "FC", 676 | "d": 0, 677 | "r": 0, 678 | "np": "Comorian francs", 679 | }, 680 | "KPW": { 681 | "s": "₩", 682 | "n": "North Korean Won", 683 | "sn": "원", 684 | "d": 0, 685 | "r": 0, 686 | "np": "North Korean Won", 687 | }, 688 | "KRW": { 689 | "s": "₩", 690 | "n": "South Korean Won", 691 | "sn": "원", 692 | "d": 0, 693 | "r": 0, 694 | "np": "South Korean won", 695 | }, 696 | "KWD": { 697 | "s": "KD", 698 | "n": "Kuwaiti Dinar", 699 | "sn": "د.ك.‏", 700 | "d": 3, 701 | "r": 0, 702 | "np": "Kuwaiti dinars", 703 | }, 704 | "KYD": { 705 | "s": "$", 706 | "n": "Cayman Islands Dollar", 707 | "sn": "$", 708 | "d": 2, 709 | "r": 0, 710 | "np": "Cayman Islands Dollars", 711 | }, 712 | "KZT": { 713 | "s": "KZT", 714 | "n": "Kazakhstani Tenge", 715 | "sn": "тңг.", 716 | "d": 2, 717 | "r": 0, 718 | "np": "Kazakhstani tenges", 719 | }, 720 | "LAK": { 721 | "s": "₭", 722 | "n": "Laos Kip", 723 | "sn": "₭", 724 | "d": 2, 725 | "r": 0, 726 | "np": "Laos Kip", 727 | }, 728 | "LBP": { 729 | "s": "LB£", 730 | "n": "Lebanese Pound", 731 | "sn": "ل.ل.‏", 732 | "d": 0, 733 | "r": 0, 734 | "np": "Lebanese pounds", 735 | }, 736 | "LKR": { 737 | "s": "SLRs", 738 | "n": "Sri Lankan Rupee", 739 | "sn": "SL Re", 740 | "d": 2, 741 | "r": 0, 742 | "np": "Sri Lankan rupees", 743 | }, 744 | "LRD": { 745 | "s": "$", 746 | "n": "Liberia Dollar", 747 | "sn": "$", 748 | "d": 2, 749 | "r": 0, 750 | "np": "Liberia Dollars", 751 | }, 752 | "LSL": { 753 | "s": "L", 754 | "n": "Lesotho loti", 755 | "sn": "L", 756 | "d": 2, 757 | "r": 0, 758 | "np": "Lesotho loti", 759 | }, 760 | "LTL": { 761 | "s": "Lt", 762 | "n": "Lithuanian Litas", 763 | "sn": "Lt", 764 | "d": 2, 765 | "r": 0, 766 | "np": "Lithuanian litai", 767 | }, 768 | "LVL": { 769 | "s": "Ls", 770 | "n": "Latvian Lats", 771 | "sn": "Ls", 772 | "d": 2, 773 | "r": 0, 774 | "np": "Latvian lati", 775 | }, 776 | "LYD": { 777 | "s": "LD", 778 | "n": "Libyan Dinar", 779 | "sn": "د.ل.‏", 780 | "d": 3, 781 | "r": 0, 782 | "np": "Libyan dinars", 783 | }, 784 | "MAD": { 785 | "s": "MAD", 786 | "n": "Moroccan Dirham", 787 | "sn": "د.م.‏", 788 | "d": 2, 789 | "r": 0, 790 | "np": "Moroccan dirhams", 791 | }, 792 | "MDL": { 793 | "s": "MDL", 794 | "n": "Moldovan Leu", 795 | "sn": "MDL", 796 | "d": 2, 797 | "r": 0, 798 | "np": "Moldovan lei", 799 | }, 800 | "MGA": { 801 | "s": "MGA", 802 | "n": "Malagasy Ariary", 803 | "sn": "MGA", 804 | "d": 0, 805 | "r": 0, 806 | "np": "Malagasy Ariaries", 807 | }, 808 | "MKD": { 809 | "s": "MKD", 810 | "n": "Macedonian Denar", 811 | "sn": "MKD", 812 | "d": 2, 813 | "r": 0, 814 | "np": "Macedonian denari", 815 | }, 816 | "MMK": { 817 | "s": "MMK", 818 | "n": "Myanma Kyat", 819 | "sn": "K", 820 | "d": 0, 821 | "r": 0, 822 | "np": "Myanma kyats", 823 | }, 824 | "MNT": { 825 | "s": "₮", 826 | "n": "Mongolia Tughrik", 827 | "sn": "₮", 828 | "d": 2, 829 | "r": 0, 830 | "np": "Mongolia Tughrik", 831 | }, 832 | "MOP": { 833 | "s": "MOP$", 834 | "n": "Macanese Pataca", 835 | "sn": "MOP$", 836 | "d": 2, 837 | "r": 0, 838 | "np": "Macanese patacas", 839 | }, 840 | "MRO": { 841 | "s": "UM", 842 | "n": "Mauritanian ouguiya", 843 | "sn": "UM", 844 | "d": 1, 845 | "r": 0, 846 | "np": "Mauritanian ouguiya", 847 | }, 848 | "MUR": { 849 | "s": "MURs", 850 | "n": "Mauritian Rupee", 851 | "sn": "MURs", 852 | "d": 0, 853 | "r": 0, 854 | "np": "Mauritian rupees", 855 | }, 856 | "MVR": { 857 | "s": "MRf", 858 | "n": "Maldivian rufiyaa", 859 | "sn": "Rf", 860 | "d": 2, 861 | "r": 0, 862 | "np": "Maldivian rufiyaa", 863 | }, 864 | "MWK": { 865 | "s": "MK", 866 | "n": "Malawian kwacha", 867 | "sn": "MK", 868 | "d": 2, 869 | "r": 0, 870 | "np": "Malawian kwacha", 871 | }, 872 | "MXN": { 873 | "s": "MX$", 874 | "n": "Mexican Peso", 875 | "sn": "$", 876 | "d": 2, 877 | "r": 0, 878 | "np": "Mexican pesos", 879 | }, 880 | "MXV": { 881 | "s": "-", 882 | "n": "Mexican Unidad de Inversion (UDI) (funds code)", 883 | "sn": "-", 884 | "d": 2, 885 | "r": 0, 886 | "np": "Mexican Unidad de Inversion (UDI) (funds code)", 887 | }, 888 | "MYR": { 889 | "s": "RM", 890 | "n": "Malaysian Ringgit", 891 | "sn": "RM", 892 | "d": 2, 893 | "r": 0, 894 | "np": "Malaysian ringgits", 895 | }, 896 | "MZN": { 897 | "s": "MTn", 898 | "n": "Mozambican Metical", 899 | "sn": "MTn", 900 | "d": 2, 901 | "r": 0, 902 | "np": "Mozambican meticals", 903 | }, 904 | "NAD": { 905 | "s": "N$", 906 | "n": "Namibian Dollar", 907 | "sn": "N$", 908 | "d": 2, 909 | "r": 0, 910 | "np": "Namibian dollars", 911 | }, 912 | "NGN": { 913 | "s": "₦", 914 | "n": "Nigerian Naira", 915 | "sn": "₦", 916 | "d": 2, 917 | "r": 0, 918 | "np": "Nigerian nairas", 919 | }, 920 | "NIO": { 921 | "s": "C$", 922 | "n": "Nicaraguan Córdoba", 923 | "sn": "C$", 924 | "d": 2, 925 | "r": 0, 926 | "np": "Nicaraguan córdobas", 927 | }, 928 | "NOK": { 929 | "s": "Nkr", 930 | "n": "Norwegian Krone", 931 | "sn": "kr", 932 | "d": 2, 933 | "r": 0, 934 | "np": "Norwegian kroner", 935 | }, 936 | "NPR": { 937 | "s": "NPRs", 938 | "n": "Nepalese Rupee", 939 | "sn": "नेरू", 940 | "d": 2, 941 | "r": 0, 942 | "np": "Nepalese rupees", 943 | }, 944 | "PRB": { 945 | "s": "руб", 946 | "n": "Transnistrian ruble", 947 | "sn": "руб", 948 | "d": 2, 949 | "r": 0, 950 | "np": "Transnistrian rubles", 951 | }, 952 | "NZD": { 953 | "s": "NZ$", 954 | "n": "New Zealand Dollar", 955 | "sn": "$", 956 | "d": 2, 957 | "r": 0, 958 | "np": "New Zealand dollars", 959 | }, 960 | "OMR": { 961 | "s": "OMR", 962 | "n": "Omani Rial", 963 | "sn": "ر.ع.‏", 964 | "d": 3, 965 | "r": 0, 966 | "np": "Omani rials", 967 | }, 968 | "PAB": { 969 | "s": "B/.", 970 | "n": "Panamanian Balboa", 971 | "sn": "B/.", 972 | "d": 2, 973 | "r": 0, 974 | "np": "Panamanian balboas", 975 | }, 976 | "PEN": { 977 | "s": "S/.", 978 | "n": "Peruvian Nuevo Sol", 979 | "sn": "S/.", 980 | "d": 2, 981 | "r": 0, 982 | "np": "Peruvian nuevos soles", 983 | }, 984 | "PGK": { 985 | "s": "K", 986 | "n": "Papua New Guinean kina", 987 | "sn": "K", 988 | "d": 2, 989 | "r": 0, 990 | "np": "Papua New Guinean kina", 991 | }, 992 | "PHP": { 993 | "s": "₱", 994 | "n": "Philippine Peso", 995 | "sn": "₱", 996 | "d": 2, 997 | "r": 0, 998 | "np": "Philippine pesos", 999 | }, 1000 | "PKR": { 1001 | "s": "PKRs", 1002 | "n": "Pakistani Rupee", 1003 | "sn": "₨", 1004 | "d": 0, 1005 | "r": 0, 1006 | "np": "Pakistani rupees", 1007 | }, 1008 | "PLN": { 1009 | "s": "zł", 1010 | "n": "Polish Zloty", 1011 | "sn": "zł", 1012 | "d": 2, 1013 | "r": 0, 1014 | "np": "Polish zlotys", 1015 | }, 1016 | "PYG": { 1017 | "s": "₲", 1018 | "n": "Paraguayan Guarani", 1019 | "sn": "₲", 1020 | "d": 0, 1021 | "r": 0, 1022 | "np": "Paraguayan guaranis", 1023 | }, 1024 | "QAR": { 1025 | "s": "QR", 1026 | "n": "Qatari Rial", 1027 | "sn": "ر.ق.‏", 1028 | "d": 2, 1029 | "r": 0, 1030 | "np": "Qatari rials", 1031 | }, 1032 | "RON": { 1033 | "s": "RON", 1034 | "n": "Romanian Leu", 1035 | "sn": "RON", 1036 | "d": 2, 1037 | "r": 0, 1038 | "np": "Romanian lei", 1039 | }, 1040 | "RSD": { 1041 | "s": "din.", 1042 | "n": "Serbian Dinar", 1043 | "sn": "дин.", 1044 | "d": 0, 1045 | "r": 0, 1046 | "np": "Serbian dinars", 1047 | }, 1048 | "RUB": { 1049 | "s": "RUB", 1050 | "n": "Russian Ruble", 1051 | "sn": "руб.", 1052 | "d": 2, 1053 | "r": 0, 1054 | "np": "Russian rubles", 1055 | }, 1056 | "RWF": { 1057 | "s": "RWF", 1058 | "n": "Rwandan Franc", 1059 | "sn": "FR", 1060 | "d": 0, 1061 | "r": 0, 1062 | "np": "Rwandan francs", 1063 | }, 1064 | "SAR": { 1065 | "s": "SR", 1066 | "n": "Saudi Riyal", 1067 | "sn": "ر.س.‏", 1068 | "d": 2, 1069 | "r": 0, 1070 | "np": "Saudi riyals", 1071 | }, 1072 | "SBD": { 1073 | "s": "$", 1074 | "n": "Solomon Islands Dollar", 1075 | "sn": "$", 1076 | "d": 2, 1077 | "r": 0, 1078 | "np": "Solomon Islands Dollars", 1079 | }, 1080 | "SCR": { 1081 | "s": "₨", 1082 | "n": "Seychelles Rupee", 1083 | "sn": "₨", 1084 | "d": 2, 1085 | "r": 0, 1086 | "np": "Seychelles Rupees", 1087 | }, 1088 | "SDG": { 1089 | "s": "SDG", 1090 | "n": "Sudanese Pound", 1091 | "sn": "SDG", 1092 | "d": 2, 1093 | "r": 0, 1094 | "np": "Sudanese pounds", 1095 | }, 1096 | "SEK": { 1097 | "s": "Skr", 1098 | "n": "Swedish Krona", 1099 | "sn": "kr", 1100 | "d": 2, 1101 | "r": 0, 1102 | "np": "Swedish kronor", 1103 | }, 1104 | "SGD": { 1105 | "s": "S$", 1106 | "n": "Singapore Dollar", 1107 | "sn": "$", 1108 | "d": 2, 1109 | "r": 0, 1110 | "np": "Singapore dollars", 1111 | }, 1112 | "SHP": { 1113 | "s": "£", 1114 | "n": "Saint Helena Pound", 1115 | "sn": "£", 1116 | "d": 2, 1117 | "r": 0, 1118 | "np": "Saint Helena Pounds", 1119 | }, 1120 | "SLL": { 1121 | "s": "Le", 1122 | "n": "Sierra Leonean leone", 1123 | "sn": "Le", 1124 | "d": 2, 1125 | "r": 0, 1126 | "np": "Sierra Leonean leone", 1127 | }, 1128 | "SOS": { 1129 | "s": "Ssh", 1130 | "n": "Somali Shilling", 1131 | "sn": "Ssh", 1132 | "d": 0, 1133 | "r": 0, 1134 | "np": "Somali shillings", 1135 | }, 1136 | "SRD": { 1137 | "s": "$", 1138 | "n": "Suriname Dollar", 1139 | "sn": "$", 1140 | "d": 2, 1141 | "r": 0, 1142 | "np": "Suriname Dollars", 1143 | }, 1144 | "SSP": { 1145 | "s": "SSP", 1146 | "n": "South Sudanese pound", 1147 | "sn": "SSP", 1148 | "d": 2, 1149 | "r": 0, 1150 | "np": "South Sudanese pound", 1151 | }, 1152 | "STD": { 1153 | "s": "Db", 1154 | "n": "São Tomé and Príncipe dobra", 1155 | "sn": "Db", 1156 | "d": 2, 1157 | "r": 0, 1158 | "np": "São Tomé and Príncipe dobra", 1159 | }, 1160 | "SVC": { 1161 | "s": "$", 1162 | "n": "El Salvador Colon", 1163 | "sn": "$", 1164 | "d": 0, 1165 | "r": 0, 1166 | "np": "El Salvador Colon", 1167 | }, 1168 | "SYP": { 1169 | "s": "SY£", 1170 | "n": "Syrian Pound", 1171 | "sn": "ل.س.‏", 1172 | "d": 0, 1173 | "r": 0, 1174 | "np": "Syrian pounds", 1175 | }, 1176 | "SZL": { 1177 | "s": "L", 1178 | "n": "Swazi lilangeni", 1179 | "sn": "L", 1180 | "d": 2, 1181 | "r": 0, 1182 | "np": "Swazi lilangeni", 1183 | }, 1184 | "THB": { 1185 | "s": "฿", 1186 | "n": "Thai Baht", 1187 | "sn": "฿", 1188 | "d": 2, 1189 | "r": 0, 1190 | "np": "Thai baht", 1191 | }, 1192 | "TJS": { 1193 | "s": "-", 1194 | "n": "Tajikistani somoni", 1195 | "sn": "-", 1196 | "d": 2, 1197 | "r": 0, 1198 | "np": "Tajikistani somoni", 1199 | }, 1200 | "TMT": { 1201 | "s": "T", 1202 | "n": "Turkmenistan manat", 1203 | "sn": "T", 1204 | "d": 2, 1205 | "r": 0, 1206 | "np": "Turkmenistan manat", 1207 | }, 1208 | "TND": { 1209 | "s": "DT", 1210 | "n": "Tunisian Dinar", 1211 | "sn": "د.ت.‏", 1212 | "d": 3, 1213 | "r": 0, 1214 | "np": "Tunisian dinars", 1215 | }, 1216 | "TOP": { 1217 | "s": "T$", 1218 | "n": "Tongan Paʻanga", 1219 | "sn": "T$", 1220 | "d": 2, 1221 | "r": 0, 1222 | "np": "Tongan paʻanga", 1223 | }, 1224 | "TRY": { 1225 | "s": "TL", 1226 | "n": "Turkish Lira", 1227 | "sn": "TL", 1228 | "d": 2, 1229 | "r": 0, 1230 | "np": "Turkish Lira", 1231 | }, 1232 | "TTD": { 1233 | "s": "TT$", 1234 | "n": "Trinidad and Tobago Dollar", 1235 | "sn": "$", 1236 | "d": 2, 1237 | "r": 0, 1238 | "np": "Trinidad and Tobago dollars", 1239 | }, 1240 | "TVD": { 1241 | "s": "$", 1242 | "n": "Tuvalu Dollar", 1243 | "sn": "$", 1244 | "d": 2, 1245 | "r": 0, 1246 | "np": "Tuvalu Dollars", 1247 | }, 1248 | "TWD": { 1249 | "s": "NT$", 1250 | "n": "New Taiwan Dollar", 1251 | "sn": "NT$", 1252 | "d": 2, 1253 | "r": 0, 1254 | "np": "New Taiwan dollars", 1255 | }, 1256 | "TZS": { 1257 | "s": "TSh", 1258 | "n": "Tanzanian Shilling", 1259 | "sn": "TSh", 1260 | "d": 0, 1261 | "r": 0, 1262 | "np": "Tanzanian shillings", 1263 | }, 1264 | "UAH": { 1265 | "s": "₴", 1266 | "n": "Ukrainian Hryvnia", 1267 | "sn": "₴", 1268 | "d": 2, 1269 | "r": 0, 1270 | "np": "Ukrainian hryvnias", 1271 | }, 1272 | "UGX": { 1273 | "s": "USh", 1274 | "n": "Ugandan Shilling", 1275 | "sn": "USh", 1276 | "d": 0, 1277 | "r": 0, 1278 | "np": "Ugandan shillings", 1279 | }, 1280 | "USD": { 1281 | "s": "$", 1282 | "n": "US Dollar", 1283 | "sn": "$", 1284 | "d": 2, 1285 | "r": 0, 1286 | "np": "US dollars", 1287 | }, 1288 | "USN": { 1289 | "s": "$", 1290 | "n": "United States dollar (next day) (funds code)", 1291 | "sn": "$", 1292 | "d": 2, 1293 | "r": 0, 1294 | "np": "United States dollars (next day) (funds code)", 1295 | }, 1296 | "UYI": { 1297 | "s": "UYI", 1298 | "n": "Uruguay Peso en Unidades Indexadas (URUIURUI) (funds code)", 1299 | "sn": "UYI", 1300 | "d": 0, 1301 | "r": 0, 1302 | "np": "Uruguay Peso en Unidades Indexadas (URUIURUI) (funds code)", 1303 | }, 1304 | "UYU": { 1305 | "s": "$U", 1306 | "n": "Uruguayan Peso", 1307 | "sn": "$", 1308 | "d": 2, 1309 | "r": 0, 1310 | "np": "Uruguayan pesos", 1311 | }, 1312 | "UZS": { 1313 | "s": "UZS", 1314 | "n": "Uzbekistan Som", 1315 | "sn": "UZS", 1316 | "d": 0, 1317 | "r": 0, 1318 | "np": "Uzbekistan som", 1319 | }, 1320 | "VEF": { 1321 | "s": "Bs.F.", 1322 | "n": "Venezuelan Bolívar", 1323 | "sn": "Bs.F.", 1324 | "d": 2, 1325 | "r": 0, 1326 | "np": "Venezuelan bolívars", 1327 | }, 1328 | "VND": { 1329 | "s": "₫", 1330 | "n": "Vietnamese Dong", 1331 | "sn": "₫", 1332 | "d": 0, 1333 | "r": 0, 1334 | "np": "Vietnamese dong", 1335 | }, 1336 | "VUV": { 1337 | "s": "VT", 1338 | "n": "Vanuatu vatu", 1339 | "sn": "VT", 1340 | "d": 0, 1341 | "r": 0, 1342 | "np": "Vanuatu vatu", 1343 | }, 1344 | "WST": { 1345 | "s": "WS$", 1346 | "n": "Samoan tala", 1347 | "sn": "$", 1348 | "d": 2, 1349 | "r": 0, 1350 | "np": "Samoan tala", 1351 | }, 1352 | "XAF": { 1353 | "s": "FCFA", 1354 | "n": "CFA Franc BEAC", 1355 | "sn": "FCFA", 1356 | "d": 0, 1357 | "r": 0, 1358 | "np": "CFA francs BEAC", 1359 | }, 1360 | "XAG": { 1361 | "s": "XAG", 1362 | "n": "Silver (one troy ounce)", 1363 | "sn": "XAG", 1364 | "d": 0, 1365 | "r": 0, 1366 | "np": "Silver (one troy ounce)", 1367 | }, 1368 | "XAU": { 1369 | "s": "XAU", 1370 | "n": "Gold (one troy ounce)", 1371 | "sn": "XAU", 1372 | "d": 0, 1373 | "r": 0, 1374 | "np": "Gold (one troy ounce)", 1375 | }, 1376 | "XBA": { 1377 | "s": "XBA", 1378 | "n": "European Composite Unit (EURCO) (bond market unit)", 1379 | "sn": "XBA", 1380 | "d": 0, 1381 | "r": 0, 1382 | "np": "European Composite Unit (EURCO) (bond market unit)", 1383 | }, 1384 | "XBB": { 1385 | "s": "XBB", 1386 | "n": "European Monetary Unit (E.M.U.-6) (bond market unit)", 1387 | "sn": "XBB", 1388 | "d": 0, 1389 | "r": 0, 1390 | "np": "European Monetary Unit (E.M.U.-6) (bond market unit)", 1391 | }, 1392 | "XBC": { 1393 | "s": "XBC", 1394 | "n": "European Unit of Account 9 (E.U.A.-9) (bond market unit)", 1395 | "sn": "XBC", 1396 | "d": 0, 1397 | "r": 0, 1398 | "np": "European Unit of Account 9 (E.U.A.-9) (bond market unit)", 1399 | }, 1400 | "XBD": { 1401 | "s": "XBD", 1402 | "n": "European Unit of Account 17 (E.U.A.-17) (bond market unit)", 1403 | "sn": "XBD", 1404 | "d": 0, 1405 | "r": 0, 1406 | "np": "European Unit of Account 17 (E.U.A.-17) (bond market unit)", 1407 | }, 1408 | "XCD": { 1409 | "s": "$", 1410 | "n": "East Caribbean Dollar", 1411 | "sn": "$", 1412 | "d": 0, 1413 | "r": 0, 1414 | "np": "East Caribbean Dollars", 1415 | }, 1416 | "XDR": { 1417 | "s": "XDR", 1418 | "n": "Special drawing rights", 1419 | "sn": "XDR", 1420 | "d": 0, 1421 | "r": 0, 1422 | "np": "Special drawing rights", 1423 | }, 1424 | "XOF": { 1425 | "s": "CFA", 1426 | "n": "CFA Franc BCEAO", 1427 | "sn": "CFA", 1428 | "d": 0, 1429 | "r": 0, 1430 | "np": "CFA francs BCEAO", 1431 | }, 1432 | "XPD": { 1433 | "s": "XPD", 1434 | "n": "Palladium (one troy ounce)", 1435 | "sn": "XPD", 1436 | "d": 0, 1437 | "r": 0, 1438 | "np": "Palladium (one troy ounce)", 1439 | }, 1440 | "XPF": { 1441 | "s": "CFP", 1442 | "n": "CFP franc (franc Pacifique)", 1443 | "sn": "CFP", 1444 | "d": 0, 1445 | "r": 0, 1446 | "np": "CFP franc (franc Pacifique)", 1447 | }, 1448 | "XPT": { 1449 | "s": "XPT", 1450 | "n": "Platinum (one troy ounce)", 1451 | "sn": "XPT", 1452 | "d": 0, 1453 | "r": 0, 1454 | "np": "Platinum (one troy ounce)", 1455 | }, 1456 | "XSU": { 1457 | "s": "Sucre", 1458 | "n": "SUCRE", 1459 | "sn": "Sucre", 1460 | "d": 0, 1461 | "r": 0, 1462 | "np": "SUCRE", 1463 | }, 1464 | "XTS": { 1465 | "s": "XTS", 1466 | "n": "Code reserved for testing purposes", 1467 | "sn": "XTS", 1468 | "d": 0, 1469 | "r": 0, 1470 | "np": "Code reserved for testing purposes", 1471 | }, 1472 | "XUA": { 1473 | "s": "XUA", 1474 | "n": "ADB Unit of Account", 1475 | "sn": "XUA", 1476 | "d": 0, 1477 | "r": 0, 1478 | "np": "ADB Unit of Account", 1479 | }, 1480 | "XXX": { 1481 | "s": "XXX", 1482 | "n": "No currency", 1483 | "sn": "XXX", 1484 | "d": 0, 1485 | "r": 0, 1486 | "np": "No currency", 1487 | }, 1488 | "YER": { 1489 | "s": "YR", 1490 | "n": "Yemeni Rial", 1491 | "sn": "ر.ي.‏", 1492 | "d": 0, 1493 | "r": 0, 1494 | "np": "Yemeni rials", 1495 | }, 1496 | "ZAR": { 1497 | "s": "R", 1498 | "n": "South African Rand", 1499 | "sn": "R", 1500 | "d": 2, 1501 | "r": 0, 1502 | "np": "South African rand", 1503 | }, 1504 | "ZMK": { 1505 | "s": "ZK", 1506 | "n": "Zambian Kwacha", 1507 | "sn": "ZK", 1508 | "d": 0, 1509 | "r": 0, 1510 | "np": "Zambian kwachas", 1511 | }, 1512 | "ZMW": { 1513 | "s": "ZK", 1514 | "n": "Zambian kwacha", 1515 | "sn": "ZK", 1516 | "d": 2, 1517 | "r": 0, 1518 | "np": "Zambian kwacha", 1519 | }, 1520 | "ZWD": { 1521 | "s": "Z$", 1522 | "n": "Zimbabwe Dollar", 1523 | "sn": "Z$", 1524 | "d": 2, 1525 | "r": 0, 1526 | "np": "Zimbabwe Dollars", 1527 | }, 1528 | "ZWL": { 1529 | "s": "$", 1530 | "n": "Zimbabwean dollar A/10", 1531 | "sn": "$", 1532 | "d": 2, 1533 | "r": 0, 1534 | "np": "Zimbabwean dollars A/10", 1535 | }, 1536 | } 1537 | 1538 | 1539 | # Commonly used unofficial names. 1540 | # See also: https://en.wikipedia.org/wiki/ISO_4217#Unofficial_currency_codes 1541 | CURRENCIES["NTD"] = CURRENCIES["TWD"] 1542 | CURRENCIES["RMB"] = CURRENCIES["CNY"] 1543 | 1544 | 1545 | REPLACED_BY_EURO = { 1546 | "ATS": { 1547 | "s": "öS", 1548 | "n": "Austrian schilling", 1549 | "sn": "öS", 1550 | "d": 2, 1551 | "r": 0, 1552 | "np": "Austrian schilling", 1553 | }, 1554 | "BEF": { 1555 | "s": "fr.", 1556 | "n": "Belgian franc", 1557 | "sn": "fr.", 1558 | "d": 2, 1559 | "r": 0, 1560 | "np": "Belgian francs", 1561 | }, 1562 | "CYP": { 1563 | "s": "CYP", 1564 | "n": "Cypriot pound", 1565 | "sn": "£", 1566 | "d": 2, 1567 | "r": 0, 1568 | "np": "Cypriot pounds", 1569 | }, 1570 | "DEM": { 1571 | "s": "DM", 1572 | "n": "Deutsche Mark", 1573 | "sn": "D-Mark", 1574 | "d": 2, 1575 | "r": 0, 1576 | "np": "Deutsche marks", 1577 | }, 1578 | "NLG": { 1579 | "s": "fl.", 1580 | "n": "Dutch guilder", 1581 | "sn": "ƒ", 1582 | "d": 2, 1583 | "r": 0, 1584 | "np": "Dutch guilders", 1585 | }, 1586 | "EEK": { 1587 | "s": "kr", 1588 | "n": "Estonian kroon", 1589 | "sn": "kroon", 1590 | "d": 2, 1591 | "r": 0, 1592 | "np": "Estonian krooni", 1593 | }, 1594 | "FIM": { 1595 | "s": "FIM", 1596 | "n": "Finnish markka", 1597 | "sn": "mk.", 1598 | "d": 2, 1599 | "r": 0, 1600 | "np": "Finnish markkaa", 1601 | }, 1602 | "FRF": { 1603 | "s": "F", 1604 | "n": "French franc", 1605 | "sn": "₣", 1606 | "d": 2, 1607 | "r": 0, 1608 | "np": "French francs", 1609 | }, 1610 | "GRD": { 1611 | "s": "GRD", 1612 | "n": "Greek drachma", 1613 | "sn": "Δρχ.", 1614 | "sn2": ["Δρ.", "₯"], 1615 | "d": 2, 1616 | "r": 0, 1617 | "np": "Greek drachmae", 1618 | }, 1619 | "IEP": { 1620 | "s": "IR£", 1621 | "n": "Irish pound", 1622 | "sn": "£", 1623 | "d": 2, 1624 | "r": 0, 1625 | "np": "Irish pounds", 1626 | }, 1627 | "ITL": { 1628 | "s": "L", 1629 | "n": "Italian lira", 1630 | "sn": "₤", 1631 | "d": 0, 1632 | "r": 0, 1633 | "np": "Italian lire", 1634 | }, 1635 | "LVL": { 1636 | "s": "Ls", 1637 | "n": "Latvian lats", 1638 | "sn": "LVL", 1639 | "d": 2, 1640 | "r": 0, 1641 | "np": "Latvian lati", 1642 | }, 1643 | "LTL": { 1644 | "s": "Lt", 1645 | "n": "Lithuanian litas", 1646 | "sn": "LTL", 1647 | "sn2": ["litų"], 1648 | "d": 2, 1649 | "r": 0, 1650 | "np": "Lithuanian litai", 1651 | }, 1652 | "LUF": { 1653 | "s": "F", 1654 | "n": "Luxembourgish franc", 1655 | "sn": "LUF", 1656 | "d": 2, 1657 | "r": 0, 1658 | "np": "Luxembourgish francs", 1659 | }, 1660 | "MTL": { 1661 | "s": "Lm", 1662 | "n": "Maltese lira", 1663 | "sn": "₤", 1664 | "d": 2, 1665 | "r": 0, 1666 | "np": "Maltese liri", 1667 | }, 1668 | "PTE": { 1669 | "s": "$", 1670 | "n": "Portuguese escudo", 1671 | "sn": "$", 1672 | "d": 2, 1673 | "r": 0, 1674 | "np": "Portuguese escudos", 1675 | }, 1676 | "SKK": { 1677 | "s": "SKK", 1678 | "n": "Slovak koruna", 1679 | "sn": "Sk", 1680 | "d": 2, 1681 | "r": 0, 1682 | "np": "Slovak Koruny", 1683 | }, 1684 | "SIT": { 1685 | "s": "SIT", 1686 | "n": "Slovenian tolar", 1687 | "sn": "SIT", 1688 | "sn2": ["tolarjev"], 1689 | "d": 2, 1690 | "r": 0, 1691 | "np": "Slovenian tolar", 1692 | }, 1693 | "ESP": { 1694 | "s": "Pta", 1695 | "n": "Spanish peseta", 1696 | "sn": "Ptas", 1697 | "sn2": ["₧", "Pts", "Pt"], 1698 | "d": 0, 1699 | "r": 0, 1700 | "np": "Spanish pesetas", 1701 | }, 1702 | "VAL": { 1703 | "s": "£", 1704 | "n": "Vatican lira", 1705 | "sn": "₤", 1706 | "d": 0, 1707 | "r": 0, 1708 | "np": "Vatican liri", 1709 | }, 1710 | } 1711 | 1712 | # updates 1713 | CURRENCIES.update(REPLACED_BY_EURO) 1714 | CURRENCIES["VND"]["sn2"] = ["đ"] 1715 | CURRENCIES["RON"]["sn2"] = ["lei", "leu", "Lei", "LEI"] 1716 | CURRENCIES["CHF"]["sn2"] = ["Fr."] 1717 | CURRENCIES["PLN"]["sn2"] = ["pln"] 1718 | CURRENCIES["INR"]["sn2"] = ["₹", "र"] 1719 | CURRENCIES["IRR"]["sn2"] = ["ریال"] 1720 | 1721 | 1722 | CURRENCY_CODES: List[str] = list(CURRENCIES.keys()) 1723 | CURRENCY_SYMBOLS: List[str] = list({c["s"] for c in CURRENCIES.values()}) 1724 | CURRENCY_NATIONAL_SYMBOLS: List[str] = list( 1725 | {c["sn"] for c in CURRENCIES.values()} 1726 | | set(chain.from_iterable(c["sn2"] for c in CURRENCIES.values() if "sn2" in c)) 1727 | ) 1728 | -------------------------------------------------------------------------------- /price_parser/parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import re 3 | import string 4 | from decimal import Decimal, InvalidOperation 5 | from typing import Callable, List, Optional, Pattern, Tuple 6 | 7 | import attr 8 | 9 | from ._currencies import CURRENCY_CODES, CURRENCY_NATIONAL_SYMBOLS, CURRENCY_SYMBOLS 10 | 11 | 12 | @attr.s(auto_attribs=True) 13 | class Price: 14 | amount: Optional[Decimal] # price numeric value, as Decimal 15 | currency: Optional[str] # currency symbol (as appeared in text) 16 | 17 | # price value, as a raw string 18 | amount_text: Optional[str] = attr.ib(repr=False) 19 | 20 | @property 21 | def amount_float(self) -> Optional[float]: 22 | """price numeric value, as float""" 23 | if self.amount is not None: 24 | return float(self.amount) 25 | 26 | @classmethod 27 | def fromstring( 28 | cls, 29 | price: Optional[str], 30 | currency_hint: Optional[str] = None, 31 | decimal_separator: Optional[str] = None, 32 | digit_group_separator: Optional[str] = None, 33 | ) -> "Price": 34 | """ 35 | Given price and currency text extracted from HTML elements, return 36 | ``Price`` instance, which provides a clean currency symbol and 37 | price amount as a Decimal number. 38 | 39 | ``currency_hint`` is optional; you can pass value of some element 40 | which may contain currency, as a hint. If currency is present in 41 | ``price`` string, it could be **preferred** over a value extracted 42 | from ``currency_hint`` string. 43 | 44 | ``decimal_separator`` is optional; it is used to determine the 45 | decimal separator in price. If ``decimal_separator`` is ``None``, 46 | then it is guessed from ``price`` string. If ``decimal_separator`` 47 | is ``"."``, then ``1.000`` is parsed as ``1``. If it is ``,```, 48 | then ``1.000`` is parsed as ``1000``. 49 | 50 | ``digit_group_separator`` is optional; it is used to determine the 51 | digit group separator in price. If ``digit_group_separator`` is 52 | ``None``, then it is guessed from ``price`` string. If 53 | ``digit_group_separator`` is ``"."``, then ``1.000`` is parsed as 54 | ``1000``. If it is ``,``, then ``1.000`` is parsed as ``1``. 55 | """ 56 | currency = extract_currency_symbol(price, currency_hint) 57 | if currency is not None: 58 | currency = currency.strip() 59 | if digit_group_separator is not None and price is not None: 60 | price = price.replace(digit_group_separator, "") 61 | amount_text = extract_price_text(price) if price is not None else None 62 | amount_num = ( 63 | parse_number(amount_text, decimal_separator) 64 | if amount_text is not None 65 | else None 66 | ) 67 | return Price( 68 | amount=amount_num, 69 | currency=currency, 70 | amount_text=amount_text, 71 | ) 72 | 73 | 74 | parse_price = Price.fromstring 75 | 76 | 77 | def or_regex(symbols: List[str]) -> Pattern: 78 | """Return a regex which matches any of ``symbols``""" 79 | return re.compile("|".join(re.escape(s) for s in symbols)) 80 | 81 | 82 | # If one of these symbols is found either in price or in currency, 83 | # it is considered currency symbol, and returned as a currency, regardless 84 | # of its position in text. 85 | SAFE_CURRENCY_SYMBOLS = [ 86 | # Variants of $, etc. They need to be before $. 87 | "Bds$", 88 | "CUC$", 89 | "MOP$", 90 | "AR$", 91 | "AU$", 92 | "BN$", 93 | "BZ$", 94 | "CA$", 95 | "CL$", 96 | "CO$", 97 | "CV$", 98 | "HK$", 99 | "MX$", 100 | "NT$", 101 | "NZ$", 102 | "TT$", 103 | "RD$", 104 | "WS$", 105 | "US$", 106 | "$U", 107 | "C$", 108 | "J$", 109 | "N$", 110 | "R$", 111 | "S$", 112 | "T$", 113 | "Z$", 114 | "A$", 115 | "SY£", 116 | "LB£", 117 | "CN¥", 118 | "GH₵", 119 | # unique currency symbols 120 | "$", 121 | "€", 122 | "£", 123 | "zł", 124 | "Zł", 125 | "Kč", 126 | "₽", 127 | "¥", 128 | "¥", 129 | "฿", 130 | "դր.", 131 | "դր", 132 | "₦", 133 | "₴", 134 | "₱", 135 | "৳", 136 | "₭", 137 | "₪", 138 | "﷼", 139 | "៛", 140 | "₩", 141 | "₫", 142 | "₡", 143 | "টকা", 144 | "ƒ", 145 | "₲", 146 | "؋", 147 | "₮", 148 | "नेरू", 149 | "₨", 150 | "₶", 151 | "₾", 152 | "֏", 153 | "ރ", 154 | "৲", 155 | "૱", 156 | "௹", 157 | "₠", 158 | "₢", 159 | "₣", 160 | "₤", 161 | "₧", 162 | "₯", 163 | "₰", 164 | "₳", 165 | "₷", 166 | "₸", 167 | "₹", 168 | "₺", 169 | "₼", 170 | "₾", 171 | "₿", 172 | "ℳ", 173 | "ر.ق.\u200f", 174 | "د.ك.\u200f", 175 | "د.ع.\u200f", 176 | "ر.ع.\u200f", 177 | "ر.ي.\u200f", 178 | "ر.س.\u200f", 179 | "د.ج.\u200f", 180 | "د.م.\u200f", 181 | "د.إ.\u200f", 182 | "د.ت.\u200f", 183 | "د.ل.\u200f", 184 | "ل.س.\u200f", 185 | "د.ب.\u200f", 186 | "د.أ.\u200f", 187 | "ج.م.\u200f", 188 | "ل.ل.\u200f", 189 | " تومان", 190 | "تومان", 191 | # other common symbols, which we consider unambiguous 192 | "EUR", 193 | "euro", 194 | "eur", 195 | "CHF", 196 | "DKK", 197 | "Rp", 198 | "lei", 199 | "руб.", 200 | "руб", 201 | "грн.", 202 | "грн", 203 | "дин.", 204 | "Dinara", 205 | "динар", 206 | "лв.", 207 | "лв", 208 | "р.", 209 | "тңг", 210 | "тңг.", 211 | "ман.", 212 | ] 213 | 214 | # "D" in some abbreviations means "dollar", and so currency 215 | # can be written as SGD$123 or NZD $123. Currency code should take priority 216 | # over $ symbol in this case. 217 | DOLLAR_CODES = [k for k in CURRENCY_CODES if k.endswith("D")] 218 | _DOLLAR_REGEX = re.compile( 219 | r""" 220 | \b 221 | (?:{}) # currency code like NZD 222 | (?= 223 | \$? # dollar sign to ignore if attached to the currency code 224 | (?:[\W\d]|$) # not a letter 225 | ) 226 | """.format( 227 | "|".join(re.escape(k) for k in DOLLAR_CODES) 228 | ), 229 | re.VERBOSE, 230 | ) 231 | 232 | 233 | # Other common currency symbols: 3-letter codes, less safe abbreviations 234 | OTHER_CURRENCY_SYMBOLS_SET = ( 235 | set( 236 | CURRENCY_CODES 237 | + CURRENCY_SYMBOLS 238 | + CURRENCY_NATIONAL_SYMBOLS 239 | + 240 | # even if they appear in text, currency is likely to be rouble 241 | ["р", "Р"] 242 | ) 243 | - set(SAFE_CURRENCY_SYMBOLS) # already handled 244 | - {"-", "XXX"} # placeholder values 245 | - set(string.ascii_uppercase) # very unreliable on their own 246 | ) 247 | OTHER_CURRENCY_SYMBOLS = sorted(OTHER_CURRENCY_SYMBOLS_SET, key=len, reverse=True) 248 | 249 | _search_dollar_code = _DOLLAR_REGEX.search 250 | _search_safe_currency = or_regex(SAFE_CURRENCY_SYMBOLS).search 251 | _search_unsafe_currency = or_regex(OTHER_CURRENCY_SYMBOLS).search 252 | 253 | 254 | def extract_currency_symbol( 255 | price: Optional[str], currency_hint: Optional[str] 256 | ) -> Optional[str]: 257 | """ 258 | Guess currency symbol from extracted price and currency strings. 259 | Return an empty string if symbol is not found. 260 | """ 261 | methods: List[Tuple[Callable, Optional[str]]] = [ 262 | (_search_safe_currency, price), 263 | (_search_safe_currency, currency_hint), 264 | (_search_unsafe_currency, price), 265 | (_search_unsafe_currency, currency_hint), 266 | ] 267 | 268 | if currency_hint and "$" in currency_hint: 269 | methods.insert(0, (_search_dollar_code, currency_hint)) 270 | 271 | if price and "$" in price: 272 | methods.insert(0, (_search_dollar_code, price)) 273 | 274 | for meth, value in methods: 275 | m = meth(value) if value else None 276 | if m: 277 | return m.group(0) 278 | 279 | return None 280 | 281 | 282 | def extract_price_text(price: str) -> Optional[str]: 283 | """ 284 | Extract text of a price from a string which contains price and 285 | maybe some other text. If multiple price-looking substrings are present, 286 | the first is returned (FIXME: it is better to return a number 287 | which is near a currency symbol). 288 | 289 | >>> extract_price_text("price: $12.99") 290 | '12.99' 291 | >>> extract_price_text("Free") 292 | '0' 293 | >>> extract_price_text("Foo") 294 | >>> extract_price_text("1,235 USD") 295 | '1,235' 296 | 297 | In addition to numbers, it has a limited support for a case where 298 | currency symbol (currently only euro) is a decimal separator: 299 | 300 | >>> extract_price_text("99 €, 79 €") 301 | '99' 302 | >>> extract_price_text("99 € 79 €") 303 | '99' 304 | >>> extract_price_text("35€ 99") 305 | '35€99' 306 | >>> extract_price_text("35€ 999") 307 | '35' 308 | >>> extract_price_text("1,235€ 99") 309 | '1,235€99' 310 | >>> extract_price_text("50% OFF") 311 | >>> extract_price_text("50%") 312 | >>> extract_price_text("50") 313 | '50' 314 | >>> extract_price_text("$1\xa0298,00") 315 | '1 298,00' 316 | >>> extract_price_text("$.75") 317 | '.75' 318 | """ 319 | price = re.sub( 320 | r"\s+", " ", price 321 | ) # clean initial text from non-breaking and extra spaces 322 | 323 | if price.count("€") == 1: 324 | m = re.search( 325 | r""" 326 | [\d\s.,]*?\d # number, probably with thousand separators 327 | \s*?€(\s*?)? # euro, probably separated by whitespace 328 | \d(?(1)\d|\d*?) # if separated by whitespace - search one digit, 329 | # multiple digits otherwise 330 | (?:$|[^\d]) # something which is not a digit 331 | """, 332 | price, 333 | re.VERBOSE, 334 | ) 335 | if m: 336 | return m.group(0).replace(" ", "") 337 | 338 | m = re.search( 339 | r""" 340 | ([.]?\d[\d\s.,]*) # number, probably with thousand separators 341 | \s*? # skip whitespace 342 | (?:[^%\d]|$) # capture next symbol - it shouldn't be % 343 | """, 344 | price, 345 | re.VERBOSE, 346 | ) 347 | 348 | if m: 349 | price_text = m.group(1).rstrip(",.") 350 | return ( 351 | price_text.strip() 352 | if price_text.count(".") == 1 353 | else price_text.lstrip(",.").strip() 354 | ) 355 | if "free" in price.lower(): 356 | return "0" 357 | return None 358 | 359 | 360 | # NOTE: Keep supported separators in sync with parse_number() 361 | _search_decimal_sep = re.compile( 362 | r""" 363 | \d* # null or more digits (there can be more before it) 364 | ([.,€]) # decimal separator 365 | (?: # 1,2 or 4+ digits. 3 digits is likely to be a thousand separator. 366 | \d{1,2}?| 367 | \d{4}\d*? 368 | ) 369 | $ 370 | """, 371 | re.VERBOSE, 372 | ).search 373 | 374 | 375 | def get_decimal_separator(price: str) -> Optional[str]: 376 | """Return decimal separator symbol or None if there 377 | is no decimal separator. 378 | 379 | >>> get_decimal_separator("1000") 380 | >>> get_decimal_separator("12.99") 381 | '.' 382 | >>> get_decimal_separator("12,99") 383 | ',' 384 | >>> get_decimal_separator("12.999") 385 | >>> get_decimal_separator("3,0000") 386 | ',' 387 | >>> get_decimal_separator("1,235€99") 388 | '€' 389 | >>> get_decimal_separator(".75") 390 | '.' 391 | """ 392 | m = _search_decimal_sep(price) 393 | if m: 394 | return m.group(1) 395 | 396 | 397 | def parse_number( 398 | num: str, decimal_separator: Optional[str] = None 399 | ) -> Optional[Decimal]: 400 | """Parse a string with a number to a Decimal, guessing its format: 401 | decimal separator, thousand separator. Return None if parsing fails. 402 | 403 | >>> parse_number("1,234") 404 | Decimal('1234') 405 | >>> parse_number("12,34") 406 | Decimal('12.34') 407 | >>> parse_number("12,345") 408 | Decimal('12345') 409 | >>> parse_number("1,1") 410 | Decimal('1.1') 411 | >>> parse_number("1.1") 412 | Decimal('1.1') 413 | >>> parse_number("1234") 414 | Decimal('1234') 415 | >>> parse_number("12€34") 416 | Decimal('12.34') 417 | >>> parse_number("12€ 34") 418 | Decimal('12.34') 419 | >>> parse_number("1 234.99") 420 | Decimal('1234.99') 421 | >>> parse_number("1,235€99") 422 | Decimal('1235.99') 423 | >>> parse_number("1 235€99") 424 | Decimal('1235.99') 425 | >>> parse_number("1.235€99") 426 | Decimal('1235.99') 427 | >>> parse_number("140.000", decimal_separator=",") 428 | Decimal('140000') 429 | >>> parse_number("140.000", decimal_separator=".") 430 | Decimal('140.000') 431 | >>> parse_number("") 432 | >>> parse_number("foo") 433 | """ 434 | if not num: 435 | return None 436 | num = num.strip().replace(" ", "") 437 | decimal_separator = decimal_separator or get_decimal_separator(num) 438 | # NOTE: Keep supported separators in sync with _search_decimal_sep 439 | if decimal_separator is None: 440 | num = num.replace(".", "").replace(",", "") 441 | elif decimal_separator == ".": 442 | num = num.replace(",", "") 443 | elif decimal_separator == ",": 444 | num = num.replace(".", "").replace(",", ".") 445 | else: 446 | assert decimal_separator == "€" 447 | num = num.replace(".", "").replace(",", "").replace("€", ".") 448 | try: 449 | return Decimal(num) 450 | except InvalidOperation: 451 | return None 452 | -------------------------------------------------------------------------------- /price_parser/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrapinghub/price-parser/1bec23c927eba2c674878200ac6a1bd475157637/price_parser/py.typed -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.bumpversion] 2 | current_version = "0.4.0" 3 | commit = true 4 | tag = true 5 | tag_name = "{new_version}" 6 | 7 | [[tool.bumpversion.files]] 8 | filename = 'CHANGES.rst' 9 | search = "\\(unreleased\\)$" 10 | replace = "({now:%Y-%m-%d})" 11 | regex = true 12 | 13 | [[tool.bumpversion.files]] 14 | filename = "setup.py" 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | setup( 4 | name="price-parser", 5 | version="0.4.0", 6 | description="Extract price and currency from a raw string", 7 | long_description=open("README.rst", encoding="utf8").read() 8 | + "\n\n" 9 | + open("CHANGES.rst").read(), 10 | author="Mikhail Korobov", 11 | author_email="kmike84@gmail.com", 12 | url="https://github.com/scrapinghub/price-parser", 13 | packages=find_packages(exclude=["tests"]), 14 | package_data={"price_parser": ["py.typed"]}, 15 | install_requires=[ 16 | "attrs >= 17.3.0", 17 | ], 18 | zip_safe=False, 19 | classifiers=[ 20 | "Development Status :: 4 - Beta", 21 | "Intended Audience :: Developers", 22 | "License :: OSI Approved :: BSD License", 23 | "Natural Language :: English", 24 | "Operating System :: OS Independent", 25 | "Programming Language :: Python :: 3", 26 | "Programming Language :: Python :: 3.9", 27 | "Programming Language :: Python :: 3.10", 28 | "Programming Language :: Python :: 3.11", 29 | "Programming Language :: Python :: 3.12", 30 | "Programming Language :: Python :: 3.13", 31 | ], 32 | ) 33 | -------------------------------------------------------------------------------- /tests/test_price_parsing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Price extractor was developed mostly using PRICE_PARSING_EXAMPLES, 4 | then evaluated on PRICE_PARSING_EXAMPLES_2, then improved to work better on 5 | PRICE_PARSING_EXAMPLES_3, PRICE_PARSING_EXAMPLES_NO_PRICE 6 | and PRICE_PARSING_EXAMPLES_NO_CURRENCY. 7 | 8 | This data is collected from a random sample of pages from different domains. 9 | 10 | PRICE_PARSING_EXAMPLES_BUGS_CAUGHT are manually added examples for the bugs 11 | we've found in a wild; PRICE_PARSING_EXAMPLES_NEW is a list of tests for 12 | new features. New tests should probably go these two lists. 13 | """ 14 | from decimal import Decimal 15 | from typing import Optional, Union 16 | 17 | import pytest 18 | 19 | from price_parser import Price 20 | 21 | 22 | class Example(Price): 23 | """A Price wrapper for tests""" 24 | 25 | def __init__( 26 | self, 27 | currency_raw: Optional[str], 28 | price_raw: Optional[str], 29 | currency: Optional[str], 30 | amount_text: Optional[str], 31 | amount_float: Optional[Union[float, Decimal]], 32 | decimal_separator: Optional[str] = None, 33 | digit_group_separator: Optional[str] = None, 34 | ) -> None: 35 | self.currency_raw = currency_raw 36 | self.price_raw = price_raw 37 | self.decimal_separator = decimal_separator 38 | amount_decimal = None # type: Optional[Decimal] 39 | if isinstance(amount_float, Decimal): 40 | amount_decimal = amount_float 41 | elif amount_float is not None: 42 | # don't use Decimal(amount_float), as this is not what 43 | # one usually means, because of float precision 44 | amount_decimal = Decimal(str(amount_float)) 45 | super().__init__( 46 | amount=amount_decimal, 47 | currency=currency, 48 | amount_text=amount_text, 49 | ) 50 | 51 | def __eq__(self, other): 52 | if not isinstance(other, Price): 53 | return super().__eq__(other) 54 | return ( 55 | self.amount == other.amount 56 | and self.currency == other.currency 57 | and self.amount_text == other.amount_text 58 | ) 59 | 60 | 61 | def idfn(val): 62 | if isinstance(val, Example): 63 | return f"{val.currency_raw}, {val.price_raw!r}" 64 | 65 | 66 | PRICE_PARSING_EXAMPLES_BUGS_CAUGHT = [ 67 | Example(None, "US$:12.99", "US$", "12.99", 12.99), 68 | Example("GBP", "34.992001", "GBP", "34.992001", 34.992001), 69 | Example("GBP", "29.1583", "GBP", "29.1583", 29.1583), 70 | Example( 71 | None, 72 | "1.11000000000000009770", 73 | None, 74 | "1.11000000000000009770", 75 | Decimal("1.11000000000000009770"), 76 | ), 77 | Example(None, " 423.923 KD", "KD", "423.923", 423.923, decimal_separator="."), 78 | Example( 79 | None, 80 | " 123,456.789 OMR", 81 | "OMR", 82 | "123,456.789", 83 | 123456.789, 84 | decimal_separator=".", 85 | digit_group_separator=",", 86 | ), 87 | ] 88 | 89 | 90 | PRICE_PARSING_EXAMPLES_NEW = [ 91 | Example(None, "PTE 120 000 000", "PTE", "120 000 000", 120000000), 92 | Example(None, "DEM 170 000", "DEM", "170 000", 170000), 93 | Example(None, "₤1700.", "₤", "1700", 1700), 94 | Example(None, "$ 1.144.000", "$", "1.144.000", 1144000), 95 | Example(None, "A$190.00", "A$", "190.00", 190), 96 | Example(None, "205,68 € 205.68", "€", "205,68", 205.68), 97 | Example(None, "AED 8000 (USD 2179)", "AED", "8000", 8000), 98 | Example(None, "13800 ₶", "₶", "13800", 13800), 99 | Example(None, "12,000원", "원", "12,000", 12000), 100 | Example(None, "3,500円", "円", "3,500", 3500), 101 | ] 102 | 103 | 104 | PRICE_PARSING_EXAMPLES = [ 105 | Example( 106 | "90 728.00 руб 103 100.00 руб", 107 | "399 167.00 руб 420 176.16 руб", 108 | "руб", 109 | "399 167.00", 110 | 399167, 111 | ), 112 | Example("45,00 zł", "45,00 zł", "zł", "45,00", 45), 113 | Example("$", "$ 22.00", "$", "22.00", 22), 114 | Example("$3.99", "$14.99", "$", "14.99", 14.99), 115 | Example("Price:", "$11.95", "$", "11.95", 11.95), 116 | Example("$19.95", "$19.95", "$", "19.95", 19.95), 117 | Example("Price From £ 39.95", "39.95", "£", "39.95", 39.95), 118 | Example("comprar", "R$260,00", "R$", "260,00", 260), 119 | Example("$79.00", "$79.00", "$", "79.00", 79), 120 | Example("34,50 € *", "34,50 € *", "€", "34,50", 34.5), 121 | Example("35,70 €", "35,70 €", "€", "35,70", 35.7), 122 | Example("Price", "$147.44", "$", "147.44", 147.44), 123 | Example("Up to", "$4.70", "$", "4.70", 4.7), 124 | Example("$999.99", "$924.99", "$", "924.99", 924.99), 125 | Example("€", "690,00", "€", "690,00", 690), 126 | Example("Cena", "Běžná cena 9 800 Kč", "Kč", "9 800", 9800), 127 | Example("729,00 €", "479,00 €", "€", "479,00", 479), 128 | Example("naše cena", "běžná cena 590 Kč", "Kč", "590", 590), 129 | Example("Statt 249,00 EUR **", "249,00 EUR", "EUR", "249,00", 249), 130 | Example("€13.95", "€13.95", "€", "13.95", 13.95), 131 | Example("€36", "€89", "€", "89", 89), 132 | Example("171900 руб.", "171900 руб.", "руб.", "171900", 171900), 133 | Example("$1.06", "$0.31", "$", "0.31", 0.31), 134 | Example("$45.49", "$45.49", "$", "45.49", 45.49), 135 | Example("$", "28", "$", "28", 28), 136 | Example("4.81 16 von 5", "4.81", None, "4.81", 4.81), 137 | Example("CHF 86.00 *", "CHF 47.00 *", "CHF", "47.00", 47), 138 | Example("320 руб. 400 руб.", "419 руб.", "руб.", "419", 419), 139 | Example("129,99 €", "129,99 €", "€", "129,99", 129.99), 140 | Example("Cena", "1,10 €", "€", "1,10", 1.1), 141 | Example("Price:", "$559.00", "$", "559.00", 559), 142 | Example("$49.99", "$49.99", "$", "49.99", 49.99), 143 | Example("Price: $32.00", "$32.00", "$", "32.00", 32), 144 | Example("Price (high to low)", "$699,000", "$", "699,000", 699000), 145 | Example("€ 11.69", "€ 11.69", "€", "11.69", 11.69), 146 | Example("100,00 руб.", "100,00", "руб.", "100,00", 100), 147 | Example("€ 1", "17,35", "€", "17,35", 17.35), 148 | Example("$19.95", "$19.95", "$", "19.95", 19.95), 149 | Example("-", "£11.95", "£", "11.95", 11.95), 150 | Example("грн.", "5 870", "грн.", "5 870", 5870), 151 | Example("Price", "80 Kč", "Kč", "80", 80), 152 | Example("€ 640,", "€ 610,", "€", "610", 610), 153 | Example("Cena", "299 Kč", "Kč", "299", 299), 154 | Example("1128240 рублей", "1128240 рублей", "руб", "1128240", 1128240), 155 | Example( 156 | "Now £9.99 $13.71 11,16 € £8.33 $13.71 9,30 €", 157 | "£9.99", 158 | "£", 159 | "9.99", 160 | 9.99, 161 | ), 162 | Example("Free!", "Free!", None, "0", 0), 163 | Example("49,96€", "49,96€", "€", "49,96", 49.96), 164 | Example("SKU:", "$9.00", "$", "9.00", 9), 165 | Example("£ 8.29", "8.29", "£", "8.29", 8.29), 166 | Example("Р", "6 300 Р", "Р", "6 300", 6300), 167 | Example("99,99 EUR (-30,00%) 69,99 EUR", "99,99 EUR", "EUR", "99,99", 99.99), 168 | Example("Р", "1 890 Р", "Р", "1 890", 1890), 169 | Example("Disposable Arts", "15,95 €", "€", "15,95", 15.95), 170 | Example("zł", "16.00", "zł", "16.00", 16), 171 | Example("£ 4.99", "4.99", "£", "4.99", 4.99), 172 | Example("Price", "$119. 95", "$", "119. 95", 119.95), 173 | Example("269 Kč", "269 Kč", "Kč", "269", 269), 174 | Example(">", "13,30 €", "€", "13,30", 13.3), 175 | Example("₪ 115", "₪98", "₪", "98", 98), 176 | Example("80,00 €", "80,00 €", "€", "80,00", 80), 177 | Example("33,95 €", "29,95 €", "€", "29,95", 29.95), 178 | Example("£ 80.00", "80.00", "£", "80.00", 80), 179 | Example("EUR", "€16.95", "€", "16.95", 16.95), 180 | Example("DKK", "23,40", "DKK", "23,40", 23.4), 181 | Example("Price: $ 34.44", "34.44", "$", "34.44", 34.44), 182 | Example("Rp 31.500", "Rp 31.500", "Rp", "31.500", 31500), 183 | Example("běžná cena 6,00 Kč", "běžná cena 6,00 Kč", "Kč", "6,00", 6), 184 | Example("€", "19,59", "€", "19,59", 19.59), 185 | Example("Out of stock", "2 285,81 грн.", "грн.", "2 285,81", 2285.81), 186 | Example("$ 119,999", "$ 119,999", "$", "119,999", 119999), 187 | Example("1,20 €", "1,20 €", "€", "1,20", 1.2), 188 | Example("€ 1.99", "1.99", "€", "1.99", 1.99), 189 | Example("Cena 283,50 PLN", "Cena 283,50 PLN", "PLN", "283,50", 283.5), 190 | Example("SKU:", "99.99", None, "99.99", 99.99), 191 | Example("$", "$ 979.00", "$", "979.00", 979), 192 | Example("Cena 169,00 Kč", "169,00 Kč", "Kč", "169,00", 169), 193 | Example("Р", "94,456 Р", "Р", "94,456", 94456), 194 | Example("€", "180", "€", "180", 180), 195 | Example("Savings: $0.90", "2.85", "$", "2.85", 2.85), 196 | Example("฿ 3.3900", "฿ 3.3900", "฿", "3.3900", 3.39), 197 | Example("5,90 €", "5,90 €", "€", "5,90", 5.90), 198 | Example("1 930 р.", "1 030 р.", "р.", "1 030", 1030), 199 | Example("$", "$ 22.00", "$", "22.00", 22), 200 | Example("HUF", "39900", "HUF", "39900", 39900), 201 | Example("59,00 €", "79,00 €", "€", "79,00", 79), 202 | Example("(-38,23%)", "429,00 EUR", "EUR", "429,00", 429), 203 | Example("302,44€ Χωρίς ΦΠΑ: 243,90€", "302,44€", "€", "302,44", 302.44), 204 | Example("RENAULT", "129.900", None, "129.900", 129900), 205 | Example("USD", "£54.17", "£", "54.17", 54.17), 206 | Example("$3.00", "$3.00", "$", "3.00", 3), 207 | Example("3,49 zł 1,75 zł", "3,49 zł 1,75 zł", "zł", "3,49", 3.49), 208 | Example("59,90 EUR 599,00 EUR pro Kilogramm", "59,90 EUR", "EUR", "59,90", 59.9), 209 | Example("Vanaf € 49.95", "Vanaf € 49.95", "€", "49.95", 49.95), 210 | Example("$299.00", "$299.00", "$", "299.00", 299), 211 | Example("€", "€30,40 **", "€", "30,40", 30.4), 212 | Example("$59.99", "$59.99", "$", "59.99", 59.99), 213 | Example("EUR", "2800", "EUR", "2800", 2800), 214 | Example("225,00 € *", "225,00 € *", "€", "225,00", 225), 215 | Example("17 940,00 руб", "17 940,00 руб", "руб", "17 940,00", 17940), 216 | Example("159,50 €", "159,50 €", "€", "159,50", 159.5), 217 | Example("£18.99", "£18.99", "£", "18.99", 18.99), 218 | Example("2.999,00 EUR (-10,00%) 2.699,00 EUR", "699,00 EUR", "EUR", "699,00", 699), 219 | Example("$", "45.33", "$", "45.33", 45.33), 220 | Example("грн.", "33", "грн.", "33", 33), 221 | Example("EUR", "16,50 €", "€", "16,50", 16.5), 222 | Example("₫", "880,000 ₫", "₫", "880,000", 880000), 223 | Example("руб.", "13.50", "руб.", "13.50", 13.5), 224 | Example("336 р.", "336 р.", "р.", "336", 336), 225 | Example("....", "200", None, "200", 200), 226 | Example("Our Price:", "$860.00", "$", "860.00", 860), 227 | Example("10 €", "10 €", "€", "10", 10), 228 | Example("€", "13", "€", "13", 13), 229 | Example("60,00 DKK", "60,00 DKK", "DKK", "60,00", 60), 230 | Example("63,90 EUR", "63,90 EUR", "EUR", "63,90", 63.9), 231 | Example("$", "$ 11.99", "$", "11.99", 11.99), 232 | Example("25,00 €", "25,00 €", "€", "25,00", 25), 233 | Example("Cena", "106,40 €", "€", "106,40", 106.4), 234 | Example("EA", "$426.20/EA", "$", "426.20", 426.2), 235 | Example("zł", "5.76", "zł", "5.76", 5.76), 236 | Example("$4.50", "$4.50", "$", "4.50", 4.5), 237 | Example("51090 M Фильтроэлемент OMEGA", "2 660 руб", "руб", "2 660", 2660), 238 | Example("Р", "5 000 Р", "Р", "5 000", 5000), 239 | Example("Add to Cart", "$4.60", "$", "4.60", 4.6), 240 | Example("£14.99", "£14.99", "£", "14.99", 14.99), 241 | Example("SKU:", "$150.00", "$", "150.00", 150), 242 | Example("€", "70,31", "€", "70,31", 70.31), 243 | Example("/", "$379", "$", "379", 379), 244 | Example("€", "139,95", "€", "139,95", 139.95), 245 | Example("Add to cart", "22,90 €", "€", "22,90", 22.9), 246 | Example("94,99 zł", "94,99 zł", "zł", "94,99", 94.99), 247 | Example("Price", "89.99", None, "89.99", 89.99), 248 | Example("USD", "$4.00", "$", "4.00", 4), 249 | Example("$", "Type of Transfer *", "$", None, None), 250 | Example("Add to Order", "$892.00", "$", "892.00", 892), 251 | Example("грн", "60800", "грн", "60800", 60800), 252 | Example("zł", "1849.00", "zł", "1849.00", 1849), 253 | Example("OK", "0,00 EUR", "EUR", "0,00", 0), 254 | Example("руб.", "5 450 руб.", "руб.", "5 450", 5450), 255 | Example("Price: $15.95/mo", "Price: $15.95/mo", "$", "15.95", 15.95), 256 | Example("MX924 IX", "850 руб.", "руб.", "850", 850), 257 | Example( 258 | "6,000.00 руб. 5,500.00 руб.", 259 | "6,000.00 руб. 5,500.00 руб.", 260 | "руб.", 261 | "6,000.00", 262 | 6000, 263 | ), 264 | Example("5.590,00 € *", "9.990,00 €", "€", "9.990,00", 9990), 265 | Example( 266 | "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26" 267 | " 27 28 29 30", 268 | "$175.00", 269 | "$", 270 | "175.00", 271 | 175, 272 | ), 273 | Example("Price", "$99.99", "$", "99.99", 99.99), 274 | Example("€", "119,00", "€", "119,00", 119), 275 | Example("$", "$ 11.96", "$", "11.96", 11.96), 276 | Example("€", "9,94", "€", "9,94", 9.94), 277 | Example("$69.00", "$69.00", "$", "69.00", 69), 278 | Example("AQUAFINESSE", "AQUAFINESSE", None, None, None), 279 | Example("£55.00", "£50.00", "£", "50.00", 50), 280 | Example("$39.99", "$39.99", "$", "39.99", 39.99), 281 | Example("65 000 руб. 75 000 руб.", "75 000 руб.", "руб.", "75 000", 75000), 282 | Example("DKK/stk", "17,95", "DKK", "17,95", 17.95), 283 | Example("841,00 zł", "841,00 zł – 995,00 zł", "zł", "841,00", 841), 284 | Example("$", "$ 18.95", "$", "18.95", 18.95), 285 | Example("9.0", "434", None, "434", 434), 286 | Example("£14.50", "£21.00", "£", "21.00", 21), 287 | Example("ab", "19,90 €", "€", "19,90", 19.9), 288 | Example("30 руб", "30 руб", "руб", "30", 30), 289 | Example("€", "684,25", "€", "684,25", 684.25), 290 | Example("€ 125.00", "€ 45.00", "€", "45.00", 45), 291 | Example("ht / L'unité", "17.90", None, "17.90", 17.9), 292 | Example("$0.00", "$0.00", "$", "0.00", 0), 293 | Example("49 Kč", "49 Kč", "Kč", "49", 49), 294 | Example("Cena 685,00 Kč", "1 995,00 Kč", "Kč", "1 995,00", 1995), 295 | Example("€ 2,99", "€ 2,99", "€", "2,99", 2.99), 296 | Example("Cкидка до 10% от цены купона", "475 руб.", "руб.", "475", 475), 297 | Example("€", "11,76", "€", "11,76", 11.76), 298 | Example("$99.99", "$99.99", "$", "99.99", 99.99), 299 | Example("1\xa0298,00 €", "1\xa0298,00 €", "€", "1 298,00", 1298.00), 300 | Example("$1\xa0298,00", "$1\xa0298,00", "$", "1 298,00", 1298.00), 301 | Example("1\xa0298,00", "1\xa0298,00", None, "1 298,00", 1298.00), 302 | Example(None, ".75 €", "€", ".75", 0.75), 303 | Example("$.75", "$.75", "$", ".75", 0.75), 304 | Example("$..75", "$..75", "$", ".75", 0.75), 305 | Example("$.75,333", "$.75,333", "$", ".75,333", 75333), 306 | Example("$.750.30", "$.750.30", "$", "750.30", 750.30), 307 | Example("i", "i", None, None, None), 308 | ] 309 | 310 | 311 | PRICE_PARSING_EXAMPLES_2 = [ 312 | Example("7,99 €", "7,99 €", "€", "7,99", 7.99), 313 | Example("2 Piece - $6.75 2 Piece - $6.75", "$6.75", "$", "6.75", 6.75), 314 | Example("£7", "£24.99", "£", "24.99", 24.99), 315 | Example("23,00 €", "23,00 €", "€", "23,00", 23.00), 316 | Example("€", "bežná cena 12,96 €", "€", "12,96", 12.96), 317 | Example("$", "3,20 €", "€", "3,20", 3.2), 318 | Example("€ 2.109,00", "€ 2.109,00", "€", "2.109,00", 2109), 319 | Example("руб.", "32690", "руб.", "32690", 32690), 320 | Example(": 3 250 000 €", ": 3 250 000 €", "€", "3 250 000", 3250000), 321 | Example("$20.00", "$20.00", "$", "20.00", 20), 322 | Example("Цена:", "950 грн.", "грн.", "950", 950), 323 | Example("TTC", "149,00", None, "149,00", 149.00), 324 | Example("A partire da 9,08 €", "166,81 € iva inclusa", "€", "166,81", 166.81), 325 | # Example('Ref:', '4229', 326 | # None, None, ), 327 | Example("EUR", "679.00", "EUR", "679.00", 679.00), 328 | Example("10,50 € 10,50 €", "10,50 €", "€", "10,50", 10.50), 329 | Example("2,20 € *", "2,20 € *", "€", "2,20", 2.2), 330 | Example("€", "€ 10.00 € 8.00 – € 10.00", "€", "10.00", 10), 331 | Example("Bestellen", "€16,95", "€", "16,95", 16.95), 332 | Example("$", "$ 95.00", "$", "95.00", 95.00), 333 | Example("Р", "1 400 Р", "Р", "1 400", 1400), 334 | Example("£", "£ 15.00", "£", "15.00", 15), 335 | Example("Price: $119.00", "$119.00", "$", "119.00", 119), 336 | Example("50,- Kč", "50,- Kč", "Kč", "50", 50), 337 | Example("What is this?", "$25.95", "$", "25.95", 25.95), 338 | Example("$29.99 Excluding Sales Tax in Illinois", "$29.99", "$", "29.99", 29.99), 339 | Example("ID:", "125,00 zł", "zł", "125,00", 125), 340 | Example("Offer Price: Rs 1,306.14", "Rs 3,600.00", "Rs", "3,600.00", 3600), 341 | Example("29 RON", "29 RON", "RON", "29", 29), 342 | Example("Р", "2 690 Р", "Р", "2 690", 2690), 343 | Example("$134.96", "$134.96", "$", "134.96", 134.96), 344 | Example("362,68 € χωρίς Φ.Π.Α", "449,72 €", "€", "449,72", 449.72), 345 | Example("MSRP: $588.00", "499.80", "$", "499.80", 499.80), 346 | Example("15.00 €", "15.00 €", "€", "15.00", 15), 347 | Example("Цена: от 2 750 руб.", "2 750 руб.", "руб.", "2 750", 2750), 348 | Example("P.V.P. 4,56 €", "P.V.P. 4,56 €", "€", "4,56", 4.56), 349 | Example("SPECIAL Add to Cart for Price", "$40.00", "$", "40.00", 40), 350 | Example("2,90 €", "2,90", "€", "2,90", 2.9), 351 | Example("Цена: 19 000 руб.", "Цена: 19 000 руб.", "руб.", "19 000", 19000), 352 | Example("COMMANDER", "184,20 €HT", "€", "184,20", 184.2), 353 | Example("Цена: 1 849 руб.", "1 849 руб.", "руб.", "1 849", 1849), 354 | Example("€", "12,00", "€", "12,00", 12), 355 | Example("Discounted Price £ 205.99", "205.99", "£", "205.99", 205.99), 356 | Example("SKU", "No longer available", None, None, None), 357 | Example("€14.90", "€69.90", "€", "69.90", 69.9), 358 | Example("руб.", "876 руб.", "руб.", "876", 876), 359 | Example("$79.95", "$79.95", "$", "79.95", 79.95), 360 | Example("3,990 Ft 2,990 Ft", "3,990 Ft", "Ft", "3,990", 3990), 361 | Example("Cena 298,00 Kč", "288,00 Kč", "Kč", "288,00", 288), 362 | Example("Pris NOK 899,00", "899,00", "NOK", "899,00", 899), 363 | Example("50,00 €", "50,00 €", "€", "50,00", 50), 364 | Example("59,99 €", "54,99 €", "€", "54,99", 54.99), 365 | Example("EAN", "139,00 € *", "€", "139,00", 139), 366 | Example("Now", "238.00", None, "238.00", 238), 367 | Example("(You save $15.01)", "$119.99", "$", "119.99", 119.99), 368 | Example("Item #:", "$ 99.95", "$", "99.95", 99.95), 369 | Example("UK:", "£14.99", "£", "14.99", 14.99), 370 | Example("р.", "150", "р.", "150", 150), 371 | Example("12x de R$ 44,00 sem juros", "R$ 528,00", "R$", "528,00", 528), 372 | Example("€", "14,90", "€", "14,90", 14.9), 373 | Example("5 290 Kč s DPH", "5 290 Kč", "Kč", "5 290", 5290), 374 | Example("(Out of Stock)", "39.95", None, "39.95", 39.95), 375 | Example("₪370", "₪370", "₪", "370", 370), 376 | Example("HUF", "39000", "HUF", "39000", 39000), 377 | Example("Kč", "1 995 Kč", "Kč", "1 995", 1995), 378 | Example("C$5.95", "C$5.95", "C$", "5.95", 5.95), 379 | Example("руб.", "22800.00", "руб.", "22800.00", 22800), 380 | Example("Cena", "359 Kč", "Kč", "359", 359), 381 | Example("Nuestro precio", "189,00 €", "€", "189,00", 189), 382 | Example("PLN", "179.62", "PLN", "179.62", 179.62), 383 | Example("€", "145,79", "€", "145,79", 145.79), 384 | Example("$", "$ 695.00", "$", "695.00", 695), 385 | Example("Р", "2 999 Р", "Р", "2 999", 2999), 386 | Example("6,50 € *", "6,50", "€", "6,50", 6.50), 387 | Example("Price: $61.00", "$61.00", "$", "61.00", 61.00), 388 | Example("Pris NOK 1 999,00", "5 799,00", "NOK", "5 799,00", 5799), 389 | Example("7,31 €", "7,31 €", "€", "7,31", 7.31), 390 | Example("53,20 €", "53,20 €", "€", "53,20", 53.20), 391 | Example("Cena", "668 Kč", "Kč", "668", 668), 392 | Example("€ 28,25", "€ 27,95", "€", "27,95", 27.95), 393 | Example("From", "From $109.90", "$", "109.90", 109.9), 394 | Example("Unit Price: $4.75", "$4.75", "$", "4.75", 4.75), 395 | Example("Więcej", "14,90 zł", "zł", "14,90", 14.90), 396 | Example("$23.00", "$23.00", "$", "23.00", 23), 397 | Example("€ 0.00", "€ 33.00", "€", "33.00", 33), 398 | Example("Zľava 36 €", "78 €", "€", "78", 78), 399 | Example("£", "5.00", "£", "5.00", 5), 400 | Example("€", "29,90", "€", "29,90", 29.9), 401 | Example("Р", "860 Р", "Р", "860", 860), 402 | Example("60,55 €", "60,55 €", "€", "60,55", 60.55), 403 | Example("€", "129.00", "€", "129.00", 129), 404 | Example("£13.50", "£13.50", "£", "13.50", 13.50), 405 | Example("SKU:", "$39.99", "$", "39.99", 39.99), 406 | Example( 407 | "Disponibilidade: Pronta entrega R$60,00 até 2x de R$30,00", 408 | "R$60,00", 409 | "R$", 410 | "60,00", 411 | 60, 412 | ), 413 | Example("67,99 €", "64,59 €", "€", "64,59", 64.59), 414 | Example("€ 9,90", "€ 13,50", "€", "13,50", 13.50), 415 | Example("Р", "30 Р", "Р", "30", 30), 416 | Example("€", "€ 139.00", "€", "139.00", 139), 417 | Example("There are 163 products.", "From 26 to 50 €", "€", "26", 26), 418 | Example("Pris NOK 1 999,00", "139,00", "NOK", "139,00", 139), 419 | Example("/sqft", "1.52", None, "1.52", 1.52), 420 | Example("$4.95", "$4.95", "$", "4.95", 4.95), 421 | Example("$38", "$12", "$", "12", 12), 422 | Example("Cena 4.10 €", "4.10", "€", "4.10", 4.1), 423 | Example("руб.", "590", "руб.", "590", 590), 424 | Example("€", "99,90", "€", "99,90", 99.9), 425 | Example("9,50 EUR", "9,50 EUR", "EUR", "9,50", 9.50), 426 | Example("$74.99", "$74.99", "$", "74.99", 74.99), 427 | Example("$", "$ 3.22", "$", "3.22", 3.22), 428 | Example("£", "3,301.00 £", "£", "3,301.00", 3301), 429 | Example("41,78 EUR", "41,78 EUR", "EUR", "41,78", 41.78), 430 | Example("44,50 EUR", "44,50 EUR", "EUR", "44,50", 44.50), 431 | Example("£4.00", "£4.00", "£", "4.00", 4), 432 | Example("Р", "7 390 Р", "Р", "7 390", 7390), 433 | Example("eMail", "34.99€", "€", "34.99", 34.99), 434 | Example("€", "12,99", "€", "12,99", 12.99), 435 | Example("RRP: £ 180.00", "149.00", "£", "149.00", 149), 436 | Example("9.00 руб.", "27.00 руб.", "руб.", "27.00", 27), 437 | Example("Price: $19.95", "$7.95", "$", "7.95", 7.95), 438 | Example("$16,500", "$16,500", "$", "16,500", 16500), 439 | Example("•", "$59.99", "$", "59.99", 59.99), 440 | Example("USD", "349.95", "USD", "349.95", 349.95), 441 | Example("35€ 99 dont 0,02 € d'éco-part", "35€ 99", "€", "35€99", 35.99), 442 | Example("5,72 €", "5,72 €", "€", "5,72", 5.72), 443 | Example("199,00 грн.", "199,00 грн.", "грн.", "199,00", 199), 444 | Example("£4.07", "£4.07", "£", "4.07", 4.07), 445 | Example("$15.00", "$15.00", "$", "15.00", 15), 446 | Example("€", "20.00", "€", "20.00", 20), 447 | Example("€", "14.50", "€", "14.50", 14.5), 448 | Example("DKK", "59,00", "DKK", "59,00", 59), 449 | Example("9 472 Ft", "9 472 Ft", "Ft", "9 472", 9472), 450 | Example("Cena", "95 Kč", "Kč", "95", 95), 451 | Example("tax excl.", "€ 0,00", "€", "0,00", 0), 452 | Example("TL", "69,50", "TL", "69,50", 69.5), 453 | Example("Price", "Rp 169.000", "Rp", "169.000", 169000), 454 | Example("En stock", "4,50 €", "€", "4,50", 4.50), 455 | Example("$183.00", "$145.00", "$", "145.00", 145), 456 | Example("£", "11.70", "£", "11.70", 11.7), 457 | Example("25,13 €", "25,13 €", "€", "25,13", 25.13), 458 | Example("58,00 zł", "58,00 zł", "zł", "58,00", 58), 459 | Example("€", "15,55", "€", "15,55", 15.55), 460 | Example("Prezzo SVB: 19,95 €", "Prezzo SVB: 19,95 €", "€", "19,95", 19.95), 461 | Example("In stock", "$185.00", "$", "185.00", 185), 462 | Example("18 €", "18 €", "€", "18", 18), 463 | Example("0€", "0 €", "€", "0", 0), 464 | Example("*", "9,41 €", "€", "9,41", 9.41), 465 | Example("грн.", "1 075", "грн.", "1 075", 1075), 466 | Example("More", "$22.99", "$", "22.99", 22.99), 467 | Example("Р", "9 282 Р", "Р", "9 282", 9282), 468 | Example("RM5", "RM50", "RM", "50", 50), 469 | Example("7,60 €", "7,60 €", "€", "7,60", 7.6), 470 | Example("$", "0.00", "$", "0.00", 0), 471 | Example(". AMOSTRA .", "R$ 6,00", "R$", "6,00", 6), 472 | Example( 473 | "5 833,00 € -5% 6 140,00 € Tasse incl. IVA 4%", 474 | "5 833,00 €", 475 | "€", 476 | "5 833,00", 477 | 5833, 478 | ), 479 | Example("€ 26,95", "€ 26,95", "€", "26,95", 26.95), 480 | Example("35,00 €", "35,00 €", "€", "35,00", 35), 481 | Example("Р", "2 500 Р", "Р", "2 500", 2500), 482 | Example("499,00 €", "499,00 €", "€", "499,00", 499), 483 | Example("руб.", "1 120 900", "руб.", "1 120 900", 1120900), 484 | Example("125,00 125,00 €", "125,00", "€", "125,00", 125), 485 | Example("₽", "7362", "₽", "7362", 7362), 486 | Example("SKU:", "€12.99", "€", "12.99", 12.99), 487 | Example("р.", "600", "р.", "600", 600), 488 | Example("79,50 €", "29,90€", "€", "29,90", 29.9), 489 | Example("$150.00", "$150.00", "$", "150.00", 150), 490 | Example("Р", "1 987 Р", "Р", "1 987", 1987), 491 | Example("€69.00", "€69.00", "€", "69.00", 69), 492 | Example("USD", "280", "USD", "280", 280), 493 | Example("Rabatt", "6 450,00", None, "6 450,00", 6450), 494 | Example("SKU:", "$178.00", "$", "178.00", 178), 495 | Example("$", "$ 300.00", "$", "300.00", 300), 496 | Example("0€", "19,1€", "€", "19,1", 19.1), 497 | Example("R$49,90", "R$49,90", "R$", "49,90", 49.9), 498 | Example("En stock", "790,00 € HT", "€", "790,00", 790), 499 | Example("Cena", "Cena 14,79 €", "€", "14,79", 14.79), 500 | Example("For Sale", "$395,000", "$", "395,000", 395000), 501 | Example("€", "10.14", "€", "10.14", 10.14), 502 | Example("₫", "5.00 trên 5", "₫", "5.00", 5), 503 | Example("$ 599.00", "549.00", "$", "549.00", 549), 504 | Example("$19", "$19", "$", "19", 19), 505 | Example("$210", "$210", "$", "210", 210), 506 | Example("$", "$ 79.95", "$", "79.95", 79.95), 507 | Example("руб.", "279 000", "руб.", "279 000", 279000), 508 | Example("2 390 €", "2 390 €", "€", "2 390", 2390), 509 | Example("$", "$ 281.40 $ 265.70", "$", "281.40", 281.4), 510 | Example("3200 руб. 1500 руб.", "3200 руб. 1500 руб.", "руб.", "3200", 3200), 511 | Example("$1,299.00", "$999.00", "$", "999.00", 999), 512 | Example("15,00 Kč", "15,00 Kč", "Kč", "15,00", 15), 513 | Example("€", "€ 20.70", "€", "20.70", 20.7), 514 | Example("out of stock", "30.00", None, "30.00", 30), 515 | Example("220,00 €", "398,00 €", "€", "398,00", 398), 516 | Example("£94.50 GBP", "£94.50 GBP", "£", "94.50", 94.5), 517 | Example("5 427 Kč", "5 427 Kč", "Kč", "5 427", 5427), 518 | Example("$ 24.99", "$ 24.99", "$", "24.99", 24.99), 519 | Example("£ 2.00", "2.00", "£", "2.00", 2), 520 | Example(">", "10,00 €", "€", "10,00", 10), 521 | Example("32,50 EUR", "32,50 EUR", "EUR", "32,50", 32.5), 522 | Example("In stock", "Rs1,599.00", "Rs", "1,599.00", 1599), 523 | Example("$", "14.95", "$", "14.95", 14.95), 524 | Example("RRP: £ 49.99", "42.90", "£", "42.90", 42.9), 525 | Example("Rs206.00 Rs164.00 SAVE 20%", "Rs164.00", "Rs", "164.00", 164), 526 | Example("Price:", "$6,200.00", "$", "6,200.00", 6200), 527 | Example("PLN", "69.99", "PLN", "69.99", 69.99), 528 | Example("€", "48,39", "€", "48,39", 48.39), 529 | Example("1.350.000", "320.000", None, "320.000", 320000), 530 | Example("1.650,00 € *", "2.130,00 €", "€", "2.130,00", 2130), 531 | Example("£537.00", "£179.00", "£", "179.00", 179), 532 | Example("SKU:", "$39.99", "$", "39.99", 39.99), 533 | Example("SGD$4.90", "SGD$4.90", "SGD", "4.90", 4.9), 534 | Example("SGD4.90 $", "SGD4.90 $", "SGD", "4.90", 4.9), 535 | Example("$ SGD4.90", "$ SGD4.90", "SGD", "4.90", 4.9), 536 | ] 537 | 538 | 539 | PRICE_PARSING_EXAMPLES_NO_PRICE = [ 540 | Example("EUR", None, "EUR", None, None), 541 | Example("Kč", None, "Kč", None, None), 542 | Example("50", None, None, None, None), 543 | Example(">", None, None, None, None), 544 | Example("REI", None, None, None, None), 545 | Example("rate", None, None, None, None), 546 | ] 547 | 548 | 549 | PRICE_PARSING_EXAMPLES_NO_CURRENCY = [ 550 | Example(None, "67", None, "67", 67), 551 | Example(None, "naša cena 67,30 €", "€", "67,30", 67.30), 552 | Example( 553 | None, 554 | "EUR29.66 (inc VAT 20% - UK & EEC) EUR24.71 (exc VAT 20% - UK & EEC)", 555 | "EUR", 556 | "29.66", 557 | 29.66, 558 | ), 559 | Example(None, "17,00 €", "€", "17,00", 17), 560 | Example(None, "0,29 € + iva", "€", "0,29", 0.29), 561 | Example(None, "39,00 €", "€", "39,00", 39), 562 | Example(None, "24,00 Kč", "Kč", "24,00", 24), 563 | Example(None, "běžná cena 7 940 Kč", "Kč", "7 940", 7940), 564 | Example(None, "$46.00", "$", "46.00", 46.00), 565 | Example(None, "$9.99 & Under", "$", "9.99", 9.99), 566 | Example(None, "běžná cena 459,00 Kč", "Kč", "459,00", 459), 567 | Example(None, "$60.00", "$", "60.00", 60), 568 | Example(None, "0,82 €", "€", "0,82", 0.82), 569 | Example(None, "599 Kč", "Kč", "599", 599), 570 | Example(None, "€10.90", "€", "10.90", 10.90), 571 | Example(None, "299,00 EUR", "EUR", "299,00", 299), 572 | Example(None, "naša cena 21,95 €", "€", "21,95", 21.95), 573 | Example(None, "343,05 €", "€", "343,05", 343.05), 574 | Example(None, "1 139,00 €", "€", "1 139,00", 1139), 575 | Example(None, "157,000 تومان", "تومان", "157,000", 157000), 576 | Example(None, "35.00", None, "35.00", 35), 577 | Example(None, "8.000.000 ₫", "₫", "8.000.000", 8000000), 578 | Example(None, "6790 Dinara", "Dinara", "6790", 6790), 579 | Example(None, "3.99", None, "3.99", 3.99), 580 | Example(None, "£ 165.00", "£", "165.00", 165.00), 581 | Example(None, "$844,900", "$", "844,900", 844900), 582 | Example(None, "140,00 pln", "pln", "140,00", 140), 583 | Example(None, "2,99 €", "€", "2,99", 2.99), 584 | Example(None, "145,00 lei", "lei", "145,00", 145), 585 | Example(None, "3,90 €", "€", "3,90", 3.90), 586 | Example(None, "$149.99", "$", "149.99", 149.99), 587 | Example(None, "4,30 €", "€", "4,30", 4.30), 588 | Example(None, "$36.95", "$", "36.95", 36.95), 589 | Example(None, "2 499,00 zł", "zł", "2 499,00", 2499), 590 | Example(None, "800 руб.", "руб.", "800", 800), 591 | Example(None, "89.00", None, "89.00", 89), 592 | Example(None, "3 100р.", "р.", "3 100", 3100), 593 | Example(None, "0,85 €", "€", "0,85", 0.85), 594 | Example(None, "35,95 €", "€", "35,95", 35.95), 595 | Example(None, "$0.42", "$", "0.42", 0.42), 596 | Example(None, "80,000 تومان", "تومان", "80,000", 80000), 597 | Example(None, "550,00 € *", "€", "550,00", 550), 598 | Example(None, "25,00 zł", "zł", "25,00", 25), 599 | Example(None, "17,45 EUR", "EUR", "17,45", 17.45), 600 | Example(None, "49,00 €", "€", "49,00", 49), 601 | Example(None, "169.00", None, "169.00", 169), 602 | Example(None, "8,99 €", "€", "8,99", 8.99), 603 | Example(None, "1 099 Kč", "Kč", "1 099", 1099), 604 | Example(None, "17.99", None, "17.99", 17.99), 605 | Example(None, "$274.95", "$", "274.95", 274.95), 606 | Example(None, "70,20 €", "€", "70,20", 70.20), 607 | Example(None, "289,00 zł", "zł", "289,00", 289), 608 | Example(None, "18,00 €", "€", "18,00", 18), 609 | Example(None, "12,00 €", "€", "12,00", 12), 610 | Example(None, "$19.97", "$", "19.97", 19.97), 611 | # Example(None, '(save $2.95)', 612 | # '$', None, None), 613 | Example(None, "749,00 euro", "euro", "749,00", 749), 614 | Example(None, "$48.25", "$", "48.25", 48.25), 615 | Example(None, "5.00", None, "5.00", 5.00), 616 | Example(None, "18,00 € *", "€", "18,00", 18), 617 | Example(None, "$3.00", "$", "3.00", 3.00), 618 | Example(None, "1,85 EUR", "EUR", "1,85", 1.85), 619 | Example(None, "4.25", None, "4.25", 4.25), 620 | Example(None, "£1.20", "£", "1.20", 1.20), 621 | Example(None, "$196.50", "$", "196.50", 196.50), 622 | Example(None, "Price: $129.00", "$", "129.00", 129.00), 623 | Example(None, "179,00 €", "€", "179,00", 179.00), 624 | Example(None, "$80.00", "$", "80.00", 80.00), 625 | Example(None, "14.50", None, "14.50", 14.50), 626 | Example(None, "From $ 24.95", "$", "24.95", 24.95), 627 | Example(None, "$5.11", "$", "5.11", 5.11), 628 | Example(None, "EUR 6,99", "EUR", "6,99", 6.99), 629 | Example(None, "40% OFF", None, None, None), 630 | Example(None, "29.99", None, "29.99", 29.99), 631 | Example(None, "14.00€", "€", "14.00", 14.00), 632 | Example(None, "22.00", None, "22.00", 22.00), 633 | Example(None, "$1000.00", "$", "1000.00", 1000.00), 634 | Example(None, "$12.95", "$", "12.95", 12.95), 635 | Example(None, "běžná cena 987,20 Kč", "Kč", "987,20", 987.20), 636 | Example(None, "104,64 zł", "zł", "104,64", 104.64), 637 | Example(None, "163,80 €", "€", "163,80", 163.80), 638 | Example(None, "$89.00", "$", "89.00", 89.00), 639 | Example(None, "1 600 руб.", "руб.", "1 600", 1600), 640 | Example(None, "20,95 € *", "€", "20,95", 20.95), 641 | Example(None, "9,50 €", "€", "9,50", 9.50), 642 | Example(None, "170,00 €", "€", "170,00", 170), 643 | Example(None, "170,00€", "€", "170,00", 170), 644 | Example(None, "6.00", None, "6.00", 6.00), 645 | Example(None, "$24.00", "$", "24.00", 24.00), 646 | Example(None, "9.95", None, "9.95", 9.95), 647 | Example(None, "34.12 (40.94 inc VAT)", None, "34.12", 34.12), 648 | Example(None, "Rp 350.000", "Rp", "350.000", 350000), 649 | Example(None, "$55.00", "$", "55.00", 55.00), 650 | Example(None, "$595.00", "$", "595.00", 595.00), 651 | Example(None, "7,00 €", "€", "7,00", 7), 652 | Example(None, "119.95", None, "119.95", 119.95), 653 | Example(None, "1.95", None, "1.95", 1.95), 654 | Example(None, "390,00 €", "€", "390,00", 390), 655 | Example(None, "3.24", None, "3.24", 3.24), 656 | Example(None, "12 590 Kč", "Kč", "12 590", 12590), 657 | Example(None, "330 Kč", "Kč", "330", 330), 658 | Example(None, "8 500 руб.", "руб.", "8 500", 8500), 659 | Example(None, "589,00 €", "€", "589,00", 589), 660 | Example(None, "1,099.99", None, "1,099.99", 1099.99), 661 | Example(None, "14 196 Р", "Р", "14 196", 14196), 662 | Example(None, "19.00", None, "19.00", 19.00), 663 | Example(None, "870 Kč", "Kč", "870", 870), 664 | Example(None, "59,00 €", "€", "59,00", 59), 665 | Example(None, "Pris från 172 kr", "kr", "172", 172), 666 | Example(None, "1 573 Kč", "Kč", "1 573", 1573), 667 | Example(None, "$2.99", "$", "2.99", 2.99), 668 | Example(None, "13,90 €", "€", "13,90", 13.90), 669 | Example(None, "29.95", None, "29.95", 29.95), 670 | Example(None, "/", None, None, None), 671 | Example(None, "16,90 €", "€", "16,90", 16.90), 672 | Example(None, "149.95", None, "149.95", 149.95), 673 | Example(None, "8.90", None, "8.90", 8.90), 674 | Example(None, "419", None, "419", 419), 675 | Example(None, "$50.00", "$", "50.00", 50.00), 676 | Example(None, "3 291,00 €", "€", "3 291,00", 3291), 677 | Example(None, "13,00 €", "€", "13,00", 13), 678 | Example(None, "DKK 449,00", "DKK", "449,00", 449), 679 | Example(None, "$20.00", "$", "20.00", 20.00), 680 | Example(None, "$154", "$", "154", 154), 681 | Example(None, "22.48", None, "22.48", 22.48), 682 | Example(None, "20,00 EUR", "EUR", "20,00", 20), 683 | Example(None, "73,460 €", "€", "73,460", 73460), 684 | Example(None, "850 руб", "руб", "850", 850), 685 | Example(None, "$14.99", "$", "14.99", 14.99), 686 | Example(None, "$79.95", "$", "79.95", 79.95), 687 | Example(None, "40,00 €", "€", "40,00", 40), 688 | Example(None, "149,98 €", "€", "149,98", 149.98), 689 | Example(None, "1 150 грн.", "грн.", "1 150", 1150), 690 | Example(None, "399.00", None, "399.00", 399.00), 691 | Example(None, "33,90 €", "€", "33,90", 33.90), 692 | Example(None, "79,50 €", "€", "79,50", 79.50), 693 | Example(None, "40 130", None, "40 130", 40130), 694 | Example(None, "$69.99", "$", "69.99", 69.99), 695 | Example(None, "1 090 Kč", "Kč", "1 090", 1090), 696 | Example(None, "395 Kč", "Kč", "395", 395), 697 | Example(None, "53,95 €", "€", "53,95", 53.95), 698 | Example(None, "£0.99", "£", "0.99", 0.99), 699 | Example(None, "5,60 € *", "€", "5,60", 5.60), 700 | Example(None, "29,50 zł", "zł", "29,50", 29.50), 701 | Example(None, "2 990", None, "2 990", 2990), 702 | Example(None, "0,00", None, "0,00", 0), 703 | Example(None, "$24.99 with card", "$", "24.99", 24.99), 704 | Example(None, "18,00€", "€", "18,00", 18), 705 | Example(None, "€600,00", "€", "600,00", 600), 706 | Example(None, "£25.00 (tax incl.)", "£", "25.00", 25), 707 | Example(None, "8,55 €", "€", "8,55", 8.55), 708 | Example(None, "1,422.50", None, "1,422.50", 1422.50), 709 | Example(None, "244,00 €", "€", "244,00", 244.00), 710 | Example(None, "12,90 €", "€", "12,90", 12.90), 711 | Example(None, "12 900,00 руб", "руб", "12 900,00", 12900), 712 | Example(None, "1.727 Ft", "Ft", "1.727", 1727), 713 | Example(None, "79,00 €", "€", "79,00", 79), 714 | Example(None, "NZD $100.70", "NZD", "100.70", 100.70), 715 | Example(None, "479.00", None, "479.00", 479.00), 716 | Example(None, "$ 69.00", "$", "69.00", 69.00), 717 | Example(None, "135,00 €", "€", "135,00", 135.00), 718 | Example(None, "25.00", None, "25.00", 25.0), 719 | Example(None, "94,90 €", "€", "94,90", 94.90), 720 | Example(None, "149.99", None, "149.99", 149.99), 721 | Example(None, "44,00 €", "€", "44,00", 44.00), 722 | Example(None, "$24.99", "$", "24.99", 24.99), 723 | Example(None, "22,00 EUR", "EUR", "22,00", 22.00), 724 | Example(None, "89,90 €", "€", "89,90", 89.90), 725 | Example(None, "$24.95", "$", "24.95", 24.95), 726 | Example(None, "£ 1.99", "£", "1.99", 1.99), 727 | Example(None, "1 099,00 zł", "zł", "1 099,00", 1099), 728 | Example(None, "běžná cena 28 270,00 Kč", "Kč", "28 270,00", 28270), 729 | Example(None, "da € 72.00", "€", "72.00", 72.00), 730 | Example(None, "$15.95", "$", "15.95", 15.95), 731 | Example(None, "تومان56,000", "تومان", "56,000", 56000), 732 | Example(None, "$1,695.00", "$", "1,695.00", 1695.00), 733 | Example(None, "£595.00", "£", "595.00", 595.00), 734 | Example(None, "$11.95", "$", "11.95", 11.95), 735 | Example(None, "290,00 Kč", "Kč", "290,00", 290), 736 | Example(None, "199.90 Fr.", "Fr.", "199.90", 199.90), 737 | Example(None, "197 PLN", "PLN", "197", 197), 738 | Example(None, "9.99", None, "9.99", 9.99), 739 | Example(None, "$56.00", "$", "56.00", 56.00), 740 | Example(None, "4 980 Kč", "Kč", "4 980", 4980), 741 | Example(None, "124,00 €", "€", "124,00", 124), 742 | Example(None, "$104.99", "$", "104.99", 104.99), 743 | Example(None, "39,00 €", "€", "39,00", 39), 744 | Example(None, "1 029,00 €", "€", "1 029,00", 1029), 745 | Example(None, "Běžná cena 299,00 Kč", "Kč", "299,00", 299), 746 | Example(None, "745,00 €", "€", "745,00", 745), 747 | Example(None, "$89.00", "$", "89.00", 89), 748 | Example(None, "$29.95", "$", "29.95", 29.95), 749 | Example(None, "2.00", None, "2.00", 2.00), 750 | Example(None, "249.99", None, "249.99", 249.99), 751 | Example(None, "24.99", None, "24.99", 24.99), 752 | Example(None, "1 499 Kč", "Kč", "1 499", 1499), 753 | Example(None, "199,95 €", "€", "199,95", 199.95), 754 | Example(None, "6,00 €", "€", "6,00", 6), 755 | Example(None, "$28.49", "$", "28.49", 28.49), 756 | Example(None, "200.000 đ", "đ", "200.000", 200000), 757 | Example(None, "9,24 €", "€", "9,24", 9.24), 758 | Example(None, "48,00 €", "€", "48,00", 48.00), 759 | Example(None, "Cena : 890 Kč", "Kč", "890", 890), 760 | Example(None, "790.00", None, "790.00", 790.00), 761 | Example(None, "17 260 руб.", "руб.", "17 260", 17260), 762 | Example(None, "227,000 تومان", "تومان", "227,000", 227000), 763 | Example(None, "295,88 €", "€", "295,88", 295.88), 764 | Example(None, "£1399", "£", "1399", 1399), 765 | Example(None, "11,33 Br", "Br", "11,33", 11.33), 766 | Example(None, "325.95", None, "325.95", 325.95), 767 | Example(None, "$19.50", "$", "19.50", 19.50), 768 | Example(None, "19,00 €", "€", "19,00", 19), 769 | Example(None, "2 999,00 €", "€", "2 999,00", 2999), 770 | Example(None, "49.95", None, "49.95", 49.95), 771 | Example(None, "99 LEI", "LEI", "99", 99), 772 | Example(None, "249 Kč", "Kč", "249", 249), 773 | Example(None, "3.79", None, "3.79", 3.79), 774 | Example(None, "běžná cena 890 Kč", "Kč", "890", 890), 775 | Example(None, "$809,000", "$", "809,000", 809000), 776 | Example(None, "450 000 ₫", "₫", "450 000", 450000), 777 | Example(None, "30,00 €", "€", "30,00", 30.00), 778 | Example(None, "14.95", None, "14.95", 14.95), 779 | Example(None, "12.50", None, "12.50", 12.50), 780 | Example(None, "129,00 € (-15%)", "€", "129,00", 129.00), 781 | Example(None, "12,90 €", "€", "12,90", 12.90), 782 | Example(None, "A partir de 11,70 €", "€", "11,70", 11.70), 783 | Example(None, "15.49", None, "15.49", 15.49), 784 | Example(None, "12.34 €", "€", "12.34", 12.34), 785 | Example(None, "€799,00", "€", "799,00", 799), 786 | Example(None, "230 лв.", "лв.", "230", 230), 787 | Example(None, "14.55 €", "€", "14.55", 14.55), 788 | Example(None, "133,86 LEI", "LEI", "133,86", 133.86), 789 | Example(None, "7 990,00 Kč", "Kč", "7 990,00", 7990), 790 | Example(None, "350.00", None, "350.00", 350.00), 791 | Example(None, "Cena: 55,72 zł brutto", "zł", "55,72", 55.72), 792 | Example(None, "O blenderach Omniblend", None, None, None), 793 | Example(None, "3,822.00", None, "3,822.00", 3822), 794 | Example(None, "0,15 €", "€", "0,15", 0.15), 795 | Example(None, "430,00 €", "€", "430,00", 430), 796 | Example(None, "$29.00", "$", "29.00", 29.00), 797 | Example(None, "39.99", None, "39.99", 39.99), 798 | Example(None, "$15.00", "$", "15.00", 15.00), 799 | Example(None, "21,00 Lei", "Lei", "21,00", 21.00), 800 | Example(None, "naše cena 250,00 Kč", "Kč", "250,00", 250.00), 801 | Example(None, "$24.95", "$", "24.95", 24.95), 802 | Example(None, "162.18", None, "162.18", 162.18), 803 | Example(None, "39,60 EUR", "EUR", "39,60", 39.60), 804 | Example(None, "10,75 €", "€", "10,75", 10.75), 805 | Example(None, "219 руб.", "руб.", "219", 219), 806 | Example(None, "89,00 € *", "€", "89,00", 89.00), 807 | Example(None, "151,200 تومان", "تومان", "151,200", 151200), 808 | Example(None, "$159.99", "$", "159.99", 159.99), 809 | Example(None, "2.49", None, "2.49", 2.49), 810 | Example(None, "7.38", None, "7.38", 7.38), 811 | Example(None, "62,00 zł", "zł", "62,00", 62.00), 812 | Example(None, "$20.00", "$", "20.00", 20), 813 | Example(None, "$ 50.00", "$", "50.00", 50), 814 | Example(None, "34.99", None, "34.99", 34.99), 815 | Example(None, "318,00 €", "€", "318,00", 318), 816 | Example(None, "11.499,00 EUR", "EUR", "11.499,00", 11499), 817 | Example(None, "571.12坪", None, "571.12", 571.12), # area, not currency 818 | Example(None, "€ 75.00", "€", "75.00", 75.00), 819 | Example(None, "11,90 € *", "€", "11,90", 11.90), 820 | Example(None, "€0.51", "€", "0.51", 0.51), 821 | Example(None, "6,50 €", "€", "6,50", 6.50), 822 | Example(None, "790 Kč", "Kč", "790", 790), 823 | Example(None, "ab 2.99 €", "€", "2.99", 2.99), 824 | Example(None, "369", None, "369", 369), 825 | Example(None, "134.96", None, "134.96", 134.96), 826 | Example(None, "135 lei", "lei", "135", 135), 827 | Example(None, "2,99 € *", "€", "2,99", 2.99), 828 | Example(None, "$9.99", "$", "9.99", 9.99), 829 | Example(None, "2.950,00 €", "€", "2.950,00", 2950), 830 | Example(None, "19.99", None, "19.99", 19.99), 831 | Example(None, "49 lei", "lei", "49", 49), 832 | Example(None, "31,07 € (bez DPH)", "€", "31,07", 31.07), 833 | Example(None, "56.00", None, "56.00", 56.00), 834 | Example(None, "54.95", None, "54.95", 54.95), 835 | Example(None, "$ 80.00", "$", "80.00", 80.00), 836 | Example(None, "$39.00", "$", "39.00", 39.00), 837 | Example(None, "Rp 221.000", "Rp", "221.000", 221000), 838 | Example(None, "35,90 EUR", "EUR", "35,90", 35.90), 839 | Example(None, "4 835,50 €", "€", "4 835,50", 4835.50), 840 | Example(None, "75,00€", "€", "75,00", 75), 841 | Example(None, "$21.95", "$", "21.95", 21.95), 842 | Example(None, "737,00", None, "737,00", 737), 843 | Example(None, "129,00 € **", "€", "129,00", 129), 844 | Example(None, "2 399 Kč", "Kč", "2 399", 2399), 845 | Example(None, "430 руб", "руб", "430", 430), 846 | Example(None, "69.95", None, "69.95", 69.95), 847 | Example(None, "$0.00", "$", "0.00", 0), 848 | Example(None, "49.56", None, "49.56", 49.56), 849 | Example(None, "0,00 EUR", "EUR", "0,00", 0), 850 | Example(None, "145,00 Kč", "Kč", "145,00", 145), 851 | Example(None, "99,00 lei", "lei", "99,00", 99), 852 | Example(None, "$750,000", "$", "750,000", 750000), 853 | Example(None, "$49.99", "$", "49.99", 49.99), 854 | Example(None, "29.00", None, "29.00", 29.00), 855 | Example(None, "$7.20", "$", "7.20", 7.20), 856 | Example(None, "69.00", None, "69.00", 69.00), 857 | Example(None, "4.47", None, "4.47", 4.47), 858 | Example(None, "39,90 € *", "€", "39,90", 39.90), 859 | Example(None, "469,00 €", "€", "469,00", 469), 860 | Example(None, "24.38", None, "24.38", 24.38), 861 | Example(None, "6,24", None, "6,24", 6.24), 862 | Example(None, "$89.00", "$", "89.00", 89.00), 863 | Example(None, "24,35 €", "€", "24,35", 24.35), 864 | Example(None, "Pris från 805 kr", "kr", "805", 805), 865 | Example(None, "295 Kč", "Kč", "295", 295), 866 | Example(None, "175.00", None, "175.00", 175.00), 867 | Example(None, "7 990 kr", "kr", "7 990", 7990), 868 | Example(None, "14,00 €", "€", "14,00", 14), 869 | Example(None, "249 Kč", "Kč", "249", 249), 870 | Example(None, "£39.95", "£", "39.95", 39.95), 871 | Example(None, "10,75 TL", "TL", "10,75", 10.75), 872 | Example(None, "$25.00", "$", "25.00", 25.00), 873 | Example(None, "1 720,00 zł", "zł", "1 720,00", 1720), 874 | Example(None, "běžná cena 749 Kč", "Kč", "749", 749), 875 | Example(None, "425,00 €", "€", "425,00", 425), 876 | Example(None, "59.00", None, "59.00", 59.00), 877 | Example(None, "1,120.00", None, "1,120.00", 1120), 878 | Example(None, "a partire da 7,32 € *", "€", "7,32", 7.32), 879 | Example(None, "148.50 Inc GST", None, "148.50", 148.50), # India? 880 | Example(None, "7.49", None, "7.49", 7.49), 881 | Example(None, "80.00", None, "80.00", 80.00), 882 | Example(None, "93 499 Kč", "Kč", "93 499", 93499), 883 | Example(None, "1.599,00 € *", "€", "1.599,00", 1599), 884 | Example(None, "ab 3,63 EUR", "EUR", "3,63", 3.63), 885 | Example(None, "29,90 EUR", "EUR", "29,90", 29.90), 886 | Example(None, "$3.95", "$", "3.95", 3.95), 887 | Example(None, "3430 лв.", "лв.", "3430", 3430), 888 | Example(None, "724,00 €", "€", "724,00", 724), 889 | Example(None, "18,00 €", "€", "18,00", 18), 890 | Example(None, "6,75 €", "€", "6,75", 6.75), 891 | Example(None, "29,90 € *", "€", "29,90", 29.90), 892 | Example(None, "135.99", None, "135.99", 135.99), 893 | Example(None, "30,000 تومان", "تومان", "30,000", 30000), 894 | # Example(None, '191.6 KB', 895 | # None, None, None), 896 | Example(None, "1 500 Kč", "Kč", "1 500", 1500), 897 | Example(None, "349,00 €", "€", "349,00", 349), 898 | Example(None, "$250.00", "$", "250.00", 250.00), 899 | Example(None, "44.95", None, "44.95", 44.95), 900 | Example(None, "$22.75", "$", "22.75", 22.75), 901 | Example(None, "250,00 €", "€", "250,00", 250), 902 | Example(None, "14.96 €", "€", "14.96", 14.96), 903 | Example(None, "$4,350.00", "$", "4,350.00", 4350), 904 | Example(None, "379 Kč", "Kč", "379", 379), 905 | Example(None, "19,50 EUR", "EUR", "19,50", 19.5), 906 | Example(None, "33,68 zł", "zł", "33,68", 33.68), 907 | Example(None, "6.70€", "€", "6.70", 6.70), 908 | Example(None, "$29.99", "$", "29.99", 29.99), 909 | Example(None, "6.50", None, "6.50", 6.50), 910 | ] 911 | 912 | 913 | PRICE_PARSING_EXAMPLES_3 = [ 914 | Example("R$", "R$ 139,99 R$ 135,99", "R$", "139,99", 139.99), 915 | Example("£", "£ 34.99", "£", "34.99", 34.99), 916 | Example("Price", "$7.65", "$", "7.65", 7.65), 917 | Example("€", "75,00", "€", "75,00", 75), 918 | Example(None, "34,90 €", "€", "34,90", 34.90), 919 | Example(None, "629.95", None, "629.95", 629.95), 920 | Example("11000 руб.", "47700 руб.", "руб.", "47700", 47700), 921 | Example("$29.99 – $74.99", "$29.99", "$", "29.99", 29.99), 922 | Example(None, "174,00 €", "€", "174,00", 174), 923 | Example(None, "18,00 €", "€", "18,00", 18), 924 | Example("Price:", "$19.50", "$", "19.50", 19.50), 925 | Example("8 390 руб.", "8 390 руб.", "руб.", "8 390", 8390), 926 | Example(None, "55,00 €", "€", "55,00", 55), 927 | Example("€333.00", "€299.71", "€", "299.71", 299.71), 928 | Example(None, "384,00 €", "€", "384,00", 384), 929 | Example("From:", "From: $14.97", "$", "14.97", 14.97), 930 | Example(None, "0,00 €", "€", "0,00", 0), 931 | Example(None, "€ 280,00", "€", "280,00", 280), 932 | Example(None, "11 450 Kč", "Kč", "11 450", 11450), 933 | Example("Price $118.15", "$118.15", "$", "118.15", 118.15), 934 | Example("€", "49.99", "€", "49.99", 49.99), 935 | Example("1,15 €", "1,15 €", "€", "1,15", 1.15), 936 | Example("17,99 €", "31,93 €", "€", "31,93", 31.93), 937 | Example(None, "र24,401", "र", "24,401", 24401), 938 | Example(None, "$60.00", "$", "60.00", 60), 939 | Example("12,15 €", "12,15 €", "€", "12,15", 12.15), 940 | Example("£ 163.95", "163.95", "£", "163.95", 163.95), 941 | Example(None, "30,00 €", "€", "30,00", 30), 942 | Example("zł", "165,00 zł", "zł", "165,00", 165), 943 | Example("469.00zł Bez podatku: 381.30zł", "469.00zł", "zł", "469.00", 469), 944 | Example(None, "72.95", None, "72.95", 72.95), 945 | Example("Costo: $2,222.- Más IVA", "MX$3,179.00", "MX$", "3,179.00", 3179), 946 | Example(None, "naše cena 4 370 Kč", "Kč", "4 370", 4370), 947 | Example("€", "21,33 €", "€", "21,33", 21.33), 948 | Example(None, "49.95", None, "49.95", 49.95), 949 | Example(None, "Před slevou 59 900 Kč", "Kč", "59 900", 59900), 950 | Example("ab", "6,78 € *", "€", "6,78", 6.78), 951 | Example(None, "442", None, "442", 442), 952 | Example("18.10 €", "16.00 €", "€", "16.00", 16.00), 953 | Example(None, "0.00", None, "0.00", 0.00), 954 | Example(None, "379,00 € *", "€", "379,00", 379.00), 955 | Example(None, "125.00", None, "125.00", 125.00), 956 | Example(None, "£ 30.84", "£", "30.84", 30.84), 957 | Example(None, "259,00 €", "€", "259,00", 259), 958 | Example("à partir de 1540 € / pers", "1540 €", "€", "1540", 1540), 959 | Example(None, "95 €", "€", "95", 95), 960 | Example(None, "53.79", None, "53.79", 53.79), 961 | Example("NT$", "NT$ 1,160", "NT$", "1,160", 1160), 962 | Example("ACTIVE", "$69,900", "$", "69,900", 69900), 963 | Example(None, "$14.95", "$", "14.95", 14.95), 964 | Example("₹", "₹ 4649", "₹", "4649", 4649), 965 | Example("25 грн", "25 грн", "грн", "25", 25), 966 | Example("€", "16,40", "€", "16,40", 16.40), 967 | Example("PLN", "0,46", "PLN", "0,46", 0.46), 968 | Example("£", "£ 261.25", "£", "261.25", 261.25), 969 | Example(None, "$0.00", "$", "0.00", 0), 970 | Example(None, "24.95", None, "24.95", 24.95), 971 | Example("грн.", "27.00", "грн.", "27.00", 27.00), 972 | Example("New", "$189,900", "$", "189,900", 189900), 973 | Example("NA", "$269", "$", "269", 269), 974 | Example("$279", "$189", "$", "189", 189), 975 | Example(None, "160,00 zł", "zł", "160,00", 160), 976 | Example("2 069 рублей", "2 400", "руб", "2 400", 2400), 977 | Example("Sale Price: $4.59", "$4.59", "$", "4.59", 4.59), 978 | Example("Купить", "542 руб.", "руб.", "542", 542), 979 | Example(None, "$19.99", "$", "19.99", 19.99), 980 | Example("Price", "$6.45", "$", "6.45", 6.45), 981 | Example(None, "32.99", None, "32.99", 32.99), 982 | Example(None, "$86.44", "$", "86.44", 86.44), 983 | Example(None, "25.00€", "€", "25.00", 25.00), 984 | Example(None, "99,00 €", "€", "99,00", 99.00), 985 | Example(None, "103.90", None, "103.90", 103.90), 986 | Example("14,00 € *", "25,00 € *", "€", "25,00", 25.00), 987 | Example(None, "$6.49", "$", "6.49", 6.49), 988 | Example("€ 59,95", "€ 59,95", "€", "59,95", 59.95), 989 | Example(None, "Běžná cena 75 990,00 Kč", "Kč", "75 990,00", 75990), 990 | Example("Price", "Rp 1.550.000", "Rp", "1.550.000", 1550000), 991 | Example("грн.", "1 430", "грн.", "1 430", 1430), 992 | Example("руб. (шт)", "1 690,54 руб. (шт)", "руб.", "1 690,54", 1690.54), 993 | Example("69 TL 41.90 TL", "69 TL 41.90 TL", "TL", "69", 69), 994 | Example("ALIDAD", "960,00 €", "€", "960,00", 960), 995 | Example(None, "184,35 lei", "lei", "184,35", 184.35), 996 | Example(None, "1 505 Kč", "Kč", "1 505", 1505), 997 | Example(None, "23,00 € *", "€", "23,00", 23), 998 | Example(None, "25.97", None, "25.97", 25.97), 999 | Example(None, "58,19 €", "€", "58,19", 58.19), 1000 | Example(None, "27.00 лв.", "лв.", "27.00", 27.00), 1001 | Example("48,00 €", "3,85 €", "€", "3,85", 3.85), 1002 | Example(None, "10,90 €", "€", "10,90", 10.90), 1003 | Example("$ 879.0", "$ 879.0", "$", "879.0", 879.0), 1004 | Example("EUR", "25.88", "EUR", "25.88", 25.88), 1005 | Example(None, "R$215,10", "R$", "215,10", 215.10), 1006 | Example("£", "£ 12.50", "£", "12.50", 12.50), 1007 | Example(None, "3 173,00 €", "€", "3 173,00", 3173), 1008 | Example(None, "34,94 € *", "€", "34,94", 34.94), 1009 | Example(None, "Ops!", None, None, None), 1010 | Example(None, "392. 00", None, "392. 00", 392), 1011 | Example("€", "213,62", "€", "213,62", 213.62), 1012 | Example("3,00 €", "3,00 €", "€", "3,00", 3), 1013 | Example("£0.00", "£0.00", "£", "0.00", 0.00), 1014 | Example("€", "10 990,00", "€", "10 990,00", 10990), 1015 | Example(None, "€ 24,95", "€", "24,95", 24.95), 1016 | Example(None, "Not Available", None, None, None), 1017 | Example(None, "$19.99", "$", "19.99", 19.99), 1018 | Example("Р", "15 130 Р", "Р", "15 130", 15130), 1019 | Example("$5.95", "$5.95", "$", "5.95", 5.95), 1020 | Example(None, "199,99 €", "€", "199,99", 199.99), 1021 | Example("Code", "£23.40", "£", "23.40", 23.40), 1022 | Example("$29.99", "$29.99", "$", "29.99", 29.99), 1023 | Example(None, "795", None, "795", 795), 1024 | Example( 1025 | ( 1026 | "Sorry, this item is currently out of stock but you can still" 1027 | " order, we will send as soon a product arrives" 1028 | ), 1029 | "34.99", 1030 | None, 1031 | "34.99", 1032 | 34.99, 1033 | ), 1034 | Example("Our Price: $149.95", "Our Price: $149.95", "$", "149.95", 149.95), 1035 | Example("$119.95", "$119.95", "$", "119.95", 119.95), 1036 | Example(None, "339 грн", "грн", "339", 339), 1037 | Example("$0.00", "$0.00", "$", "0.00", 0.00), 1038 | Example("€", "79,00", "€", "79,00", 79.00), 1039 | Example(None, "378.00", None, "378.00", 378.00), 1040 | Example(None, "Pure & IP BP Ph. Eur. USP ACS AR LR", None, None, None), 1041 | Example(None, "$356.03", "$", "356.03", 356.03), 1042 | Example("naše cena", "běžná cena 890 Kč", "Kč", "890", 890), 1043 | Example(None, "$49.99", "$", "49.99", 49.99), 1044 | Example(None, "5 550 Kč", "Kč", "5 550", 5550), 1045 | Example(None, "5 770 Kč", "Kč", "5 770", 5770), 1046 | Example(None, "Free!", None, "0", 0), 1047 | Example("194 ₹", "199 ₹", "₹", "199", 199), 1048 | Example("5€", "16,50 € *", "€", "16,50", 16.50), 1049 | Example(None, "$42.95", "$", "42.95", 42.95), 1050 | Example(None, "1.837, 32 €", "€", "1.837, 32", 1837.32), 1051 | Example("$", "$ 791.00 $ 479.00", "$", "791.00", 791.00), 1052 | Example(None, "$69.30", "$", "69.30", 69.30), 1053 | Example(None, "$163,900", "$", "163,900", 163900), 1054 | Example(None, "36.95", None, "36.95", 36.95), 1055 | Example("Rp 235.000", "Rp 235.000", "Rp", "235.000", 235000), 1056 | Example("£", "11,13 €", "€", "11,13", 11.13), 1057 | Example(None, "160,00 lei", "lei", "160,00", 160), 1058 | Example("3 300 руб", "3 300 руб", "руб", "3 300", 3300), 1059 | Example("Р", "4 690 Р", "Р", "4 690", 4690), 1060 | Example("189,00 € *", "189,00 € *", "€", "189,00", 189), 1061 | Example("€", None, "€", None, None), 1062 | Example("$ 30.00", "$ 30.00", "$", "30.00", 30.00), 1063 | Example("$", "$ 5.95", "$", "5.95", 5.95), 1064 | Example("£62.90", "£74.00", "£", "74.00", 74.00), 1065 | Example(None, "158,24 €", "€", "158,24", 158.24), 1066 | Example(None, "550,00 лв", "лв", "550,00", 550), 1067 | Example("7,25 € *", "7,25 € *", "€", "7,25", 7.25), 1068 | Example(None, "94,000 تومان", "تومان", "94,000", 94000), 1069 | Example(None, "$8.27", "$", "8.27", 8.27), 1070 | Example("Đã có VAT", "12.500 ₫", "₫", "12.500", 12500), 1071 | Example(None, "27.50", None, "27.50", 27.50), 1072 | Example("23.90", "23.90", None, "23.90", 23.90), 1073 | Example("Р", "18 000 Р", "Р", "18 000", 18000), 1074 | Example(None, "48,96 €", "€", "48,96", 48.96), 1075 | Example("DKK", "199 DKK", "DKK", "199", 199), 1076 | Example("Price: £6.95 - £9.95", "£6.95 - £9.95", "£", "6.95", 6.95), 1077 | Example(None, "599.97", None, "599.97", 599.97), 1078 | Example(None, "$40.00", "$", "40.00", 40.00), 1079 | Example("Cena 300,00 Kč", "100,00 Kč", "Kč", "100,00", 100), 1080 | Example("18,25 €", "18,25 €", "€", "18,25", 18.25), 1081 | Example(None, "29,00 €", "€", "29,00", 29), 1082 | Example("€", "€ 39,95", "€", "39,95", 39.95), 1083 | Example(None, "32.00", None, "32.00", 32.00), 1084 | Example(None, "32.99", None, "32.99", 32.99), 1085 | Example("HUF", "39000", "HUF", "39000", 39000), 1086 | Example(None, "850,000 ریال", "ریال", "850,000", 850000), 1087 | Example(None, "24,00 €", "€", "24,00", 24.00), 1088 | Example("Versand", "CHF 19.90", "CHF", "19.90", 19.90), 1089 | Example("", "530,42 Zł", "Zł", "530,42", 530.42), 1090 | Example(None, "Was: $124.95 Now: $0.00", "$", "124.95", 124.95), 1091 | Example(None, "Our Price 344.99 - 444.99", None, "344.99", 344.99), 1092 | Example(None, "3089.9 *", None, "3089.9", 3089.9), 1093 | Example(None, "40.00/each", None, "40.00", 40.00), 1094 | Example(None, "105. –", None, "105.", 105.0), 1095 | Example(None, "1.899,-", None, "1.899", 1899.0), 1096 | Example(None, "Sheet Set", None, None, None), 1097 | Example(None, "36. 24", None, "36. 24", 36.24), 1098 | Example(None, "32جن", None, "32", 32.0), # جن is not a currency 1099 | Example(None, "Price 29", None, "29", 29.0), 1100 | Example( 1101 | None, 1102 | "559. 99 undefined 559.99 inkl. MwSt. zzgl. Versand", 1103 | None, 1104 | "559. 99", 1105 | 559.99, 1106 | ), 1107 | Example(None, "$36 /", "$", "36", 36.0), 1108 | Example(None, "1.800.000₫", "₫", "1.800.000", 1800000.0), # Vietnamese dong (VND) 1109 | Example(None, "0,40 LEI", "LEI", "0,40", 0.4), # Romanian Leu (RON) 1110 | Example(None, "R$ 1.500,00", "R$", "1.500,00", 1500.0), 1111 | Example(None, "$ 8. 94", "$", "8. 94", 8.94), 1112 | Example(None, "$30.56/Month", "$", "30.56", 30.56), 1113 | Example(None, "from $742.50", "$", "742.50", 742.5), 1114 | Example(None, "25,00 € *", "€", "25,00", 25.0), 1115 | Example(None, "$3,690.00 (ea)", "$", "3,690.00", 3690.0), 1116 | Example(None, "AED 26.30", "AED", "26.30", 26.3), 1117 | Example(None, "15,000 руб.", "руб.", "15,000", 15000.0), 1118 | Example(None, "$5,246.58", "$", "5,246.58", 5246.58), 1119 | Example(None, "118, 99 TL", "TL", "118, 99", 118.99), 1120 | Example(None, "$575.00*", "$", "575.00", 575.0), 1121 | Example(None, "150 kr", "kr", "150", 150.0), 1122 | Example(None, "Estimate Price: $45,000 - $70,000", "$", "45,000", 45000.0), 1123 | Example(None, "Your Price: $7.99", "$", "7.99", 7.99), 1124 | Example(None, "$80.00 U.S.", "$", "80.00", 80.0), 1125 | Example(None, "253,89 lei", "lei", "253,89", 253.89), 1126 | Example(None, "$1,190.00 AUD", "AUD", "1,190.00", 1190.0), 1127 | Example(None, "€6.56 + VAT", "€", "6.56", 6.56), 1128 | Example(None, "4,35 € TTC", "€", "4,35", 4.35), 1129 | Example(None, "1.890,00 DKK", "DKK", "1.890,00", 1890.0), 1130 | Example(None, "₹397.00", "₹", "397.00", 397.0), 1131 | Example(None, "- $44.99", "$", "44.99", 44.99), 1132 | Example(None, "AU$ 1,175", "AU$", "1,175", 1175.0), 1133 | Example(None, "130, 38 zł", "zł", "130, 38", 130.38), 1134 | Example(None, "C$1.23", "C$", "1.23", 1.23), 1135 | Example(None, "CA$1.23", "CA$", "1.23", 1.23), 1136 | Example(None, "1 ؋", "؋", "1", 1.0), 1137 | Example(None, "1 Lek", "Lek", "1", 1.0), 1138 | Example(None, "1 ֏", "֏", "1", 1.0), 1139 | Example(None, "1 ƒ", "ƒ", "1", 1.0), 1140 | Example(None, "1 ₼", "₼", "1", 1.0), 1141 | Example(None, "1 ৳", "৳", "1", 1.0), 1142 | Example(None, "1 Br", "Br", "1", 1.0), 1143 | Example(None, "1 BZ$", "BZ$", "1", 1.0), 1144 | Example(None, "1 Nu.", "Nu.", "1", 1.0), 1145 | Example(None, "1 KM", "KM", "1", 1.0), 1146 | Example(None, "1 R$", "R$", "1", 1.0), 1147 | Example(None, "1 лв", "лв", "1", 1.0), 1148 | Example(None, "1 FBu", "FBu", "1", 1.0), 1149 | Example(None, "1 BD", "BD", "1", 1.0), # Bahraini Dinar 1150 | Example(None, "1 ៛", "៛", "1", 1.0), 1151 | Example(None, "1 CFA", "CFA", "1", 1.0), 1152 | Example(None, "1 FCFA", "FCFA", "1", 1.0), 1153 | Example(None, "1 KMF", "KMF", "1", 1.0), 1154 | Example(None, "1 ₣", "₣", "1", 1.0), 1155 | Example(None, "1 ₡", "₡", "1", 1.0), 1156 | Example(None, "1 kn", "kn", "1", 1.0), 1157 | Example(None, "1 CUC$", "CUC$", "1", 1.0), 1158 | Example(None, "1 ₱", "₱", "1", 1.0), 1159 | Example(None, "1 Kč", "Kč", "1", 1.0), 1160 | Example(None, "1 Fdj", "Fdj", "1", 1.0), 1161 | Example(None, "1 RD$", "RD$", "1", 1.0), 1162 | Example(None, "1 Nfk", "Nfk", "1", 1.0), 1163 | Example(None, "1 GH₵", "GH₵", "1", 1.0), # Ghanaian cedi 1164 | Example(None, "1 Ft", "Ft", "1", 1.0), 1165 | Example(None, "1 Rp", "Rp", "1", 1.0), 1166 | Example(None, "1 ﷼", "﷼", "1", 1.0), # Saudi riyal 1167 | Example(None, "1 ₪", "₪", "1", 1.0), 1168 | Example(None, "1 J$", "J$", "1", 1.0), 1169 | Example(None, "1 KD", "KD", "1", 1.0), # Kuwaiti dinar 1170 | Example(None, "1 ₭", "₭", "1", 1.0), # Lao kip 1171 | Example(None, "1 LD", "LD", "1", 1.0), 1172 | Example(None, "1 MOP$", "MOP$", "1", 1.0), 1173 | Example(None, "1 MK", "MK", "1", 1.0), 1174 | Example(None, "1 RM", "RM", "1", 1.0), 1175 | Example(None, "1 Rf", "Rf", "1", 1.0), 1176 | Example(None, "1 UM", "UM", "1", 1.0), 1177 | Example(None, "1 ₨", "₨", "1", 1.0), 1178 | Example(None, "1 ₮", "₮", "1", 1.0), 1179 | Example(None, "1 ƒ", "ƒ", "1", 1.0), 1180 | Example(None, "1 C$", "C$", "1", 1.0), 1181 | Example(None, "1 ₦", "₦", "1", 1.0), 1182 | Example(None, "1 B/.", "B/.", "1", 1.0), 1183 | Example(None, "1 S/.", "S/.", "1", 1.0), 1184 | Example(None, "1 ₽", "₽", "1", 1.0), 1185 | Example(None, "1 Db", "Db", "1", 1.0), 1186 | Example(None, "1 NT$", "NT$", "1", 1.0), 1187 | Example(None, "1 ЅM", None, "1", 1.0), # It's not a currency 1188 | Example(None, "1 TSh", "TSh", "1", 1.0), 1189 | Example(None, "1 T$", "T$", "1", 1.0), 1190 | Example(None, "1 TT$", "TT$", "1", 1.0), 1191 | Example(None, "1 DT", "DT", "1", 1.0), 1192 | Example(None, "1 ₺", "₺", "1", 1.0), 1193 | Example(None, "1 USh", "USh", "1", 1.0), 1194 | Example(None, "1 ₴", "₴", "1", 1.0), 1195 | Example(None, "1 $U", "$U", "1", 1.0), 1196 | Example(None, "1 VT", "VT", "1", 1.0), 1197 | Example(None, "1 Bs", "Bs", "1", 1.0), 1198 | Example(None, "1 ZK", "ZK", "1", 1.0), 1199 | Example(None, "1 Z$", "Z$", "1", 1.0), 1200 | Example(None, "1 ₿", "₿", "1", 1.0), 1201 | Example(None, "1 Br", "Br", "1", 1.0), # Ethiopian birr, Belarusian ruble 1202 | Example(None, "1 美股", None, "1", 1.0), # not a currency 1203 | Example(None, "1 GEL", "GEL", "1", 1.0), # Georgian lari 1204 | Example(None, "1 FG", "FG", "1", 1.0), # Guinean franc 1205 | Example(None, "14.00 SGD / Each", "SGD", "14.00", 14.0), # Singapore Dollar 1206 | ] 1207 | 1208 | 1209 | PRICE_PARSING_EXAMPLES_XFAIL = [ 1210 | # amount is picked as a price 1211 | Example( 1212 | "3 Ausgaben für nur 14,85 EUR", 1213 | "3 Ausgaben für nur 14,85 EUR", 1214 | "EUR", 1215 | "14,85", 1216 | 14.85, 1217 | ), 1218 | Example(None, "Buy Now - 2 Litre Was $120.00 Now $60.00", "$", "60.00", 60), 1219 | Example( 1220 | "Цена: уточняйте (мин. заказ: 1 )", 1221 | "Цена: уточняйте (мин. заказ: 1 )", 1222 | None, 1223 | None, 1224 | None, 1225 | ), 1226 | Example( 1227 | None, 1228 | "50 - $2.00 100 - $2.75 400 - $4.50 1,000 - $9.00 " 1229 | "2,000 - $17.00 3,000 - $24.00 10,000 - $75.00", 1230 | "$", 1231 | "2.00", 1232 | 2, 1233 | ), 1234 | # no detection of such single-letter currencies 1235 | Example("R273.00", "R273.00", "R", "273.00", 273), 1236 | Example("R8,499", "R8,499", "R", "8,499", 8499), 1237 | Example("Cuneo", "61.858 L", "L", "61.858", 61858), # Romanian New Leu 1238 | # "р" / "руб" is detected as currency 1239 | Example(">", "См. цену в прайсе", None, None, None), 1240 | Example("Купить", "Печная труба", None, None, None), 1241 | Example(None, "Код товара: 884", None, "884", 884.0), 1242 | # dates 1243 | Example(None, "July, 2004", None, None, None), 1244 | Example(None, "15.08.2017", None, None, None), 1245 | # other incorrectly extracted prices 1246 | Example("8.5", "25-09", None, None, None), 1247 | # misc 1248 | Example("of", "16.00 ft", None, None, None), 1249 | # Example('7 724 134.40 114.32', '7 724 134.40 114.32', 1250 | # '', ''), 1251 | # Example('中古価格(税込): ¥20,800', '132', 1252 | # '¥', '132', 132), 1253 | Example( 1254 | "Free Shipping on Orders $49+.", 1255 | "Free Shipping on Orders $49+.", 1256 | "$", 1257 | None, 1258 | None, 1259 | ), 1260 | ] 1261 | 1262 | # Valid currencies not detected by price-parser 1263 | # Move to regular tests when added 1264 | PRICE_PARSING_EXAMPLES_XFAIL_CURRENCIES_TO_BE_ADDED = [ 1265 | Example(None, "22000.00元/台", "元", "22000.00", 22000.00), # 元 is yuan 1266 | Example(None, "2963yen", "yen", "2963", 2963), 1267 | Example(None, "1 บาท", "บาท", "1", 1.0), # Thai baht 1268 | Example(None, "1 ر.س", "ر.س", "1", 1.0), # Saudi riyal 1269 | Example(None, "1.198,- Kr", "Kr", "1.198", 1198.0), 1270 | Example(None, "45 جنيه", "جنيه", "45", 45.0), # Egyptian Pound (EGP) 1271 | Example(None, "45 ج.م", "ج.م", "45", 45.0), # Egyptian Pound (EGP) 1272 | Example(None, "45 E£", "E£", "45", 45.0), # Egyptian Pound (EGP) 1273 | Example(None, "2000 zl", "zl", "2000", 2000.0), # Polish zloty (PLN) 1274 | Example(None, "CAN$1.23", "CAN$", "1.23", 1.23), 1275 | Example(None, "200q", "q", "200", 200.0), # Guatemalan Quetzal 1276 | Example(None, "200Q", "Q", "200", 200.0), # Guatemalan Quetzal 1277 | Example(None, "1 دج", "دج", "1", 1.0), # Algerian Dinar 1278 | Example(None, "1 .د.ب", ".د.ب", "1", 1.0), # Bahraini Dinar 1279 | Example(None, "1 $b", "$b", "1", 1.0), # Barbadian dollar 1280 | Example(None, "1 P", "P", "1", 1.0), # Pound sterling, Botswana pula 1281 | Example(None, "1 franc", "franc", "1", 1.0), 1282 | Example(None, "2 francs", "francs", "2", 2.0), 1283 | Example(None, "1 ናቕፋ", "ናቕፋ", "1", 1.0), # Eritrean Nakfa 1284 | Example( 1285 | None, 1286 | "1 نافكا", # Arabic "نافكا" stands for "Navka", 1287 | # which can refer to Eritrean Nakfa 1288 | "نافكا", 1289 | "1", 1290 | 1.0, 1291 | ), 1292 | Example(None, "1 E", "E", "1", 1.0), # Can refer to Egyptian pound 1293 | Example(None, "1 ብር", "ብር", "1", 1.0), # Ethiopian birr 1294 | Example(None, "1 D", "D", "1", 1.0), # Gambian dalasi 1295 | Example(None, "1 ლ", "ლ", "1", 1.0), # Georgian lari 1296 | Example(None, "1 ¢", "¢", "1", 1.0), # Ghanaian cedi, cents 1297 | Example(None, "1 ₵", "₵", "1", 1.0), # Ghanaian cedi 1298 | Example(None, "1 GFr", "GFr", "1", 1.0), # Guinean franc 1299 | Example(None, "1 L", "L", "1", 1.0), 1300 | Example(None, "1 ع.د", "ع.د", "1", 1.0), # Iraqi dinar 1301 | Example( 1302 | None, 1303 | "1 د.", # Arabic "د." stands for dr. refers to "dinar" and "dirham": 1304 | # United Arab Emirates dirham (AED), Bahraini dinar (BHD), 1305 | # Algerian dinar (DZD), etc. 1306 | "د.", 1307 | "1", 1308 | 1.0, 1309 | ), 1310 | Example(None, "1 KSh", "KSh", "1", 1.0), # Kenyan shilling 1311 | Example(None, "1 د.ك", "د.ك", "1", 1.0), # Kuwaiti dinar 1312 | Example(None, "1 ل.د", "ل.د", "1", 1.0), # Libyan dinar 1313 | Example(None, "1 ден", "ден", "1", 1.0), # Macedonian denar 1314 | Example(None, "1 ДЕН", "ДЕН", "1", 1.0), # Macedonian denar 1315 | Example(None, "1 Ar", "Ar", "1", 1.0), # Malagasy ariary 1316 | Example(None, "1 د.إ", "د.إ", "1", 1.0), # United Arab Emirates dirham 1317 | Example(None, "1 MT", "MT", "1", 1.0), # Mozambican metical 1318 | Example(None, "1 K", "K", "1", 1.0), # Papua New Guinean kina 1319 | Example(None, "1 FRw", "FRw", "1", 1.0), # Rwandan Franc 1320 | Example(None, "1 RF", "RF", "1", 1.0), # Rwandan Franc 1321 | Example(None, "1 R₣", "R₣", "1", 1.0), # Rwandan Franc 1322 | Example(None, "1 Дин", "Дин", "1", 1.0), 1323 | Example(None, "1 SPL", "SPL", "1", 1.0), # Seborgan Luigino (SPL) 1324 | Example(None, "1 ج.س.", "ج.س.", "1", 1.0), # Sudanese pound 1325 | Example(None, "1 د.ت", "د.ت", "1", 1.0), # Tunisian dinar 1326 | Example(None, "AU $59.95", "AU $", "59.95", 59.95), 1327 | Example(None, "US $1.23", "US $", "1.23", 1.23), 1328 | Example(None, "CAD$1.23", "CAD$", "1.23", 1.23), 1329 | Example(None, "1 G$", "G$", "1", 1.0), # Guyanese dollar 1330 | Example(None, "1 GY$", "GY$", "1", 1.0), # Guyanese dollar 1331 | Example(None, "1 BTC", "BTC", "1", 1.0), # Bitcoin 1332 | Example(None, "1 CHf", "CHf", "1", 1.0), # Swiss franc 1333 | Example(None, "1 Dh", "Dh", "1", 1.0), # United Arab Emirates dirham 1334 | Example(None, "1 Dhs", "Dhs", "1", 1.0), # United Arab Emirates dirham 1335 | Example(None, "1 Esc", "Esc", "1", 1.0), # Cape Verdean escudo 1336 | Example(None, "1 Fbu", "Fbu", "1", 1.0), # Burundian franc 1337 | Example(None, "1 ID", "ID", "1", 1.0), # Iraqi dinar 1338 | Example(None, "1 Ks", "Ks", "1", 1.0), # Burmese kyat 1339 | Example(None, "1 NT", "NT", "1", 1.0), # New Taiwan dollar 1340 | Example(None, "1 Nu", "Nu", "1", 1.0), # Bhutanese ngultrum 1341 | Example(None, "1 Rbl", "Rbl", "1", 1.0), # Belarusian ruble, Russian ruble 1342 | Example(None, "1 Re", "Re", "1", 1.0), # Indian rupee, Sri Lankan rupee 1343 | Example(None, "1 S/", "S/", "1", 1.0), # Peruvian sol 1344 | Example(None, "1 SFr", "SFr", "1", 1.0), # Swiss franc 1345 | Example(None, "1 Sl", "Sl", "1", 1.0), # Somaliland shilling 1346 | Example(None, "1 VNĐ", "VNĐ", "1", 1.0), # Vietnamese đồng 1347 | Example(None, "1 Vt", "Vt", "1", 1.0), # Vanuatu vatu 1348 | Example(None, "1 din", "din", "1", 1.0), # Serbian dinar 1349 | Example(None, "1 hrn", "hrn", "1", 1.0), # Ukrainian hryvnia 1350 | Example(None, "1 kwz", "kwz", "1", 1.0), # Angolan kwanza 1351 | Example(None, "1 lari", "lari", "1", 1.0), # Georgian lari 1352 | Example(None, "1 nis", "nis", "1", 1.0), # Israeli new shekel 1353 | Example(None, "1 pound", "pound", "1", 1.0), # Pound sterling 1354 | Example(None, "1 Sʻ", "Sʻ", "1", 1.0), # Uzbekistani soʻm 1355 | Example(None, "1 S'", "S'", "1", 1.0), # Uzbekistani soʻm 1356 | Example(None, "1 so'm", "so'm", "1", 1.0), # Uzbekistani soʻm 1357 | Example(None, "1 som", "som", "1", 1.0), # Uzbekistani soʻm 1358 | Example(None, "1 stg", "stg", "1", 1.0), # Pound sterling 1359 | Example(None, "1 yuan", "yuan", "1", 1.0), # Renminbi/Chinese yuan 1360 | Example(None, "1 Ƀ", "Ƀ", "1", 1.0), # Bitcoin 1361 | Example(None, "1 дин", "дин", "1", 1.0), # Serbian dinar 1362 | Example(None, "1 км", "км", "1", 1.0), # Bosnia and Herzegovina convertible mark 1363 | Example(None, "1 с", "с", "1", 1.0), # Kyrgyz som, Tajikistani somoni 1364 | Example(None, "1 ש״ח", "ש״ח", "1", 1.0), # Israeli new shekel 1365 | Example(None, "1 ج.س", "ج.س", "1", 1.0), # Sudanese pound 1366 | Example(None, "1 د.أ", "د.أ", "1", 1.0), # Jordanian dinar 1367 | Example(None, "1 د.ا", "د.ا", "1", 1.0), # Jordanian dinar 1368 | Example(None, "1 د.ج", "د.ج", "1", 1.0), # Algerian dinar 1369 | Example(None, "1 د.م", "د.م", "1", 1.0), # Moroccan dirham 1370 | Example( 1371 | None, 1372 | "1 دينار", # Bahraini dinar, Algerian dinar, Iraqi dinar, 1373 | # Jordanian dinar, Kuwaiti dinar, Libyan dinar, 1374 | # Tunisian dinar 1375 | "دينار", 1376 | "1", 1377 | 1.0, 1378 | ), 1379 | Example(None, "1 دينار أردني", "دينار أردني", "1", 1.0), # Jordanian dinar 1380 | Example(None, "1 دينار كويتي", "دينار كويتي", "1", 1.0), # Kuwaiti dinar 1381 | Example(None, "1 ر.ع", "ر.ع", "1", 1.0), # Omani rial 1382 | Example(None, "1 ر.ق", "ر.ق", "1", 1.0), # Qatari riyal 1383 | Example(None, "1 ريال", "ريال", "1", 1.0), # Saudi riyal 1384 | Example(None, "1 ش.ج", "ش.ج", "1", 1.0), # Israeli new shekel 1385 | Example(None, "1 ل.س", "ل.س", "1", 1.0), # Syrian pound 1386 | Example(None, "1 ل.ل", "ل.ل", "1", 1.0), # Lebanese pound 1387 | Example(None, "1 ரூ", "ரூ", "1", 1.0), # Sri Lankan rupee 1388 | Example(None, "1 රු", "රු", "1", 1.0), # Sri Lankan rupee 1389 | Example(None, "1 ლარი", "ლარი", "1", 1.0), # Georgian lari 1390 | Example(None, "1 人民币", "人民币", "1", 1.0), # Renminbi/Chinese yuan 1391 | Example(None, "1 圆", "圆", "1", 1.0), # Renminbi/Chinese yuan 1392 | Example(None, "1 圓", "圓", "1", 1.0), # Renminbi/Chinese yuan 1393 | Example(None, "1 GBp", "GBp", "1", 1.0), # British Pound 1394 | Example(None, "1 £", "£", "1", 1.0), # British Pound 1395 | Example(None, "1 nzd", "nzd", "1", 1.0), # New Zealand dollar 1396 | Example(None, "1 mkd", "mkd", "1", 1.0), # Macedonian denar 1397 | Example(None, "CND", "CND", "1", 1.0), # Canadian dollar 1398 | Example(None, "KShs", "KShs", "1", 1.0), # Kenyan shilling 1399 | Example(None, "chf", "chf", "1", 1.0), # Swiss franc 1400 | Example(None, "so'm", "so'm", "1", 1.0), # Uzbekistani soʻm 1401 | Example(None, "тг", "тг", "1", 1.0), # Kazakhstani tenge 1402 | Example(None, 'ש"ח', 'ש"ח', "1", 1.0), # Israeli new shekel 1403 | Example(None, "ש'ח", "ש'ח", "1", 1.0), # Israeli new shekel 1404 | Example(None, "د.ع", "د.ع", "1", 1.0), # Iraqi dinar 1405 | Example(None, "د.ل", "د.ل", "1", 1.0), # Libyan dinar 1406 | Example( 1407 | None, "درهم", "درهم", "1", 1.0 # United Arab Emirates dirham, Moroccan dirham 1408 | ), 1409 | Example(None, "ر.ي", "ر.ي", "1", 1.0), # Yemeni rial 1410 | Example(None, "ش.ص", "ش.ص", "1", 1.0), # Somali shilling 1411 | Example( 1412 | None, 1413 | "1 G", # "G" may mean Guyanese dollar, but it is too ambiguous 1414 | "G", 1415 | "1", 1416 | 1.0, 1417 | ), 1418 | ] 1419 | 1420 | 1421 | PRICE_PARSING_DECIMAL_SEPARATOR_EXAMPLES = [ 1422 | Example(None, "1250€ 600", "€", "1250", 1250, "€"), 1423 | Example(None, "1250€ 60", "€", "1250€60", 1250.60, "€"), 1424 | Example(None, "1250€600", "€", "1250€600", 1250.600, "€"), 1425 | Example(None, ".75 €", "€", ".75", 0.75, "."), 1426 | Example("$.75", "$.75", "$", ".75", 0.75, "."), 1427 | Example("$..75", "$..75", "$", ".75", 0.75, "."), 1428 | Example("$..75,333", "$..75,333", "$", ".75,333", 0.75333, "."), 1429 | Example("$..75,333", "$..75,333", "$", ".75,333", 75.333, ","), 1430 | Example("$.750.30", "$.750.30", "$", "750.30", 750.30, "."), 1431 | ] 1432 | 1433 | 1434 | @pytest.mark.parametrize( 1435 | ["example"], 1436 | [[e] for e in PRICE_PARSING_EXAMPLES_BUGS_CAUGHT] 1437 | + [[e] for e in PRICE_PARSING_EXAMPLES_NEW] 1438 | + [[e] for e in PRICE_PARSING_EXAMPLES] 1439 | + [[e] for e in PRICE_PARSING_EXAMPLES_2] 1440 | + [[e] for e in PRICE_PARSING_EXAMPLES_3] 1441 | + [[e] for e in PRICE_PARSING_EXAMPLES_NO_PRICE] 1442 | + [[e] for e in PRICE_PARSING_EXAMPLES_NO_CURRENCY] 1443 | + [[e] for e in PRICE_PARSING_DECIMAL_SEPARATOR_EXAMPLES] 1444 | + [ 1445 | pytest.param(e, marks=pytest.mark.xfail(strict=True)) 1446 | for e in PRICE_PARSING_EXAMPLES_XFAIL 1447 | + PRICE_PARSING_EXAMPLES_XFAIL_CURRENCIES_TO_BE_ADDED 1448 | ], 1449 | ids=idfn, 1450 | ) 1451 | def test_parsing(example: Example): 1452 | parsed = Price.fromstring( 1453 | example.price_raw, example.currency_raw, example.decimal_separator 1454 | ) 1455 | assert parsed == example, ( 1456 | f"Failed scenario: price={example.price_raw}, " 1457 | f"currency_hint={example.currency_raw}" 1458 | ) 1459 | 1460 | 1461 | @pytest.mark.parametrize( 1462 | "amount,amount_float", 1463 | ( 1464 | (None, None), 1465 | (Decimal("1.23"), 1.23), 1466 | ), 1467 | ) 1468 | def test_price_amount_float(amount, amount_float): 1469 | assert Price(amount, None, None).amount_float == amount_float 1470 | 1471 | 1472 | @pytest.mark.parametrize( 1473 | "price_raw,decimal_separator,expected_result", 1474 | ( 1475 | ("140.000", None, Decimal("140000")), 1476 | ("140.000", ",", Decimal("140000")), 1477 | ("140.000", ".", Decimal("140.000")), 1478 | ("140€33", "€", Decimal("140.33")), 1479 | ("140,000€33", "€", Decimal("140000.33")), 1480 | ("140.000€33", "€", Decimal("140000.33")), 1481 | ), 1482 | ) 1483 | def test_price_decimal_separator(price_raw, decimal_separator, expected_result): 1484 | parsed = Price.fromstring(price_raw, decimal_separator=decimal_separator) 1485 | assert parsed.amount == expected_result 1486 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36,py37,py38,py39,py310,py311,py312,py313,mypy 3 | 4 | [testenv] 5 | deps = 6 | pytest 7 | pytest-cov 8 | 9 | commands = 10 | py.test \ 11 | --cov-report=term --cov-report= --cov-report=xml --cov=price_parser \ 12 | --doctest-modules \ 13 | {posargs:price_parser tests README.rst} 14 | 15 | [testenv:mypy] 16 | deps = 17 | mypy==0.982 18 | 19 | commands = mypy -v --ignore-missing-imports --no-warn-no-return price_parser tests 20 | 21 | [testenv:pre-commit] 22 | deps = pre-commit 23 | commands = pre-commit run --all-files --show-diff-on-failure 24 | skip_install = true 25 | --------------------------------------------------------------------------------