Repository: scrapinghub/price-parser Branch: master Commit: 64e213a46a40 Files: 16 Total size: 132.9 KB Directory structure: gitextract_u09a6gwo/ ├── .git-blame-ignore-revs ├── .github/ │ └── workflows/ │ ├── main.yml │ └── publish.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGES.rst ├── LICENSE ├── README.rst ├── codecov.yml ├── price_parser/ │ ├── __init__.py │ ├── _currencies.py │ ├── parser.py │ └── py.typed ├── pyproject.toml ├── tests/ │ └── test_price_parsing.py └── tox.ini ================================================ FILE CONTENTS ================================================ ================================================ FILE: .git-blame-ignore-revs ================================================ # applying pre-commit hooks to the project ca42e4247d872a174afc539902e156282940ad5d ================================================ FILE: .github/workflows/main.yml ================================================ name: Tests on: [push, pull_request] jobs: tests: runs-on: ubuntu-latest if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name strategy: fail-fast: false matrix: include: - python-version: "3.9" env: TOXENV: py - python-version: "3.10" env: TOXENV: py - python-version: "3.11" env: TOXENV: py - python-version: "3.12" env: TOXENV: py - python-version: "3.13" env: TOXENV: py - python-version: "3.14.0-rc.3" env: TOXENV: py - python-version: "3.13" env: TOXENV: mypy steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Run tests env: ${{ matrix.env }} run: | pip install -U tox tox - name: Upload coverage report uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} pre-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: pre-commit/action@v3.0.1 ================================================ FILE: .github/workflows/publish.yml ================================================ name: Publish on: push: tags: - '[0-9]+.[0-9]+.[0-9]+' jobs: publish: runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/p/${{ github.event.repository.name }} permissions: id-token: write steps: - uses: actions/checkout@v5 - uses: actions/setup-python@v6 with: python-version: 3.13 - run: | python -m pip install --upgrade build python -m build - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.PYPI_TOKEN }} ================================================ FILE: .gitignore ================================================ # Python *.pyc *.pyo /build/ /dist/ *.egg-info # Mac OS *.DS_Store # IDE /.idea/ /.vscode/ .mypy_cache/ .cache/ .tox/ .pytest_cache/ .coverage coverage.xml # python virtual env .venv/ venv/ .env/ env/ ================================================ FILE: .pre-commit-config.yaml ================================================ exclude: tests/test_webpages repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.13.2 hooks: - id: ruff-check args: [ --fix ] - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: end-of-file-fixer - id: trailing-whitespace ================================================ FILE: CHANGES.rst ================================================ Changes ======= 0.5.1 (2026-03-19) ------------------ * Added support for additional currencies: Egyptian pound (EGP), UAE dirham (AED) and Saudi riyal (SAR). 0.5.0 (2025-10-06) ------------------ * Added support for Python 3.14. * Added support for ``'`` as a thousands separator. * Migrated the build system to ``hatchling``. * Improved type hints. * CI improvements. 0.4.0 (2025-02-05) ------------------ * Dropped support for Python 3.8 and lower, added support for Python 3.11-3.13. * Added a ``digit_group_separator`` parameter to ``Price.fromstring()``. 0.3.4 (2020-11-25) ------------------ * Improved parsing of prices without digits before a decimal point ('.75'), https://github.com/scrapinghub/price-parser/pull/42 * Fix parsing of price with non-breaking spaces https://github.com/scrapinghub/price-parser/pull/43 0.3.3 (2020-02-05) ------------------ * Fixed installation issue on some Windows machines. 0.3.2 (2020-01-28) ------------------ * Improved Korean and Japanese currency detection. * Declare Python 3.8 support. 0.3.1 (2019-10-21) ------------------ * Redundant $ signs are no longer returned as a part of currency, e.g. for ``SGD$ 100`` currency would be ``SGD``, not ``SGD$``. 0.3.0 (2019-10-19) ------------------ * New ``Price.fromstring`` argument ``decimal_separator`` allows to override decimal separator for the cases where it is known (i.e. disable decimal separator detection); * NTD and RBM unofficial currency names are added; * quantifiers in regular expressions are made non-greedy, which provides a small speedup; * test improvements. 0.2.4 (2019-07-03) ------------------ * Declare price-parser as providing type annotations (pep-561). This enables better type checking for projects using price-parser. * improved test coverage 0.2.3 (2019-06-18) ------------------ * Follow-up for 0.2.2 release: improved parsing of prices with 4+ digits after a decimal separator. 0.2.2 (2019-06-18) ------------------ * Fixed parsing of prices with 4+ digits after a decimal separator. 0.2.1 (2019-04-19) ------------------ * 23 additional currency symbols are added; * ``A$`` alias for Australian Dollar is added. 0.2 (2019-04-12) ---------------- Added support for currencies replaced by euro. 0.1.1 (2019-04-12) ------------------ Minor packaging fixes. 0.1 (2019-04-12) ---------------- Initial release. ================================================ FILE: LICENSE ================================================ Copyright (c) Scrapinghub All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of ScrapingHub nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.rst ================================================ ============ price-parser ============ .. image:: https://img.shields.io/pypi/v/price-parser.svg :target: https://pypi.python.org/pypi/price-parser :alt: PyPI Version .. image:: https://img.shields.io/pypi/pyversions/price-parser.svg :target: https://pypi.python.org/pypi/price-parser :alt: Supported Python Versions .. image:: https://github.com/scrapinghub/price-parser/actions/workflows/main.yml/badge.svg?branch=master :target: https://github.com/scrapinghub/price-parser/actions?workflow=Tests :alt: Build Status .. image:: https://codecov.io/github/scrapinghub/price-parser/coverage.svg?branch=master :target: https://codecov.io/gh/scrapinghub/price-parser :alt: Coverage report ``price-parser`` is a small library for extracting price and currency from raw text strings. Features: * robust price amount and currency symbol extraction * zero-effort handling of thousand and decimal separators The main use case is parsing prices extracted from web pages. For example, you can write a CSS/XPath selector which targets an element with a price, and then use this library for cleaning it up, instead of writing custom site-specific regex or Python code. License is BSD 3-clause. Installation ============ :: pip install price-parser price-parser requires Python 3.9+. Usage ===== Basic usage ----------- >>> from price_parser import Price >>> price = Price.fromstring("22,90 €") >>> price Price(amount=Decimal('22.90'), currency='€') >>> price.amount # numeric price amount Decimal('22.90') >>> price.currency # currency symbol, as appears in the string '€' >>> price.amount_text # price amount, as appears in the string '22,90' >>> price.amount_float # price amount as float, not Decimal 22.9 If you prefer, ``Price.fromstring`` has an alias ``price_parser.parse_price``, they do the same: >>> from price_parser import parse_price >>> parse_price("22,90 €") Price(amount=Decimal('22.90'), currency='€') The library has extensive tests (900+ real-world examples of price strings). Some of the supported cases are described below. Supported cases --------------- Unclean price strings with various currencies are supported; thousand separators and decimal separators are handled: >>> Price.fromstring("Price: $119.00") Price(amount=Decimal('119.00'), currency='$') >>> Price.fromstring("15 130 Р") Price(amount=Decimal('15130'), currency='Р') >>> Price.fromstring("151,200 تومان") Price(amount=Decimal('151200'), currency='تومان') >>> Price.fromstring("Rp 1.550.000") Price(amount=Decimal('1550000'), currency='Rp') >>> Price.fromstring("Běžná cena 75 990,00 Kč") Price(amount=Decimal('75990.00'), currency='Kč') Euro sign is used as a decimal separator in a wild: >>> Price.fromstring("1,235€ 99") Price(amount=Decimal('1235.99'), currency='€') >>> Price.fromstring("99 € 95 €") Price(amount=Decimal('99'), currency='€') >>> Price.fromstring("35€ 999") Price(amount=Decimal('35'), currency='€') Some special cases are handled: >>> Price.fromstring("Free") Price(amount=Decimal('0'), currency=None) When price or currency can't be extracted, corresponding attribute values are set to None: >>> Price.fromstring("") Price(amount=None, currency=None) >>> Price.fromstring("Foo") Price(amount=None, currency=None) >>> Price.fromstring("50% OFF") Price(amount=None, currency=None) >>> Price.fromstring("50") Price(amount=Decimal('50'), currency=None) >>> Price.fromstring("R$") Price(amount=None, currency='R$') Currency hints -------------- ``currency_hint`` argument allows to pass a text string which may (or may not) contain currency information. This feature is most useful for automated price extraction. >>> Price.fromstring("34.99", currency_hint="руб. (шт)") Price(amount=Decimal('34.99'), currency='руб.') Note that currency mentioned in the main price string may be **preferred** over currency specified in ``currency_hint`` argument; it depends on currency symbols found there. If you know the correct currency, you can set it directly: >>> price = Price.fromstring("1 000") >>> price.currency = 'EUR' >>> price Price(amount=Decimal('1000'), currency='EUR') Decimal separator ----------------- If you know which symbol is used as a decimal separator in the input string, pass that symbol in the ``decimal_separator`` argument to prevent price-parser from guessing the wrong decimal separator symbol. >>> Price.fromstring("Price: $140.600", decimal_separator=".") Price(amount=Decimal('140.600'), currency='$') >>> Price.fromstring("Price: $140.600", decimal_separator=",") Price(amount=Decimal('140600'), currency='$') Contributing ============ * Source code: https://github.com/scrapinghub/price-parser * Issue tracker: https://github.com/scrapinghub/price-parser/issues Use tox_ to run tests with different Python versions:: tox The command above also runs type checks; we use mypy. .. _tox: https://tox.readthedocs.io ================================================ FILE: codecov.yml ================================================ comment: layout: "header, diff, tree" coverage: status: project: false ================================================ FILE: price_parser/__init__.py ================================================ from .parser import Price, parse_price __all__ = ["Price", "parse_price"] ================================================ FILE: price_parser/_currencies.py ================================================ """ Currency information. ``CURRENCIES`` data is from https://github.com/StorePilot/coinify, which is supposed to provide combined data from * https://gist.github.com/Fluidbyte/2973986 * https://en.wikipedia.org/wiki/ISO_4217 * http://www.iotafinance.com/en/ISO-4217-Currency-Codes.html * http://www.xe.com/symbols.php Field meaning: * s - currency main symbol * n - currency name (currently unused) * sn - currency native symbol * d - decimal digits (currently unused) * r - rounding (currently unused) * np - currency name, plural (currently unused) * sn2 - other currency symbols Some extra abbreviations are added to the list (they are set below ``CURRENCIES`` variable, scroll to the bottom). """ from __future__ import annotations from itertools import chain from typing import TYPE_CHECKING, TypedDict if TYPE_CHECKING: # typing.NotRequired requires Python 3.11 from typing_extensions import NotRequired class CurrencyInfo(TypedDict): s: str n: str sn: str d: int r: float np: str sn2: NotRequired[list[str]] CURRENCIES: dict[str, CurrencyInfo] = { "AED": { "s": "AED", "n": "United Arab Emirates Dirham", "sn": "د.إ.‏", "d": 2, "r": 0, "np": "UAE dirhams", }, "AFN": { "s": "Af", "n": "Afghan Afghani", "sn": "؋", "d": 0, "r": 0, "np": "Afghan Afghanis", }, "ALL": { "s": "ALL", "n": "Albanian Lek", "sn": "Lek", "d": 0, "r": 0, "np": "Albanian lekë", }, "AMD": { "s": "AMD", "n": "Armenian Dram", "sn": "դր.", "d": 0, "r": 0, "np": "Armenian drams", }, "ANG": { "s": "ƒ", "n": "Netherlands Antilles Guilder", "sn": "ƒ", "d": 2, "r": 0, "np": "Netherlands Antilles Guilder", }, "AOA": { "s": "Kz", "n": "Angolan kwanza", "sn": "Kz", "d": 2, "r": 0, "np": "Angolan kwanza", }, "ARS": { "s": "AR$", "n": "Argentine Peso", "sn": "$", "d": 2, "r": 0, "np": "Argentine pesos", }, "AUD": { "s": "AU$", "n": "Australian Dollar", "sn": "$", "d": 2, "r": 0, "np": "Australian dollars", }, "AWG": { "s": "ƒ", "n": "Aruban Guilder", "sn": "ƒ", "d": 2, "r": 0, "np": "Aruban Guilders", }, "AFL": { "s": "Afl.", "n": "Aruban Florin", "sn": "Afl.", "d": 2, "r": 0, "np": "Aruban Florins", }, "AZN": { "s": "man.", "n": "Azerbaijani Manat", "sn": "ман.", "d": 2, "r": 0, "np": "Azerbaijani manats", }, "BAM": { "s": "KM", "n": "Bosnia-Herzegovina Convertible Mark", "sn": "KM", "d": 2, "r": 0, "np": "Bosnia-Herzegovina convertible marks", }, "BDT": { "s": "Tk", "n": "Bangladeshi Taka", "sn": "৳", "d": 2, "r": 0, "np": "Bangladeshi takas", }, "BBD": { "s": "Bds$", "n": "Barbados dollar", "sn": "$", "d": 2, "r": 0, "np": "Barbados dollar", }, "BGN": { "s": "BGN", "n": "Bulgarian Lev", "sn": "лв.", "d": 2, "r": 0, "np": "Bulgarian leva", }, "BHD": { "s": "BD", "n": "Bahraini Dinar", "sn": "د.ب.‏", "d": 3, "r": 0, "np": "Bahraini dinars", }, "BIF": { "s": "FBu", "n": "Burundian Franc", "sn": "FBu", "d": 0, "r": 0, "np": "Burundian francs", }, "BSD": { "s": "$", "n": "Bahamas Dollar", "sn": "$", "d": 2, "r": 0, "np": "Bahamas Dollar", }, "BMD": { "s": "$", "n": "Bermuda Dollar", "sn": "$", "d": 2, "r": 0, "np": "Bermuda Dollars", }, "BND": { "s": "BN$", "n": "Brunei Dollar", "sn": "$", "d": 2, "r": 0, "np": "Brunei dollars", }, "BOB": { "s": "Bs", "n": "Bolivian Boliviano", "sn": "Bs", "d": 2, "r": 0, "np": "Bolivian bolivianos", }, "BOV": { "s": "-", "n": "Bolivian Mvdol", "sn": "-", "d": 2, "r": 0, "np": "Bolivian Mvdol", }, "BRL": { "s": "R$", "n": "Brazilian Real", "sn": "R$", "d": 2, "r": 0, "np": "Brazilian reals", }, "BTN": { "s": "Nu.", "n": "Bhutanese ngultrum", "sn": "Nu.", "d": 2, "r": 0, "np": "Bhutanese ngultrum", }, "BWP": { "s": "BWP", "n": "Botswanan Pula", "sn": "P", "d": 2, "r": 0, "np": "Botswanan pulas", }, "BYN": { "s": "Br", "n": "Belarusian Ruble", "sn": "Br", "d": 0, "r": 0, "np": "Belarusian rubles", }, "BZD": { "s": "BZ$", "n": "Belize Dollar", "sn": "$", "d": 2, "r": 0, "np": "Belize dollars", }, "CAD": { "s": "CA$", "n": "Canadian Dollar", "sn": "$", "d": 2, "r": 0, "np": "Canadian dollars", }, "CDF": { "s": "CDF", "n": "Congolese Franc", "sn": "FrCD", "d": 2, "r": 0, "np": "Congolese francs", }, "CHE": { "s": "-", "n": "WIR Euro (complementary currency)", "sn": "-", "d": 2, "r": 0, "np": "WIR Euros (complementary currency)", }, "CHF": { "s": "CHF", "n": "Swiss Franc", "sn": "CHF", "d": 2, "r": 0.05, "np": "Swiss francs", }, "CHW": { "s": "-", "n": "WIR Franc (complementary currency)", "sn": "-", "d": 2, "r": 0, "np": "WIR Franc (complementary currency)", }, "CLF": { "s": "UF", "n": "Unidad de Fomento (funds code)", "sn": "UF", "d": 4, "r": 0, "np": "Unidad de Fomento (funds code)", }, "CLP": { "s": "CL$", "n": "Chilean Peso", "sn": "$", "d": 0, "r": 0, "np": "Chilean pesos", }, "CNY": { "s": "CN¥", "n": "Chinese Yuan", "sn": "CN¥", "d": 2, "r": 0, "np": "Chinese yuan", }, "COP": { "s": "CO$", "n": "Colombian Peso", "sn": "$", "d": 0, "r": 0, "np": "Colombian pesos", }, "COU": { "s": "-", "n": "Unidad de Valor Real (UVR) (funds code)", "sn": "-", "d": 2, "r": 0, "np": "Unidad de Valor Real (UVR) (funds code)", }, "CRC": { "s": "₡", "n": "Costa Rican Colón", "sn": "₡", "d": 0, "r": 0, "np": "Costa Rican colóns", }, "CUC": { "s": "CUC$", "n": "Cuban convertible peso", "sn": "$", "d": 2, "r": 0, "np": "Cuban convertible pesos", }, "CUP": { "s": "₱", "n": "Cuba Peso", "sn": "₱", "d": 0, "r": 0, "np": "Cuba Pesos", }, "CVE": { "s": "CV$", "n": "Cape Verdean Escudo", "sn": "CV$", "d": 0, "r": 0, "np": "Cape Verdean escudos", }, "CZK": { "s": "Kč", "n": "Czech Republic Koruna", "sn": "Kč", "d": 2, "r": 0, "np": "Czech Republic korunas", }, "DJF": { "s": "Fdj", "n": "Djiboutian Franc", "sn": "Fdj", "d": 0, "r": 0, "np": "Djiboutian francs", }, "DKK": { "s": "Dkr", "n": "Danish Krone", "sn": "kr", "d": 2, "r": 0, "np": "Danish kroner", }, "DOP": { "s": "RD$", "n": "Dominican Peso", "sn": "RD$", "d": 2, "r": 0, "np": "Dominican pesos", }, "DZD": { "s": "DA", "n": "Algerian Dinar", "sn": "د.ج.‏", "d": 2, "r": 0, "np": "Algerian dinars", }, "EEK": { "s": "Ekr", "n": "Estonian Kroon", "sn": "kr", "d": 2, "r": 0, "np": "Estonian kroons", }, "EGP": { "s": "EGP", "n": "Egyptian Pound", "sn": "ج.م.‏", "d": 2, "r": 0, "np": "Egyptian pounds", }, "ERN": { "s": "Nfk", "n": "Eritrean Nakfa", "sn": "Nfk", "d": 2, "r": 0, "np": "Eritrean nakfas", }, "ETB": { "s": "Br", "n": "Ethiopian Birr", "sn": "Br", "d": 2, "r": 0, "np": "Ethiopian birrs", }, "EUR": {"s": "€", "n": "Euro", "sn": "€", "d": 2, "r": 0, "np": "euros"}, "FJD": { "s": "$", "n": "Fiji Dollar", "sn": "$", "d": 2, "r": 0, "np": "Fiji Dollars", }, "FKP": { "s": "£", "n": "Falkland Islands (Malvinas) Pound", "sn": "£", "d": 2, "r": 0, "np": "Falkland Islands (Malvinas) Pound", }, "GBP": { "s": "£", "n": "British Pound Sterling", "sn": "£", "d": 2, "r": 0, "np": "British pounds sterling", }, "GEL": { "s": "GEL", "n": "Georgian Lari", "sn": "GEL", "d": 2, "r": 0, "np": "Georgian laris", }, "GGP": { "s": "£", "n": "Guernsey Pound", "sn": "£", "d": 2, "r": 0, "np": "Guernsey Pounds", }, "GHS": { "s": "GH₵", "n": "Ghanaian Cedi", "sn": "GH₵", "d": 2, "r": 0, "np": "Ghanaian cedis", }, "GIP": { "s": "£", "n": "Gibraltar Pound", "sn": "£", "d": 2, "r": 0, "np": "Gibraltar Pounds", }, "GMD": { "s": "D", "n": "Gambian dalasi", "sn": "D", "d": 2, "r": 0, "np": "Gambian dalasi", }, "GNF": { "s": "FG", "n": "Guinean Franc", "sn": "FG", "d": 0, "r": 0, "np": "Guinean francs", }, "GTQ": { "s": "GTQ", "n": "Guatemalan Quetzal", "sn": "Q", "d": 2, "r": 0, "np": "Guatemalan quetzals", }, "GYD": { "s": "$", "n": "Guyana Dollar", "sn": "$", "d": 2, "r": 0, "np": "Guyana Dollars", }, "HKD": { "s": "HK$", "n": "Hong Kong Dollar", "sn": "$", "d": 2, "r": 0, "np": "Hong Kong dollars", }, "HNL": { "s": "HNL", "n": "Honduran Lempira", "sn": "L", "d": 2, "r": 0, "np": "Honduran lempiras", }, "HRK": { "s": "kn", "n": "Croatian Kuna", "sn": "kn", "d": 2, "r": 0, "np": "Croatian kunas", }, "HTG": { "s": "G", "n": "Haitian gourde", "sn": "G", "d": 2, "r": 0, "np": "Haitian gourde", }, "HUF": { "s": "Ft", "n": "Hungarian Forint", "sn": "Ft", "d": 0, "r": 0, "np": "Hungarian forints", }, "IDR": { "s": "Rp", "n": "Indonesian Rupiah", "sn": "Rp", "d": 0, "r": 0, "np": "Indonesian rupiahs", }, "ILS": { "s": "₪", "n": "Israeli New Sheqel", "sn": "₪", "d": 2, "r": 0, "np": "Israeli new sheqels", }, "IMP": { "s": "£", "n": "Isle of Man Pound", "sn": "£", "d": 2, "r": 0, "np": "Isle of Man Pounds", }, "INR": { "s": "Rs", "n": "Indian Rupee", "sn": "টকা", "d": 2, "r": 0, "np": "Indian rupees", }, "IQD": { "s": "IQD", "n": "Iraqi Dinar", "sn": "د.ع.‏", "d": 3, "r": 0, "np": "Iraqi dinars", }, "IRR": { "s": "IRR", "n": "Iranian Rial", "sn": "﷼", "d": 0, "r": 0, "np": "Iranian rials", }, "ISK": { "s": "Ikr", "n": "Icelandic Króna", "sn": "kr", "d": 0, "r": 0, "np": "Icelandic krónur", }, "JEP": { "s": "£", "n": "Jersey Pound", "sn": "£", "d": 2, "r": 0, "np": "Jersey Pounds", }, "JMD": { "s": "J$", "n": "Jamaican Dollar", "sn": "$", "d": 2, "r": 0, "np": "Jamaican dollars", }, "JOD": { "s": "JD", "n": "Jordanian Dinar", "sn": "د.أ.‏", "d": 3, "r": 0, "np": "Jordanian dinars", }, "JPY": { "s": "¥", "n": "Japanese Yen", "sn": "¥", "sn2": ["円"], "d": 0, "r": 0, "np": "Japanese yen", }, "KES": { "s": "Ksh", "n": "Kenyan Shilling", "sn": "Ksh", "d": 2, "r": 0, "np": "Kenyan shillings", }, "KGS": { "s": "лв", "n": "Kyrgyzstan Som", "sn": "лв", "d": 2, "r": 0, "np": "Kyrgyzstan Som", }, "KHR": { "s": "KHR", "n": "Cambodian Riel", "sn": "៛", "d": 2, "r": 0, "np": "Cambodian riels", }, "KMF": { "s": "CF", "n": "Comorian Franc", "sn": "FC", "d": 0, "r": 0, "np": "Comorian francs", }, "KPW": { "s": "₩", "n": "North Korean Won", "sn": "원", "d": 0, "r": 0, "np": "North Korean Won", }, "KRW": { "s": "₩", "n": "South Korean Won", "sn": "원", "d": 0, "r": 0, "np": "South Korean won", }, "KWD": { "s": "KD", "n": "Kuwaiti Dinar", "sn": "د.ك.‏", "d": 3, "r": 0, "np": "Kuwaiti dinars", }, "KYD": { "s": "$", "n": "Cayman Islands Dollar", "sn": "$", "d": 2, "r": 0, "np": "Cayman Islands Dollars", }, "KZT": { "s": "KZT", "n": "Kazakhstani Tenge", "sn": "тңг.", "d": 2, "r": 0, "np": "Kazakhstani tenges", }, "LAK": { "s": "₭", "n": "Laos Kip", "sn": "₭", "d": 2, "r": 0, "np": "Laos Kip", }, "LBP": { "s": "LB£", "n": "Lebanese Pound", "sn": "ل.ل.‏", "d": 0, "r": 0, "np": "Lebanese pounds", }, "LKR": { "s": "SLRs", "n": "Sri Lankan Rupee", "sn": "SL Re", "d": 2, "r": 0, "np": "Sri Lankan rupees", }, "LRD": { "s": "$", "n": "Liberia Dollar", "sn": "$", "d": 2, "r": 0, "np": "Liberia Dollars", }, "LSL": { "s": "L", "n": "Lesotho loti", "sn": "L", "d": 2, "r": 0, "np": "Lesotho loti", }, "LTL": { "s": "Lt", "n": "Lithuanian Litas", "sn": "Lt", "d": 2, "r": 0, "np": "Lithuanian litai", }, "LVL": { "s": "Ls", "n": "Latvian Lats", "sn": "Ls", "d": 2, "r": 0, "np": "Latvian lati", }, "LYD": { "s": "LD", "n": "Libyan Dinar", "sn": "د.ل.‏", "d": 3, "r": 0, "np": "Libyan dinars", }, "MAD": { "s": "MAD", "n": "Moroccan Dirham", "sn": "د.م.‏", "d": 2, "r": 0, "np": "Moroccan dirhams", }, "MDL": { "s": "MDL", "n": "Moldovan Leu", "sn": "MDL", "d": 2, "r": 0, "np": "Moldovan lei", }, "MGA": { "s": "MGA", "n": "Malagasy Ariary", "sn": "MGA", "d": 0, "r": 0, "np": "Malagasy Ariaries", }, "MKD": { "s": "MKD", "n": "Macedonian Denar", "sn": "MKD", "d": 2, "r": 0, "np": "Macedonian denari", }, "MMK": { "s": "MMK", "n": "Myanma Kyat", "sn": "K", "d": 0, "r": 0, "np": "Myanma kyats", }, "MNT": { "s": "₮", "n": "Mongolia Tughrik", "sn": "₮", "d": 2, "r": 0, "np": "Mongolia Tughrik", }, "MOP": { "s": "MOP$", "n": "Macanese Pataca", "sn": "MOP$", "d": 2, "r": 0, "np": "Macanese patacas", }, "MRO": { "s": "UM", "n": "Mauritanian ouguiya", "sn": "UM", "d": 1, "r": 0, "np": "Mauritanian ouguiya", }, "MUR": { "s": "MURs", "n": "Mauritian Rupee", "sn": "MURs", "d": 0, "r": 0, "np": "Mauritian rupees", }, "MVR": { "s": "MRf", "n": "Maldivian rufiyaa", "sn": "Rf", "d": 2, "r": 0, "np": "Maldivian rufiyaa", }, "MWK": { "s": "MK", "n": "Malawian kwacha", "sn": "MK", "d": 2, "r": 0, "np": "Malawian kwacha", }, "MXN": { "s": "MX$", "n": "Mexican Peso", "sn": "$", "d": 2, "r": 0, "np": "Mexican pesos", }, "MXV": { "s": "-", "n": "Mexican Unidad de Inversion (UDI) (funds code)", "sn": "-", "d": 2, "r": 0, "np": "Mexican Unidad de Inversion (UDI) (funds code)", }, "MYR": { "s": "RM", "n": "Malaysian Ringgit", "sn": "RM", "d": 2, "r": 0, "np": "Malaysian ringgits", }, "MZN": { "s": "MTn", "n": "Mozambican Metical", "sn": "MTn", "d": 2, "r": 0, "np": "Mozambican meticals", }, "NAD": { "s": "N$", "n": "Namibian Dollar", "sn": "N$", "d": 2, "r": 0, "np": "Namibian dollars", }, "NGN": { "s": "₦", "n": "Nigerian Naira", "sn": "₦", "d": 2, "r": 0, "np": "Nigerian nairas", }, "NIO": { "s": "C$", "n": "Nicaraguan Córdoba", "sn": "C$", "d": 2, "r": 0, "np": "Nicaraguan córdobas", }, "NOK": { "s": "Nkr", "n": "Norwegian Krone", "sn": "kr", "d": 2, "r": 0, "np": "Norwegian kroner", }, "NPR": { "s": "NPRs", "n": "Nepalese Rupee", "sn": "नेरू", "d": 2, "r": 0, "np": "Nepalese rupees", }, "PRB": { "s": "руб", "n": "Transnistrian ruble", "sn": "руб", "d": 2, "r": 0, "np": "Transnistrian rubles", }, "NZD": { "s": "NZ$", "n": "New Zealand Dollar", "sn": "$", "d": 2, "r": 0, "np": "New Zealand dollars", }, "OMR": { "s": "OMR", "n": "Omani Rial", "sn": "ر.ع.‏", "d": 3, "r": 0, "np": "Omani rials", }, "PAB": { "s": "B/.", "n": "Panamanian Balboa", "sn": "B/.", "d": 2, "r": 0, "np": "Panamanian balboas", }, "PEN": { "s": "S/.", "n": "Peruvian Nuevo Sol", "sn": "S/.", "d": 2, "r": 0, "np": "Peruvian nuevos soles", }, "PGK": { "s": "K", "n": "Papua New Guinean kina", "sn": "K", "d": 2, "r": 0, "np": "Papua New Guinean kina", }, "PHP": { "s": "₱", "n": "Philippine Peso", "sn": "₱", "d": 2, "r": 0, "np": "Philippine pesos", }, "PKR": { "s": "PKRs", "n": "Pakistani Rupee", "sn": "₨", "d": 0, "r": 0, "np": "Pakistani rupees", }, "PLN": { "s": "zł", "n": "Polish Zloty", "sn": "zł", "d": 2, "r": 0, "np": "Polish zlotys", }, "PYG": { "s": "₲", "n": "Paraguayan Guarani", "sn": "₲", "d": 0, "r": 0, "np": "Paraguayan guaranis", }, "QAR": { "s": "QR", "n": "Qatari Rial", "sn": "ر.ق.‏", "d": 2, "r": 0, "np": "Qatari rials", }, "RON": { "s": "RON", "n": "Romanian Leu", "sn": "RON", "d": 2, "r": 0, "np": "Romanian lei", }, "RSD": { "s": "din.", "n": "Serbian Dinar", "sn": "дин.", "d": 0, "r": 0, "np": "Serbian dinars", }, "RUB": { "s": "RUB", "n": "Russian Ruble", "sn": "руб.", "d": 2, "r": 0, "np": "Russian rubles", }, "RWF": { "s": "RWF", "n": "Rwandan Franc", "sn": "FR", "d": 0, "r": 0, "np": "Rwandan francs", }, "SAR": { "s": "SR", "n": "Saudi Riyal", "sn": "ر.س.‏", "d": 2, "r": 0, "np": "Saudi riyals", }, "SBD": { "s": "$", "n": "Solomon Islands Dollar", "sn": "$", "d": 2, "r": 0, "np": "Solomon Islands Dollars", }, "SCR": { "s": "₨", "n": "Seychelles Rupee", "sn": "₨", "d": 2, "r": 0, "np": "Seychelles Rupees", }, "SDG": { "s": "SDG", "n": "Sudanese Pound", "sn": "SDG", "d": 2, "r": 0, "np": "Sudanese pounds", }, "SEK": { "s": "Skr", "n": "Swedish Krona", "sn": "kr", "d": 2, "r": 0, "np": "Swedish kronor", }, "SGD": { "s": "S$", "n": "Singapore Dollar", "sn": "$", "d": 2, "r": 0, "np": "Singapore dollars", }, "SHP": { "s": "£", "n": "Saint Helena Pound", "sn": "£", "d": 2, "r": 0, "np": "Saint Helena Pounds", }, "SLL": { "s": "Le", "n": "Sierra Leonean leone", "sn": "Le", "d": 2, "r": 0, "np": "Sierra Leonean leone", }, "SOS": { "s": "Ssh", "n": "Somali Shilling", "sn": "Ssh", "d": 0, "r": 0, "np": "Somali shillings", }, "SRD": { "s": "$", "n": "Suriname Dollar", "sn": "$", "d": 2, "r": 0, "np": "Suriname Dollars", }, "SSP": { "s": "SSP", "n": "South Sudanese pound", "sn": "SSP", "d": 2, "r": 0, "np": "South Sudanese pound", }, "STD": { "s": "Db", "n": "São Tomé and Príncipe dobra", "sn": "Db", "d": 2, "r": 0, "np": "São Tomé and Príncipe dobra", }, "SVC": { "s": "$", "n": "El Salvador Colon", "sn": "$", "d": 0, "r": 0, "np": "El Salvador Colon", }, "SYP": { "s": "SY£", "n": "Syrian Pound", "sn": "ل.س.‏", "d": 0, "r": 0, "np": "Syrian pounds", }, "SZL": { "s": "L", "n": "Swazi lilangeni", "sn": "L", "d": 2, "r": 0, "np": "Swazi lilangeni", }, "THB": { "s": "฿", "n": "Thai Baht", "sn": "฿", "d": 2, "r": 0, "np": "Thai baht", }, "TJS": { "s": "-", "n": "Tajikistani somoni", "sn": "-", "d": 2, "r": 0, "np": "Tajikistani somoni", }, "TMT": { "s": "T", "n": "Turkmenistan manat", "sn": "T", "d": 2, "r": 0, "np": "Turkmenistan manat", }, "TND": { "s": "DT", "n": "Tunisian Dinar", "sn": "د.ت.‏", "d": 3, "r": 0, "np": "Tunisian dinars", }, "TOP": { "s": "T$", "n": "Tongan Paʻanga", "sn": "T$", "d": 2, "r": 0, "np": "Tongan paʻanga", }, "TRY": { "s": "TL", "n": "Turkish Lira", "sn": "TL", "d": 2, "r": 0, "np": "Turkish Lira", }, "TTD": { "s": "TT$", "n": "Trinidad and Tobago Dollar", "sn": "$", "d": 2, "r": 0, "np": "Trinidad and Tobago dollars", }, "TVD": { "s": "$", "n": "Tuvalu Dollar", "sn": "$", "d": 2, "r": 0, "np": "Tuvalu Dollars", }, "TWD": { "s": "NT$", "n": "New Taiwan Dollar", "sn": "NT$", "d": 2, "r": 0, "np": "New Taiwan dollars", }, "TZS": { "s": "TSh", "n": "Tanzanian Shilling", "sn": "TSh", "d": 0, "r": 0, "np": "Tanzanian shillings", }, "UAH": { "s": "₴", "n": "Ukrainian Hryvnia", "sn": "₴", "d": 2, "r": 0, "np": "Ukrainian hryvnias", }, "UGX": { "s": "USh", "n": "Ugandan Shilling", "sn": "USh", "d": 0, "r": 0, "np": "Ugandan shillings", }, "USD": { "s": "$", "n": "US Dollar", "sn": "$", "d": 2, "r": 0, "np": "US dollars", }, "USN": { "s": "$", "n": "United States dollar (next day) (funds code)", "sn": "$", "d": 2, "r": 0, "np": "United States dollars (next day) (funds code)", }, "UYI": { "s": "UYI", "n": "Uruguay Peso en Unidades Indexadas (URUIURUI) (funds code)", "sn": "UYI", "d": 0, "r": 0, "np": "Uruguay Peso en Unidades Indexadas (URUIURUI) (funds code)", }, "UYU": { "s": "$U", "n": "Uruguayan Peso", "sn": "$", "d": 2, "r": 0, "np": "Uruguayan pesos", }, "UZS": { "s": "UZS", "n": "Uzbekistan Som", "sn": "UZS", "d": 0, "r": 0, "np": "Uzbekistan som", }, "VEF": { "s": "Bs.F.", "n": "Venezuelan Bolívar", "sn": "Bs.F.", "d": 2, "r": 0, "np": "Venezuelan bolívars", }, "VND": { "s": "₫", "n": "Vietnamese Dong", "sn": "₫", "d": 0, "r": 0, "np": "Vietnamese dong", }, "VUV": { "s": "VT", "n": "Vanuatu vatu", "sn": "VT", "d": 0, "r": 0, "np": "Vanuatu vatu", }, "WST": { "s": "WS$", "n": "Samoan tala", "sn": "$", "d": 2, "r": 0, "np": "Samoan tala", }, "XAF": { "s": "FCFA", "n": "CFA Franc BEAC", "sn": "FCFA", "d": 0, "r": 0, "np": "CFA francs BEAC", }, "XAG": { "s": "XAG", "n": "Silver (one troy ounce)", "sn": "XAG", "d": 0, "r": 0, "np": "Silver (one troy ounce)", }, "XAU": { "s": "XAU", "n": "Gold (one troy ounce)", "sn": "XAU", "d": 0, "r": 0, "np": "Gold (one troy ounce)", }, "XBA": { "s": "XBA", "n": "European Composite Unit (EURCO) (bond market unit)", "sn": "XBA", "d": 0, "r": 0, "np": "European Composite Unit (EURCO) (bond market unit)", }, "XBB": { "s": "XBB", "n": "European Monetary Unit (E.M.U.-6) (bond market unit)", "sn": "XBB", "d": 0, "r": 0, "np": "European Monetary Unit (E.M.U.-6) (bond market unit)", }, "XBC": { "s": "XBC", "n": "European Unit of Account 9 (E.U.A.-9) (bond market unit)", "sn": "XBC", "d": 0, "r": 0, "np": "European Unit of Account 9 (E.U.A.-9) (bond market unit)", }, "XBD": { "s": "XBD", "n": "European Unit of Account 17 (E.U.A.-17) (bond market unit)", "sn": "XBD", "d": 0, "r": 0, "np": "European Unit of Account 17 (E.U.A.-17) (bond market unit)", }, "XCD": { "s": "$", "n": "East Caribbean Dollar", "sn": "$", "d": 0, "r": 0, "np": "East Caribbean Dollars", }, "XDR": { "s": "XDR", "n": "Special drawing rights", "sn": "XDR", "d": 0, "r": 0, "np": "Special drawing rights", }, "XOF": { "s": "CFA", "n": "CFA Franc BCEAO", "sn": "CFA", "d": 0, "r": 0, "np": "CFA francs BCEAO", }, "XPD": { "s": "XPD", "n": "Palladium (one troy ounce)", "sn": "XPD", "d": 0, "r": 0, "np": "Palladium (one troy ounce)", }, "XPF": { "s": "CFP", "n": "CFP franc (franc Pacifique)", "sn": "CFP", "d": 0, "r": 0, "np": "CFP franc (franc Pacifique)", }, "XPT": { "s": "XPT", "n": "Platinum (one troy ounce)", "sn": "XPT", "d": 0, "r": 0, "np": "Platinum (one troy ounce)", }, "XSU": { "s": "Sucre", "n": "SUCRE", "sn": "Sucre", "d": 0, "r": 0, "np": "SUCRE", }, "XTS": { "s": "XTS", "n": "Code reserved for testing purposes", "sn": "XTS", "d": 0, "r": 0, "np": "Code reserved for testing purposes", }, "XUA": { "s": "XUA", "n": "ADB Unit of Account", "sn": "XUA", "d": 0, "r": 0, "np": "ADB Unit of Account", }, "XXX": { "s": "XXX", "n": "No currency", "sn": "XXX", "d": 0, "r": 0, "np": "No currency", }, "YER": { "s": "YR", "n": "Yemeni Rial", "sn": "ر.ي.‏", "d": 0, "r": 0, "np": "Yemeni rials", }, "ZAR": { "s": "R", "n": "South African Rand", "sn": "R", "d": 2, "r": 0, "np": "South African rand", }, "ZMK": { "s": "ZK", "n": "Zambian Kwacha", "sn": "ZK", "d": 0, "r": 0, "np": "Zambian kwachas", }, "ZMW": { "s": "ZK", "n": "Zambian kwacha", "sn": "ZK", "d": 2, "r": 0, "np": "Zambian kwacha", }, "ZWD": { "s": "Z$", "n": "Zimbabwe Dollar", "sn": "Z$", "d": 2, "r": 0, "np": "Zimbabwe Dollars", }, "ZWL": { "s": "$", "n": "Zimbabwean dollar A/10", "sn": "$", "d": 2, "r": 0, "np": "Zimbabwean dollars A/10", }, } # Commonly used unofficial names. # See also: https://en.wikipedia.org/wiki/ISO_4217#Unofficial_currency_codes CURRENCIES["NTD"] = CURRENCIES["TWD"] CURRENCIES["RMB"] = CURRENCIES["CNY"] REPLACED_BY_EURO: dict[str, CurrencyInfo] = { "ATS": { "s": "öS", "n": "Austrian schilling", "sn": "öS", "d": 2, "r": 0, "np": "Austrian schilling", }, "BEF": { "s": "fr.", "n": "Belgian franc", "sn": "fr.", "d": 2, "r": 0, "np": "Belgian francs", }, "CYP": { "s": "CYP", "n": "Cypriot pound", "sn": "£", "d": 2, "r": 0, "np": "Cypriot pounds", }, "DEM": { "s": "DM", "n": "Deutsche Mark", "sn": "D-Mark", "d": 2, "r": 0, "np": "Deutsche marks", }, "NLG": { "s": "fl.", "n": "Dutch guilder", "sn": "ƒ", "d": 2, "r": 0, "np": "Dutch guilders", }, "EEK": { "s": "kr", "n": "Estonian kroon", "sn": "kroon", "d": 2, "r": 0, "np": "Estonian krooni", }, "FIM": { "s": "FIM", "n": "Finnish markka", "sn": "mk.", "d": 2, "r": 0, "np": "Finnish markkaa", }, "FRF": { "s": "F", "n": "French franc", "sn": "₣", "d": 2, "r": 0, "np": "French francs", }, "GRD": { "s": "GRD", "n": "Greek drachma", "sn": "Δρχ.", "sn2": ["Δρ.", "₯"], "d": 2, "r": 0, "np": "Greek drachmae", }, "IEP": { "s": "IR£", "n": "Irish pound", "sn": "£", "d": 2, "r": 0, "np": "Irish pounds", }, "ITL": { "s": "L", "n": "Italian lira", "sn": "₤", "d": 0, "r": 0, "np": "Italian lire", }, "LVL": { "s": "Ls", "n": "Latvian lats", "sn": "LVL", "d": 2, "r": 0, "np": "Latvian lati", }, "LTL": { "s": "Lt", "n": "Lithuanian litas", "sn": "LTL", "sn2": ["litų"], "d": 2, "r": 0, "np": "Lithuanian litai", }, "LUF": { "s": "F", "n": "Luxembourgish franc", "sn": "LUF", "d": 2, "r": 0, "np": "Luxembourgish francs", }, "MTL": { "s": "Lm", "n": "Maltese lira", "sn": "₤", "d": 2, "r": 0, "np": "Maltese liri", }, "PTE": { "s": "$", "n": "Portuguese escudo", "sn": "$", "d": 2, "r": 0, "np": "Portuguese escudos", }, "SKK": { "s": "SKK", "n": "Slovak koruna", "sn": "Sk", "d": 2, "r": 0, "np": "Slovak Koruny", }, "SIT": { "s": "SIT", "n": "Slovenian tolar", "sn": "SIT", "sn2": ["tolarjev"], "d": 2, "r": 0, "np": "Slovenian tolar", }, "ESP": { "s": "Pta", "n": "Spanish peseta", "sn": "Ptas", "sn2": ["₧", "Pts", "Pt"], "d": 0, "r": 0, "np": "Spanish pesetas", }, "VAL": { "s": "£", "n": "Vatican lira", "sn": "₤", "d": 0, "r": 0, "np": "Vatican liri", }, } # updates CURRENCIES.update(REPLACED_BY_EURO) CURRENCIES["VND"]["sn2"] = ["đ"] CURRENCIES["RON"]["sn2"] = ["lei", "leu", "Lei", "LEI"] CURRENCIES["CHF"]["sn2"] = ["Fr."] CURRENCIES["PLN"]["sn2"] = ["pln"] CURRENCIES["INR"]["sn2"] = ["₹", "र"] CURRENCIES["IRR"]["sn2"] = ["ریال"] CURRENCY_CODES: list[str] = list(CURRENCIES.keys()) CURRENCY_SYMBOLS: list[str] = list({c["s"] for c in CURRENCIES.values()}) CURRENCY_NATIONAL_SYMBOLS: list[str] = list( {c["sn"] for c in CURRENCIES.values()} | set(chain.from_iterable(c["sn2"] for c in CURRENCIES.values() if "sn2" in c)) ) ================================================ FILE: price_parser/parser.py ================================================ import re import string from decimal import Decimal, InvalidOperation from re import Pattern from typing import Callable, Optional import attr from ._currencies import CURRENCY_CODES, CURRENCY_NATIONAL_SYMBOLS, CURRENCY_SYMBOLS @attr.s(auto_attribs=True) class Price: amount: Optional[Decimal] # price numeric value, as Decimal currency: Optional[str] # currency symbol (as appeared in text) # price value, as a raw string amount_text: Optional[str] = attr.ib(repr=False) @property def amount_float(self) -> Optional[float]: """price numeric value, as float""" if self.amount is not None: return float(self.amount) return None @classmethod def fromstring( cls, price: Optional[str], currency_hint: Optional[str] = None, decimal_separator: Optional[str] = None, digit_group_separator: Optional[str] = None, ) -> "Price": """ Given price and currency text extracted from HTML elements, return ``Price`` instance, which provides a clean currency symbol and price amount as a Decimal number. ``currency_hint`` is optional; you can pass value of some element which may contain currency, as a hint. If currency is present in ``price`` string, it could be **preferred** over a value extracted from ``currency_hint`` string. ``decimal_separator`` is optional; it is used to determine the decimal separator in price. If ``decimal_separator`` is ``None``, then it is guessed from ``price`` string. If ``decimal_separator`` is ``"."``, then ``1.000`` is parsed as ``1``. If it is ``,```, then ``1.000`` is parsed as ``1000``. ``digit_group_separator`` is optional; it is used to determine the digit group separator in price. If ``digit_group_separator`` is ``None``, then it is guessed from ``price`` string. If ``digit_group_separator`` is ``"."``, then ``1.000`` is parsed as ``1000``. If it is ``,``, then ``1.000`` is parsed as ``1``. """ currency = extract_currency_symbol(price, currency_hint) if currency is not None: currency = currency.strip() if digit_group_separator is not None and price is not None: price = price.replace(digit_group_separator, "") amount_text = extract_price_text(price) if price is not None else None amount_num = ( parse_number(amount_text, decimal_separator) if amount_text is not None else None ) return Price( amount=amount_num, currency=currency, amount_text=amount_text, ) parse_price = Price.fromstring def or_regex(symbols: list[str]) -> Pattern[str]: """Return a regex which matches any of ``symbols``""" return re.compile("|".join(re.escape(s) for s in symbols)) # If one of these symbols is found either in price or in currency, # it is considered currency symbol, and returned as a currency, regardless # of its position in text. SAFE_CURRENCY_SYMBOLS = [ # Variants of $, etc. They need to be before $. "Bds$", "CUC$", "MOP$", "AR$", "AU$", "BN$", "BZ$", "CA$", "CL$", "CO$", "CV$", "HK$", "MX$", "NT$", "NZ$", "TT$", "RD$", "WS$", "US$", "$U", "C$", "J$", "N$", "R$", "S$", "T$", "Z$", "A$", "SY£", "LB£", "CN¥", "GH₵", # unique currency symbols "$", "€", "£", "zł", "Zł", "Kč", "₽", "¥", "¥", "฿", "դր.", "դր", "₦", "₴", "₱", "৳", "₭", "₪", "﷼", "៛", "₩", "₫", "₡", "টকা", "ƒ", "₲", "؋", "₮", "नेरू", "₨", "₶", "₾", "֏", "ރ", "৲", "૱", "௹", "₠", "₢", "₣", "₤", "₧", "₯", "₰", "₳", "₷", "₸", "₹", "₺", "₼", "₾", "₿", "ℳ", "ر.ق.\u200f", "د.ك.\u200f", "د.ع.\u200f", "ر.ع.\u200f", "ر.ي.\u200f", "ر.س.\u200f", "د.ج.\u200f", "د.م.\u200f", "د.إ.\u200f", "د.ت.\u200f", "د.ل.\u200f", "ل.س.\u200f", "د.ب.\u200f", "د.أ.\u200f", "ج.م.\u200f", "ل.ل.\u200f", " تومان", "تومان", "درهم", "ريال", "جنيه", # other common symbols, which we consider unambiguous "EUR", "euro", "eur", "CHF", "DKK", "Rp", "lei", "руб.", "руб", "грн.", "грн", "дин.", "Dinara", "динар", "лв.", "лв", "р.", "тңг", "тңг.", "ман.", ] # "D" in some abbreviations means "dollar", and so currency # can be written as SGD$123 or NZD $123. Currency code should take priority # over $ symbol in this case. DOLLAR_CODES = [k for k in CURRENCY_CODES if k.endswith("D")] _DOLLAR_REGEX = re.compile( r""" \b (?:{}) # currency code like NZD (?= \$? # dollar sign to ignore if attached to the currency code (?:[\W\d]|$) # not a letter ) """.format("|".join(re.escape(k) for k in DOLLAR_CODES)), re.VERBOSE, ) # Other common currency symbols: 3-letter codes, less safe abbreviations OTHER_CURRENCY_SYMBOLS_SET = ( set( CURRENCY_CODES + CURRENCY_SYMBOLS + CURRENCY_NATIONAL_SYMBOLS + # even if they appear in text, currency is likely to be rouble ["р", "Р"] ) - set(SAFE_CURRENCY_SYMBOLS) # already handled - {"-", "XXX"} # placeholder values - set(string.ascii_uppercase) # very unreliable on their own ) OTHER_CURRENCY_SYMBOLS = sorted(OTHER_CURRENCY_SYMBOLS_SET, key=len, reverse=True) _search_dollar_code = _DOLLAR_REGEX.search _search_safe_currency = or_regex(SAFE_CURRENCY_SYMBOLS).search _search_unsafe_currency = or_regex(OTHER_CURRENCY_SYMBOLS).search def extract_currency_symbol( price: Optional[str], currency_hint: Optional[str] ) -> Optional[str]: """ Guess currency symbol from extracted price and currency strings. Return an empty string if symbol is not found. """ methods: list[tuple[Callable[[str], Optional[re.Match[str]]], Optional[str]]] = [ (_search_safe_currency, price), (_search_safe_currency, currency_hint), (_search_unsafe_currency, price), (_search_unsafe_currency, currency_hint), ] if currency_hint and "$" in currency_hint: methods.insert(0, (_search_dollar_code, currency_hint)) if price and "$" in price: methods.insert(0, (_search_dollar_code, price)) for meth, value in methods: m = meth(value) if value else None if m: return m.group(0) return None def extract_price_text(price: str) -> Optional[str]: r""" Extract text of a price from a string which contains price and maybe some other text. If multiple price-looking substrings are present, the first is returned (FIXME: it is better to return a number which is near a currency symbol). >>> extract_price_text("price: $12.99") '12.99' >>> extract_price_text("Free") '0' >>> extract_price_text("Foo") >>> extract_price_text("1,235 USD") '1,235' In addition to numbers, it has a limited support for a case where currency symbol (currently only euro) is a decimal separator: >>> extract_price_text("99 €, 79 €") '99' >>> extract_price_text("99 € 79 €") '99' >>> extract_price_text("35€ 99") '35€99' >>> extract_price_text("35€ 999") '35' >>> extract_price_text("1,235€ 99") '1,235€99' >>> extract_price_text("50% OFF") >>> extract_price_text("50%") >>> extract_price_text("50") '50' >>> extract_price_text("$1\xa0298,00") '1 298,00' >>> extract_price_text("$.75") '.75' """ price = re.sub( r"\s+", " ", price ) # clean initial text from non-breaking and extra spaces if price.count("€") == 1: m = re.search( r""" [\d\s.,']*?\d # number, probably with thousand separators \s*?€(\s*?)? # euro, probably separated by whitespace \d(?(1)\d|\d*?) # if separated by whitespace - search one digit, # multiple digits otherwise (?:$|[^\d]) # something which is not a digit """, price, re.VERBOSE, ) if m: return m.group(0).replace(" ", "") m = re.search( r""" ([.]?\d[\d\s.,']*) # number, probably with thousand separators \s*? # skip whitespace (?:[^%\d]|$) # capture next symbol - it shouldn't be % """, price, re.VERBOSE, ) if m: price_text = m.group(1).rstrip(",.") price_text = price_text.replace("'", "") return ( price_text.strip() if price_text.count(".") == 1 else price_text.lstrip(",.").strip() ) if "free" in price.lower(): return "0" return None # NOTE: Keep supported separators in sync with parse_number() _search_decimal_sep = re.compile( r""" \d* # null or more digits (there can be more before it) ([.,€]) # decimal separator (?: # 1,2 or 4+ digits. 3 digits is likely to be a thousand separator. \d{1,2}?| \d{4}\d*? ) $ """, re.VERBOSE, ).search def get_decimal_separator(price: str) -> Optional[str]: """Return decimal separator symbol or None if there is no decimal separator. >>> get_decimal_separator("1000") >>> get_decimal_separator("12.99") '.' >>> get_decimal_separator("12,99") ',' >>> get_decimal_separator("12.999") >>> get_decimal_separator("3,0000") ',' >>> get_decimal_separator("1,235€99") '€' >>> get_decimal_separator(".75") '.' """ m = _search_decimal_sep(price) if m: return m.group(1) return None def parse_number( num: str, decimal_separator: Optional[str] = None ) -> Optional[Decimal]: """Parse a string with a number to a Decimal, guessing its format: decimal separator, thousand separator. Return None if parsing fails. >>> parse_number("1,234") Decimal('1234') >>> parse_number("12,34") Decimal('12.34') >>> parse_number("12,345") Decimal('12345') >>> parse_number("1,1") Decimal('1.1') >>> parse_number("1.1") Decimal('1.1') >>> parse_number("1234") Decimal('1234') >>> parse_number("12€34") Decimal('12.34') >>> parse_number("12€ 34") Decimal('12.34') >>> parse_number("1 234.99") Decimal('1234.99') >>> parse_number("1,235€99") Decimal('1235.99') >>> parse_number("1 235€99") Decimal('1235.99') >>> parse_number("1.235€99") Decimal('1235.99') >>> parse_number("140.000", decimal_separator=",") Decimal('140000') >>> parse_number("140.000", decimal_separator=".") Decimal('140.000') >>> parse_number("") >>> parse_number("foo") """ if not num: return None num = num.strip().replace(" ", "") decimal_separator = decimal_separator or get_decimal_separator(num) # NOTE: Keep supported separators in sync with _search_decimal_sep if decimal_separator is None: num = num.replace(".", "").replace(",", "") elif decimal_separator == ".": num = num.replace(",", "") elif decimal_separator == ",": num = num.replace(".", "").replace(",", ".") else: assert decimal_separator == "€" num = num.replace(".", "").replace(",", "").replace("€", ".") try: return Decimal(num) except InvalidOperation: return None ================================================ FILE: price_parser/py.typed ================================================ ================================================ FILE: pyproject.toml ================================================ [build-system] requires = ["hatchling>=1.27.0"] build-backend = "hatchling.build" [project] name = "price-parser" version = "0.5.1" description = "Extract price and currency from a raw string" readme = "README.rst" license = "BSD-3-Clause" license-files = ["LICENSE"] authors = [ { name = "Mikhail Korobov", email = "kmike84@gmail.com" }, ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] dependencies = [ "attrs >= 17.3.0", ] requires-python = ">=3.9" [project.urls] Homepage = "https://github.com/scrapinghub/price-parser" Source = "https://github.com/scrapinghub/price-parser" Tracker = "https://github.com/scrapinghub/price-parser/issues" [tool.bumpversion] commit = true tag = true tag_name = "{new_version}" [[tool.bumpversion.files]] filename = 'CHANGES.rst' search = "\\(unreleased\\)$" replace = "({now:%Y-%m-%d})" regex = true [tool.coverage.run] branch = true [tool.hatch.build.targets.sdist] include = [ "/price_parser", "/tests", "/CHANGES.rst", "/tox.ini", ] [tool.mypy] strict = true [tool.ruff.lint] extend-select = [ # flake8-builtins "A", # flake8-async "ASYNC", # flake8-bugbear "B", # flake8-comprehensions "C4", # flake8-commas "COM", # pydocstyle "D", # flake8-future-annotations "FA", # flynt "FLY", # refurb "FURB", # isort "I", # flake8-implicit-str-concat "ISC", # flake8-logging "LOG", # Perflint "PERF", # pygrep-hooks "PGH", # flake8-pie "PIE", # pylint "PL", # flake8-pytest-style "PT", # flake8-use-pathlib "PTH", # flake8-pyi "PYI", # flake8-quotes "Q", # flake8-return "RET", # flake8-raise "RSE", # Ruff-specific rules "RUF", # flake8-bandit "S", # flake8-simplify "SIM", # flake8-slots "SLOT", # flake8-debugger "T10", # flake8-type-checking "TC", # pyupgrade "UP", # pycodestyle warnings "W", # flake8-2020 "YTT", ] ignore = [ # Trailing comma missing "COM812", # Missing docstring in public module "D100", # Missing docstring in public class "D101", # Missing docstring in public method "D102", # Missing docstring in public function "D103", # Missing docstring in public package "D104", # Missing docstring in magic method "D105", # Missing docstring in public nested class "D106", # Missing docstring in __init__ "D107", # One-line docstring should fit on one line with quotes "D200", # No blank lines allowed after function docstring "D202", # 1 blank line required between summary line and description "D205", # Multi-line docstring closing quotes should be on a separate line "D209", # First line should end with a period "D400", # First line should be in imperative mood; try rephrasing "D401", # First line should not be the function's "signature" "D402", # First word of the first line should be properly capitalized "D403", # No blank lines allowed between a section header and its content "D412", # Contains control characters that can permit obfuscated code "PLE2502", # Too many return statements "PLR0911", # Too many branches "PLR0912", # Too many arguments in function definition "PLR0913", # Too many statements "PLR0915", # Magic value used in comparison "PLR2004", # String contains ambiguous {}. "RUF001", # Docstring contains ambiguous {}. "RUF002", # Comment contains ambiguous {}. "RUF003", # Mutable class attributes should be annotated with `typing.ClassVar` "RUF012", # Use of `assert` detected "S101", ] [tool.ruff.lint.flake8-type-checking] runtime-evaluated-decorators = ["attr.s"] [tool.ruff.lint.isort] split-on-trailing-comma = false [tool.ruff.lint.per-file-ignores] # To not introduce annotations potentially not resolvable at the run time "price_parser/parser.py" = ["FA100"] [tool.ruff.lint.pydocstyle] convention = "pep257" ================================================ FILE: tests/test_price_parsing.py ================================================ """ Price extractor was developed mostly using PRICE_PARSING_EXAMPLES, then evaluated on PRICE_PARSING_EXAMPLES_2, then improved to work better on PRICE_PARSING_EXAMPLES_3, PRICE_PARSING_EXAMPLES_NO_PRICE and PRICE_PARSING_EXAMPLES_NO_CURRENCY. This data is collected from a random sample of pages from different domains. PRICE_PARSING_EXAMPLES_BUGS_CAUGHT are manually added examples for the bugs we've found in a wild; PRICE_PARSING_EXAMPLES_NEW is a list of tests for new features. New tests should probably go these two lists. """ from __future__ import annotations from decimal import Decimal import pytest from price_parser import Price class Example(Price): # noqa: PLW1641 """A Price wrapper for tests""" def __init__( self, currency_raw: str | None, price_raw: str | None, currency: str | None, amount_text: str | None, amount_float: float | Decimal | None, decimal_separator: str | None = None, digit_group_separator: str | None = None, ) -> None: self.currency_raw = currency_raw self.price_raw = price_raw self.decimal_separator = decimal_separator amount_decimal: Decimal | None = None if isinstance(amount_float, Decimal): amount_decimal = amount_float elif amount_float is not None: # don't use Decimal(amount_float), as this is not what # one usually means, because of float precision amount_decimal = Decimal(str(amount_float)) super().__init__( amount=amount_decimal, currency=currency, amount_text=amount_text, ) def __eq__(self, other: object) -> bool: if not isinstance(other, Price): return super().__eq__(other) return ( self.amount == other.amount and self.currency == other.currency and self.amount_text == other.amount_text ) def idfn(val: object) -> str | None: if isinstance(val, Example): return f"{val.currency_raw}, {val.price_raw!r}" return None PRICE_PARSING_EXAMPLES_BUGS_CAUGHT = [ Example(None, "US$:12.99", "US$", "12.99", 12.99), Example("GBP", "34.992001", "GBP", "34.992001", 34.992001), Example("GBP", "29.1583", "GBP", "29.1583", 29.1583), Example( None, "1.11000000000000009770", None, "1.11000000000000009770", Decimal("1.11000000000000009770"), ), Example(None, " 423.923 KD", "KD", "423.923", 423.923, decimal_separator="."), Example( None, " 123,456.789 OMR", "OMR", "123,456.789", 123456.789, decimal_separator=".", digit_group_separator=",", ), ] PRICE_PARSING_EXAMPLES_NEW = [ Example(None, "PTE 120 000 000", "PTE", "120 000 000", 120000000), Example(None, "DEM 170 000", "DEM", "170 000", 170000), Example(None, "₤1700.", "₤", "1700", 1700), Example(None, "$ 1.144.000", "$", "1.144.000", 1144000), Example(None, "A$190.00", "A$", "190.00", 190), Example(None, "205,68 € 205.68", "€", "205,68", 205.68), Example(None, "AED 8000 (USD 2179)", "AED", "8000", 8000), Example(None, "13800 ₶", "₶", "13800", 13800), Example(None, "12,000원", "원", "12,000", 12000), Example(None, "3,500円", "円", "3,500", 3500), Example(None, "CHF 1'049,95", "CHF", "1049,95", 1049.95), Example(None, "€1'049,95", "€", "1049,95", 1049.95), Example(None, "644.00 جنيه", "جنيه", "644.00", 644.0), # Egyptian pound (EGP) Example(None, "3,439.00 درهم", "درهم", "3,439.00", 3439.0), # UAE dirham (AED) Example(None, "106.61 ريال", "ريال", "106.61", 106.61), # Saudi riyal (SAR) ] PRICE_PARSING_EXAMPLES = [ Example( "90 728.00 руб 103 100.00 руб", "399 167.00 руб 420 176.16 руб", "руб", "399 167.00", 399167, ), Example("45,00 zł", "45,00 zł", "zł", "45,00", 45), Example("$", "$ 22.00", "$", "22.00", 22), Example("$3.99", "$14.99", "$", "14.99", 14.99), Example("Price:", "$11.95", "$", "11.95", 11.95), Example("$19.95", "$19.95", "$", "19.95", 19.95), Example("Price From £ 39.95", "39.95", "£", "39.95", 39.95), Example("comprar", "R$260,00", "R$", "260,00", 260), Example("$79.00", "$79.00", "$", "79.00", 79), Example("34,50 € *", "34,50 € *", "€", "34,50", 34.5), Example("35,70 €", "35,70 €", "€", "35,70", 35.7), Example("Price", "$147.44", "$", "147.44", 147.44), Example("Up to", "$4.70", "$", "4.70", 4.7), Example("$999.99", "$924.99", "$", "924.99", 924.99), Example("€", "690,00", "€", "690,00", 690), Example("Cena", "Běžná cena 9 800 Kč", "Kč", "9 800", 9800), Example("729,00 €", "479,00 €", "€", "479,00", 479), Example("naše cena", "běžná cena 590 Kč", "Kč", "590", 590), Example("Statt 249,00 EUR **", "249,00 EUR", "EUR", "249,00", 249), Example("€13.95", "€13.95", "€", "13.95", 13.95), Example("€36", "€89", "€", "89", 89), Example("171900 руб.", "171900 руб.", "руб.", "171900", 171900), Example("$1.06", "$0.31", "$", "0.31", 0.31), Example("$45.49", "$45.49", "$", "45.49", 45.49), Example("$", "28", "$", "28", 28), Example("4.81 16 von 5", "4.81", None, "4.81", 4.81), Example("CHF 86.00 *", "CHF 47.00 *", "CHF", "47.00", 47), Example("320 руб. 400 руб.", "419 руб.", "руб.", "419", 419), Example("129,99 €", "129,99 €", "€", "129,99", 129.99), Example("Cena", "1,10 €", "€", "1,10", 1.1), Example("Price:", "$559.00", "$", "559.00", 559), Example("$49.99", "$49.99", "$", "49.99", 49.99), Example("Price: $32.00", "$32.00", "$", "32.00", 32), Example("Price (high to low)", "$699,000", "$", "699,000", 699000), Example("€ 11.69", "€ 11.69", "€", "11.69", 11.69), Example("100,00 руб.", "100,00", "руб.", "100,00", 100), Example("€ 1", "17,35", "€", "17,35", 17.35), Example("$19.95", "$19.95", "$", "19.95", 19.95), Example("-", "£11.95", "£", "11.95", 11.95), Example("грн.", "5 870", "грн.", "5 870", 5870), Example("Price", "80 Kč", "Kč", "80", 80), Example("€ 640,", "€ 610,", "€", "610", 610), Example("Cena", "299 Kč", "Kč", "299", 299), Example("1128240 рублей", "1128240 рублей", "руб", "1128240", 1128240), Example( "Now £9.99 $13.71 11,16 € £8.33 $13.71 9,30 €", "£9.99", "£", "9.99", 9.99, ), Example("Free!", "Free!", None, "0", 0), Example("49,96€", "49,96€", "€", "49,96", 49.96), Example("SKU:", "$9.00", "$", "9.00", 9), Example("£ 8.29", "8.29", "£", "8.29", 8.29), Example("Р", "6 300 Р", "Р", "6 300", 6300), Example("99,99 EUR (-30,00%) 69,99 EUR", "99,99 EUR", "EUR", "99,99", 99.99), Example("Р", "1 890 Р", "Р", "1 890", 1890), Example("Disposable Arts", "15,95 €", "€", "15,95", 15.95), Example("zł", "16.00", "zł", "16.00", 16), Example("£ 4.99", "4.99", "£", "4.99", 4.99), Example("Price", "$119. 95", "$", "119. 95", 119.95), Example("269 Kč", "269 Kč", "Kč", "269", 269), Example(">", "13,30 €", "€", "13,30", 13.3), Example("₪ 115", "₪98", "₪", "98", 98), Example("80,00 €", "80,00 €", "€", "80,00", 80), Example("33,95 €", "29,95 €", "€", "29,95", 29.95), Example("£ 80.00", "80.00", "£", "80.00", 80), Example("EUR", "€16.95", "€", "16.95", 16.95), Example("DKK", "23,40", "DKK", "23,40", 23.4), Example("Price: $ 34.44", "34.44", "$", "34.44", 34.44), Example("Rp 31.500", "Rp 31.500", "Rp", "31.500", 31500), Example("běžná cena 6,00 Kč", "běžná cena 6,00 Kč", "Kč", "6,00", 6), Example("€", "19,59", "€", "19,59", 19.59), Example("Out of stock", "2 285,81 грн.", "грн.", "2 285,81", 2285.81), Example("$ 119,999", "$ 119,999", "$", "119,999", 119999), Example("1,20 €", "1,20 €", "€", "1,20", 1.2), Example("€ 1.99", "1.99", "€", "1.99", 1.99), Example("Cena 283,50 PLN", "Cena 283,50 PLN", "PLN", "283,50", 283.5), Example("SKU:", "99.99", None, "99.99", 99.99), Example("$", "$ 979.00", "$", "979.00", 979), Example("Cena 169,00 Kč", "169,00 Kč", "Kč", "169,00", 169), Example("Р", "94,456 Р", "Р", "94,456", 94456), Example("€", "180", "€", "180", 180), Example("Savings: $0.90", "2.85", "$", "2.85", 2.85), Example("฿ 3.3900", "฿ 3.3900", "฿", "3.3900", 3.39), Example("5,90 €", "5,90 €", "€", "5,90", 5.90), Example("1 930 р.", "1 030 р.", "р.", "1 030", 1030), Example("$", "$ 22.00", "$", "22.00", 22), Example("HUF", "39900", "HUF", "39900", 39900), Example("59,00 €", "79,00 €", "€", "79,00", 79), Example("(-38,23%)", "429,00 EUR", "EUR", "429,00", 429), Example("302,44€ Χωρίς ΦΠΑ: 243,90€", "302,44€", "€", "302,44", 302.44), Example("RENAULT", "129.900", None, "129.900", 129900), Example("USD", "£54.17", "£", "54.17", 54.17), Example("$3.00", "$3.00", "$", "3.00", 3), Example("3,49 zł 1,75 zł", "3,49 zł 1,75 zł", "zł", "3,49", 3.49), Example("59,90 EUR 599,00 EUR pro Kilogramm", "59,90 EUR", "EUR", "59,90", 59.9), Example("Vanaf € 49.95", "Vanaf € 49.95", "€", "49.95", 49.95), Example("$299.00", "$299.00", "$", "299.00", 299), Example("€", "€30,40 **", "€", "30,40", 30.4), Example("$59.99", "$59.99", "$", "59.99", 59.99), Example("EUR", "2800", "EUR", "2800", 2800), Example("225,00 € *", "225,00 € *", "€", "225,00", 225), Example("17 940,00 руб", "17 940,00 руб", "руб", "17 940,00", 17940), Example("159,50 €", "159,50 €", "€", "159,50", 159.5), Example("£18.99", "£18.99", "£", "18.99", 18.99), Example("2.999,00 EUR (-10,00%) 2.699,00 EUR", "699,00 EUR", "EUR", "699,00", 699), Example("$", "45.33", "$", "45.33", 45.33), Example("грн.", "33", "грн.", "33", 33), Example("EUR", "16,50 €", "€", "16,50", 16.5), Example("₫", "880,000 ₫", "₫", "880,000", 880000), Example("руб.", "13.50", "руб.", "13.50", 13.5), Example("336 р.", "336 р.", "р.", "336", 336), Example("....", "200", None, "200", 200), Example("Our Price:", "$860.00", "$", "860.00", 860), Example("10 €", "10 €", "€", "10", 10), Example("€", "13", "€", "13", 13), Example("60,00 DKK", "60,00 DKK", "DKK", "60,00", 60), Example("63,90 EUR", "63,90 EUR", "EUR", "63,90", 63.9), Example("$", "$ 11.99", "$", "11.99", 11.99), Example("25,00 €", "25,00 €", "€", "25,00", 25), Example("Cena", "106,40 €", "€", "106,40", 106.4), Example("EA", "$426.20/EA", "$", "426.20", 426.2), Example("zł", "5.76", "zł", "5.76", 5.76), Example("$4.50", "$4.50", "$", "4.50", 4.5), Example("51090 M Фильтроэлемент OMEGA", "2 660 руб", "руб", "2 660", 2660), Example("Р", "5 000 Р", "Р", "5 000", 5000), Example("Add to Cart", "$4.60", "$", "4.60", 4.6), Example("£14.99", "£14.99", "£", "14.99", 14.99), Example("SKU:", "$150.00", "$", "150.00", 150), Example("€", "70,31", "€", "70,31", 70.31), Example("/", "$379", "$", "379", 379), Example("€", "139,95", "€", "139,95", 139.95), Example("Add to cart", "22,90 €", "€", "22,90", 22.9), Example("94,99 zł", "94,99 zł", "zł", "94,99", 94.99), Example("Price", "89.99", None, "89.99", 89.99), Example("USD", "$4.00", "$", "4.00", 4), Example("$", "Type of Transfer *", "$", None, None), Example("Add to Order", "$892.00", "$", "892.00", 892), Example("грн", "60800", "грн", "60800", 60800), Example("zł", "1849.00", "zł", "1849.00", 1849), Example("OK", "0,00 EUR", "EUR", "0,00", 0), Example("руб.", "5 450 руб.", "руб.", "5 450", 5450), Example("Price: $15.95/mo", "Price: $15.95/mo", "$", "15.95", 15.95), Example("MX924 IX", "850 руб.", "руб.", "850", 850), Example( "6,000.00 руб. 5,500.00 руб.", "6,000.00 руб. 5,500.00 руб.", "руб.", "6,000.00", 6000, ), Example("5.590,00 € *", "9.990,00 €", "€", "9.990,00", 9990), Example( "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" " 27 28 29 30", "$175.00", "$", "175.00", 175, ), Example("Price", "$99.99", "$", "99.99", 99.99), Example("€", "119,00", "€", "119,00", 119), Example("$", "$ 11.96", "$", "11.96", 11.96), Example("€", "9,94", "€", "9,94", 9.94), Example("$69.00", "$69.00", "$", "69.00", 69), Example("AQUAFINESSE", "AQUAFINESSE", None, None, None), Example("£55.00", "£50.00", "£", "50.00", 50), Example("$39.99", "$39.99", "$", "39.99", 39.99), Example("65 000 руб. 75 000 руб.", "75 000 руб.", "руб.", "75 000", 75000), Example("DKK/stk", "17,95", "DKK", "17,95", 17.95), Example("841,00 zł", "841,00 zł – 995,00 zł", "zł", "841,00", 841), Example("$", "$ 18.95", "$", "18.95", 18.95), Example("9.0", "434", None, "434", 434), Example("£14.50", "£21.00", "£", "21.00", 21), Example("ab", "19,90 €", "€", "19,90", 19.9), Example("30 руб", "30 руб", "руб", "30", 30), Example("€", "684,25", "€", "684,25", 684.25), Example("€ 125.00", "€ 45.00", "€", "45.00", 45), Example("ht / L'unité", "17.90", None, "17.90", 17.9), Example("$0.00", "$0.00", "$", "0.00", 0), Example("49 Kč", "49 Kč", "Kč", "49", 49), Example("Cena 685,00 Kč", "1 995,00 Kč", "Kč", "1 995,00", 1995), Example("€ 2,99", "€ 2,99", "€", "2,99", 2.99), Example("Cкидка до 10% от цены купона", "475 руб.", "руб.", "475", 475), Example("€", "11,76", "€", "11,76", 11.76), Example("$99.99", "$99.99", "$", "99.99", 99.99), Example("1\xa0298,00 €", "1\xa0298,00 €", "€", "1 298,00", 1298.00), Example("$1\xa0298,00", "$1\xa0298,00", "$", "1 298,00", 1298.00), Example("1\xa0298,00", "1\xa0298,00", None, "1 298,00", 1298.00), Example(None, ".75 €", "€", ".75", 0.75), Example("$.75", "$.75", "$", ".75", 0.75), Example("$..75", "$..75", "$", ".75", 0.75), Example("$.75,333", "$.75,333", "$", ".75,333", 75333), Example("$.750.30", "$.750.30", "$", "750.30", 750.30), Example("i", "i", None, None, None), ] PRICE_PARSING_EXAMPLES_2 = [ Example("7,99 €", "7,99 €", "€", "7,99", 7.99), Example("2 Piece - $6.75 2 Piece - $6.75", "$6.75", "$", "6.75", 6.75), Example("£7", "£24.99", "£", "24.99", 24.99), Example("23,00 €", "23,00 €", "€", "23,00", 23.00), Example("€", "bežná cena 12,96 €", "€", "12,96", 12.96), Example("$", "3,20 €", "€", "3,20", 3.2), Example("€ 2.109,00", "€ 2.109,00", "€", "2.109,00", 2109), Example("руб.", "32690", "руб.", "32690", 32690), Example(": 3 250 000 €", ": 3 250 000 €", "€", "3 250 000", 3250000), Example("$20.00", "$20.00", "$", "20.00", 20), Example("Цена:", "950 грн.", "грн.", "950", 950), Example("TTC", "149,00", None, "149,00", 149.00), Example("A partire da 9,08 €", "166,81 € iva inclusa", "€", "166,81", 166.81), # Example('Ref:', '4229', # None, None, ), Example("EUR", "679.00", "EUR", "679.00", 679.00), Example("10,50 € 10,50 €", "10,50 €", "€", "10,50", 10.50), Example("2,20 € *", "2,20 € *", "€", "2,20", 2.2), Example("€", "€ 10.00 € 8.00 – € 10.00", "€", "10.00", 10), Example("Bestellen", "€16,95", "€", "16,95", 16.95), Example("$", "$ 95.00", "$", "95.00", 95.00), Example("Р", "1 400 Р", "Р", "1 400", 1400), Example("£", "£ 15.00", "£", "15.00", 15), Example("Price: $119.00", "$119.00", "$", "119.00", 119), Example("50,- Kč", "50,- Kč", "Kč", "50", 50), Example("What is this?", "$25.95", "$", "25.95", 25.95), Example("$29.99 Excluding Sales Tax in Illinois", "$29.99", "$", "29.99", 29.99), Example("ID:", "125,00 zł", "zł", "125,00", 125), Example("Offer Price: Rs 1,306.14", "Rs 3,600.00", "Rs", "3,600.00", 3600), Example("29 RON", "29 RON", "RON", "29", 29), Example("Р", "2 690 Р", "Р", "2 690", 2690), Example("$134.96", "$134.96", "$", "134.96", 134.96), Example("362,68 € χωρίς Φ.Π.Α", "449,72 €", "€", "449,72", 449.72), Example("MSRP: $588.00", "499.80", "$", "499.80", 499.80), Example("15.00 €", "15.00 €", "€", "15.00", 15), Example("Цена: от 2 750 руб.", "2 750 руб.", "руб.", "2 750", 2750), Example("P.V.P. 4,56 €", "P.V.P. 4,56 €", "€", "4,56", 4.56), Example("SPECIAL Add to Cart for Price", "$40.00", "$", "40.00", 40), Example("2,90 €", "2,90", "€", "2,90", 2.9), Example("Цена: 19 000 руб.", "Цена: 19 000 руб.", "руб.", "19 000", 19000), Example("COMMANDER", "184,20 €HT", "€", "184,20", 184.2), Example("Цена: 1 849 руб.", "1 849 руб.", "руб.", "1 849", 1849), Example("€", "12,00", "€", "12,00", 12), Example("Discounted Price £ 205.99", "205.99", "£", "205.99", 205.99), Example("SKU", "No longer available", None, None, None), Example("€14.90", "€69.90", "€", "69.90", 69.9), Example("руб.", "876 руб.", "руб.", "876", 876), Example("$79.95", "$79.95", "$", "79.95", 79.95), Example("3,990 Ft 2,990 Ft", "3,990 Ft", "Ft", "3,990", 3990), Example("Cena 298,00 Kč", "288,00 Kč", "Kč", "288,00", 288), Example("Pris NOK 899,00", "899,00", "NOK", "899,00", 899), Example("50,00 €", "50,00 €", "€", "50,00", 50), Example("59,99 €", "54,99 €", "€", "54,99", 54.99), Example("EAN", "139,00 € *", "€", "139,00", 139), Example("Now", "238.00", None, "238.00", 238), Example("(You save $15.01)", "$119.99", "$", "119.99", 119.99), Example("Item #:", "$ 99.95", "$", "99.95", 99.95), Example("UK:", "£14.99", "£", "14.99", 14.99), Example("р.", "150", "р.", "150", 150), Example("12x de R$ 44,00 sem juros", "R$ 528,00", "R$", "528,00", 528), Example("€", "14,90", "€", "14,90", 14.9), Example("5 290 Kč s DPH", "5 290 Kč", "Kč", "5 290", 5290), Example("(Out of Stock)", "39.95", None, "39.95", 39.95), Example("₪370", "₪370", "₪", "370", 370), Example("HUF", "39000", "HUF", "39000", 39000), Example("Kč", "1 995 Kč", "Kč", "1 995", 1995), Example("C$5.95", "C$5.95", "C$", "5.95", 5.95), Example("руб.", "22800.00", "руб.", "22800.00", 22800), Example("Cena", "359 Kč", "Kč", "359", 359), Example("Nuestro precio", "189,00 €", "€", "189,00", 189), Example("PLN", "179.62", "PLN", "179.62", 179.62), Example("€", "145,79", "€", "145,79", 145.79), Example("$", "$ 695.00", "$", "695.00", 695), Example("Р", "2 999 Р", "Р", "2 999", 2999), Example("6,50 € *", "6,50", "€", "6,50", 6.50), Example("Price: $61.00", "$61.00", "$", "61.00", 61.00), Example("Pris NOK 1 999,00", "5 799,00", "NOK", "5 799,00", 5799), Example("7,31 €", "7,31 €", "€", "7,31", 7.31), Example("53,20 €", "53,20 €", "€", "53,20", 53.20), Example("Cena", "668 Kč", "Kč", "668", 668), Example("€ 28,25", "€ 27,95", "€", "27,95", 27.95), Example("From", "From $109.90", "$", "109.90", 109.9), Example("Unit Price: $4.75", "$4.75", "$", "4.75", 4.75), Example("Więcej", "14,90 zł", "zł", "14,90", 14.90), Example("$23.00", "$23.00", "$", "23.00", 23), Example("€ 0.00", "€ 33.00", "€", "33.00", 33), Example("Zľava 36 €", "78 €", "€", "78", 78), Example("£", "5.00", "£", "5.00", 5), Example("€", "29,90", "€", "29,90", 29.9), Example("Р", "860 Р", "Р", "860", 860), Example("60,55 €", "60,55 €", "€", "60,55", 60.55), Example("€", "129.00", "€", "129.00", 129), Example("£13.50", "£13.50", "£", "13.50", 13.50), Example("SKU:", "$39.99", "$", "39.99", 39.99), Example( "Disponibilidade: Pronta entrega R$60,00 até 2x de R$30,00", "R$60,00", "R$", "60,00", 60, ), Example("67,99 €", "64,59 €", "€", "64,59", 64.59), Example("€ 9,90", "€ 13,50", "€", "13,50", 13.50), Example("Р", "30 Р", "Р", "30", 30), Example("€", "€ 139.00", "€", "139.00", 139), Example("There are 163 products.", "From 26 to 50 €", "€", "26", 26), Example("Pris NOK 1 999,00", "139,00", "NOK", "139,00", 139), Example("/sqft", "1.52", None, "1.52", 1.52), Example("$4.95", "$4.95", "$", "4.95", 4.95), Example("$38", "$12", "$", "12", 12), Example("Cena 4.10 €", "4.10", "€", "4.10", 4.1), Example("руб.", "590", "руб.", "590", 590), Example("€", "99,90", "€", "99,90", 99.9), Example("9,50 EUR", "9,50 EUR", "EUR", "9,50", 9.50), Example("$74.99", "$74.99", "$", "74.99", 74.99), Example("$", "$ 3.22", "$", "3.22", 3.22), Example("£", "3,301.00 £", "£", "3,301.00", 3301), Example("41,78 EUR", "41,78 EUR", "EUR", "41,78", 41.78), Example("44,50 EUR", "44,50 EUR", "EUR", "44,50", 44.50), Example("£4.00", "£4.00", "£", "4.00", 4), Example("Р", "7 390 Р", "Р", "7 390", 7390), Example("eMail", "34.99€", "€", "34.99", 34.99), Example("€", "12,99", "€", "12,99", 12.99), Example("RRP: £ 180.00", "149.00", "£", "149.00", 149), Example("9.00 руб.", "27.00 руб.", "руб.", "27.00", 27), Example("Price: $19.95", "$7.95", "$", "7.95", 7.95), Example("$16,500", "$16,500", "$", "16,500", 16500), Example("•", "$59.99", "$", "59.99", 59.99), Example("USD", "349.95", "USD", "349.95", 349.95), Example("35€ 99 dont 0,02 € d'éco-part", "35€ 99", "€", "35€99", 35.99), Example("5,72 €", "5,72 €", "€", "5,72", 5.72), Example("199,00 грн.", "199,00 грн.", "грн.", "199,00", 199), Example("£4.07", "£4.07", "£", "4.07", 4.07), Example("$15.00", "$15.00", "$", "15.00", 15), Example("€", "20.00", "€", "20.00", 20), Example("€", "14.50", "€", "14.50", 14.5), Example("DKK", "59,00", "DKK", "59,00", 59), Example("9 472 Ft", "9 472 Ft", "Ft", "9 472", 9472), Example("Cena", "95 Kč", "Kč", "95", 95), Example("tax excl.", "€ 0,00", "€", "0,00", 0), Example("TL", "69,50", "TL", "69,50", 69.5), Example("Price", "Rp 169.000", "Rp", "169.000", 169000), Example("En stock", "4,50 €", "€", "4,50", 4.50), Example("$183.00", "$145.00", "$", "145.00", 145), Example("£", "11.70", "£", "11.70", 11.7), Example("25,13 €", "25,13 €", "€", "25,13", 25.13), Example("58,00 zł", "58,00 zł", "zł", "58,00", 58), Example("€", "15,55", "€", "15,55", 15.55), Example("Prezzo SVB: 19,95 €", "Prezzo SVB: 19,95 €", "€", "19,95", 19.95), Example("In stock", "$185.00", "$", "185.00", 185), Example("18 €", "18 €", "€", "18", 18), Example("0€", "0 €", "€", "0", 0), Example("*", "9,41 €", "€", "9,41", 9.41), Example("грн.", "1 075", "грн.", "1 075", 1075), Example("More", "$22.99", "$", "22.99", 22.99), Example("Р", "9 282 Р", "Р", "9 282", 9282), Example("RM5", "RM50", "RM", "50", 50), Example("7,60 €", "7,60 €", "€", "7,60", 7.6), Example("$", "0.00", "$", "0.00", 0), Example(". AMOSTRA .", "R$ 6,00", "R$", "6,00", 6), Example( "5 833,00 € -5% 6 140,00 € Tasse incl. IVA 4%", "5 833,00 €", "€", "5 833,00", 5833, ), Example("€ 26,95", "€ 26,95", "€", "26,95", 26.95), Example("35,00 €", "35,00 €", "€", "35,00", 35), Example("Р", "2 500 Р", "Р", "2 500", 2500), Example("499,00 €", "499,00 €", "€", "499,00", 499), Example("руб.", "1 120 900", "руб.", "1 120 900", 1120900), Example("125,00 125,00 €", "125,00", "€", "125,00", 125), Example("₽", "7362", "₽", "7362", 7362), Example("SKU:", "€12.99", "€", "12.99", 12.99), Example("р.", "600", "р.", "600", 600), Example("79,50 €", "29,90€", "€", "29,90", 29.9), Example("$150.00", "$150.00", "$", "150.00", 150), Example("Р", "1 987 Р", "Р", "1 987", 1987), Example("€69.00", "€69.00", "€", "69.00", 69), Example("USD", "280", "USD", "280", 280), Example("Rabatt", "6 450,00", None, "6 450,00", 6450), Example("SKU:", "$178.00", "$", "178.00", 178), Example("$", "$ 300.00", "$", "300.00", 300), Example("0€", "19,1€", "€", "19,1", 19.1), Example("R$49,90", "R$49,90", "R$", "49,90", 49.9), Example("En stock", "790,00 € HT", "€", "790,00", 790), Example("Cena", "Cena 14,79 €", "€", "14,79", 14.79), Example("For Sale", "$395,000", "$", "395,000", 395000), Example("€", "10.14", "€", "10.14", 10.14), Example("₫", "5.00 trên 5", "₫", "5.00", 5), Example("$ 599.00", "549.00", "$", "549.00", 549), Example("$19", "$19", "$", "19", 19), Example("$210", "$210", "$", "210", 210), Example("$", "$ 79.95", "$", "79.95", 79.95), Example("руб.", "279 000", "руб.", "279 000", 279000), Example("2 390 €", "2 390 €", "€", "2 390", 2390), Example("$", "$ 281.40 $ 265.70", "$", "281.40", 281.4), Example("3200 руб. 1500 руб.", "3200 руб. 1500 руб.", "руб.", "3200", 3200), Example("$1,299.00", "$999.00", "$", "999.00", 999), Example("15,00 Kč", "15,00 Kč", "Kč", "15,00", 15), Example("€", "€ 20.70", "€", "20.70", 20.7), Example("out of stock", "30.00", None, "30.00", 30), Example("220,00 €", "398,00 €", "€", "398,00", 398), Example("£94.50 GBP", "£94.50 GBP", "£", "94.50", 94.5), Example("5 427 Kč", "5 427 Kč", "Kč", "5 427", 5427), Example("$ 24.99", "$ 24.99", "$", "24.99", 24.99), Example("£ 2.00", "2.00", "£", "2.00", 2), Example(">", "10,00 €", "€", "10,00", 10), Example("32,50 EUR", "32,50 EUR", "EUR", "32,50", 32.5), Example("In stock", "Rs1,599.00", "Rs", "1,599.00", 1599), Example("$", "14.95", "$", "14.95", 14.95), Example("RRP: £ 49.99", "42.90", "£", "42.90", 42.9), Example("Rs206.00 Rs164.00 SAVE 20%", "Rs164.00", "Rs", "164.00", 164), Example("Price:", "$6,200.00", "$", "6,200.00", 6200), Example("PLN", "69.99", "PLN", "69.99", 69.99), Example("€", "48,39", "€", "48,39", 48.39), Example("1.350.000", "320.000", None, "320.000", 320000), Example("1.650,00 € *", "2.130,00 €", "€", "2.130,00", 2130), Example("£537.00", "£179.00", "£", "179.00", 179), Example("SKU:", "$39.99", "$", "39.99", 39.99), Example("SGD$4.90", "SGD$4.90", "SGD", "4.90", 4.9), Example("SGD4.90 $", "SGD4.90 $", "SGD", "4.90", 4.9), Example("$ SGD4.90", "$ SGD4.90", "SGD", "4.90", 4.9), ] PRICE_PARSING_EXAMPLES_NO_PRICE = [ Example("EUR", None, "EUR", None, None), Example("Kč", None, "Kč", None, None), Example("50", None, None, None, None), Example(">", None, None, None, None), Example("REI", None, None, None, None), Example("rate", None, None, None, None), ] PRICE_PARSING_EXAMPLES_NO_CURRENCY = [ Example(None, "67", None, "67", 67), Example(None, "naša cena 67,30 €", "€", "67,30", 67.30), Example( None, "EUR29.66 (inc VAT 20% - UK & EEC) EUR24.71 (exc VAT 20% - UK & EEC)", "EUR", "29.66", 29.66, ), Example(None, "17,00 €", "€", "17,00", 17), Example(None, "0,29 € + iva", "€", "0,29", 0.29), Example(None, "39,00 €", "€", "39,00", 39), Example(None, "24,00 Kč", "Kč", "24,00", 24), Example(None, "běžná cena 7 940 Kč", "Kč", "7 940", 7940), Example(None, "$46.00", "$", "46.00", 46.00), Example(None, "$9.99 & Under", "$", "9.99", 9.99), Example(None, "běžná cena 459,00 Kč", "Kč", "459,00", 459), Example(None, "$60.00", "$", "60.00", 60), Example(None, "0,82 €", "€", "0,82", 0.82), Example(None, "599 Kč", "Kč", "599", 599), Example(None, "€10.90", "€", "10.90", 10.90), Example(None, "299,00 EUR", "EUR", "299,00", 299), Example(None, "naša cena 21,95 €", "€", "21,95", 21.95), Example(None, "343,05 €", "€", "343,05", 343.05), Example(None, "1 139,00 €", "€", "1 139,00", 1139), Example(None, "157,000 تومان", "تومان", "157,000", 157000), Example(None, "35.00", None, "35.00", 35), Example(None, "8.000.000 ₫", "₫", "8.000.000", 8000000), Example(None, "6790 Dinara", "Dinara", "6790", 6790), Example(None, "3.99", None, "3.99", 3.99), Example(None, "£ 165.00", "£", "165.00", 165.00), Example(None, "$844,900", "$", "844,900", 844900), Example(None, "140,00 pln", "pln", "140,00", 140), Example(None, "2,99 €", "€", "2,99", 2.99), Example(None, "145,00 lei", "lei", "145,00", 145), Example(None, "3,90 €", "€", "3,90", 3.90), Example(None, "$149.99", "$", "149.99", 149.99), Example(None, "4,30 €", "€", "4,30", 4.30), Example(None, "$36.95", "$", "36.95", 36.95), Example(None, "2 499,00 zł", "zł", "2 499,00", 2499), Example(None, "800 руб.", "руб.", "800", 800), Example(None, "89.00", None, "89.00", 89), Example(None, "3 100р.", "р.", "3 100", 3100), Example(None, "0,85 €", "€", "0,85", 0.85), Example(None, "35,95 €", "€", "35,95", 35.95), Example(None, "$0.42", "$", "0.42", 0.42), Example(None, "80,000 تومان", "تومان", "80,000", 80000), Example(None, "550,00 € *", "€", "550,00", 550), Example(None, "25,00 zł", "zł", "25,00", 25), Example(None, "17,45 EUR", "EUR", "17,45", 17.45), Example(None, "49,00 €", "€", "49,00", 49), Example(None, "169.00", None, "169.00", 169), Example(None, "8,99 €", "€", "8,99", 8.99), Example(None, "1 099 Kč", "Kč", "1 099", 1099), Example(None, "17.99", None, "17.99", 17.99), Example(None, "$274.95", "$", "274.95", 274.95), Example(None, "70,20 €", "€", "70,20", 70.20), Example(None, "289,00 zł", "zł", "289,00", 289), Example(None, "18,00 €", "€", "18,00", 18), Example(None, "12,00 €", "€", "12,00", 12), Example(None, "$19.97", "$", "19.97", 19.97), # Example(None, '(save $2.95)', # '$', None, None), Example(None, "749,00 euro", "euro", "749,00", 749), Example(None, "$48.25", "$", "48.25", 48.25), Example(None, "5.00", None, "5.00", 5.00), Example(None, "18,00 € *", "€", "18,00", 18), Example(None, "$3.00", "$", "3.00", 3.00), Example(None, "1,85 EUR", "EUR", "1,85", 1.85), Example(None, "4.25", None, "4.25", 4.25), Example(None, "£1.20", "£", "1.20", 1.20), Example(None, "$196.50", "$", "196.50", 196.50), Example(None, "Price: $129.00", "$", "129.00", 129.00), Example(None, "179,00 €", "€", "179,00", 179.00), Example(None, "$80.00", "$", "80.00", 80.00), Example(None, "14.50", None, "14.50", 14.50), Example(None, "From $ 24.95", "$", "24.95", 24.95), Example(None, "$5.11", "$", "5.11", 5.11), Example(None, "EUR 6,99", "EUR", "6,99", 6.99), Example(None, "40% OFF", None, None, None), Example(None, "29.99", None, "29.99", 29.99), Example(None, "14.00€", "€", "14.00", 14.00), Example(None, "22.00", None, "22.00", 22.00), Example(None, "$1000.00", "$", "1000.00", 1000.00), Example(None, "$12.95", "$", "12.95", 12.95), Example(None, "běžná cena 987,20 Kč", "Kč", "987,20", 987.20), Example(None, "104,64 zł", "zł", "104,64", 104.64), Example(None, "163,80 €", "€", "163,80", 163.80), Example(None, "$89.00", "$", "89.00", 89.00), Example(None, "1 600 руб.", "руб.", "1 600", 1600), Example(None, "20,95 € *", "€", "20,95", 20.95), Example(None, "9,50 €", "€", "9,50", 9.50), Example(None, "170,00 €", "€", "170,00", 170), Example(None, "170,00€", "€", "170,00", 170), Example(None, "6.00", None, "6.00", 6.00), Example(None, "$24.00", "$", "24.00", 24.00), Example(None, "9.95", None, "9.95", 9.95), Example(None, "34.12 (40.94 inc VAT)", None, "34.12", 34.12), Example(None, "Rp 350.000", "Rp", "350.000", 350000), Example(None, "$55.00", "$", "55.00", 55.00), Example(None, "$595.00", "$", "595.00", 595.00), Example(None, "7,00 €", "€", "7,00", 7), Example(None, "119.95", None, "119.95", 119.95), Example(None, "1.95", None, "1.95", 1.95), Example(None, "390,00 €", "€", "390,00", 390), Example(None, "3.24", None, "3.24", 3.24), Example(None, "12 590 Kč", "Kč", "12 590", 12590), Example(None, "330 Kč", "Kč", "330", 330), Example(None, "8 500 руб.", "руб.", "8 500", 8500), Example(None, "589,00 €", "€", "589,00", 589), Example(None, "1,099.99", None, "1,099.99", 1099.99), Example(None, "14 196 Р", "Р", "14 196", 14196), Example(None, "19.00", None, "19.00", 19.00), Example(None, "870 Kč", "Kč", "870", 870), Example(None, "59,00 €", "€", "59,00", 59), Example(None, "Pris från 172 kr", "kr", "172", 172), Example(None, "1 573 Kč", "Kč", "1 573", 1573), Example(None, "$2.99", "$", "2.99", 2.99), Example(None, "13,90 €", "€", "13,90", 13.90), Example(None, "29.95", None, "29.95", 29.95), Example(None, "/", None, None, None), Example(None, "16,90 €", "€", "16,90", 16.90), Example(None, "149.95", None, "149.95", 149.95), Example(None, "8.90", None, "8.90", 8.90), Example(None, "419", None, "419", 419), Example(None, "$50.00", "$", "50.00", 50.00), Example(None, "3 291,00 €", "€", "3 291,00", 3291), Example(None, "13,00 €", "€", "13,00", 13), Example(None, "DKK 449,00", "DKK", "449,00", 449), Example(None, "$20.00", "$", "20.00", 20.00), Example(None, "$154", "$", "154", 154), Example(None, "22.48", None, "22.48", 22.48), Example(None, "20,00 EUR", "EUR", "20,00", 20), Example(None, "73,460 €", "€", "73,460", 73460), Example(None, "850 руб", "руб", "850", 850), Example(None, "$14.99", "$", "14.99", 14.99), Example(None, "$79.95", "$", "79.95", 79.95), Example(None, "40,00 €", "€", "40,00", 40), Example(None, "149,98 €", "€", "149,98", 149.98), Example(None, "1 150 грн.", "грн.", "1 150", 1150), Example(None, "399.00", None, "399.00", 399.00), Example(None, "33,90 €", "€", "33,90", 33.90), Example(None, "79,50 €", "€", "79,50", 79.50), Example(None, "40 130", None, "40 130", 40130), Example(None, "$69.99", "$", "69.99", 69.99), Example(None, "1 090 Kč", "Kč", "1 090", 1090), Example(None, "395 Kč", "Kč", "395", 395), Example(None, "53,95 €", "€", "53,95", 53.95), Example(None, "£0.99", "£", "0.99", 0.99), Example(None, "5,60 € *", "€", "5,60", 5.60), Example(None, "29,50 zł", "zł", "29,50", 29.50), Example(None, "2 990", None, "2 990", 2990), Example(None, "0,00", None, "0,00", 0), Example(None, "$24.99 with card", "$", "24.99", 24.99), Example(None, "18,00€", "€", "18,00", 18), Example(None, "€600,00", "€", "600,00", 600), Example(None, "£25.00 (tax incl.)", "£", "25.00", 25), Example(None, "8,55 €", "€", "8,55", 8.55), Example(None, "1,422.50", None, "1,422.50", 1422.50), Example(None, "244,00 €", "€", "244,00", 244.00), Example(None, "12,90 €", "€", "12,90", 12.90), Example(None, "12 900,00 руб", "руб", "12 900,00", 12900), Example(None, "1.727 Ft", "Ft", "1.727", 1727), Example(None, "79,00 €", "€", "79,00", 79), Example(None, "NZD $100.70", "NZD", "100.70", 100.70), Example(None, "479.00", None, "479.00", 479.00), Example(None, "$ 69.00", "$", "69.00", 69.00), Example(None, "135,00 €", "€", "135,00", 135.00), Example(None, "25.00", None, "25.00", 25.0), Example(None, "94,90 €", "€", "94,90", 94.90), Example(None, "149.99", None, "149.99", 149.99), Example(None, "44,00 €", "€", "44,00", 44.00), Example(None, "$24.99", "$", "24.99", 24.99), Example(None, "22,00 EUR", "EUR", "22,00", 22.00), Example(None, "89,90 €", "€", "89,90", 89.90), Example(None, "$24.95", "$", "24.95", 24.95), Example(None, "£ 1.99", "£", "1.99", 1.99), Example(None, "1 099,00 zł", "zł", "1 099,00", 1099), Example(None, "běžná cena 28 270,00 Kč", "Kč", "28 270,00", 28270), Example(None, "da € 72.00", "€", "72.00", 72.00), Example(None, "$15.95", "$", "15.95", 15.95), Example(None, "تومان56,000", "تومان", "56,000", 56000), Example(None, "$1,695.00", "$", "1,695.00", 1695.00), Example(None, "£595.00", "£", "595.00", 595.00), Example(None, "$11.95", "$", "11.95", 11.95), Example(None, "290,00 Kč", "Kč", "290,00", 290), Example(None, "199.90 Fr.", "Fr.", "199.90", 199.90), Example(None, "197 PLN", "PLN", "197", 197), Example(None, "9.99", None, "9.99", 9.99), Example(None, "$56.00", "$", "56.00", 56.00), Example(None, "4 980 Kč", "Kč", "4 980", 4980), Example(None, "124,00 €", "€", "124,00", 124), Example(None, "$104.99", "$", "104.99", 104.99), Example(None, "39,00 €", "€", "39,00", 39), Example(None, "1 029,00 €", "€", "1 029,00", 1029), Example(None, "Běžná cena 299,00 Kč", "Kč", "299,00", 299), Example(None, "745,00 €", "€", "745,00", 745), Example(None, "$89.00", "$", "89.00", 89), Example(None, "$29.95", "$", "29.95", 29.95), Example(None, "2.00", None, "2.00", 2.00), Example(None, "249.99", None, "249.99", 249.99), Example(None, "24.99", None, "24.99", 24.99), Example(None, "1 499 Kč", "Kč", "1 499", 1499), Example(None, "199,95 €", "€", "199,95", 199.95), Example(None, "6,00 €", "€", "6,00", 6), Example(None, "$28.49", "$", "28.49", 28.49), Example(None, "200.000 đ", "đ", "200.000", 200000), Example(None, "9,24 €", "€", "9,24", 9.24), Example(None, "48,00 €", "€", "48,00", 48.00), Example(None, "Cena : 890 Kč", "Kč", "890", 890), Example(None, "790.00", None, "790.00", 790.00), Example(None, "17 260 руб.", "руб.", "17 260", 17260), Example(None, "227,000 تومان", "تومان", "227,000", 227000), Example(None, "295,88 €", "€", "295,88", 295.88), Example(None, "£1399", "£", "1399", 1399), Example(None, "11,33 Br", "Br", "11,33", 11.33), Example(None, "325.95", None, "325.95", 325.95), Example(None, "$19.50", "$", "19.50", 19.50), Example(None, "19,00 €", "€", "19,00", 19), Example(None, "2 999,00 €", "€", "2 999,00", 2999), Example(None, "49.95", None, "49.95", 49.95), Example(None, "99 LEI", "LEI", "99", 99), Example(None, "249 Kč", "Kč", "249", 249), Example(None, "3.79", None, "3.79", 3.79), Example(None, "běžná cena 890 Kč", "Kč", "890", 890), Example(None, "$809,000", "$", "809,000", 809000), Example(None, "450 000 ₫", "₫", "450 000", 450000), Example(None, "30,00 €", "€", "30,00", 30.00), Example(None, "14.95", None, "14.95", 14.95), Example(None, "12.50", None, "12.50", 12.50), Example(None, "129,00 € (-15%)", "€", "129,00", 129.00), Example(None, "12,90 €", "€", "12,90", 12.90), Example(None, "A partir de 11,70 €", "€", "11,70", 11.70), Example(None, "15.49", None, "15.49", 15.49), Example(None, "12.34 €", "€", "12.34", 12.34), Example(None, "€799,00", "€", "799,00", 799), Example(None, "230 лв.", "лв.", "230", 230), Example(None, "14.55 €", "€", "14.55", 14.55), Example(None, "133,86 LEI", "LEI", "133,86", 133.86), Example(None, "7 990,00 Kč", "Kč", "7 990,00", 7990), Example(None, "350.00", None, "350.00", 350.00), Example(None, "Cena: 55,72 zł brutto", "zł", "55,72", 55.72), Example(None, "O blenderach Omniblend", None, None, None), Example(None, "3,822.00", None, "3,822.00", 3822), Example(None, "0,15 €", "€", "0,15", 0.15), Example(None, "430,00 €", "€", "430,00", 430), Example(None, "$29.00", "$", "29.00", 29.00), Example(None, "39.99", None, "39.99", 39.99), Example(None, "$15.00", "$", "15.00", 15.00), Example(None, "21,00 Lei", "Lei", "21,00", 21.00), Example(None, "naše cena 250,00 Kč", "Kč", "250,00", 250.00), Example(None, "$24.95", "$", "24.95", 24.95), Example(None, "162.18", None, "162.18", 162.18), Example(None, "39,60 EUR", "EUR", "39,60", 39.60), Example(None, "10,75 €", "€", "10,75", 10.75), Example(None, "219 руб.", "руб.", "219", 219), Example(None, "89,00 € *", "€", "89,00", 89.00), Example(None, "151,200 تومان", "تومان", "151,200", 151200), Example(None, "$159.99", "$", "159.99", 159.99), Example(None, "2.49", None, "2.49", 2.49), Example(None, "7.38", None, "7.38", 7.38), Example(None, "62,00 zł", "zł", "62,00", 62.00), Example(None, "$20.00", "$", "20.00", 20), Example(None, "$ 50.00", "$", "50.00", 50), Example(None, "34.99", None, "34.99", 34.99), Example(None, "318,00 €", "€", "318,00", 318), Example(None, "11.499,00 EUR", "EUR", "11.499,00", 11499), Example(None, "571.12坪", None, "571.12", 571.12), # area, not currency Example(None, "€ 75.00", "€", "75.00", 75.00), Example(None, "11,90 € *", "€", "11,90", 11.90), Example(None, "€0.51", "€", "0.51", 0.51), Example(None, "6,50 €", "€", "6,50", 6.50), Example(None, "790 Kč", "Kč", "790", 790), Example(None, "ab 2.99 €", "€", "2.99", 2.99), Example(None, "369", None, "369", 369), Example(None, "134.96", None, "134.96", 134.96), Example(None, "135 lei", "lei", "135", 135), Example(None, "2,99 € *", "€", "2,99", 2.99), Example(None, "$9.99", "$", "9.99", 9.99), Example(None, "2.950,00 €", "€", "2.950,00", 2950), Example(None, "19.99", None, "19.99", 19.99), Example(None, "49 lei", "lei", "49", 49), Example(None, "31,07 € (bez DPH)", "€", "31,07", 31.07), Example(None, "56.00", None, "56.00", 56.00), Example(None, "54.95", None, "54.95", 54.95), Example(None, "$ 80.00", "$", "80.00", 80.00), Example(None, "$39.00", "$", "39.00", 39.00), Example(None, "Rp 221.000", "Rp", "221.000", 221000), Example(None, "35,90 EUR", "EUR", "35,90", 35.90), Example(None, "4 835,50 €", "€", "4 835,50", 4835.50), Example(None, "75,00€", "€", "75,00", 75), Example(None, "$21.95", "$", "21.95", 21.95), Example(None, "737,00", None, "737,00", 737), Example(None, "129,00 € **", "€", "129,00", 129), Example(None, "2 399 Kč", "Kč", "2 399", 2399), Example(None, "430 руб", "руб", "430", 430), Example(None, "69.95", None, "69.95", 69.95), Example(None, "$0.00", "$", "0.00", 0), Example(None, "49.56", None, "49.56", 49.56), Example(None, "0,00 EUR", "EUR", "0,00", 0), Example(None, "145,00 Kč", "Kč", "145,00", 145), Example(None, "99,00 lei", "lei", "99,00", 99), Example(None, "$750,000", "$", "750,000", 750000), Example(None, "$49.99", "$", "49.99", 49.99), Example(None, "29.00", None, "29.00", 29.00), Example(None, "$7.20", "$", "7.20", 7.20), Example(None, "69.00", None, "69.00", 69.00), Example(None, "4.47", None, "4.47", 4.47), Example(None, "39,90 € *", "€", "39,90", 39.90), Example(None, "469,00 €", "€", "469,00", 469), Example(None, "24.38", None, "24.38", 24.38), Example(None, "6,24", None, "6,24", 6.24), Example(None, "$89.00", "$", "89.00", 89.00), Example(None, "24,35 €", "€", "24,35", 24.35), Example(None, "Pris från 805 kr", "kr", "805", 805), Example(None, "295 Kč", "Kč", "295", 295), Example(None, "175.00", None, "175.00", 175.00), Example(None, "7 990 kr", "kr", "7 990", 7990), Example(None, "14,00 €", "€", "14,00", 14), Example(None, "249 Kč", "Kč", "249", 249), Example(None, "£39.95", "£", "39.95", 39.95), Example(None, "10,75 TL", "TL", "10,75", 10.75), Example(None, "$25.00", "$", "25.00", 25.00), Example(None, "1 720,00 zł", "zł", "1 720,00", 1720), Example(None, "běžná cena 749 Kč", "Kč", "749", 749), Example(None, "425,00 €", "€", "425,00", 425), Example(None, "59.00", None, "59.00", 59.00), Example(None, "1,120.00", None, "1,120.00", 1120), Example(None, "a partire da 7,32 € *", "€", "7,32", 7.32), Example(None, "148.50 Inc GST", None, "148.50", 148.50), # India? Example(None, "7.49", None, "7.49", 7.49), Example(None, "80.00", None, "80.00", 80.00), Example(None, "93 499 Kč", "Kč", "93 499", 93499), Example(None, "1.599,00 € *", "€", "1.599,00", 1599), Example(None, "ab 3,63 EUR", "EUR", "3,63", 3.63), Example(None, "29,90 EUR", "EUR", "29,90", 29.90), Example(None, "$3.95", "$", "3.95", 3.95), Example(None, "3430 лв.", "лв.", "3430", 3430), Example(None, "724,00 €", "€", "724,00", 724), Example(None, "18,00 €", "€", "18,00", 18), Example(None, "6,75 €", "€", "6,75", 6.75), Example(None, "29,90 € *", "€", "29,90", 29.90), Example(None, "135.99", None, "135.99", 135.99), Example(None, "30,000 تومان", "تومان", "30,000", 30000), # Example(None, '191.6 KB', # None, None, None), Example(None, "1 500 Kč", "Kč", "1 500", 1500), Example(None, "349,00 €", "€", "349,00", 349), Example(None, "$250.00", "$", "250.00", 250.00), Example(None, "44.95", None, "44.95", 44.95), Example(None, "$22.75", "$", "22.75", 22.75), Example(None, "250,00 €", "€", "250,00", 250), Example(None, "14.96 €", "€", "14.96", 14.96), Example(None, "$4,350.00", "$", "4,350.00", 4350), Example(None, "379 Kč", "Kč", "379", 379), Example(None, "19,50 EUR", "EUR", "19,50", 19.5), Example(None, "33,68 zł", "zł", "33,68", 33.68), Example(None, "6.70€", "€", "6.70", 6.70), Example(None, "$29.99", "$", "29.99", 29.99), Example(None, "6.50", None, "6.50", 6.50), ] PRICE_PARSING_EXAMPLES_3 = [ Example("R$", "R$ 139,99 R$ 135,99", "R$", "139,99", 139.99), Example("£", "£ 34.99", "£", "34.99", 34.99), Example("Price", "$7.65", "$", "7.65", 7.65), Example("€", "75,00", "€", "75,00", 75), Example(None, "34,90 €", "€", "34,90", 34.90), Example(None, "629.95", None, "629.95", 629.95), Example("11000 руб.", "47700 руб.", "руб.", "47700", 47700), Example("$29.99 – $74.99", "$29.99", "$", "29.99", 29.99), Example(None, "174,00 €", "€", "174,00", 174), Example(None, "18,00 €", "€", "18,00", 18), Example("Price:", "$19.50", "$", "19.50", 19.50), Example("8 390 руб.", "8 390 руб.", "руб.", "8 390", 8390), Example(None, "55,00 €", "€", "55,00", 55), Example("€333.00", "€299.71", "€", "299.71", 299.71), Example(None, "384,00 €", "€", "384,00", 384), Example("From:", "From: $14.97", "$", "14.97", 14.97), Example(None, "0,00 €", "€", "0,00", 0), Example(None, "€ 280,00", "€", "280,00", 280), Example(None, "11 450 Kč", "Kč", "11 450", 11450), Example("Price $118.15", "$118.15", "$", "118.15", 118.15), Example("€", "49.99", "€", "49.99", 49.99), Example("1,15 €", "1,15 €", "€", "1,15", 1.15), Example("17,99 €", "31,93 €", "€", "31,93", 31.93), Example(None, "र24,401", "र", "24,401", 24401), Example(None, "$60.00", "$", "60.00", 60), Example("12,15 €", "12,15 €", "€", "12,15", 12.15), Example("£ 163.95", "163.95", "£", "163.95", 163.95), Example(None, "30,00 €", "€", "30,00", 30), Example("zł", "165,00 zł", "zł", "165,00", 165), Example("469.00zł Bez podatku: 381.30zł", "469.00zł", "zł", "469.00", 469), Example(None, "72.95", None, "72.95", 72.95), Example("Costo: $2,222.- Más IVA", "MX$3,179.00", "MX$", "3,179.00", 3179), Example(None, "naše cena 4 370 Kč", "Kč", "4 370", 4370), Example("€", "21,33 €", "€", "21,33", 21.33), Example(None, "49.95", None, "49.95", 49.95), Example(None, "Před slevou 59 900 Kč", "Kč", "59 900", 59900), Example("ab", "6,78 € *", "€", "6,78", 6.78), Example(None, "442", None, "442", 442), Example("18.10 €", "16.00 €", "€", "16.00", 16.00), Example(None, "0.00", None, "0.00", 0.00), Example(None, "379,00 € *", "€", "379,00", 379.00), Example(None, "125.00", None, "125.00", 125.00), Example(None, "£ 30.84", "£", "30.84", 30.84), Example(None, "259,00 €", "€", "259,00", 259), Example("à partir de 1540 € / pers", "1540 €", "€", "1540", 1540), Example(None, "95 €", "€", "95", 95), Example(None, "53.79", None, "53.79", 53.79), Example("NT$", "NT$ 1,160", "NT$", "1,160", 1160), Example("ACTIVE", "$69,900", "$", "69,900", 69900), Example(None, "$14.95", "$", "14.95", 14.95), Example("₹", "₹ 4649", "₹", "4649", 4649), Example("25 грн", "25 грн", "грн", "25", 25), Example("€", "16,40", "€", "16,40", 16.40), Example("PLN", "0,46", "PLN", "0,46", 0.46), Example("£", "£ 261.25", "£", "261.25", 261.25), Example(None, "$0.00", "$", "0.00", 0), Example(None, "24.95", None, "24.95", 24.95), Example("грн.", "27.00", "грн.", "27.00", 27.00), Example("New", "$189,900", "$", "189,900", 189900), Example("NA", "$269", "$", "269", 269), Example("$279", "$189", "$", "189", 189), Example(None, "160,00 zł", "zł", "160,00", 160), Example("2 069 рублей", "2 400", "руб", "2 400", 2400), Example("Sale Price: $4.59", "$4.59", "$", "4.59", 4.59), Example("Купить", "542 руб.", "руб.", "542", 542), Example(None, "$19.99", "$", "19.99", 19.99), Example("Price", "$6.45", "$", "6.45", 6.45), Example(None, "32.99", None, "32.99", 32.99), Example(None, "$86.44", "$", "86.44", 86.44), Example(None, "25.00€", "€", "25.00", 25.00), Example(None, "99,00 €", "€", "99,00", 99.00), Example(None, "103.90", None, "103.90", 103.90), Example("14,00 € *", "25,00 € *", "€", "25,00", 25.00), Example(None, "$6.49", "$", "6.49", 6.49), Example("€ 59,95", "€ 59,95", "€", "59,95", 59.95), Example(None, "Běžná cena 75 990,00 Kč", "Kč", "75 990,00", 75990), Example("Price", "Rp 1.550.000", "Rp", "1.550.000", 1550000), Example("грн.", "1 430", "грн.", "1 430", 1430), Example("руб. (шт)", "1 690,54 руб. (шт)", "руб.", "1 690,54", 1690.54), Example("69 TL 41.90 TL", "69 TL 41.90 TL", "TL", "69", 69), Example("ALIDAD", "960,00 €", "€", "960,00", 960), Example(None, "184,35 lei", "lei", "184,35", 184.35), Example(None, "1 505 Kč", "Kč", "1 505", 1505), Example(None, "23,00 € *", "€", "23,00", 23), Example(None, "25.97", None, "25.97", 25.97), Example(None, "58,19 €", "€", "58,19", 58.19), Example(None, "27.00 лв.", "лв.", "27.00", 27.00), Example("48,00 €", "3,85 €", "€", "3,85", 3.85), Example(None, "10,90 €", "€", "10,90", 10.90), Example("$ 879.0", "$ 879.0", "$", "879.0", 879.0), Example("EUR", "25.88", "EUR", "25.88", 25.88), Example(None, "R$215,10", "R$", "215,10", 215.10), Example("£", "£ 12.50", "£", "12.50", 12.50), Example(None, "3 173,00 €", "€", "3 173,00", 3173), Example(None, "34,94 € *", "€", "34,94", 34.94), Example(None, "Ops!", None, None, None), Example(None, "392. 00", None, "392. 00", 392), Example("€", "213,62", "€", "213,62", 213.62), Example("3,00 €", "3,00 €", "€", "3,00", 3), Example("£0.00", "£0.00", "£", "0.00", 0.00), Example("€", "10 990,00", "€", "10 990,00", 10990), Example(None, "€ 24,95", "€", "24,95", 24.95), Example(None, "Not Available", None, None, None), Example(None, "$19.99", "$", "19.99", 19.99), Example("Р", "15 130 Р", "Р", "15 130", 15130), Example("$5.95", "$5.95", "$", "5.95", 5.95), Example(None, "199,99 €", "€", "199,99", 199.99), Example("Code", "£23.40", "£", "23.40", 23.40), Example("$29.99", "$29.99", "$", "29.99", 29.99), Example(None, "795", None, "795", 795), Example( ( "Sorry, this item is currently out of stock but you can still" " order, we will send as soon a product arrives" ), "34.99", None, "34.99", 34.99, ), Example("Our Price: $149.95", "Our Price: $149.95", "$", "149.95", 149.95), Example("$119.95", "$119.95", "$", "119.95", 119.95), Example(None, "339 грн", "грн", "339", 339), Example("$0.00", "$0.00", "$", "0.00", 0.00), Example("€", "79,00", "€", "79,00", 79.00), Example(None, "378.00", None, "378.00", 378.00), Example(None, "Pure & IP BP Ph. Eur. USP ACS AR LR", None, None, None), Example(None, "$356.03", "$", "356.03", 356.03), Example("naše cena", "běžná cena 890 Kč", "Kč", "890", 890), Example(None, "$49.99", "$", "49.99", 49.99), Example(None, "5 550 Kč", "Kč", "5 550", 5550), Example(None, "5 770 Kč", "Kč", "5 770", 5770), Example(None, "Free!", None, "0", 0), Example("194 ₹", "199 ₹", "₹", "199", 199), Example("5€", "16,50 € *", "€", "16,50", 16.50), Example(None, "$42.95", "$", "42.95", 42.95), Example(None, "1.837, 32 €", "€", "1.837, 32", 1837.32), Example("$", "$ 791.00 $ 479.00", "$", "791.00", 791.00), Example(None, "$69.30", "$", "69.30", 69.30), Example(None, "$163,900", "$", "163,900", 163900), Example(None, "36.95", None, "36.95", 36.95), Example("Rp 235.000", "Rp 235.000", "Rp", "235.000", 235000), Example("£", "11,13 €", "€", "11,13", 11.13), Example(None, "160,00 lei", "lei", "160,00", 160), Example("3 300 руб", "3 300 руб", "руб", "3 300", 3300), Example("Р", "4 690 Р", "Р", "4 690", 4690), Example("189,00 € *", "189,00 € *", "€", "189,00", 189), Example("€", None, "€", None, None), Example("$ 30.00", "$ 30.00", "$", "30.00", 30.00), Example("$", "$ 5.95", "$", "5.95", 5.95), Example("£62.90", "£74.00", "£", "74.00", 74.00), Example(None, "158,24 €", "€", "158,24", 158.24), Example(None, "550,00 лв", "лв", "550,00", 550), Example("7,25 € *", "7,25 € *", "€", "7,25", 7.25), Example(None, "94,000 تومان", "تومان", "94,000", 94000), Example(None, "$8.27", "$", "8.27", 8.27), Example("Đã có VAT", "12.500 ₫", "₫", "12.500", 12500), Example(None, "27.50", None, "27.50", 27.50), Example("23.90", "23.90", None, "23.90", 23.90), Example("Р", "18 000 Р", "Р", "18 000", 18000), Example(None, "48,96 €", "€", "48,96", 48.96), Example("DKK", "199 DKK", "DKK", "199", 199), Example("Price: £6.95 - £9.95", "£6.95 - £9.95", "£", "6.95", 6.95), Example(None, "599.97", None, "599.97", 599.97), Example(None, "$40.00", "$", "40.00", 40.00), Example("Cena 300,00 Kč", "100,00 Kč", "Kč", "100,00", 100), Example("18,25 €", "18,25 €", "€", "18,25", 18.25), Example(None, "29,00 €", "€", "29,00", 29), Example("€", "€ 39,95", "€", "39,95", 39.95), Example(None, "32.00", None, "32.00", 32.00), Example(None, "32.99", None, "32.99", 32.99), Example("HUF", "39000", "HUF", "39000", 39000), Example(None, "850,000 ریال", "ریال", "850,000", 850000), Example(None, "24,00 €", "€", "24,00", 24.00), Example("Versand", "CHF 19.90", "CHF", "19.90", 19.90), Example("", "530,42 Zł", "Zł", "530,42", 530.42), Example(None, "Was: $124.95 Now: $0.00", "$", "124.95", 124.95), Example(None, "Our Price 344.99 - 444.99", None, "344.99", 344.99), Example(None, "3089.9 *", None, "3089.9", 3089.9), Example(None, "40.00/each", None, "40.00", 40.00), Example(None, "105. –", None, "105.", 105.0), Example(None, "1.899,-", None, "1.899", 1899.0), Example(None, "Sheet Set", None, None, None), Example(None, "36. 24", None, "36. 24", 36.24), Example(None, "32جن", None, "32", 32.0), # جن is not a currency Example(None, "Price 29", None, "29", 29.0), Example( None, "559. 99 undefined 559.99 inkl. MwSt. zzgl. Versand", None, "559. 99", 559.99, ), Example(None, "$36 /", "$", "36", 36.0), Example(None, "1.800.000₫", "₫", "1.800.000", 1800000.0), # Vietnamese dong (VND) Example(None, "0,40 LEI", "LEI", "0,40", 0.4), # Romanian Leu (RON) Example(None, "R$ 1.500,00", "R$", "1.500,00", 1500.0), Example(None, "$ 8. 94", "$", "8. 94", 8.94), Example(None, "$30.56/Month", "$", "30.56", 30.56), Example(None, "from $742.50", "$", "742.50", 742.5), Example(None, "25,00 € *", "€", "25,00", 25.0), Example(None, "$3,690.00 (ea)", "$", "3,690.00", 3690.0), Example(None, "AED 26.30", "AED", "26.30", 26.3), Example(None, "15,000 руб.", "руб.", "15,000", 15000.0), Example(None, "$5,246.58", "$", "5,246.58", 5246.58), Example(None, "118, 99 TL", "TL", "118, 99", 118.99), Example(None, "$575.00*", "$", "575.00", 575.0), Example(None, "150 kr", "kr", "150", 150.0), Example(None, "Estimate Price: $45,000 - $70,000", "$", "45,000", 45000.0), Example(None, "Your Price: $7.99", "$", "7.99", 7.99), Example(None, "$80.00 U.S.", "$", "80.00", 80.0), Example(None, "253,89 lei", "lei", "253,89", 253.89), Example(None, "$1,190.00 AUD", "AUD", "1,190.00", 1190.0), Example(None, "€6.56 + VAT", "€", "6.56", 6.56), Example(None, "4,35 € TTC", "€", "4,35", 4.35), Example(None, "1.890,00 DKK", "DKK", "1.890,00", 1890.0), Example(None, "₹397.00", "₹", "397.00", 397.0), Example(None, "- $44.99", "$", "44.99", 44.99), Example(None, "AU$ 1,175", "AU$", "1,175", 1175.0), Example(None, "130, 38 zł", "zł", "130, 38", 130.38), Example(None, "C$1.23", "C$", "1.23", 1.23), Example(None, "CA$1.23", "CA$", "1.23", 1.23), Example(None, "1 ؋", "؋", "1", 1.0), Example(None, "1 Lek", "Lek", "1", 1.0), Example(None, "1 ֏", "֏", "1", 1.0), Example(None, "1 ƒ", "ƒ", "1", 1.0), Example(None, "1 ₼", "₼", "1", 1.0), Example(None, "1 ৳", "৳", "1", 1.0), Example(None, "1 Br", "Br", "1", 1.0), Example(None, "1 BZ$", "BZ$", "1", 1.0), Example(None, "1 Nu.", "Nu.", "1", 1.0), Example(None, "1 KM", "KM", "1", 1.0), Example(None, "1 R$", "R$", "1", 1.0), Example(None, "1 лв", "лв", "1", 1.0), Example(None, "1 FBu", "FBu", "1", 1.0), Example(None, "1 BD", "BD", "1", 1.0), # Bahraini Dinar Example(None, "1 ៛", "៛", "1", 1.0), Example(None, "1 CFA", "CFA", "1", 1.0), Example(None, "1 FCFA", "FCFA", "1", 1.0), Example(None, "1 KMF", "KMF", "1", 1.0), Example(None, "1 ₣", "₣", "1", 1.0), Example(None, "1 ₡", "₡", "1", 1.0), Example(None, "1 kn", "kn", "1", 1.0), Example(None, "1 CUC$", "CUC$", "1", 1.0), Example(None, "1 ₱", "₱", "1", 1.0), Example(None, "1 Kč", "Kč", "1", 1.0), Example(None, "1 Fdj", "Fdj", "1", 1.0), Example(None, "1 RD$", "RD$", "1", 1.0), Example(None, "1 Nfk", "Nfk", "1", 1.0), Example(None, "1 GH₵", "GH₵", "1", 1.0), # Ghanaian cedi Example(None, "1 Ft", "Ft", "1", 1.0), Example(None, "1 Rp", "Rp", "1", 1.0), Example(None, "1 ﷼", "﷼", "1", 1.0), # Saudi riyal Example(None, "1 ₪", "₪", "1", 1.0), Example(None, "1 J$", "J$", "1", 1.0), Example(None, "1 KD", "KD", "1", 1.0), # Kuwaiti dinar Example(None, "1 ₭", "₭", "1", 1.0), # Lao kip Example(None, "1 LD", "LD", "1", 1.0), Example(None, "1 MOP$", "MOP$", "1", 1.0), Example(None, "1 MK", "MK", "1", 1.0), Example(None, "1 RM", "RM", "1", 1.0), Example(None, "1 Rf", "Rf", "1", 1.0), Example(None, "1 UM", "UM", "1", 1.0), Example(None, "1 ₨", "₨", "1", 1.0), Example(None, "1 ₮", "₮", "1", 1.0), Example(None, "1 ƒ", "ƒ", "1", 1.0), Example(None, "1 C$", "C$", "1", 1.0), Example(None, "1 ₦", "₦", "1", 1.0), Example(None, "1 B/.", "B/.", "1", 1.0), Example(None, "1 S/.", "S/.", "1", 1.0), Example(None, "1 ₽", "₽", "1", 1.0), Example(None, "1 Db", "Db", "1", 1.0), Example(None, "1 NT$", "NT$", "1", 1.0), Example(None, "1 ЅM", None, "1", 1.0), # It's not a currency Example(None, "1 TSh", "TSh", "1", 1.0), Example(None, "1 T$", "T$", "1", 1.0), Example(None, "1 TT$", "TT$", "1", 1.0), Example(None, "1 DT", "DT", "1", 1.0), Example(None, "1 ₺", "₺", "1", 1.0), Example(None, "1 USh", "USh", "1", 1.0), Example(None, "1 ₴", "₴", "1", 1.0), Example(None, "1 $U", "$U", "1", 1.0), Example(None, "1 VT", "VT", "1", 1.0), Example(None, "1 Bs", "Bs", "1", 1.0), Example(None, "1 ZK", "ZK", "1", 1.0), Example(None, "1 Z$", "Z$", "1", 1.0), Example(None, "1 ₿", "₿", "1", 1.0), Example(None, "1 Br", "Br", "1", 1.0), # Ethiopian birr, Belarusian ruble Example(None, "1 美股", None, "1", 1.0), # not a currency Example(None, "1 GEL", "GEL", "1", 1.0), # Georgian lari Example(None, "1 FG", "FG", "1", 1.0), # Guinean franc Example(None, "14.00 SGD / Each", "SGD", "14.00", 14.0), # Singapore Dollar ] PRICE_PARSING_EXAMPLES_XFAIL = [ # amount is picked as a price Example( "3 Ausgaben für nur 14,85 EUR", "3 Ausgaben für nur 14,85 EUR", "EUR", "14,85", 14.85, ), Example(None, "Buy Now - 2 Litre Was $120.00 Now $60.00", "$", "60.00", 60), Example( "Цена: уточняйте (мин. заказ: 1 )", "Цена: уточняйте (мин. заказ: 1 )", None, None, None, ), Example( None, "50 - $2.00 100 - $2.75 400 - $4.50 1,000 - $9.00 " "2,000 - $17.00 3,000 - $24.00 10,000 - $75.00", "$", "2.00", 2, ), # no detection of such single-letter currencies Example("R273.00", "R273.00", "R", "273.00", 273), Example("R8,499", "R8,499", "R", "8,499", 8499), Example("Cuneo", "61.858 L", "L", "61.858", 61858), # Romanian New Leu # "р" / "руб" is detected as currency Example(">", "См. цену в прайсе", None, None, None), Example("Купить", "Печная труба", None, None, None), Example(None, "Код товара: 884", None, "884", 884.0), # dates Example(None, "July, 2004", None, None, None), Example(None, "15.08.2017", None, None, None), # other incorrectly extracted prices Example("8.5", "25-09", None, None, None), # misc Example("of", "16.00 ft", None, None, None), # Example('7 724 134.40 114.32', '7 724 134.40 114.32', # '', ''), # Example('中古価格(税込): ¥20,800', '132', # '¥', '132', 132), Example( "Free Shipping on Orders $49+.", "Free Shipping on Orders $49+.", "$", None, None, ), ] # Valid currencies not detected by price-parser # Move to regular tests when added PRICE_PARSING_EXAMPLES_XFAIL_CURRENCIES_TO_BE_ADDED = [ Example(None, "22000.00元/台", "元", "22000.00", 22000.00), # 元 is yuan Example(None, "2963yen", "yen", "2963", 2963), Example(None, "1 บาท", "บาท", "1", 1.0), # Thai baht Example(None, "1 ر.س", "ر.س", "1", 1.0), # Saudi riyal Example(None, "1.198,- Kr", "Kr", "1.198", 1198.0), Example(None, "45 ج.م", "ج.م", "45", 45.0), # Egyptian Pound (EGP) Example(None, "45 E£", "E£", "45", 45.0), # Egyptian Pound (EGP) Example(None, "2000 zl", "zl", "2000", 2000.0), # Polish zloty (PLN) Example(None, "CAN$1.23", "CAN$", "1.23", 1.23), Example(None, "200q", "q", "200", 200.0), # Guatemalan Quetzal Example(None, "200Q", "Q", "200", 200.0), # Guatemalan Quetzal Example(None, "1 دج", "دج", "1", 1.0), # Algerian Dinar Example(None, "1 .د.ب", ".د.ب", "1", 1.0), # Bahraini Dinar Example(None, "1 $b", "$b", "1", 1.0), # Barbadian dollar Example(None, "1 P", "P", "1", 1.0), # Pound sterling, Botswana pula Example(None, "1 franc", "franc", "1", 1.0), Example(None, "2 francs", "francs", "2", 2.0), Example(None, "1 ናቕፋ", "ናቕፋ", "1", 1.0), # Eritrean Nakfa Example( None, "1 نافكا", # Arabic "نافكا" stands for "Navka", # which can refer to Eritrean Nakfa "نافكا", "1", 1.0, ), Example(None, "1 E", "E", "1", 1.0), # Can refer to Egyptian pound Example(None, "1 ብር", "ብር", "1", 1.0), # Ethiopian birr Example(None, "1 D", "D", "1", 1.0), # Gambian dalasi Example(None, "1 ლ", "ლ", "1", 1.0), # Georgian lari Example(None, "1 ¢", "¢", "1", 1.0), # Ghanaian cedi, cents Example(None, "1 ₵", "₵", "1", 1.0), # Ghanaian cedi Example(None, "1 GFr", "GFr", "1", 1.0), # Guinean franc Example(None, "1 L", "L", "1", 1.0), Example(None, "1 ع.د", "ع.د", "1", 1.0), # Iraqi dinar Example( None, "1 د.", # Arabic "د." stands for dr. refers to "dinar" and "dirham": # United Arab Emirates dirham (AED), Bahraini dinar (BHD), # Algerian dinar (DZD), etc. "د.", "1", 1.0, ), Example(None, "1 KSh", "KSh", "1", 1.0), # Kenyan shilling Example(None, "1 د.ك", "د.ك", "1", 1.0), # Kuwaiti dinar Example(None, "1 ل.د", "ل.د", "1", 1.0), # Libyan dinar Example(None, "1 ден", "ден", "1", 1.0), # Macedonian denar Example(None, "1 ДЕН", "ДЕН", "1", 1.0), # Macedonian denar Example(None, "1 Ar", "Ar", "1", 1.0), # Malagasy ariary Example(None, "1 د.إ", "د.إ", "1", 1.0), # United Arab Emirates dirham Example(None, "1 MT", "MT", "1", 1.0), # Mozambican metical Example(None, "1 K", "K", "1", 1.0), # Papua New Guinean kina Example(None, "1 FRw", "FRw", "1", 1.0), # Rwandan Franc Example(None, "1 RF", "RF", "1", 1.0), # Rwandan Franc Example(None, "1 R₣", "R₣", "1", 1.0), # Rwandan Franc Example(None, "1 Дин", "Дин", "1", 1.0), Example(None, "1 SPL", "SPL", "1", 1.0), # Seborgan Luigino (SPL) Example(None, "1 ج.س.", "ج.س.", "1", 1.0), # Sudanese pound Example(None, "1 د.ت", "د.ت", "1", 1.0), # Tunisian dinar Example(None, "AU $59.95", "AU $", "59.95", 59.95), Example(None, "US $1.23", "US $", "1.23", 1.23), Example(None, "CAD$1.23", "CAD$", "1.23", 1.23), Example(None, "1 G$", "G$", "1", 1.0), # Guyanese dollar Example(None, "1 GY$", "GY$", "1", 1.0), # Guyanese dollar Example(None, "1 BTC", "BTC", "1", 1.0), # Bitcoin Example(None, "1 CHf", "CHf", "1", 1.0), # Swiss franc Example(None, "1 Dh", "Dh", "1", 1.0), # United Arab Emirates dirham Example(None, "1 Dhs", "Dhs", "1", 1.0), # United Arab Emirates dirham Example(None, "1 Esc", "Esc", "1", 1.0), # Cape Verdean escudo Example(None, "1 Fbu", "Fbu", "1", 1.0), # Burundian franc Example(None, "1 ID", "ID", "1", 1.0), # Iraqi dinar Example(None, "1 Ks", "Ks", "1", 1.0), # Burmese kyat Example(None, "1 NT", "NT", "1", 1.0), # New Taiwan dollar Example(None, "1 Nu", "Nu", "1", 1.0), # Bhutanese ngultrum Example(None, "1 Rbl", "Rbl", "1", 1.0), # Belarusian ruble, Russian ruble Example(None, "1 Re", "Re", "1", 1.0), # Indian rupee, Sri Lankan rupee Example(None, "1 S/", "S/", "1", 1.0), # Peruvian sol Example(None, "1 SFr", "SFr", "1", 1.0), # Swiss franc Example(None, "1 Sl", "Sl", "1", 1.0), # Somaliland shilling Example(None, "1 VNĐ", "VNĐ", "1", 1.0), # Vietnamese đồng Example(None, "1 Vt", "Vt", "1", 1.0), # Vanuatu vatu Example(None, "1 din", "din", "1", 1.0), # Serbian dinar Example(None, "1 hrn", "hrn", "1", 1.0), # Ukrainian hryvnia Example(None, "1 kwz", "kwz", "1", 1.0), # Angolan kwanza Example(None, "1 lari", "lari", "1", 1.0), # Georgian lari Example(None, "1 nis", "nis", "1", 1.0), # Israeli new shekel Example(None, "1 pound", "pound", "1", 1.0), # Pound sterling Example(None, "1 Sʻ", "Sʻ", "1", 1.0), # Uzbekistani soʻm Example(None, "1 S'", "S'", "1", 1.0), # Uzbekistani soʻm Example(None, "1 so'm", "so'm", "1", 1.0), # Uzbekistani soʻm Example(None, "1 som", "som", "1", 1.0), # Uzbekistani soʻm Example(None, "1 stg", "stg", "1", 1.0), # Pound sterling Example(None, "1 yuan", "yuan", "1", 1.0), # Renminbi/Chinese yuan Example(None, "1 Ƀ", "Ƀ", "1", 1.0), # Bitcoin Example(None, "1 дин", "дин", "1", 1.0), # Serbian dinar Example(None, "1 км", "км", "1", 1.0), # Bosnia and Herzegovina convertible mark Example(None, "1 с", "с", "1", 1.0), # Kyrgyz som, Tajikistani somoni Example(None, "1 ש״ח", "ש״ח", "1", 1.0), # Israeli new shekel Example(None, "1 ج.س", "ج.س", "1", 1.0), # Sudanese pound Example(None, "1 د.أ", "د.أ", "1", 1.0), # Jordanian dinar Example(None, "1 د.ا", "د.ا", "1", 1.0), # Jordanian dinar Example(None, "1 د.ج", "د.ج", "1", 1.0), # Algerian dinar Example(None, "1 د.م", "د.م", "1", 1.0), # Moroccan dirham Example( None, "1 دينار", # Bahraini dinar, Algerian dinar, Iraqi dinar, # Jordanian dinar, Kuwaiti dinar, Libyan dinar, # Tunisian dinar "دينار", "1", 1.0, ), Example(None, "1 دينار أردني", "دينار أردني", "1", 1.0), # Jordanian dinar Example(None, "1 دينار كويتي", "دينار كويتي", "1", 1.0), # Kuwaiti dinar Example(None, "1 ر.ع", "ر.ع", "1", 1.0), # Omani rial Example(None, "1 ر.ق", "ر.ق", "1", 1.0), # Qatari riyal Example(None, "1 ش.ج", "ش.ج", "1", 1.0), # Israeli new shekel Example(None, "1 ل.س", "ل.س", "1", 1.0), # Syrian pound Example(None, "1 ل.ل", "ل.ل", "1", 1.0), # Lebanese pound Example(None, "1 ரூ", "ரூ", "1", 1.0), # Sri Lankan rupee Example(None, "1 රු", "රු", "1", 1.0), # Sri Lankan rupee Example(None, "1 ლარი", "ლარი", "1", 1.0), # Georgian lari Example(None, "1 人民币", "人民币", "1", 1.0), # Renminbi/Chinese yuan Example(None, "1 圆", "圆", "1", 1.0), # Renminbi/Chinese yuan Example(None, "1 圓", "圓", "1", 1.0), # Renminbi/Chinese yuan Example(None, "1 GBp", "GBp", "1", 1.0), # British Pound Example(None, "1 £", "£", "1", 1.0), # British Pound Example(None, "1 nzd", "nzd", "1", 1.0), # New Zealand dollar Example(None, "1 mkd", "mkd", "1", 1.0), # Macedonian denar Example(None, "CND", "CND", "1", 1.0), # Canadian dollar Example(None, "KShs", "KShs", "1", 1.0), # Kenyan shilling Example(None, "chf", "chf", "1", 1.0), # Swiss franc Example(None, "so'm", "so'm", "1", 1.0), # Uzbekistani soʻm Example(None, "тг", "тг", "1", 1.0), # Kazakhstani tenge Example(None, 'ש"ח', 'ש"ח', "1", 1.0), # Israeli new shekel Example(None, "ש'ח", "ש'ח", "1", 1.0), # Israeli new shekel Example(None, "د.ع", "د.ع", "1", 1.0), # Iraqi dinar Example(None, "د.ل", "د.ل", "1", 1.0), # Libyan dinar Example(None, "ر.ي", "ر.ي", "1", 1.0), # Yemeni rial Example(None, "ش.ص", "ش.ص", "1", 1.0), # Somali shilling Example( None, "1 G", # "G" may mean Guyanese dollar, but it is too ambiguous "G", "1", 1.0, ), ] PRICE_PARSING_DECIMAL_SEPARATOR_EXAMPLES = [ Example(None, "1250€ 600", "€", "1250", 1250, "€"), Example(None, "1250€ 60", "€", "1250€60", 1250.60, "€"), Example(None, "1250€600", "€", "1250€600", 1250.600, "€"), Example(None, ".75 €", "€", ".75", 0.75, "."), Example("$.75", "$.75", "$", ".75", 0.75, "."), Example("$..75", "$..75", "$", ".75", 0.75, "."), Example("$..75,333", "$..75,333", "$", ".75,333", 0.75333, "."), Example("$..75,333", "$..75,333", "$", ".75,333", 75.333, ","), Example("$.750.30", "$.750.30", "$", "750.30", 750.30, "."), ] @pytest.mark.parametrize( "example", PRICE_PARSING_EXAMPLES_BUGS_CAUGHT + PRICE_PARSING_EXAMPLES_NEW + PRICE_PARSING_EXAMPLES + PRICE_PARSING_EXAMPLES_2 + PRICE_PARSING_EXAMPLES_3 + PRICE_PARSING_EXAMPLES_NO_PRICE + PRICE_PARSING_EXAMPLES_NO_CURRENCY + PRICE_PARSING_DECIMAL_SEPARATOR_EXAMPLES + [ pytest.param(e, marks=pytest.mark.xfail(strict=True)) for e in PRICE_PARSING_EXAMPLES_XFAIL + PRICE_PARSING_EXAMPLES_XFAIL_CURRENCIES_TO_BE_ADDED ], ids=idfn, ) def test_parsing(example: Example) -> None: parsed = Price.fromstring( example.price_raw, example.currency_raw, example.decimal_separator ) assert parsed == example, ( f"Failed scenario: price={example.price_raw}, " f"currency_hint={example.currency_raw}" ) @pytest.mark.parametrize( ("amount", "amount_float"), [ (None, None), (Decimal("1.23"), 1.23), ], ) def test_price_amount_float(amount: Decimal | None, amount_float: float | None) -> None: assert Price(amount, None, None).amount_float == amount_float @pytest.mark.parametrize( ("price_raw", "decimal_separator", "expected_result"), [ ("140.000", None, Decimal(140000)), ("140.000", ",", Decimal(140000)), ("140.000", ".", Decimal("140.000")), ("140€33", "€", Decimal("140.33")), ("140,000€33", "€", Decimal("140000.33")), ("140.000€33", "€", Decimal("140000.33")), ], ) def test_price_decimal_separator( price_raw: str, decimal_separator: str | None, expected_result: Decimal ) -> None: parsed = Price.fromstring(price_raw, decimal_separator=decimal_separator) assert parsed.amount == expected_result ================================================ FILE: tox.ini ================================================ [tox] envlist = py39,py310,py311,py312,py313,py314,mypy [testenv] deps = pytest pytest-cov >= 7.0.0 commands = pytest \ --cov-report=term-missing --cov-report= --cov-report=xml --cov=price_parser \ --doctest-modules \ {posargs:price_parser tests README.rst} [testenv:mypy] deps = mypy==1.18.2 pytest==8.4.2 commands = mypy price_parser tests [testenv:pre-commit] deps = pre-commit commands = pre-commit run --all-files --show-diff-on-failure skip_install = true