├── .deepsource.toml ├── .github └── workflows │ ├── python-package.yml │ └── python-publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── receipt_parser ├── __init__.py ├── benchmarks │ ├── README.md │ ├── evaluate.ipynb │ ├── prepare_benchmark.ipynb │ ├── standard.csv │ └── tinkoff_test.csv ├── cat_model.py ├── data │ ├── blacklist.csv │ └── cleaned │ │ ├── all_clean.csv │ │ ├── brands_en.csv │ │ ├── brands_ru.csv │ │ └── products.csv ├── dicts.py ├── finder.py ├── models │ ├── cat_bpe_model.yttm │ └── cat_model.pth ├── normalizer.py ├── notebooks │ └── cat_model.ipynb ├── parsers │ ├── README.md │ ├── magnit.py │ ├── perecrestok.py │ ├── peterochka.py │ └── tinkoff.py └── receipt_parser.py ├── requirements.txt └── setup.py /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | test_patterns = [ 4 | "test*.py" 5 | ] 6 | 7 | [[analyzers]] 8 | name = "python" 9 | enabled = true 10 | 11 | [analyzers.meta] 12 | runtime_version = "3.x.x" 13 | max_line_length = 88 14 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies and run pylint, mypy and black 2 | name: build 3 | 4 | on: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: '3.x' 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install -r requirements.txt 24 | pip install pylint mypy black 25 | - name: Check format with Black 26 | run: black --check receipt_parser/ 27 | - name: Lint with Pylint 28 | run: pylint receipt_parser/ 29 | - name: Check with Mypy 30 | run: mypy receipt_parser/ 31 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine 25 | pip install pylint mypy black 26 | pip install -r requirements.txt 27 | - name: Check format with Black 28 | run: black --check receipt_parser/ 29 | - name: Lint with Pylint 30 | run: pylint receipt_parser/ 31 | - name: Check with Mypy 32 | run: mypy receipt_parser/ 33 | - name: Build and publish 34 | env: 35 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 36 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 37 | run: | 38 | python setup.py sdist bdist_wheel 39 | twine upload dist/* 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Deploy to PyPi 10 | deploy.sh 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | pip-wheel-metadata/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | Untitled*.ipynb 83 | first_model.ipynb 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 SERGEY SAVVOV 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | Version 5 | GitHub Workflow Status 6 | Upload Python Package 8 | CodeFactor 9 | GitHub
10 |

11 | 12 | # Receipt parser🧾 13 | ## What is it? 14 | **receipt_parser** - Python библиотека, помогающая распознавать товарную позицию из чеков. Для это задачи есть хороший [сервис](https://receiptnlp.tinkoff.ru/#/) от Тинькофф, однако он не справляется с [грязными данными](https://proverkacheka.com/check/9282000100225162-17705-420231526), как на картинке выше. Изначально была задумка использовать нейронные сети, однако в процессе работы, понял, что на разметку нужно потратить много времени/денег, да и модель, основанная на правилах и словарях, даёт хороший [результат](https://github.com/slgero/receipt_parser/tree/master/receipt_parser/benchmarks). 15 | 16 | ## Features 17 | * распознавание продукта; 18 | * определение категории товара; 19 | * распознавание брендов; 20 | * перевод англицизмов (`хугарден --> hoegaarden`)🍺 21 | 22 | 23 | ## Where to get it 24 | Исходный код в размещен на [GitHub](https://github.com/slgero/receipt_parser). 25 | 26 | Библиотека размещёна на [Python package index](https://pypi.org/project/receipt-parser/): 27 | ```bash 28 | pip install receipt-parser 29 | ``` 30 | >Если возникнет ошибка при установке пакета: 31 | >`Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-izdic4qt/youtokentome/` 32 | >То установите Cython и повторите попытку: 33 | ``` 34 | pip install Cython 35 | ``` 36 | 37 | ## Usage 38 | Для распознавания сейчас доступна только [RuleBased](https://github.com/slgero/receipt_parser/blob/master/receipt_parser/receipt_parser.py#L75) модель. 39 | 40 | На вход можно подавать как строку: 41 | ```python 42 | from receipt_parser import RuleBased 43 | 44 | product_desription = 'Нап.пив.ХУГАР.ГРЕЙПФ.н/ф 0.47л' 45 | rb = RuleBased() 46 | rb.parse(product_desription) 47 | ``` 48 | *output*: 49 | 50 | | | name | product_norm | brand_norm | cat_norm | 51 | |---:|:-------------------------------|:---------------|:-------------|:--------------------| 52 | | 0 | Нап.пив.ХУГАР.ГРЕЙПФ.н/ф 0.47л | напиток, пиво | hoegaarden | Воды, соки, напитки | 53 | 54 | Так и `pd.DataFrame` *(колонка с товарной позицией должна называться __name__)*: 55 | ```python 56 | from receipt_parser import RuleBased 57 | 58 | rb = RuleBased() 59 | rb.parse(df) 60 | ``` 61 | Также в библиотеке есть два вспомогательных класса: 62 | * Normalizer - для нормализации; 63 | * Finder - для поиска по словарям. 64 | 65 | ## Future work 66 | 67 | - [ ] Добавить тесты 68 | - [ ] Дополнить словари и собранные датасеты 69 | - [ ] Поднять сервис 70 | - [ ] Перейти на нейронные сети... 71 | 72 | ## Support the project 🤗 73 | Буду рад, если вы: 74 | * найдёте баги; 75 | * сможете оптимизировать код; 76 | * дополните словари и датасеты; 77 | * поможете с разметкой. 78 | -------------------------------------------------------------------------------- /receipt_parser/__init__.py: -------------------------------------------------------------------------------- 1 | """A package which allow parsing Reussian receipts.""" 2 | 3 | __version__ = "0.0.28" 4 | __license__ = "MIT" 5 | 6 | 7 | from .receipt_parser import RuleBased # type: ignore 8 | from .finder import Finder # type: ignore 9 | from .normalizer import Normalizer # type: ignore 10 | from .cat_model import PredictCategory # type: ignore 11 | -------------------------------------------------------------------------------- /receipt_parser/benchmarks/README.md: -------------------------------------------------------------------------------- 1 | ## Датасет 2 | Для оценки работы разных моделей нормализаторов [собран](https://github.com/slgero/check_parser/blob/master/receipt_parser/benchmarks/prepare_benchmark.ipynb) следующий датасет [standard.csv](https://github.com/slgero/check_parser/blob/master/receipt_parser/benchmarks/standard.csv "standard.csv"): 3 | 4 | ```python 5 | pd.read_csv('standard.csv').sample(5) 6 | ``` 7 | | | shop_name | Название | product | brand | category | 8 | |---:|:------------|:-------------------------------------------------|:-----------------|:--------|:--------------------------------| 9 | | 27 | МАГНИТ | ПЕРЕЦ красный 1кг | перец | - | Овощи, фрукты, ягоды | 10 | | 4 | АТАК | СР-ВО АНТИЖИР 500МЛ | средство антижир | - | Красота, гигиена, бытовая химия | 11 | | 10 | БИЛЛА | БАНАНЫ | бананы | - | Овощи, фрукты, ягоды | 12 | | 37 | ПЕРЕКРЕСТОК | БЗМЖ Йогурт EPICA SIMPLE 130г | йогурт | epica | Молоко, сыр, яйца | 13 | | 28 | МАГНИТ | РУБАТКИ Котлеты куриные с чесноком 450г п/п(Котл | котлеты | рубатки | Птица, мясо, деликатесы | 14 | 15 | Он включает в себя 5 рандомно выбранных товарных позиций для каждого магазина из списка: Атак, Ашан, Билла, Дикси, Лента, Магнит, О'кей, Перекрёсток, Пятёрочка. 16 | 17 | ## Система оценки 18 | Оценка производится по 3 категориям: 19 | 1. Название товара 20 | 2. Бренд товара 21 | 3. Категория товара 22 | 23 | Для оценки использовалась самописная accuracy: 24 | - за каждый верное угадывание +1 балл 25 | - за частично верное +0.5 26 | - за неверное +0 27 | 28 | Затем общая оценка переводится в проценты. 29 | 30 | ## Результаты 31 | Качество распознавания для [Тинькофф](https://receiptnlp.tinkoff.ru/#/): 32 | * Название товара: 86% 33 | * Бренд товара: 67% 34 | * Категория товара: 76% 35 | 36 | Качество распознавания для [RuleBased model](https://github.com/slgero/check_parser/blob/master/receipt_parser/reciept_parser.py#L12): 37 | * Название товара: 92% 38 | * Бренд товара: 84% 39 | * Категория товара: 76% 40 | 41 | Более подробно можно ознакомится [здесь](https://github.com/slgero/check_parser/blob/master/receipt_parser/benchmarks/evaluate.ipynb). 42 | 43 | *** 44 | P.S. Не хочется занижать работу ребят из Тинькофф, возможно, на сайте представлена урезанная версия для распознавания. Насколько я понял, занимаясь этой задачей, они используют несколько разных нейронных сетей для распознавания, что должно быть куда круче, чем моя модель, основанная на простых правилах :) -------------------------------------------------------------------------------- /receipt_parser/benchmarks/evaluate.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Система оценки:\n", 8 | "Аналогично Accuracy\n", 9 | "\n", 10 | "Всего 3 категории (название, продукт, бренд)\n", 11 | "* за каждый верное угадывание 1 балл\n", 12 | "* за частично верное 0.5\n", 13 | "* за неверное 0\n", 14 | "\n", 15 | "Затем всё это переводится в проценты" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 1, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "import pandas as pd\n", 25 | "import numpy as np\n", 26 | "from IPython.display import clear_output" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 23, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "df = pd.read_csv('standard.csv')\n", 36 | "tin = pd.read_csv('tinkoff_test.csv')\n", 37 | "\n", 38 | "tin_df = pd.concat([df, tin], axis=1)" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 24, 44 | "metadata": {}, 45 | "outputs": [ 46 | { 47 | "data": { 48 | "text/html": [ 49 | "
\n", 50 | "\n", 63 | "\n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | "
shop_nameНазваниеproductbrandcategory
0АТАКЖЕЛУДКИ 500Г/ПОДЛ/ТРжелудкитроекуровоПтица, мясо, деликатесы
1АТАКСОК ЯБЛ/БАНАН 6М 0,2сок-Воды, соки, напитки
\n", 93 | "
" 94 | ], 95 | "text/plain": [ 96 | " shop_name Название product brand \\\n", 97 | "0 АТАК ЖЕЛУДКИ 500Г/ПОДЛ/ТР желудки троекурово \n", 98 | "1 АТАК СОК ЯБЛ/БАНАН 6М 0,2 сок - \n", 99 | "\n", 100 | " category \n", 101 | "0 Птица, мясо, деликатесы \n", 102 | "1 Воды, соки, напитки " 103 | ] 104 | }, 105 | "execution_count": 24, 106 | "metadata": {}, 107 | "output_type": "execute_result" 108 | } 109 | ], 110 | "source": [ 111 | "df.head(2)" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 19, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "def quality_assessment(df: pd.DataFrame, type_: str):\n", 121 | " if type_ == 'name':\n", 122 | " columns = ['Название', 'Продукт']\n", 123 | " elif type_ == 'brand':\n", 124 | " columns = ['brand', 'Бренд']\n", 125 | " elif type_ == 'category':\n", 126 | " columns = ['category', 'Категория']\n", 127 | " else:\n", 128 | " raise ValueError('Тип может быть только `name`, `brand` или `category`.')\n", 129 | " \n", 130 | " assessment = 0\n", 131 | " for test, pred in df[columns].values:\n", 132 | " print(test, '---->', pred, sep='\\t')\n", 133 | " assessment += float(input('Оценка: [1, 0.5, 0]'))\n", 134 | " clear_output()\n", 135 | " \n", 136 | " final_score = assessment / len(df) * 100\n", 137 | " print(f'Final score for type {type_} is', '{:.2f}%.'.format(final_score))\n", 138 | " return final_score" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "#### Оценка через Тинькофф:" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 43, 151 | "metadata": {}, 152 | "outputs": [ 153 | { 154 | "name": "stdout", 155 | "output_type": "stream", 156 | "text": [ 157 | "Final score for type name is 85.56%.\n" 158 | ] 159 | }, 160 | { 161 | "data": { 162 | "text/plain": [ 163 | "85.55555555555556" 164 | ] 165 | }, 166 | "execution_count": 43, 167 | "metadata": {}, 168 | "output_type": "execute_result" 169 | } 170 | ], 171 | "source": [ 172 | "quality_assessment(df, type_='name')" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": 44, 178 | "metadata": {}, 179 | "outputs": [ 180 | { 181 | "name": "stdout", 182 | "output_type": "stream", 183 | "text": [ 184 | "Final score for type brand is 66.67%.\n" 185 | ] 186 | }, 187 | { 188 | "data": { 189 | "text/plain": [ 190 | "66.66666666666666" 191 | ] 192 | }, 193 | "execution_count": 44, 194 | "metadata": {}, 195 | "output_type": "execute_result" 196 | } 197 | ], 198 | "source": [ 199 | "quality_assessment(df, type_='brand')" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": 46, 205 | "metadata": {}, 206 | "outputs": [ 207 | { 208 | "name": "stdout", 209 | "output_type": "stream", 210 | "text": [ 211 | "Final score for type category is 75.56%.\n" 212 | ] 213 | }, 214 | { 215 | "data": { 216 | "text/plain": [ 217 | "75.55555555555556" 218 | ] 219 | }, 220 | "execution_count": 46, 221 | "metadata": {}, 222 | "output_type": "execute_result" 223 | } 224 | ], 225 | "source": [ 226 | "quality_assessment(df, type_='category')" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "#### Оценка через мою rule based model:" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": 1, 239 | "metadata": {}, 240 | "outputs": [], 241 | "source": [ 242 | "import sys\n", 243 | "sys.path.append('../')\n", 244 | "from receipt_parser import RuleBased" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 5, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "df = pd.read_csv('standard.csv')" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 6, 259 | "metadata": {}, 260 | "outputs": [], 261 | "source": [ 262 | "pathes = {\n", 263 | " 'rus_brands': '../data/cleaned/brands_ru.csv',\n", 264 | " 'products': '../data/cleaned/products.csv',\n", 265 | " 'product_db': '../data/cleaned/all_clean.csv',\n", 266 | " 'blacklist': '../data/blacklist.csv',\n", 267 | " 'brands': '../data/cleaned/brands_en.csv'\n", 268 | "}\n", 269 | "\n", 270 | "\n", 271 | "rb = RuleBased(pathes)\n", 272 | "rb_df = rb.parse(df[['Название']].rename(columns={'Название': 'name'}))\n", 273 | "rb_df = pd.concat([df, rb_df], axis=1).rename(columns={'product_norm': 'Продукт', 'brand_norm': 'Бренд', 'cat_norm': 'Категория'})" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 20, 279 | "metadata": {}, 280 | "outputs": [ 281 | { 282 | "name": "stdout", 283 | "output_type": "stream", 284 | "text": [ 285 | "Final score for type name is 92.22%.\n" 286 | ] 287 | }, 288 | { 289 | "data": { 290 | "text/plain": [ 291 | "92.22222222222223" 292 | ] 293 | }, 294 | "execution_count": 20, 295 | "metadata": {}, 296 | "output_type": "execute_result" 297 | } 298 | ], 299 | "source": [ 300 | "quality_assessment(rb_df, type_='name')" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 21, 306 | "metadata": {}, 307 | "outputs": [ 308 | { 309 | "name": "stdout", 310 | "output_type": "stream", 311 | "text": [ 312 | "Final score for type brand is 84.44%.\n" 313 | ] 314 | }, 315 | { 316 | "data": { 317 | "text/plain": [ 318 | "84.44444444444444" 319 | ] 320 | }, 321 | "execution_count": 21, 322 | "metadata": {}, 323 | "output_type": "execute_result" 324 | } 325 | ], 326 | "source": [ 327 | "quality_assessment(rb_df, type_='brand')" 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": 22, 333 | "metadata": {}, 334 | "outputs": [ 335 | { 336 | "name": "stdout", 337 | "output_type": "stream", 338 | "text": [ 339 | "Final score for type category is 75.56%.\n" 340 | ] 341 | }, 342 | { 343 | "data": { 344 | "text/plain": [ 345 | "75.55555555555556" 346 | ] 347 | }, 348 | "execution_count": 22, 349 | "metadata": {}, 350 | "output_type": "execute_result" 351 | } 352 | ], 353 | "source": [ 354 | "quality_assessment(rb_df, type_='category')" 355 | ] 356 | }, 357 | { 358 | "cell_type": "code", 359 | "execution_count": null, 360 | "metadata": {}, 361 | "outputs": [], 362 | "source": [] 363 | } 364 | ], 365 | "metadata": { 366 | "kernelspec": { 367 | "display_name": "Python 3", 368 | "language": "python", 369 | "name": "python3" 370 | }, 371 | "language_info": { 372 | "codemirror_mode": { 373 | "name": "ipython", 374 | "version": 3 375 | }, 376 | "file_extension": ".py", 377 | "mimetype": "text/x-python", 378 | "name": "python", 379 | "nbconvert_exporter": "python", 380 | "pygments_lexer": "ipython3", 381 | "version": "3.6.9" 382 | } 383 | }, 384 | "nbformat": 4, 385 | "nbformat_minor": 4 386 | } 387 | -------------------------------------------------------------------------------- /receipt_parser/benchmarks/prepare_benchmark.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 12, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import pandas as pd\n", 10 | "import numpy as np" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 8, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "df = pd.read_excel('data/data.xlsx')" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 9, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "target_shops = [\n", 29 | " 'АО \"ТОРГОВЫЙ ДОМ \"ПЕРЕКРЕСТОК\"', 'ООО \"АГРОТОРГ\"', 'ООО \"ЛЕНТА\"',\n", 30 | " 'АО \"ДИКСИ ЮГ\"', 'ООО \"АШАН\"', 'АО \"ТАНДЕР\"', 'ООО \"БИЛЛА\"', 'ООО \"О`КЕЙ\"',\n", 31 | " 'ООО \"АГРОАСПЕКТ\"', 'ООО \"АТАК\"'\n", 32 | "]\n", 33 | "\n", 34 | "shops_rename = {\n", 35 | " 'АО \"ТОРГОВЫЙ ДОМ \"ПЕРЕКРЕСТОК\"': 'ПЕРЕКРЕСТОК',\n", 36 | " 'ООО \"АГРОТОРГ\"': 'ПЯТЕРОЧКА',\n", 37 | " 'ООО \"ЛЕНТА\"': 'ЛЕНТА',\n", 38 | " 'АО \"ДИКСИ ЮГ\"': 'ДИКСИ',\n", 39 | " 'ООО \"АШАН\"': 'АШАН',\n", 40 | " 'АО \"ТАНДЕР\"': 'МАГНИТ',\n", 41 | " 'ООО \"БИЛЛА\"': 'БИЛЛА',\n", 42 | " 'ООО \"О`КЕЙ\"': 'О`КЕЙ',\n", 43 | " 'ООО \"АГРОАСПЕКТ\"': 'ПЯТЕРОЧКА',\n", 44 | " 'ООО \"АТАК\"': 'АТАК'\n", 45 | "}\n", 46 | "\n", 47 | "df = df[df['shop_name'].isin(target_shops)].copy()\n", 48 | "df['shop_name'] = df['shop_name'].apply(lambda x: shops_rename[x])" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 10, 54 | "metadata": {}, 55 | "outputs": [ 56 | { 57 | "data": { 58 | "text/html": [ 59 | "
\n", 60 | "\n", 73 | "\n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | "
shop_namenamequantitypricesum
19ДИКСИМИНТАЙ ФИЛЕ Б/К СВ/МОР П/ПАК 81.0199.90199.90
20ДИКСИБЗМЖ МОЛОКО СТРАНА ВАСИЛЬКИ У/1.049.9949.99
21ДИКСИСУШКИ ТАРАЛЛИНИ С ЧЕСНОКОМ 1801.029.9929.99
22ДИКСИЯЙЦО КУРИНОЕ СТОЛОВОЕ 1КАТЕГОР1.063.9963.99
23ДИКСИПЕЧЕНЬЕ ЮБИЛЕЙНОЕ ВИТАМИНИЗИРО2.029.9959.98
\n", 127 | "
" 128 | ], 129 | "text/plain": [ 130 | " shop_name name quantity price sum\n", 131 | "19 ДИКСИ МИНТАЙ ФИЛЕ Б/К СВ/МОР П/ПАК 8 1.0 199.90 199.90\n", 132 | "20 ДИКСИ БЗМЖ МОЛОКО СТРАНА ВАСИЛЬКИ У/ 1.0 49.99 49.99\n", 133 | "21 ДИКСИ СУШКИ ТАРАЛЛИНИ С ЧЕСНОКОМ 180 1.0 29.99 29.99\n", 134 | "22 ДИКСИ ЯЙЦО КУРИНОЕ СТОЛОВОЕ 1КАТЕГОР 1.0 63.99 63.99\n", 135 | "23 ДИКСИ ПЕЧЕНЬЕ ЮБИЛЕЙНОЕ ВИТАМИНИЗИРО 2.0 29.99 59.98" 136 | ] 137 | }, 138 | "execution_count": 10, 139 | "metadata": {}, 140 | "output_type": "execute_result" 141 | } 142 | ], 143 | "source": [ 144 | "df.head()" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 23, 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "benchmarks = pd.DataFrame(columns=['shop_name', 'name'])" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": 24, 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [ 162 | "for shop in np.unique(list(shops_rename.values())):\n", 163 | " benchmarks = pd.concat([benchmarks, df[df['shop_name'] == shop].sample(5)[['shop_name', 'name']]])" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 61, 169 | "metadata": {}, 170 | "outputs": [ 171 | { 172 | "data": { 173 | "text/html": [ 174 | "
\n", 175 | "\n", 188 | "\n", 189 | " \n", 190 | " \n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | " \n", 218 | " \n", 219 | " \n", 220 | " \n", 221 | " \n", 222 | " \n", 223 | "
shop_namename
41261АТАКЖЕЛУДКИ 500Г/ПОДЛ/ТР
41677АТАКСОК ЯБЛ/БАНАН 6М 0,2
42169АТАКТОМАТ БАКИНСКИЙ ВЕС
41432АТАКЙОГ ВИШНЯ 250ГР
42541АТАКСР-ВО АНТИЖИР 500МЛ
\n", 224 | "
" 225 | ], 226 | "text/plain": [ 227 | " shop_name name\n", 228 | "41261 АТАК ЖЕЛУДКИ 500Г/ПОДЛ/ТР\n", 229 | "41677 АТАК СОК ЯБЛ/БАНАН 6М 0,2\n", 230 | "42169 АТАК ТОМАТ БАКИНСКИЙ ВЕС\n", 231 | "41432 АТАК ЙОГ ВИШНЯ 250ГР\n", 232 | "42541 АТАК СР-ВО АНТИЖИР 500МЛ" 233 | ] 234 | }, 235 | "execution_count": 61, 236 | "metadata": {}, 237 | "output_type": "execute_result" 238 | } 239 | ], 240 | "source": [ 241 | "benchmarks.head()" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": null, 247 | "metadata": {}, 248 | "outputs": [], 249 | "source": [ 250 | "Красота, гигиена, бытовая химия\n", 251 | "Хлеб, сладости, снеки\n", 252 | "Молоко, сыр, яйца\n", 253 | "Соусы, орехи, консервы\n", 254 | "Макароны, крупы, специи\n", 255 | "Другое\n", 256 | "Воды, соки, напитки\n", 257 | "Рыба, икра\n", 258 | "Чай, кофе, сахар\n", 259 | "Замороженные продукты\n", 260 | "Товары для мам и детей\n", 261 | "Птица, мясо, деликатесы\n", 262 | "Посуда\n", 263 | "Овощи, фрукты, ягоды\n", 264 | "Товары для дома и дачи\n", 265 | "Подборки и готовые блюда\n", 266 | "Зоотовары\n", 267 | "Бытовая техника\n", 268 | "Дача и гриль\n", 269 | "Алкоголь\n", 270 | "Постные продукты" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": 67, 276 | "metadata": {}, 277 | "outputs": [], 278 | "source": [ 279 | "from IPython.display import clear_output\n", 280 | "\n", 281 | "names = []\n", 282 | "brands = []\n", 283 | "category = []\n", 284 | "\n", 285 | "for name in benchmarks['name']:\n", 286 | " print(name)\n", 287 | " names.append(input('Продукт:'))\n", 288 | " brands.append(input('Бренд:'))\n", 289 | " category.append(input('Категория:'))\n", 290 | " clear_output" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 68, 296 | "metadata": {}, 297 | "outputs": [], 298 | "source": [ 299 | "benchmarks['product'] = names\n", 300 | "benchmarks['brand'] = brands\n", 301 | "benchmarks['category'] = category" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": 70, 307 | "metadata": {}, 308 | "outputs": [], 309 | "source": [ 310 | "benchmarks['product'] = benchmarks['product'].str.lower()\n", 311 | "benchmarks['brand'] = benchmarks['brand'].str.lower()\n", 312 | "benchmarks['product'] = benchmarks['product'].str.lower()" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": 71, 318 | "metadata": {}, 319 | "outputs": [ 320 | { 321 | "data": { 322 | "text/html": [ 323 | "
\n", 324 | "\n", 337 | "\n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | "
shop_namenameproductbrandcategory
41261АТАКЖЕЛУДКИ 500Г/ПОДЛ/ТРжелудкитроекуровоПтица, мясо, деликатесы
41677АТАКСОК ЯБЛ/БАНАН 6М 0,2сок-Воды, соки, напитки
42169АТАКТОМАТ БАКИНСКИЙ ВЕСтомат-Овощи, фрукты, ягоды
41432АТАКЙОГ ВИШНЯ 250ГРйогурт-Молоко, сыр, яйца
42541АТАКСР-ВО АНТИЖИР 500МЛсредство антижир-Красота, гигиена, бытовая химия
\n", 391 | "
" 392 | ], 393 | "text/plain": [ 394 | " shop_name name product brand \\\n", 395 | "41261 АТАК ЖЕЛУДКИ 500Г/ПОДЛ/ТР желудки троекурово \n", 396 | "41677 АТАК СОК ЯБЛ/БАНАН 6М 0,2 сок - \n", 397 | "42169 АТАК ТОМАТ БАКИНСКИЙ ВЕС томат - \n", 398 | "41432 АТАК ЙОГ ВИШНЯ 250ГР йогурт - \n", 399 | "42541 АТАК СР-ВО АНТИЖИР 500МЛ средство антижир - \n", 400 | "\n", 401 | " category \n", 402 | "41261 Птица, мясо, деликатесы \n", 403 | "41677 Воды, соки, напитки \n", 404 | "42169 Овощи, фрукты, ягоды \n", 405 | "41432 Молоко, сыр, яйца \n", 406 | "42541 Красота, гигиена, бытовая химия " 407 | ] 408 | }, 409 | "execution_count": 71, 410 | "metadata": {}, 411 | "output_type": "execute_result" 412 | } 413 | ], 414 | "source": [ 415 | "benchmarks.head()" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": 74, 421 | "metadata": {}, 422 | "outputs": [ 423 | { 424 | "data": { 425 | "text/plain": [ 426 | "Овощи, фрукты, ягоды 11\n", 427 | "Молоко, сыр, яйца 10\n", 428 | "Хлеб, сладости, снеки 6\n", 429 | "Птица, мясо, деликатесы 4\n", 430 | "Товары для дома и дачи 3\n", 431 | "Соусы, орехи, консервы 3\n", 432 | "Воды, соки, напитки 2\n", 433 | "Замороженные продукты 1\n", 434 | "Красота, гигиена, бытовая химия 1\n", 435 | "Другое 1\n", 436 | "Макароны, крупы, специи 1\n", 437 | "Алкоголь 1\n", 438 | "Чай, кофе, сахар 1\n", 439 | "Name: category, dtype: int64" 440 | ] 441 | }, 442 | "execution_count": 74, 443 | "metadata": {}, 444 | "output_type": "execute_result" 445 | } 446 | ], 447 | "source": [ 448 | "benchmarks.category.value_counts()" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 75, 454 | "metadata": {}, 455 | "outputs": [], 456 | "source": [ 457 | "benchmarks.rename(columns={'name': 'Название'}, inplace=True)" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": null, 463 | "metadata": {}, 464 | "outputs": [], 465 | "source": [ 466 | "benchmarks.to_csv(be)" 467 | ] 468 | }, 469 | { 470 | "cell_type": "code", 471 | "execution_count": null, 472 | "metadata": {}, 473 | "outputs": [], 474 | "source": [] 475 | } 476 | ], 477 | "metadata": { 478 | "kernelspec": { 479 | "display_name": "Python 3", 480 | "language": "python", 481 | "name": "python3" 482 | }, 483 | "language_info": { 484 | "codemirror_mode": { 485 | "name": "ipython", 486 | "version": 3 487 | }, 488 | "file_extension": ".py", 489 | "mimetype": "text/x-python", 490 | "name": "python", 491 | "nbconvert_exporter": "python", 492 | "pygments_lexer": "ipython3", 493 | "version": "3.6.9" 494 | } 495 | }, 496 | "nbformat": 4, 497 | "nbformat_minor": 4 498 | } 499 | -------------------------------------------------------------------------------- /receipt_parser/benchmarks/standard.csv: -------------------------------------------------------------------------------- 1 | shop_name,Название,product,brand,category 2 | АТАК, ЖЕЛУДКИ 500Г/ПОДЛ/ТР,желудки,троекурово,"Птица, мясо, деликатесы" 3 | АТАК,"СОК ЯБЛ/БАНАН 6М 0,2",сок,-,"Воды, соки, напитки" 4 | АТАК, ТОМАТ БАКИНСКИЙ ВЕС,томат,-,"Овощи, фрукты, ягоды" 5 | АТАК, ЙОГ ВИШНЯ 250ГР,йогурт,-,"Молоко, сыр, яйца" 6 | АТАК, СР-ВО АНТИЖИР 500МЛ,средство антижир,-,"Красота, гигиена, бытовая химия" 7 | АШАН,КИСЛОТА УКСУСНАЯ 70%,кислота уксусная,-,"Макароны, крупы, специи" 8 | АШАН,СЫР СЛИМ ФИТ 27% 250,сыр,-,"Молоко, сыр, яйца" 9 | АШАН,СТИКЕРЫ 7ШТ 17Х55,стикеры,-,Другое 10 | АШАН,ГРИБЫ ШАМПИНЬОНЫ ВЕС,грибы,-,"Овощи, фрукты, ягоды" 11 | АШАН,АГРИКОЛА АКВА ДЕКОР,агрикола,-,Товары для дома и дачи 12 | БИЛЛА,БАНАНЫ,бананы,-,"Овощи, фрукты, ягоды" 13 | БИЛЛА,БАЗИЛИК,базилик,-,"Овощи, фрукты, ягоды" 14 | БИЛЛА,БАТОН НАРЕЗНОЙ 400,батон,-,"Хлеб, сладости, снеки" 15 | БИЛЛА,СЛИВ.195МЛ CLEVER,сливки,clever,"Молоко, сыр, яйца" 16 | БИЛЛА,ХЛЕБ АРОМАТНЫЙ НАР.У,хлеб,-,"Хлеб, сладости, снеки" 17 | ДИКСИ,ГРУША СЕЗОННАЯ ВЕС,груша,-,"Овощи, фрукты, ягоды" 18 | ДИКСИ,БАЛЫК СВИНОЙ С/В В/У НАР.100Г ,балык,-,"Птица, мясо, деликатесы" 19 | ДИКСИ,КОФЕ NESCAFE GOLD РАСТВ.М/У 19,кофе,nescafe,"Чай, кофе, сахар" 20 | ДИКСИ,ПЕРЕЦ КРАСНЫЙ ВЕС,перец,-,"Овощи, фрукты, ягоды" 21 | ДИКСИ,"БЗМЖ МОЛОКО ПИТЬЕВОЕ У/П 3,2% ",молоко,-,"Молоко, сыр, яйца" 22 | ЛЕНТА,Нап пив б/а БАЛТИКА Пшен N0 ж/б 0.45L,пиво,балтика,Алкоголь 23 | ЛЕНТА,Шоколад FAZER горький 200г,шоколад,fazer,"Хлеб, сладости, снеки" 24 | ЛЕНТА,Фольга ФРЕКЕН БОК алюм 10м Арт. 14803000,фольга,фрекен бок,Товары для дома и дачи 25 | ЛЕНТА,Майонез MR.RICCO пер.яйц Organic д/п 220,майонез ,mr.ricco,"Соусы, орехи, консервы" 26 | ЛЕНТА,"Напиток йог CAMPINA Нежн арбуз 0,1% 285г",йогурт,campina,"Молоко, сыр, яйца" 27 | МАГНИТ,САЛАТ листовой в горшочке,салат,-,"Овощи, фрукты, ягоды" 28 | МАГНИТ,"РОГАЧЕВ Молоко сгущенное цельное 8,5% 280г д/п(Рогачев):24",молоко сгущенное,рогачев,"Соусы, орехи, консервы" 29 | МАГНИТ,ПЕРЕЦ красный 1кг,перец,-,"Овощи, фрукты, ягоды" 30 | МАГНИТ,РУБАТКИ Котлеты куриные с чесноком 450г п/п(Котл,котлеты,рубатки,"Птица, мясо, деликатесы" 31 | МАГНИТ,ОГУРЦЫ гладкие 1кг,огурцы,-,"Овощи, фрукты, ягоды" 32 | О`КЕЙ,БЗМЖ Масло сладкосливочное Брест-Литовск,масло,брест-литовск,"Молоко, сыр, яйца" 33 | О`КЕЙ,Лайм шт,лайм,-,"Овощи, фрукты, ягоды" 34 | О`КЕЙ,Перчатки хозяйственные латексн,перчатки,-,Товары для дома и дачи 35 | О`КЕЙ,Вода питьевая Шишкин лес н/газ,вода,шишкин лес,"Воды, соки, напитки" 36 | О`КЕЙ,БЗМЖ Сыр Ламбер мдж в сух в-е,сыр,ламбер,"Молоко, сыр, яйца" 37 | ПЕРЕКРЕСТОК,БЗМЖ Биойогурт АКТИВИА 260г,йогурт,активиа,"Молоко, сыр, яйца" 38 | ПЕРЕКРЕСТОК,Персики МИКАДО в сиропе 410г,персики,микадо,"Соусы, орехи, консервы" 39 | ПЕРЕКРЕСТОК,БЗМЖ Йогурт EPICA SIMPLE 130г,йогурт,epica,"Молоко, сыр, яйца" 40 | ПЕРЕКРЕСТОК,Грибы ШАМПИНЬОНЫ 1кг,грибы,-,"Овощи, фрукты, ягоды" 41 | ПЕРЕКРЕСТОК,Смесь ГАВАЙСКАЯ 400г,смесь,-,Замороженные продукты 42 | ПЯТЕРОЧКА,"РАСТ.Твор.клуб.Са/Д3 3,5% 100г",товрог,растишка,"Молоко, сыр, яйца" 43 | ПЯТЕРОЧКА,П.СВ.Филе ЦБ в паниров.охл.500г,филе,первая свежесть,"Птица, мясо, деликатесы" 44 | ПЯТЕРОЧКА,С.ОЗБИ Мини-сушки с ванил.150г,сушки,семейка озби,"Хлеб, сладости, снеки" 45 | ПЯТЕРОЧКА,Батон НОВЫЙ в/с неуп.380г,батон,-,"Хлеб, сладости, снеки" 46 | ПЯТЕРОЧКА,КР.ЦЕНА Рул.бискв.фр.клуб.200г,рулет бисквитный,красная цена,"Хлеб, сладости, снеки" 47 | -------------------------------------------------------------------------------- /receipt_parser/benchmarks/tinkoff_test.csv: -------------------------------------------------------------------------------- 1 | Наименование,Продукт,Бренд,Категория 2 | ЖЕЛУДКИ 500Г/ПОДЛ/ТР,желудки,тр,"Продукты питания; Мясо, птица" 3 | "СОК ЯБЛ/БАНАН 6М 0,2",сок,—,"Продукты питания; Напитки, вода, соки" 4 | ТОМАТ БАКИНСКИЙ ВЕС,томат,—,Продукты питания; Другое 5 | ЙОГ ВИШНЯ 250ГР,,, 6 | СР-ВО АНТИЖИР 500МЛ,средство,—,Товары для дома; Бытовая химия 7 | КИСЛОТА УКСУСНАЯ 70%,—,—,"Продукты питания; Макароны, крупы, бакалея" 8 | СЫР СЛИМ ФИТ 27% 250,сыр,фитнес,"Продукты питания; Молочные продукты, сыры, яйца" 9 | СТИКЕРЫ 7ШТ 17Х55,стикеры,—,Книги и канцтовары; Канцтовары 10 | ГРИБЫ ШАМПИНЬОНЫ ВЕС,грибы шампиньоны,—,"Продукты питания; Овощи, фрукты" 11 | АГРИКОЛА АКВА ДЕКОР,декор,"агрикола, аква","Товары для дома; Дача, сад, растениеводство" 12 | БАНАНЫ,бананы,—,"Продукты питания; Овощи, фрукты" 13 | БАЗИЛИК,базилик,—,"Товары для дома; Дача, сад, растениеводство" 14 | БАТОН НАРЕЗНОЙ 400,батон нарезной,—,Продукты питания; Хлеб и хлебобулочные изделия 15 | СЛИВ.195МЛ CLEVER,слив,clever,Продукты питания; Другое 16 | ХЛЕБ АРОМАТНЫЙ НАР.У,хлеб,у,Продукты питания; Хлеб и хлебобулочные изделия 17 | ГРУША СЕЗОННАЯ ВЕС,груша,—,"Продукты питания; Овощи, фрукты" 18 | БАЛЫК СВИНОЙ С/В В/У НАР.100Г ,"нарезка, балык свиной",—,"Продукты питания; Мясо, птица" 19 | КОФЕ NESCAFE GOLD РАСТВ.М/У 19,кофе,nescafe,"Продукты питания; Чай, кофе, какао" 20 | ПЕРЕЦ КРАСНЫЙ ВЕС,перец,—,"Товары для дома; Дача, сад, растениеводство" 21 | "БЗМЖ МОЛОКО ПИТЬЕВОЕ У/П 3,2% ",молоко,бзмж,"Продукты питания; Молочные продукты, сыры, яйца" 22 | Нап пив б/а БАЛТИКА Пшен N0 ж/б 0.45L,железная банка,балтика,"Продукты питания; Напитки, вода, соки" 23 | Шоколад FAZER горький 200г,шоколад,fazer,Продукты питания; Хлеб и хлебобулочные изделия 24 | Фольга ФРЕКЕН БОК алюм 10м Арт. 14803000,фольга,фрекен бок,"Товары для ремонта и мебель; Мебель, кухня, ванная" 25 | Майонез MR.RICCO пер.яйц Organic д/п 220,"яйцо, майонез","organic, mr . ricco",Продукты питания; Консервированные продукты 26 | "Напиток йог CAMPINA Нежн арбуз 0,1% 285г","арбуз, напиток","нежный, campina","Продукты питания; Молочные продукты, сыры, яйца" 27 | САЛАТ листовой в горшочке,салат,—,"Товары для дома; Дача, сад, растениеводство" 28 | "РОГАЧЕВ Молоко сгущенное цельное 8,5% 280г д/п(Рогачев):24",,, 29 | ПЕРЕЦ красный 1кг,перец,—,"Товары для дома; Дача, сад, растениеводство" 30 | РУБАТКИ Котлеты куриные с чесноком 450г п/п(Котл,котлеты,—,"Продукты питания; Полуфабрикаты, кулинария" 31 | ОГУРЦЫ гладкие 1кг,огурцы,—,"Продукты питания; Овощи, фрукты" 32 | БЗМЖ Масло сладкосливочное Брест-Литовск,масло,"бзмж, брест-литовск","Продукты питания; Молочные продукты, сыры, яйца" 33 | Лайм шт,—,—,Продукты питания; Другое 34 | Перчатки хозяйственные латексн,перчатки,—,Товары для дома; Другое 35 | Вода питьевая Шишкин лес н/газ,вода питьевая,шишкин лес,"Продукты питания; Напитки, вода, соки" 36 | БЗМЖ Сыр Ламбер мдж в сух в-е,сыр,бзмж,"Продукты питания; Молочные продукты, сыры, яйца" 37 | БЗМЖ Биойогурт АКТИВИА 260г,биойогурт,"активиа, бзмж","Продукты питания; Молочные продукты, сыры, яйца" 38 | Персики МИКАДО в сиропе 410г,персики,mikado,"Продукты питания; Овощи, фрукты" 39 | БЗМЖ Йогурт EPICA SIMPLE 130г,йогурт,бзмж,"Продукты питания; Молочные продукты, сыры, яйца" 40 | Грибы ШАМПИНЬОНЫ 1кг,грибы шампиньоны,—,"Продукты питания; Овощи, фрукты" 41 | Смесь ГАВАЙСКАЯ 400г,фруктово-ореховая смесь,—,Другое; Другое 42 | "РАСТ.Твор.клуб.Са/Д3 3,5% 100г",творог,—,"Продукты питания; Молочные продукты, сыры, яйца" 43 | П.СВ.Филе ЦБ в паниров.охл.500г,,, 44 | С.ОЗБИ Мини-сушки с ванил.150г,"сушки, мини","с, озби",Продукты питания; Кондитерские изделия 45 | Батон НОВЫЙ в/с неуп.380г,батончики,—,Продукты питания; Хлеб и хлебобулочные изделия 46 | КР.ЦЕНА Рул.бискв.фр.клуб.200г,"бисквит, рулет",—,Продукты питания; Кондитерские изделия 47 | -------------------------------------------------------------------------------- /receipt_parser/cat_model.py: -------------------------------------------------------------------------------- 1 | """Predict a category using a neural network.""" 2 | # pylint: skip-file 3 | from typing import Dict, List 4 | import youtokentome as yttm # type: ignore 5 | import torch 6 | from torch import nn 7 | 8 | 9 | class CategoryClassifier(nn.Module): 10 | """A simple perceptron baseline moedel.""" 11 | 12 | def __init__( 13 | self, vocab_size: int, embed_dim: int, num_class: int, pad_idx: int = 0 14 | ): 15 | super(CategoryClassifier, self).__init__() 16 | self.pad_idx = pad_idx 17 | self.embedding = nn.EmbeddingBag(vocab_size, embed_dim) 18 | self.fc = nn.Linear(in_features=embed_dim, out_features=num_class) 19 | self.init_weights() 20 | 21 | def init_weights(self) -> None: 22 | """Init embedding and fc weights.""" 23 | 24 | initrange = 0.5 25 | self.embedding.weight.data.uniform_(-initrange, initrange) 26 | self.fc.weight.data.uniform_(-initrange, initrange) 27 | self.fc.bias.data.zero_() 28 | 29 | def forward( 30 | self, x_in: torch.Tensor, offsets: torch.Tensor, apply_sigmoid: bool = False 31 | ) -> torch.Tensor: 32 | """ 33 | The forward pass of the classifier. 34 | 35 | Parameters 36 | ---------- 37 | x_in : torch.Tensor 38 | Input array. 39 | offsets : torch.Tensor 40 | Array with lenghts of input texts. 41 | apply_sigmoid : bool (default=False) 42 | Indicates whether to use `torch.sigmoid`. 43 | 44 | Returns 45 | ------- 46 | torch.Tensor [batch_size x num_class] 47 | """ 48 | 49 | embedded = self.embedding(x_in, offsets) 50 | y_out = self.fc(embedded) 51 | if apply_sigmoid: 52 | y_out = torch.sigmoid(y_out) 53 | return y_out 54 | 55 | 56 | class PredictCategory: 57 | """Predict a category using a neural network.""" 58 | 59 | def __init__( 60 | self, path_to_bpe: str, path_to_model: str, model_params: Dict[str, int] 61 | ): 62 | self.bpe_model = yttm.BPE(path_to_bpe) 63 | self.categories: List[str] = [ 64 | "Алкоголь", 65 | "Бытовая техника", 66 | "Воды, соки, напитки", 67 | "Дача и гриль", 68 | "Другое", 69 | "Замороженные продукты", 70 | "Зоотовары", 71 | "Красота, гигиена, бытовая химия", 72 | "Макароны, крупы, специи", 73 | "Молоко, сыр, яйца", 74 | "Овощи, фрукты, ягоды", 75 | "Подборки и готовые блюда", 76 | "Постные продукты", 77 | "Посуда", 78 | "Птица, мясо, деликатесы", 79 | "Рыба, икра", 80 | "Соусы, орехи, консервы", 81 | "Товары для дома и дачи", 82 | "Товары для мам и детей", 83 | "Хлеб, сладости, снеки", 84 | "Чай, кофе, сахар", 85 | ] 86 | self.device = torch.device("cpu") 87 | self.model = CategoryClassifier(**model_params) 88 | self.model.load_state_dict(torch.load(path_to_model, map_location=self.device)) 89 | self.model.eval() 90 | 91 | def predict(self, name_norm: str) -> str: 92 | """Predict category by name norm.""" 93 | 94 | text = self.bpe_model.encode(name_norm) 95 | text = torch.tensor(text).to(self.device) 96 | output = self.model(text, torch.tensor([0]).to(self.device)) 97 | return self.categories[output.argmax(1).item()] 98 | -------------------------------------------------------------------------------- /receipt_parser/data/blacklist.csv: -------------------------------------------------------------------------------- 1 | name 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 | красна ягода 31 | хлебопек 32 | пачка 33 | шашл 34 | ский 35 | плавл 36 | гречн 37 | хлебзернопрод 38 | мин 39 | горький 40 | отборный 41 | жел 42 | светлое 43 | премиум 44 | форелл 45 | нар 46 | стол 47 | эрив 48 | свет 49 | горшочке 50 | литовский 51 | московск 52 | рожки 53 | зер 54 | класс 55 | баварская 56 | фетакса 57 | ябл-черная 58 | цветы 59 | тсх 60 | ржаной 61 | финский 62 | рус 63 | короткоплодн 64 | ломт 65 | тд-холдинг 66 | курин 67 | длинноплодный 68 | свежий 69 | раств 70 | рисунком 71 | мытая 72 | упак 73 | газовая 74 | инд 75 | ранняя 76 | универсал 77 | вес 78 | франц 79 | вал 80 | морская 81 | кета 82 | завар 83 | кос 84 | шоколадн 85 | кваш 86 | бумаж 87 | суп&соус 88 | пшен 89 | самокл 90 | бакинские 91 | косметик 92 | классика 93 | изм 94 | красные 95 | бородинский 96 | декор 97 | атл 98 | тепличные 99 | жёлтый 100 | моц 101 | больш 102 | рислинг 103 | хруст 104 | черные 105 | жев 106 | ишка 107 | прод 108 | печ 109 | вильямс 110 | соев 111 | шок-ор 112 | пит 113 | оливлайн 114 | окваш 115 | крупные 116 | ябл-бан 117 | [м] 118 | плавленый 119 | фасованные 120 | нат 121 | обжар 122 | выпечки 123 | кукурузный 124 | прем 125 | ломтик 126 | сетке 127 | для 128 | слив 129 | по-домашнему 130 | избр 131 | прополис 132 | завтр 133 | лес 134 | мор 135 | итал 136 | зерн 137 | марин 138 | среднеплодные 139 | освет 140 | навелин 141 | бел 142 | зеленый 143 | фас 144 | пец-хаас 145 | древ 146 | нарезка 147 | киш-миш 148 | нарезной 149 | докторская 150 | туал 151 | ассорти 152 | пмм 153 | тенд 154 | каймак 155 | бзмж 156 | кисло-сладкий 157 | паст 158 | экон 159 | конд 160 | подл 161 | молочный 162 | гладкий 163 | дет 164 | детский 165 | спз 166 | изд 167 | классич 168 | premium 169 | ромашка 170 | комсомольская правда 171 | палермо 172 | выгодно 173 | обезж 174 | one 175 | мульт 176 | мягкий 177 | фин 178 | дск 179 | национал 180 | средний 181 | бум 182 | подс 183 | р-р 184 | мытый 185 | гладк 186 | ванильный 187 | чесноком 188 | белокочанная 189 | классический 190 | мга 191 | рж-пш 192 | среднепл 193 | чизбург 194 | зерненый 195 | струч 196 | кривское 197 | стер 198 | домашний 199 | пинк 200 | берканские 201 | соленые 202 | бок 203 | вбд 204 | классическое 205 | карам 206 | домашние 207 | пищевой 208 | тон 209 | отб 210 | зимой 211 | бриут 212 | много 213 | овсянный 214 | дой-пак 215 | ветке 216 | маринован 217 | даниловский 218 | колотый 219 | глад 220 | спм 221 | дуболистный 222 | детские 223 | дрож 224 | пшенич 225 | элит 226 | столовое 227 | венские 228 | слоеное 229 | крас 230 | нарез 231 | гос 232 | ассорт 233 | яблоко-вишня 234 | черн-овес 235 | бород 236 | темн 237 | ригли 238 | газ 239 | семейка 240 | зеленью 241 | арт 242 | сдоб 243 | оквашино 244 | тер 245 | лбп 246 | белые 247 | прозр 248 | цыпл-брой 249 | изо 250 | овские 251 | подложка 252 | дрожжевое 253 | по-корейски 254 | дров 255 | сметанковыймдж 256 | сушеные 257 | шейкер 258 | бокс 259 | репчатый 260 | н-р 261 | голден 262 | смо 263 | нач 264 | зерно 265 | барное 266 | гов 267 | пищ 268 | раст 269 | темное 270 | фрито 271 | сливочный 272 | -сл 273 | гладкие 274 | пшеничная 275 | адриатика 276 | рж 277 | украинск 278 | творожный 279 | абр 280 | азербайджан 281 | детское 282 | россия 283 | val 284 | розовые 285 | кдв 286 | красный 287 | охл 288 | парижский 289 | кокосом 290 | медвеж 291 | асс 292 | оливковый 293 | -------------------------------------------------------------------------------- /receipt_parser/data/cleaned/brands_en.csv: -------------------------------------------------------------------------------- 1 | brand 2 | president 3 | heineken 4 | cricket 5 | hochland 6 | bio balance 7 | rich 8 | mars 9 | оral-b 10 | figaro 11 | billa 12 | nescafe 13 | aura family 14 | danone 15 | bonduelle 16 | baisad 17 | miller genuine 18 | chupa chups 19 | lays 20 | ld 21 | senator 22 | geese 23 | mr. proper 24 | maggi 25 | extreme 26 | milka 27 | felix 28 | purina 29 | viola 30 | winston 31 | contex 32 | dirol 33 | granola 34 | maxibon 35 | mersi 36 | palmolive 37 | nemoloko 38 | philip morris 39 | miller 40 | esse 41 | mccormick 42 | henkel 43 | heinz 44 | kitekat 45 | zatecky gus 46 | tolli 47 | gourmet gold 48 | colgate 49 | ritter sport 50 | landliebe 51 | dr. korner 52 | rothmans 53 | foxlite 54 | astoria 55 | gosser 56 | kamis 57 | m&ms 58 | marlboro 59 | eco-botanica 60 | always 61 | pepsi 62 | dr. oetker 63 | deluxe 64 | kotex 65 | bond 66 | chocopie 67 | velle 68 | oreo 69 | nestle 70 | activia 71 | orbit 72 | alpen gold 73 | lorenz naturals 74 | barilla 75 | almette 76 | greenfield 77 | violette 78 | global village 79 | ecomilk 80 | natural 81 | turbo diesel 82 | oral-b 83 | huggies 84 | mamba 85 | coca-cola 86 | coca cola 87 | lukoil 88 | grand duet 89 | zewa 90 | hoegaarden 91 | terre allegre 92 | mentos 93 | fa 94 | -------------------------------------------------------------------------------- /receipt_parser/data/cleaned/brands_ru.csv: -------------------------------------------------------------------------------- 1 | brand 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 | вкусные консервы 31 | вязанка 32 | папа может 33 | атяшево 34 | родные просторы 35 | российский 36 | старейшина 37 | старый кенигсберг 38 | жокей 39 | черная карта 40 | конти 41 | яшкино 42 | ушастый нянь 43 | бархатные ручки 44 | золотой петушок 45 | доширак 46 | миладора 47 | ряба 48 | шебекинские 49 | брест-литовск 50 | благо 51 | простоквашино 52 | домик деревне 53 | кубанский молочник 54 | золотой стандарт 55 | лакомка 56 | добрый 57 | мираторг 58 | любимый 59 | фрутмотив 60 | фруктовый сад 61 | помидорка 62 | цезарь 63 | любятово 64 | хлебный спас 65 | эдельвейс 66 | балтика 67 | агуша 68 | семейный бюджет 69 | старый мельник 70 | морей 71 | сады придонья 72 | велком 73 | стародворские колбасы 74 | стебель бамбука 75 | черкизово 76 | миф 77 | воронцовские 78 | сыробогатов 79 | свитлогорье 80 | растишка 81 | активиа 82 | круазетт 83 | увелка 84 | лисма 85 | майский 86 | принцесса нури 87 | нежный 88 | бабаевский 89 | главпродукт 90 | дмитровские овощи 91 | останкинский 92 | славянка 93 | магуро 94 | капитан вкусов 95 | виваль 96 | чим-чим 97 | каскад 98 | александров 99 | лянеж 100 | прямо фермы 101 | вологды 102 | трапеза 103 | экомилк 104 | тысяча озер 105 | белый город 106 | сезона 107 | петмол 108 | дымов 109 | веселый молочник 110 | черемушки 111 | солнце луна 112 | тянь жень 113 | скатерть-самобранка 114 | карат 115 | фермерские решения 116 | новый жемчуг 117 | невская косметика 118 | благода 119 | дары кубани 120 | останкинское 1955 121 | ультрамарин 122 | красный октябрь 123 | ростагроэкспорт 124 | сенежская 125 | мистраль 126 | чистая линия 127 | диадар 128 | бон пари 129 | микоян 130 | царицыно 131 | рогачевъ 132 | домашняя кухня 133 | калиновъ 134 | умалат 135 | родные дали 136 | тирольские пироги 137 | гусь-хрустальный стекольный завод 138 | мартина 139 | рублевский 140 | луговая свежесть 141 | сat 142 | верес 143 | предгорье кавказа 144 | скорая помощь 145 | нмп нева металл посуда 146 | останкино 147 | еврохлеб 148 | конфитрейд 149 | клинский мясокомбинат 150 | рязаночка 151 | старт 152 | маре 153 | русская рыбная фактория 154 | окраина 155 | мясной дом бородина 156 | натурбуфет 157 | распак 158 | бабушкино лукошко 159 | старые добрые традиции 160 | живой кофе 161 | тема 162 | победа вкуса 163 | юбилейное 164 | 7up 165 | коровка кореновки 166 | молком 167 | клинский 168 | полезные продукты 169 | искренне ваш 170 | слобода 171 | астория 172 | актуаль 173 | углече поле 174 | белая дача 175 | дым дымыч 176 | московский 177 | акульчев 178 | белебеевский 179 | кондитерская прохорова 180 | кулинариум 181 | балтимор 182 | чуточка 183 | сдобная особа 184 | московский провансаль 185 | балтком 186 | фили бейкер 187 | россия щедрая душа 188 | творобушки 189 | коркунов 190 | сваля 191 | меридиан 192 | фэг 193 | экопродукт 194 | теплые традиции 195 | санта бремор 196 | ваша ферма 197 | коломенское 198 | кабош 199 | мясницкий ряд 200 | пава пава 201 | азерчай 202 | тестов 203 | кухмастер 204 | комильфо 205 | луч 206 | макфа 207 | 1toy 208 | олейна 209 | герои масках 210 | тик-так 211 | золотая семечка 212 | чайкофский 213 | еврозлак 214 | семейка озби 215 | моя семья 216 | петруха 217 | парфэ 218 | пудовъ 219 | горячая штучка 220 | ясно солнышко 221 | ремит 222 | имунеле 223 | магнат 224 | копеек 225 | фрутинг 226 | осз 227 | гамма 228 | коралл 229 | бабкины семечки 230 | россиянка 231 | фруже 232 | сладбери 233 | архыз 234 | родился 235 | туалетный утенок 236 | дивидик 237 | напитки черноголовки 238 | трофи 239 | пиканта 240 | киржачский молочный завод 241 | соток 242 | авида 243 | костровок 244 | фотон 245 | альтернатива 246 | промагро 247 | националь 248 | печем дома 249 | росмэн 250 | елинский 251 | гродфуд 252 | снежок 253 | мастер пирогов 254 | родные корма 255 | алексеевское 256 | арт дизайн 257 | мясо есть! 258 | рижский хлеб 259 | башкирский фарфор 260 | авторские сыры 261 | охотный ряд 262 | семейное дело 263 | русский икорный дом 264 | савушкин 265 | русский продукт 266 | агро-альянс 267 | лакомства для здоровья 268 | стародворье 269 | самая 270 | got2b 271 | палыча 272 | альт 273 | вараксино 274 | бухта изобилия 275 | барьер 276 | бытпласт 277 | наслада 278 | вико 279 | индилайт 280 | новотерская 281 | шишкин лес 282 | святой источник 283 | славяновская 284 | калинов родник 285 | ностальгия 286 | сладкая сказка 287 | краснобор 288 | черноголовская 289 | джермук 290 | смешарики 291 | нашей дойки 292 | камчатское море 293 | акватория 294 | жигули 295 | сибирская коллекция 296 | марсианка 297 | океан вкуса 298 | праймбиф 299 | свобода 300 | ррк 301 | слонимский мясокомбинат 302 | мнямс 303 | полимербыт 304 | союзгриль 305 | московская кофейня паяхъ 306 | петелинка 307 | эколь 308 | салина 309 | ржевское подворье 310 | хлебный дом 311 | березники 312 | яигрушка 313 | пава-пава 314 | троекурово 315 | амца 316 | эконива 317 | кинто 318 | эра 319 | контур лайн 320 | частная галерея 321 | самсон 322 | конфаэль 323 | мажитэль 324 | кедровый бор 325 | малаховский мясокомбинат 326 | фру-фру 327 | хлебозавод 328 | исландка 329 | стрекоза 330 | космос 331 | липецкое мраморное мясо 332 | момент 333 | сила 334 | милен 335 | куриное царство 336 | ясные зори 337 | вологодский пломбир 338 | стандарт пластик групп 339 | русский огород 340 | медовый дом 341 | мое солнышко 342 | путина 343 | полиформ 344 | хлебное местечко 345 | аладушкин 346 | ама 347 | север 348 | вдохновение 349 | рококо 350 | белоручка 351 | ударница 352 | талосто 353 | звездный 354 | максо 355 | пемолюкс 356 | нордпласт 357 | брестские 358 | био баланс 359 | метрополь 360 | вкуснотеево 361 | 4life 362 | тревиль 363 | биол 364 | волоконовское 365 | аленка 366 | дарница 367 | ржаной край 368 | русская нива 369 | венский цех 370 | усладов 371 | меленъ 372 | молочный стиль 373 | эрмигурт 374 | хозяюшка 375 | правильное молоко 376 | можайское 377 | альпенгурт 378 | город сыра 379 | село зеленое 380 | бжни 381 | красное золото 382 | здоровье 383 | гарнец 384 | ого! 385 | умка 386 | ядрена копоть 387 | бискотти 388 | сладкие истории 389 | априори 390 | столичные штучки 391 | щедрые 392 | тимоша 393 | нмжк 394 | чудо-ягода 395 | асеньевская ферма 396 | братья чебурашкины 397 | фермерский 398 | рычал-су 399 | бодрость 400 | коммунарка 401 | эkzo 402 | козельский 403 | ламбер 404 | бос 405 | красная линия 406 | рецепты бабушки агафьи 407 | маленькая фея 408 | морозко 409 | детское 410 | пемос 411 | биолан 412 | дракоша 413 | прелесть 414 | спелёнок 415 | маша медведь 416 | ближние горки 417 | олива факел 418 | филевское 419 | vитамин 420 | европейский хлеб 421 | пылу жару 422 | сытоедов 423 | лукашинские 424 | малышам 425 | умница 426 | угощение славянки 427 | байкал 1977 428 | райские облака 429 | воздушный 430 | сто рецептов красоты 431 | аист 432 | принцесса ява 433 | ярмарка 434 | крепыш 435 | саф-момент 436 | утренней дойки 437 | раптика 438 | равиолло 439 | суперсуп 440 | коровка 441 | афанасий 442 | фрау марта 443 | сибирский кедр 444 | лысьвенские эмали 445 | козельская 446 | нэнни 447 | волшебное дерево 448 | курганский стандарт 449 | домашние шедевры 450 | здоровей 451 | россия-щедрая душа 452 | посиделкино 453 | тунгутун 454 | леовит 455 | кладезь 456 | киприно 457 | тчн! 458 | играем вместе 459 | ильиной 460 | спеленок 461 | о'кей 462 | собственное производство 463 | технопарк 464 | шереметьевские торты 465 | малаховский 466 | евростандарт 467 | дмитровский молочный завод 468 | лужский консервный завод 469 | рокс 470 | эксмо 471 | брест- литовск 472 | ярославский бройлер 473 | абрау-дюрсо 474 | хаски 475 | сокольническая 476 | мой 477 | лудинг 478 | царские припасы 479 | хрусteam 480 | белая березка 481 | липецкий бювет 482 | домашний доктор 483 | пять озер 484 | раптор 485 | каравай 486 | а'море 487 | архимед 488 | аланталь 489 | великолукский мясокомбинат 490 | томилинская 491 | 101 зерно 492 | лукойл 493 | санта-бремор 494 | фабрика крупской 495 | казачьи разносолы 496 | бабаевский комб 497 | /мультидом 498 | большая кружка 499 | калури 500 | ладоград 501 | люблю готовить 502 | сладко 503 | апк черкизовский 504 | сзт 505 | нижегородский хлеб 506 | мона лиза 507 | мажитель 508 | самарский кондитер 509 | иней 510 | ичалки 511 | колбасы деликатесы рублевские 512 | русское море 513 | контакт 514 | особая серия 515 | хлебцы-молодцы 516 | хортица 517 | фили-бейкер 518 | петродиет 519 | победа 520 | рублёвский 521 | русский дар 522 | добрый гном 523 | пит-продукт 524 | нтк 525 | солнце востока 526 | русский цикорий 527 | никола 528 | алианта 529 | баланс 530 | маринадовъ 531 | венский цех/большевик 532 | север-метрополь 533 | фрисо 534 | славянский текстиль 535 | футляр 536 | кампомос 537 | маскулан 538 | волжанин 539 | морской котик 540 | худеем неделю 541 | океан трк 542 | дедушкин улей 543 | роскар 544 | белуга 545 | молочная культура 546 | гейзер 547 | пискаревский 548 | приосколье 549 | банные штучки 550 | форт 551 | зеленая аптека 552 | фтородент 553 | егорьевская колбасно-гастрономическая фабрика 554 | русский завтрак 555 | лето 556 | байкал 557 | сухогруз 558 | атамана 559 | мирэль 560 | айдиго 561 | экспотрейд 562 | северная компания 563 | чим чим 564 | волжский пекарь 565 | мягков 566 | центробалт 567 | андреевский кондитерский дом 568 | ресторация обломов 569 | азовская 570 | скит 571 | домашнее бистро 572 | лесные угодья 573 | петра+ 574 | народный грунт 575 | картошечка 576 | доброфлот 577 | ложкаревъ 578 | ратибор 579 | наша мама 580 | зелёная марка 581 | арарат 582 | семушка 583 | рыбное меню 584 | мистерия 585 | валдайский погребок 586 | невские берега 587 | главбаня 588 | дары природы 589 | едим дома 590 | океанов 591 | фругурт 592 | останкинское 593 | морозко 594 | московский картофель 595 | фитокосметик 596 | петрохлеб 597 | здоровье! 598 | сочный мир 599 | кубаночка 600 | грин бэлт 601 | "ооо""колорит""" 602 | полет 603 | рузское молоко 604 | кубанские семечки 605 | джаз 606 | беринг 607 | сrisp 608 | квест 609 | фрекен бок 610 | гранум 611 | родной 612 | спейс 613 | капитан 614 | синтек 615 | сохраним традиции 616 | гранд десерт 617 | маркус 618 | сэн сой 619 | аква минерале 620 | пластишка 621 | дары моря 622 | милава 623 | мирный 624 | окей лоджистик 625 | буздякский 626 | новгородский бекон 627 | питер 628 | невская трапеза 629 | арнаут 630 | славянский дар 631 | устюгмолоко 632 | совок 633 | сосна 634 | коноплянка 635 | это лето 636 | востряково 637 | хамовники 638 | свой урожай 639 | расти большой 640 | асепта 641 | королевская коллекция 642 | василеостровская пивоварня 643 | пилигрим 644 | шоколадница 645 | своя рыбка 646 | ливингрин 647 | семь ручьев 648 | натахтари 649 | вкусы мира 650 | гудвилл 651 | ягоды карелии 652 | свежее завтра 653 | арджи 654 | биг ланч 655 | теди 656 | мишкино 657 | мороша 658 | беленькая 659 | свинка пеппа 660 | знатные 661 | экстра 662 | пекарь 663 | алтайская сказка 664 | окское 665 | фараоновы ванны 666 | коркунов 667 | родники кавказа 668 | тдс 669 | парламент 670 | сами усами 671 | молочная страна 672 | немчиновка 673 | ной 674 | шунгит 675 | россия 676 | фишка 677 | рестоль 678 | севзапуголь 679 | маска 680 | кружево вкуса 681 | калинов родничок 682 | артколор 683 | озерский сувенир 684 | первая свежесть 685 | нутрилак 686 | петрохолод 687 | бис 688 | советские традиции 689 | дар гор 690 | радость вкуса 691 | боржоми 692 | черноголовка 693 | пепси-кола 694 | медвежонок барни 695 | шармэль 696 | русалочка 697 | просто 698 | маркет перекресток 699 | айсберри 700 | голицин 701 | пышечка 702 | леди джем 703 | русская картошка 704 | аморе 705 | клины 706 | для внучат 707 | малаховская 708 | белорусские 709 | александров 710 | хлебцы молодцы 711 | вологодское варенье 712 | новый океан 713 | диyes 714 | эльбрус 715 | шеф перекресток 716 | густо марини 717 | богатство вкуса 718 | матиас 719 | перекресток 720 | сибуки 721 | чистотел 722 | хамелеон 723 | русский иван-чай 724 | моё солнышко 725 | бибиколь 726 | липуня 727 | квашенка 728 | егорьевская 729 | артколор 730 | сарафаново 731 | добрынинский 732 | знак качества 733 | сазановские продукты 734 | бахрушинъ 735 | спецзаказ 736 | вегана 737 | мясной дворик 738 | снежана 739 | доктор робик 740 | o12 741 | три несушки 742 | зеленика 743 | верховье 744 | деревенские лакомства 745 | здоровое меню 746 | приправка 747 | вкус польза 748 | маркет зеленая линия 749 | атлантика 750 | три корочки 751 | кафе красоты 752 | егор иваныч 753 | ухтышки 754 | умные сладости 755 | ветер травах 756 | великославич 757 | пряничное настроение 758 | зеленая панда 759 | зоогурман 760 | четвероногий гурман 761 | зооник 762 | перекресток 763 | авз 764 | доброзверики 765 | хорошка 766 | вкусвилл 767 | вастэко 768 | великолукский 769 | рототайка 770 | хрустящий картофель 771 | дом вкусов 772 | ешь деревенское 773 | зеленая марка 774 | хортиця 775 | очаково 776 | когда вырасту 777 | красная цена 778 | егорьевская кгф 779 | бородина 780 | черкизовский 781 | шарлиз 782 | тонус 783 | барinoff 784 | полезный сок 785 | акашево 786 | латифа 787 | веско 788 | кфх еремкина 789 | горчичная поляна 790 | избёнка 791 | тёма 792 | cезона 793 | беби ситтер 794 | аmeria 795 | йошкар-ола 796 | селигер-агро 797 | азовская кондитерская фабрика 798 | грация 799 | агро-альянс экстра 800 | черный дракон 801 | фитнес- 802 | троих 803 | пелигрин 804 | диетмарка 805 | "продукт ""чистая линия""" 806 | курносики 807 | старооскольский 808 | мистер мускул 809 | баскин роббинс 810 | чистые лапки 811 | монастырские оливы 812 | настюша 813 | мамако 814 | витацентр 815 | мак мастер 816 | максан 817 | чистин 818 | ферэльгам 819 | щелковохлеб 820 | хлебный cпас 821 | тирольский 822 | гавриш 823 | корега 824 | лошадиная cила 825 | аспера 826 | белевская 827 | егорьевская фкг 828 | нева-металл 829 | ясно cолнышко 830 | фронтлайн 831 | твороженое 832 | цирлих манирлих 833 | урбеч 834 | клинса 835 | бальзамир 836 | васька 837 | вилли хвост 838 | источник здоровой жизни 839 | аква меню 840 | мир детства 841 | пелагус 842 | фрутилад 843 | 5морей 844 | сова жаворонок 845 | крым 846 | бибеrika 847 | совтехстром 848 | тянь-жень 849 | мозаика-синтез 850 | микаелло 851 | старые традиции 852 | внмд 853 | емельяновская биофабрика 854 | масляный король 855 | братья асканели 856 | моя собака 857 | гурман 858 | репьевский 859 | пекарня софи 860 | стэлмас 861 | дивеевский хлеб 862 | вака 863 | экси 864 | сера 865 | белита 866 | репьёвский 867 | благодарное 868 | тек-а-тек 869 | давняя традиция 870 | русские традиции 871 | тайны востока 872 | здоровье детям 873 | деревенька 874 | доктор сольморей 875 | dамате 876 | мила феля 877 | халеда 878 | славянсалат 879 | сихарули 880 | прикиндер 881 | серпуховхлеб 882 | тек тек 883 | домашние рецепты 884 | золотая балка 885 | подушкино 886 | мастер тим 887 | подари 888 | чабан 889 | бумба 890 | мастерская десертов бисквит 891 | домовита 892 | мармеладная сказка 893 | вологодская 894 | краболов 895 | просто-постно 896 | мой уютный домик 897 | андерсон 898 | раменский деликатес 899 | наша игрушка 900 | эконова 901 | коломенское молоко 902 | пома 903 | сheese 904 | златиборац 905 | овсянки бейкери 906 | гагаринские мануфактуры 907 | мануфактура доброхлеб 908 | дары океана 909 | воробьев 910 | мака 911 | первый мясокомбинат 912 | малышарики 913 | остров сокровищ 914 | юнландия 915 | калужская зорька 916 | вещицы 917 | хатбер-пресс 918 | углича 919 | жирафики 920 | пламенный мотор 921 | беллакт 922 | спартак 923 | ла-кри 924 | царка 925 | вологжанка 926 | зеленоградское 927 | alw 928 | тараллини 929 | каприз 930 | золотой ларец 931 | посиделкино 932 | кузмино 933 | праздничная 934 | золотой стандарт 935 | шишкин лес 936 | красная цена 937 | провансаль 938 | карусель 939 | волковская пивоварня 940 | ролтон 941 | окей 942 | сиббиттер 943 | славянка 944 | моя цена 945 | чистая линия 946 | озорная пчелка 947 | я самая 948 | варвара краса 949 | брест-литовский 950 | верховье 951 | маракас 952 | хлебцы-молодцы 953 | орловский 954 | рот-фронт 955 | залесский фермер 956 | от ильиной 957 | роллтон 958 | агро-альянс 959 | стародворские 960 | рублевский 961 | круглый год 962 | золотой ярлык 963 | даниссимо 964 | российский 965 | мираторг 966 | рязаночка 967 | любодарово 968 | море рядом 969 | родные просторы 970 | черноголовка 971 | коммунарка 972 | наше солнышко 973 | здоровое меню 974 | индана 975 | орион 976 | просто 977 | хлебный спас 978 | русский холод 979 | мягкий знак 980 | свитлогорье 981 | доброфлот 982 | рестория 983 | село зеленое 984 | малиновка 985 | овощная семейка 986 | сибирская коллекция 987 | ява 988 | щелковский 989 | маркет перекресток 990 | пятерочка 991 | маруся 992 | бабаевский 993 | барни 994 | иммунеле 995 | из углича 996 | велком 997 | простоквашино 998 | топ шеф 999 | яков давыдов 1000 | бифилайф 1001 | ростагроэкспорт 1002 | три корочки 1003 | восточный гость 1004 | волоконовское 1005 | первым делом 1006 | хлебозавод 1007 | фрекен бок 1008 | саф-момент 1009 | предгорье кавказа 1010 | кириешки 1011 | националь 1012 | с. пудовъ 1013 | дикси 1014 | петр I 1015 | первак 1016 | завтрак+ 1017 | мясной дом бородина 1018 | цукерман 1019 | тульский 1020 | растишка 1021 | мера вкуса 1022 | степановна 1023 | юбилейное 1024 | ля фам 1025 | виола виттрока 1026 | рогачевъ 1027 | кока кола 1028 | марьин 1029 | вкусвилл 1030 | страна васильки 1031 | бархатные ручки 1032 | как раньше 1033 | юрма 1034 | белая долина 1035 | ярославский бройлер 1036 | савушкин хуторок 1037 | воронцовские сухарики 1038 | мистраль 1039 | асеньевская ферма 1040 | советские традиции 1041 | коптильный двор 1042 | клинский 1043 | гиагинский 1044 | перекресток 1045 | фрутоняня 1046 | лента 1047 | золотая симфония 1048 | краски лета 1049 | любимый 1050 | кремлевский 1051 | молти 1052 | фруто-няня 1053 | особый 1054 | воронцовские 1055 | приосколье 1056 | вдохновение 1057 | отличная цена 1058 | сады придонья 1059 | домашняя кухня 1060 | добрый 1061 | агуша 1062 | варенька 1063 | океан 1064 | мясной дом 1065 | дмитровский 1066 | красна ягода 1067 | кузя 1068 | азовская кф 1069 | принцесса ява 1070 | сладкая слобода 1071 | крестьянское 1072 | белевская пастила 1073 | ашан 1074 | московский картофель 1075 | ротфронт 1076 | царский вкус 1077 | овсяная изюминка 1078 | доширак 1079 | большая кружка 1080 | зеленая линия 1081 | донской табак 1082 | актвия 1083 | томмолоко 1084 | домик в деревне 1085 | шарлиз 1086 | первая свежесть 1087 | му-у 1088 | семейка озби 1089 | золотая бочка 1090 | магнит 1091 | -------------------------------------------------------------------------------- /receipt_parser/data/cleaned/products.csv: -------------------------------------------------------------------------------- 1 | product,category 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 | завтрак,"Макароны, крупы, специи" 31 | заправка,"Соусы, орехи, консервы" 32 | зефир,Другое 33 | зубная паста,"Красота, гигиена, бытовая химия" 34 | игрушка,Другое 35 | икра,"Рыба, икра" 36 | ирис,Другое 37 | кетчуп,Другое 38 | йогурт,"Молоко, сыр, яйца" 39 | какао,"Чай, кофе, сахар" 40 | капсулы для стирки,"Красота, гигиена, бытовая химия" 41 | капуста,Замороженные продукты 42 | каша овсяная,"Макароны, крупы, специи" 43 | кашка,Другое 44 | килька,"Рыба, икра" 45 | колбаса,"Птица, мясо, деликатесы" 46 | колбаса вареная,"Птица, мясо, деликатесы" 47 | колбаски,"Птица, мясо, деликатесы" 48 | кондиционер для белья,"Красота, гигиена, бытовая химия" 49 | фасоль,"Соусы, орехи, консервы" 50 | конфеты,"Хлеб, сладости, снеки" 51 | коньяк,Другое 52 | корм для кошек,Другое 53 | кофе,"Чай, кофе, сахар" 54 | кофе растворимый,"Чай, кофе, сахар" 55 | крекер,"Хлеб, сладости, снеки" 56 | крекеры,"Хлеб, сладости, снеки" 57 | крем для лица,"Красота, гигиена, бытовая химия" 58 | чернослив,Другое 59 | шпроты,"Рыба, икра" 60 | лазанья,Замороженные продукты 61 | лакомство для собак,Другое 62 | лапша,"Макароны, крупы, специи" 63 | леденцы,"Хлеб, сладости, снеки" 64 | ликер,Другое 65 | майонез,"Соусы, орехи, консервы" 66 | макаронные изделия,"Макароны, крупы, специи" 67 | макароны,"Макароны, крупы, специи" 68 | мармелад,"Хлеб, сладости, снеки" 69 | масло,"Молоко, сыр, яйца" 70 | масло подсолнечное,"Макароны, крупы, специи" 71 | молоко,"Молоко, сыр, яйца" 72 | мороженое,"Молоко, сыр, яйца" 73 | морс,"Воды, соки, напитки" 74 | мыло жидкое,"Красота, гигиена, бытовая химия" 75 | мыло,"Красота, гигиена, бытовая химия" 76 | мясо цыпленка,"Соусы, орехи, консервы" 77 | набор,Другое 78 | нож,Посуда 79 | набор конфет,"Хлеб, сладости, снеки" 80 | наггетсы,Замороженные продукты 81 | напиток,"Воды, соки, напитки" 82 | напиток пивной,"Воды, соки, напитки" 83 | нектар,"Воды, соки, напитки" 84 | овощечистка,Другое 85 | огурцы,"Овощи, фрукты, ягоды" 86 | ополаскиватель для полости рта,Другое 87 | паста зубная,"Красота, гигиена, бытовая химия" 88 | паста томатная,Другое 89 | паштет,"Соусы, орехи, консервы" 90 | пельмени,Замороженные продукты 91 | петрушка,"Овощи, фрукты, ягоды" 92 | печенье,"Хлеб, сладости, снеки" 93 | пивной напиток,Другое 94 | пиво,Другое 95 | питьевой йогурт,"Молоко, сыр, яйца" 96 | пицца,Замороженные продукты 97 | подгузники,Другое 98 | подушечки,"Макароны, крупы, специи" 99 | прокладки,"Красота, гигиена, бытовая химия" 100 | пюре,Товары для мам и детей 101 | ряженка,"Молоко, сыр, яйца" 102 | говядина,"Соусы, орехи, консервы" 103 | сайра,"Соусы, орехи, консервы" 104 | салфетки,"Красота, гигиена, бытовая химия" 105 | смесь,Другое 106 | сметана,"Молоко, сыр, яйца" 107 | сок,Товары для мам и детей 108 | сосиски,"Птица, мясо, деликатесы" 109 | соус,"Соусы, орехи, консервы" 110 | средство для стирки,"Красота, гигиена, бытовая химия" 111 | стейк,"Птица, мясо, деликатесы" 112 | стиральный порошок,"Красота, гигиена, бытовая химия" 113 | суп,Замороженные продукты 114 | сухарики,"Хлеб, сладости, снеки" 115 | сухой корм,Другое 116 | сыр,"Молоко, сыр, яйца" 117 | сырники,"Молоко, сыр, яйца" 118 | тампоны,"Красота, гигиена, бытовая химия" 119 | творог,"Молоко, сыр, яйца" 120 | йогуртный продукт,"Молоко, сыр, яйца" 121 | томатная паста,Другое 122 | трусики,Другое 123 | халва,"Хлеб, сладости, снеки" 124 | хлебцы,"Хлеб, сладости, снеки" 125 | хлопья,"Макароны, крупы, специи" 126 | чай,"Чай, кофе, сахар" 127 | чипсы картофельные,"Хлеб, сладости, снеки" 128 | шампанское,Другое 129 | шампиньоны,"Овощи, фрукты, ягоды" 130 | шампунь,"Красота, гигиена, бытовая химия" 131 | шоколад,"Хлеб, сладости, снеки" 132 | шоколад темный,"Хлеб, сладости, снеки" 133 | батарейки,"Хлеб, сладости, снеки" 134 | батарейка,"Хлеб, сладости, снеки" 135 | аккумулятор,"Хлеб, сладости, снеки" 136 | морковь,"Овощи, фрукты, ягоды" 137 | комплект,"Хлеб, сладости, снеки" 138 | кефир,"Молоко, сыр, яйца" 139 | простокваша,"Молоко, сыр, яйца" 140 | творожный сыр,"Молоко, сыр, яйца" 141 | оливки,"Соусы, орехи, консервы" 142 | чай черный,"Чай, кофе, сахар" 143 | чай зеленый,"Чай, кофе, сахар" 144 | ручка шариковая,Другое 145 | скраб,"Красота, гигиена, бытовая химия" 146 | ручки,Другое 147 | сливочное масло,"Молоко, сыр, яйца" 148 | перчатки,Товары для дома и дачи 149 | арахис,"Соусы, орехи, консервы" 150 | бульон,Подборки и готовые блюда 151 | перец,"Соусы, орехи, консервы" 152 | сливки,"Молоко, сыр, яйца" 153 | тряпка,Товары для дома и дачи 154 | кондиционер,"Красота, гигиена, бытовая химия" 155 | хрен,"Соусы, орехи, консервы" 156 | пряники,"Хлеб, сладости, снеки" 157 | тесто,Подборки и готовые блюда 158 | маска,"Красота, гигиена, бытовая химия" 159 | оливковое масло,"Макароны, крупы, специи" 160 | рулет,"Хлеб, сладости, снеки" 161 | контейнер,Товары для дома и дачи 162 | форма,Товары для дома и дачи 163 | крем-гель для душа,"Красота, гигиена, бытовая химия" 164 | лосьон для тела,"Красота, гигиена, бытовая химия" 165 | маркер,Другое 166 | лосось,"Соусы, орехи, консервы" 167 | бальзам после бритья,"Красота, гигиена, бытовая химия" 168 | фарш,"Птица, мясо, деликатесы" 169 | точилка,Товары для мам и детей 170 | сахар,"Чай, кофе, сахар" 171 | карандаши,Товары для мам и детей 172 | сырок,"Молоко, сыр, яйца" 173 | сардина,"Соусы, орехи, консервы" 174 | плавленый сыр,"Молоко, сыр, яйца" 175 | линейка,Товары для мам и детей 176 | приправа,"Соусы, орехи, консервы" 177 | пена,"Красота, гигиена, бытовая химия" 178 | лак,"Красота, гигиена, бытовая химия" 179 | биойогурт,"Молоко, сыр, яйца" 180 | салат,"Овощи, фрукты, ягоды" 181 | круассаны,"Хлеб, сладости, снеки" 182 | сковорода,Товары для дома и дачи 183 | очиститель,Товары для дома и дачи 184 | освежитель,Товары для дома и дачи 185 | картофель,"Овощи, фрукты, ягоды" 186 | кружка,Товары для дома и дачи 187 | кисель,"Воды, соки, напитки" 188 | тарелка,Товары для дома и дачи 189 | кальмар,"Рыба, икра" 190 | корм,Зоотовары 191 | салатник,Товары для дома и дачи 192 | напиток сокосодержащий,"Воды, соки, напитки" 193 | кукуруза,"Соусы, орехи, консервы" 194 | заварочный чайник,Товары для дома и дачи 195 | тунец,"Соусы, орехи, консервы" 196 | драже,"Хлеб, сладости, снеки" 197 | курага,"Хлеб, сладости, снеки" 198 | ежедневник,Другое 199 | ручка,Другое 200 | отруби,"Макароны, крупы, специи" 201 | дезодорант-спрей,"Красота, гигиена, бытовая химия" 202 | презервативы,"Хлеб, сладости, снеки" 203 | баклажаны,Подборки и готовые блюда 204 | скатерть,Товары для дома и дачи 205 | станок для бритья,"Хлеб, сладости, снеки" 206 | мороженое сливочное,"Молоко, сыр, яйца" 207 | зубная щетка,"Красота, гигиена, бытовая химия" 208 | туалетное мыло,"Красота, гигиена, бытовая химия" 209 | суфле,"Хлеб, сладости, снеки" 210 | диспенсер,Товары для дома и дачи 211 | жевательная резинка,"Хлеб, сладости, снеки" 212 | щетка,Товары для дома и дачи 213 | сушки,"Хлеб, сладости, снеки" 214 | бритвенный станок,"Хлеб, сладости, снеки" 215 | кассеты,"Хлеб, сладости, снеки" 216 | ветчина,"Птица, мясо, деликатесы" 217 | закуска,"Соусы, орехи, консервы" 218 | вырезка,"Птица, мясо, деликатесы" 219 | корейка,"Птица, мясо, деликатесы" 220 | освежитель воздуха,Товары для дома и дачи 221 | средство,Товары для дома и дачи 222 | капсулы,Товары для дома и дачи 223 | абрикосы,"Соусы, орехи, консервы" 224 | сгущенное молоко,"Молоко, сыр, яйца" 225 | крем-краска,"Красота, гигиена, бытовая химия" 226 | краска,"Красота, гигиена, бытовая химия" 227 | чистящее средство,Товары для дома и дачи 228 | дрожжи,"Макароны, крупы, специи" 229 | мука,"Макароны, крупы, специи" 230 | лимонад,"Воды, соки, напитки" 231 | маслины,"Соусы, орехи, консервы" 232 | порошок,Товары для дома и дачи 233 | крем-гель,"Красота, гигиена, бытовая химия" 234 | грудинка,"Птица, мясо, деликатесы" 235 | суп-пюре,Подборки и готовые блюда 236 | ершик,Товары для дома и дачи 237 | бумажные полотенца,"Красота, гигиена, бытовая химия" 238 | продукт,"Молоко, сыр, яйца" 239 | кулич,"Хлеб, сладости, снеки" 240 | пирог,"Хлеб, сладости, снеки" 241 | стаканы,Товары для дома и дачи 242 | бокалы,Товары для дома и дачи 243 | набор бокалов,Товары для дома и дачи 244 | семечки подсолнечные,"Соусы, орехи, консервы" 245 | гель-крем для душа,"Красота, гигиена, бытовая химия" 246 | студень,"Птица, мясо, деликатесы" 247 | горчица,"Соусы, орехи, консервы" 248 | компот,Товары для мам и детей 249 | спрей,"Красота, гигиена, бытовая химия" 250 | бальзам-ополаскиватель,"Красота, гигиена, бытовая химия" 251 | папка-регистратор,Другое 252 | ребрышки свиные,"Птица, мясо, деликатесы" 253 | круассан,Подборки и готовые блюда 254 | булочка,Подборки и готовые блюда 255 | жидкое мыло,"Красота, гигиена, бытовая химия" 256 | палочки,"Хлеб, сладости, снеки" 257 | пенка,"Красота, гигиена, бытовая химия" 258 | таблетки,Товары для дома и дачи 259 | консервы,Зоотовары 260 | влажные салфетки,Товары для мам и детей 261 | сыр мягкий,"Молоко, сыр, яйца" 262 | трусики-подгузники,Товары для мам и детей 263 | термос,Товары для дома и дачи 264 | корица,"Соусы, орехи, консервы" 265 | лампа светодиодная,Бытовая техника 266 | креветки,"Рыба, икра" 267 | детское молочко,Товары для мам и детей 268 | молочная смесь,Товары для мам и детей 269 | кастрюля,Товары для дома и дачи 270 | банка,Товары для дома и дачи 271 | прокладки женские,"Красота, гигиена, бытовая химия" 272 | холодец,"Птица, мясо, деликатесы" 273 | чайная пара,Товары для дома и дачи 274 | крахмал,"Макароны, крупы, специи" 275 | сухари,"Макароны, крупы, специи" 276 | пудра,"Макароны, крупы, специи" 277 | батончик,"Хлеб, сладости, снеки" 278 | скумбрия,"Соусы, орехи, консервы" 279 | желатин,"Макароны, крупы, специи" 280 | шоколадные конфеты,"Хлеб, сладости, снеки" 281 | шампунь-уход,"Красота, гигиена, бытовая химия" 282 | разделочная доска,Товары для дома и дачи 283 | лампа накаливания,Бытовая техника 284 | сковорода-гриль,Товары для дома и дачи 285 | ассорти,"Птица, мясо, деликатесы" 286 | закваска,"Молоко, сыр, яйца" 287 | вишня,"Соусы, орехи, консервы" 288 | блок для унитаза,Товары для дома и дачи 289 | питьевой биойогурт,"Молоко, сыр, яйца" 290 | сывороточный напиток,"Молоко, сыр, яйца" 291 | коктейль йогуртный,"Молоко, сыр, яйца" 292 | мешки для мусора,Товары для дома и дачи 293 | сардельки,"Птица, мясо, деликатесы" 294 | уксус,"Макароны, крупы, специи" 295 | торт,"Хлеб, сладости, снеки" 296 | хлеб,"Хлеб, сладости, снеки" 297 | паста,"Хлеб, сладости, снеки" 298 | вафли,"Хлеб, сладости, снеки" 299 | пирожные,"Хлеб, сладости, снеки" 300 | треска,Подборки и готовые блюда 301 | пудинг,"Молоко, сыр, яйца" 302 | творожок,"Молоко, сыр, яйца" 303 | запеканка,"Молоко, сыр, яйца" 304 | бекон,"Птица, мясо, деликатесы" 305 | кофейный напиток,"Чай, кофе, сахар" 306 | сыр полутвердый,"Молоко, сыр, яйца" 307 | рис,"Макароны, крупы, специи" 308 | горошек,"Соусы, орехи, консервы" 309 | брусника,"Соусы, орехи, консервы" 310 | коктейль,"Соусы, орехи, консервы" 311 | гречка,"Макароны, крупы, специи" 312 | мидии,"Рыба, икра" 313 | булочки,"Хлеб, сладости, снеки" 314 | сыр твердый,"Молоко, сыр, яйца" 315 | рахат-лукум,"Хлеб, сладости, снеки" 316 | десерт творожный,"Молоко, сыр, яйца" 317 | имбирь,Подборки и готовые блюда 318 | овощи,"Овощи, фрукты, ягоды" 319 | шоколадный набор,"Хлеб, сладости, снеки" 320 | конфеты шоколадные,"Хлеб, сладости, снеки" 321 | ранец,Товары для мам и детей 322 | рюкзак,Товары для мам и детей 323 | пенал-книжка,Товары для мам и детей 324 | пластилин,Товары для мам и детей 325 | крупа,"Макароны, крупы, специи" 326 | машина,Товары для мам и детей 327 | бластер,Товары для мам и детей 328 | игровой набор,Товары для мам и детей 329 | подсолнечное масло,"Макароны, крупы, специи" 330 | чечевица,"Макароны, крупы, специи" 331 | игровой,Товары для мам и детей 332 | говядина тушеная,"Соусы, орехи, консервы" 333 | подгузники-трусики,Товары для мам и детей 334 | крем-суп,Товары для мам и детей 335 | кукла,Товары для мам и детей 336 | настольная игра,Товары для мам и детей 337 | пазл,Товары для мам и детей 338 | тортилья,"Хлеб, сладости, снеки" 339 | клейкая лента,Товары для дома и дачи 340 | стакан,Товары для дома и дачи 341 | стул,Товары для дома и дачи 342 | стол,Товары для дома и дачи 343 | кашпо,Товары для дома и дачи 344 | набор для творчества,Товары для мам и детей 345 | конструктор,Товары для мам и детей 346 | готовый завтрак,"Макароны, крупы, специи" 347 | спагетти,"Макароны, крупы, специи" 348 | аджика,"Соусы, орехи, консервы" 349 | смузи,"Воды, соки, напитки" 350 | топпинг,"Хлеб, сладости, снеки" 351 | мороженое молочное,"Молоко, сыр, яйца" 352 | глазурь,"Макароны, крупы, специи" 353 | соус томатный,"Соусы, орехи, консервы" 354 | соль,"Макароны, крупы, специи" 355 | галеты,"Макароны, крупы, специи" 356 | сорбет,"Молоко, сыр, яйца" 357 | щербет,"Молоко, сыр, яйца" 358 | мюсли,"Макароны, крупы, специи" 359 | мороженое пломбир,"Молоко, сыр, яйца" 360 | напиток кисломолочный,"Молоко, сыр, яйца" 361 | молочный коктейль,"Молоко, сыр, яйца" 362 | салфетка,Товары для дома и дачи 363 | папка,Другое 364 | швабра,Товары для дома и дачи 365 | насадка,Товары для дома и дачи 366 | мусс,"Красота, гигиена, бытовая химия" 367 | лакомства,Зоотовары 368 | смесь сухая,Товары для мам и детей 369 | смесь nestle,Товары для мам и детей 370 | кувшин,Товары для дома и дачи 371 | клей,Другое 372 | губки,Товары для дома и дачи 373 | гель для бритья,"Красота, гигиена, бытовая химия" 374 | пена для бритья,"Красота, гигиена, бытовая химия" 375 | лосьон после бритья,"Красота, гигиена, бытовая химия" 376 | ополаскиватель для рта,"Красота, гигиена, бытовая химия" 377 | зубная,"Красота, гигиена, бытовая химия" 378 | зубная нить,"Красота, гигиена, бытовая химия" 379 | блокнот,Товары для мам и детей 380 | блюдо,Товары для дома и дачи 381 | тетрадь,Товары для мам и детей 382 | сахарница,Товары для дома и дачи 383 | чайник,Товары для дома и дачи 384 | набор подарочный,"Хлеб, сладости, снеки" 385 | карамель,"Хлеб, сладости, снеки" 386 | пастила,"Хлеб, сладости, снеки" 387 | поднос,Товары для дома и дачи 388 | сменный баллон,Товары для дома и дачи 389 | горошек зеленый,"Соусы, орехи, консервы" 390 | грибы,"Соусы, орехи, консервы" 391 | блок,Товары для дома и дачи 392 | моющее средство,Товары для дома и дачи 393 | губка,Товары для дома и дачи 394 | жидкость,Товары для дома и дачи 395 | фонарь,Товары для дома и дачи 396 | наушники,Бытовая техника 397 | лечо,"Соусы, орехи, консервы" 398 | доска разделочная,Товары для дома и дачи 399 | чашка,Товары для дома и дачи 400 | крем для тела,"Красота, гигиена, бытовая химия" 401 | скраб для тела,"Красота, гигиена, бытовая химия" 402 | ковш,Товары для дома и дачи 403 | набор посуды,Товары для дома и дачи 404 | молочко,"Красота, гигиена, бытовая химия" 405 | карта памяти,Бытовая техника 406 | маска для лица,"Красота, гигиена, бытовая химия" 407 | мицеллярная вода,"Красота, гигиена, бытовая химия" 408 | дезодорант-аэрозоль,"Красота, гигиена, бытовая химия" 409 | кубики,Товары для мам и детей 410 | коврик,Товары для мам и детей 411 | корнишоны,"Соусы, орехи, консервы" 412 | френч-пресс,Товары для дома и дачи 413 | ведро,Товары для дома и дачи 414 | хлопья овсяные,"Макароны, крупы, специи" 415 | купаты,"Птица, мясо, деликатесы" 416 | туалетная бумага,"Красота, гигиена, бытовая химия" 417 | карандаш,Другое 418 | лоток,Другое 419 | соль для ванн,"Красота, гигиена, бытовая химия" 420 | форма для запекания,Товары для дома и дачи 421 | персики,"Соусы, орехи, консервы" 422 | смесь для выпечки,"Макароны, крупы, специи" 423 | подставка,Другое 424 | щипцы,Товары для дома и дачи 425 | ложка,Товары для дома и дачи 426 | вилка,Товары для дома и дачи 427 | клубника,"Соусы, орехи, консервы" 428 | подушка,Другое 429 | свинина,"Соусы, орехи, консервы" 430 | подарочный набор,"Хлеб, сладости, снеки" 431 | машинка,Товары для мам и детей 432 | лосьон,"Красота, гигиена, бытовая химия" 433 | слойка,"Хлеб, сладости, снеки" 434 | смесь овощная,"Овощи, фрукты, ягоды" 435 | светильник,Бытовая техника 436 | пенал,Товары для мам и детей 437 | ополаскиватель,Товары для мам и детей 438 | оливки зеленые,"Соусы, орехи, консервы" 439 | нож универсальный,Товары для дома и дачи 440 | сироп,"Хлеб, сладости, снеки" 441 | конфитюр,"Хлеб, сладости, снеки" 442 | аэрозоль,Товары для дома и дачи 443 | нерка,"Соусы, орехи, консервы" 444 | филе,"Птица, мясо, деликатесы" 445 | салфетки бумажные,"Красота, гигиена, бытовая химия" 446 | лук,"Овощи, фрукты, ягоды" 447 | поддон,Товары для дома и дачи 448 | крышка,Товары для дома и дачи 449 | половник,Товары для дома и дачи 450 | манго,"Соусы, орехи, консервы" 451 | чеснок,"Соусы, орехи, консервы" 452 | органайзер,Товары для дома и дачи 453 | печенье сдобное,"Хлеб, сладости, снеки" 454 | полотенца,Товары для дома и дачи 455 | горох,"Макароны, крупы, специи" 456 | бумага для печати,Другое 457 | бургер,Подборки и готовые блюда 458 | лопатка,"Птица, мясо, деликатесы" 459 | окорок свиной,"Птица, мясо, деликатесы" 460 | биолакт,Товары для мам и детей 461 | кофе в капсулах,"Чай, кофе, сахар" 462 | лакомство для кошек,Зоотовары 463 | кофе в зернах,"Чай, кофе, сахар" 464 | семена,"Соусы, орехи, консервы" 465 | фисташки,"Соусы, орехи, консервы" 466 | крем-краска для волос,"Красота, гигиена, бытовая химия" 467 | вареники,Подборки и готовые блюда 468 | мармелад жевательный,"Хлеб, сладости, снеки" 469 | жевательные конфеты,"Хлеб, сладости, снеки" 470 | пятновыводитель,Товары для дома и дачи 471 | фарш говяжий,"Птица, мясо, деликатесы" 472 | биточки,Подборки и готовые блюда 473 | доска,Товары для мам и детей 474 | удлинитель,Бытовая техника 475 | лакомство,Зоотовары 476 | лак для волос,"Красота, гигиена, бытовая химия" 477 | яйцо куриное,"Молоко, сыр, яйца" 478 | жидкое,Товары для дома и дачи 479 | дезодорант-антиперспирант,"Красота, гигиена, бытовая химия" 480 | шампунь-гель,Товары для мам и детей 481 | кассета,Товары для дома и дачи 482 | шумовка,Товары для дома и дачи 483 | ящик,Товары для дома и дачи 484 | терка,Товары для дома и дачи 485 | пинцет,Товары для дома и дачи 486 | набор ножей,Товары для дома и дачи 487 | набор стаканов,Товары для дома и дачи 488 | ароматизатор,Товары для дома и дачи 489 | бокал,Товары для дома и дачи 490 | свеча,Товары для дома и дачи 491 | штрудель,Подборки и готовые блюда 492 | шейка,"Птица, мясо, деликатесы" 493 | печень трески,"Соусы, орехи, консервы" 494 | сельдь,"Рыба, икра" 495 | котлеты,Подборки и готовые блюда 496 | плов,Подборки и готовые блюда 497 | смесь молочная,Товары для мам и детей 498 | йогурт питьевой,"Молоко, сыр, яйца" 499 | помидоры,"Соусы, орехи, консервы" 500 | карбонад,"Птица, мясо, деликатесы" 501 | буженина,"Птица, мясо, деликатесы" 502 | ластик,Другое 503 | картридж,Бытовая техника 504 | термокружка,Товары для дома и дачи 505 | козинак,"Хлеб, сладости, снеки" 506 | каша молочная,Товары для мам и детей 507 | крем чистящий,Товары для дома и дачи 508 | гриль,Товары для дома и дачи 509 | тушка цыпленка-бройлера,"Птица, мясо, деликатесы" 510 | сэндвич,Подборки и готовые блюда 511 | маска для волос,"Красота, гигиена, бытовая химия" 512 | хумус,Подборки и готовые блюда 513 | патчи,"Хлеб, сладости, снеки" 514 | лампа,Бытовая техника 515 | чизкейк,"Хлеб, сладости, снеки" 516 | палтус,"Рыба, икра" 517 | котлета,Подборки и готовые блюда 518 | тушка,"Птица, мясо, деликатесы" 519 | корюшка,"Рыба, икра" 520 | масло оливковое,"Макароны, крупы, специи" 521 | салатная смесь,"Овощи, фрукты, ягоды" 522 | календарь,Другое 523 | базилик,"Соусы, орехи, консервы" 524 | карамель леденцовая,"Хлеб, сладости, снеки" 525 | фартук,Товары для мам и детей 526 | ножницы,Другое 527 | альбом для рисования,Товары для мам и детей 528 | кисти,Товары для мам и детей 529 | гуашь,Товары для мам и детей 530 | карандаши цветные,Товары для мам и детей 531 | фломастеры,Товары для мам и детей 532 | икра мойвы,"Рыба, икра" 533 | туалетный блок,Товары для дома и дачи 534 | погремушка,Товары для мам и детей 535 | блинная сковорода,Товары для дома и дачи 536 | баранина,"Птица, мясо, деликатесы" 537 | крыло цыпленка-бройлера,"Птица, мясо, деликатесы" 538 | продукт творожный,"Молоко, сыр, яйца" 539 | набор карандашей,Другое 540 | вырезка свиная,"Птица, мясо, деликатесы" 541 | бедро,"Птица, мясо, деликатесы" 542 | свекла,Подборки и готовые блюда 543 | ватные палочки,"Красота, гигиена, бытовая химия" 544 | окорочок,"Птица, мясо, деликатесы" 545 | емкость,Товары для дома и дачи 546 | ананасы,"Соусы, орехи, консервы" 547 | шоколадный батончик,"Хлеб, сладости, снеки" 548 | шашлык,"Птица, мясо, деликатесы" 549 | кисломолочный продукт,"Молоко, сыр, яйца" 550 | желе,"Молоко, сыр, яйца" 551 | крабовое мясо,"Рыба, икра" 552 | наклейки,Товары для мам и детей 553 | крем для рук,"Красота, гигиена, бытовая химия" 554 | шейка свиная,"Птица, мясо, деликатесы" 555 | кофеварка,Бытовая техника 556 | тетрадь предметная,Товары для мам и детей 557 | биопродукт,"Молоко, сыр, яйца" 558 | укроп,"Соусы, орехи, консервы" 559 | сыр плавленый,"Молоко, сыр, яйца" 560 | печенье овсяное,"Хлеб, сладости, снеки" 561 | ребрышки,"Птица, мясо, деликатесы" 562 | средство для купания,Товары для мам и детей 563 | сотейник,Товары для дома и дачи 564 | игрушка развивающая,Товары для мам и детей 565 | волчок,Товары для мам и детей 566 | кабель,Бытовая техника 567 | корзина,Товары для дома и дачи 568 | пленка для ламинирования,Другое 569 | табличка,Другое 570 | матрас надувной,Товары для дома и дачи 571 | крем-мыло,"Красота, гигиена, бытовая химия" 572 | мозаика,Товары для мам и детей 573 | подарочный,"Хлеб, сладости, снеки" 574 | креветка,"Рыба, икра" 575 | лампа галогенная,Бытовая техника 576 | мультиварка,Бытовая техника 577 | форма для выпечки,Товары для дома и дачи 578 | тоник,"Красота, гигиена, бытовая химия" 579 | держатель,Бытовая техника 580 | шоколад молочный,"Хлеб, сладости, снеки" 581 | стейк говяжий,"Птица, мясо, деликатесы" 582 | пеленки,Товары для мам и детей 583 | игра,Товары для мам и детей 584 | набор шоколадных конфет,"Хлеб, сладости, снеки" 585 | тарелка десертная,Товары для дома и дачи 586 | блендер,Бытовая техника 587 | фен,Бытовая техника 588 | миксер,Бытовая техника 589 | мясорубка,Бытовая техника 590 | сушилка,Бытовая техника 591 | пылесос,Бытовая техника 592 | триммер,Бытовая техника 593 | утюг,Бытовая техника 594 | окорок,"Птица, мясо, деликатесы" 595 | индейка,"Птица, мясо, деликатесы" 596 | рулька,"Птица, мясо, деликатесы" 597 | масло для тела,"Красота, гигиена, бытовая химия" 598 | клюква,"Овощи, фрукты, ягоды" 599 | пшено,"Макароны, крупы, специи" 600 | капуста брокколи,"Овощи, фрукты, ягоды" 601 | маринад,"Макароны, крупы, специи" 602 | телевизор,Бытовая техника 603 | кронштейн,Бытовая техника 604 | рулька свиная,"Птица, мясо, деликатесы" 605 | раскраска,Товары для мам и детей 606 | грунт,Товары для дома и дачи 607 | медальоны,"Птица, мясо, деликатесы" 608 | отвертка,Товары для дома и дачи 609 | степлер,Товары для дома и дачи 610 | герметик,Товары для дома и дачи 611 | сверло,Товары для дома и дачи 612 | крючок,Товары для дома и дачи 613 | кисть,Товары для дома и дачи 614 | фильтр,Товары для дома и дачи 615 | монтажная лента,Товары для дома и дачи 616 | голень,"Птица, мясо, деликатесы" 617 | грудка,"Птица, мясо, деликатесы" 618 | рулетка,Товары для дома и дачи 619 | смеситель,Товары для дома и дачи 620 | мыльница,Товары для дома и дачи 621 | шапка,Товары для дома и дачи 622 | мочалка,Товары для дома и дачи 623 | кресло,Товары для дома и дачи 624 | шланг,Товары для дома и дачи 625 | мёд,"Хлеб, сладости, снеки" 626 | весы,Бытовая техника 627 | фоторамка,Товары для дома и дачи 628 | мангал,Товары для дома и дачи 629 | тарелки,Товары для дома и дачи 630 | краб,"Рыба, икра" 631 | горшок,Товары для дома и дачи 632 | садовая фигура,Товары для дома и дачи 633 | батон,"Хлеб, сладости, снеки" 634 | баранки,"Хлеб, сладости, снеки" 635 | кекс,"Хлеб, сладости, снеки" 636 | дезодорант спрей,"Красота, гигиена, бытовая химия" 637 | риет,"Соусы, орехи, консервы" 638 | набор пирожных,"Хлеб, сладости, снеки" 639 | гастроемкость,Товары для дома и дачи 640 | яйцо,"Хлеб, сладости, снеки" 641 | яйца,"Хлеб, сладости, снеки" 642 | черника,"Овощи, фрукты, ягоды" 643 | бумага,"Красота, гигиена, бытовая химия" 644 | ватные диски,"Красота, гигиена, бытовая химия" 645 | ловушка,Товары для дома и дачи 646 | стиральный,Товары для дома и дачи 647 | колготки женские,Другое 648 | носки мужские,Другое 649 | носки женские,Другое 650 | колготки,Другое 651 | простыня на резинке,Товары для дома и дачи 652 | простыня,Товары для дома и дачи 653 | комплект постельного белья,Товары для дома и дачи 654 | наволочка,Товары для дома и дачи 655 | пододеяльник,Товары для дома и дачи 656 | мыло детское,"Красота, гигиена, бытовая химия" 657 | корм для собак,Зоотовары 658 | наполнитель,Зоотовары 659 | корм сухой,Зоотовары 660 | вешалка,Товары для дома и дачи 661 | скребок,Товары для дома и дачи 662 | корм для котят,Зоотовары 663 | крупа гречневая,"Макароны, крупы, специи" 664 | тарталетки,"Хлеб, сладости, снеки" 665 | хлебные палочки,"Хлеб, сладости, снеки" 666 | зажигалка,Товары для дома и дачи 667 | бланки,Другое 668 | книга,Другое 669 | вкладыши,Другое 670 | биойогурт питьевой,Товары для мам и детей 671 | губки для посуды,Товары для дома и дачи 672 | шампунь для волос,"Красота, гигиена, бытовая химия" 673 | кондиционер для волос,"Красота, гигиена, бытовая химия" 674 | мусс для волос,"Красота, гигиена, бытовая химия" 675 | балык,"Птица, мясо, деликатесы" 676 | сдоба,"Хлеб, сладости, снеки" 677 | соус соевый,"Соусы, орехи, консервы" 678 | ацидофилин,"Молоко, сыр, яйца" 679 | маргарин,"Молоко, сыр, яйца" 680 | творог зерненый,"Молоко, сыр, яйца" 681 | творожное зерно,"Молоко, сыр, яйца" 682 | хлебная смесь,"Макароны, крупы, специи" 683 | сухой завтрак,"Макароны, крупы, специи" 684 | снеки,"Птица, мясо, деликатесы" 685 | конфета,"Хлеб, сладости, снеки" 686 | чипсы,Подборки и готовые блюда 687 | чайный напиток,"Чай, кофе, сахар" 688 | жевательный мармелад,"Хлеб, сладости, снеки" 689 | напиток кофейный,"Чай, кофе, сахар" 690 | пирожок,"Хлеб, сладости, снеки" 691 | рагу,Товары для мам и детей 692 | геркулес,"Макароны, крупы, специи" 693 | отбеливатель,Товары для дома и дачи 694 | сменный,Товары для дома и дачи 695 | крем для обуви,Товары для дома и дачи 696 | губка для обуви,Товары для дома и дачи 697 | антиперспирант-аэрозоль,"Красота, гигиена, бытовая химия" 698 | гель-шампунь,"Красота, гигиена, бытовая химия" 699 | крем для ног,"Красота, гигиена, бытовая химия" 700 | бальзам для губ,"Хлеб, сладости, снеки" 701 | полироль,Товары для дома и дачи 702 | порошок чистящий,Товары для дома и дачи 703 | краска для волос,"Красота, гигиена, бытовая химия" 704 | сыворотка,"Красота, гигиена, бытовая химия" 705 | масло для волос,"Красота, гигиена, бытовая химия" 706 | гигиенические прокладки,"Красота, гигиена, бытовая химия" 707 | горбуша,"Соусы, орехи, консервы" 708 | салфетки влажные,Товары для мам и детей 709 | шпинат,"Овощи, фрукты, ягоды" 710 | картофель фри,"Овощи, фрукты, ягоды" 711 | малина,"Овощи, фрукты, ягоды" 712 | багет,Подборки и готовые блюда 713 | блинчики,Подборки и готовые блюда 714 | фрикадельки,Подборки и готовые блюда 715 | азу,Подборки и готовые блюда 716 | миндаль,"Макароны, крупы, специи" 717 | прокладки гигиенические,"Красота, гигиена, бытовая химия" 718 | мыло хозяйственное,"Красота, гигиена, бытовая химия" 719 | сало,"Птица, мясо, деликатесы" 720 | форель,"Рыба, икра" 721 | ананас,"Овощи, фрукты, ягоды" 722 | шницель,Подборки и готовые блюда 723 | огурчики,"Соусы, орехи, консервы" 724 | решетка-гриль,Товары для дома и дачи 725 | уголь древесный,Товары для дома и дачи 726 | опята,"Овощи, фрукты, ягоды" 727 | капуста квашеная,"Соусы, орехи, консервы" 728 | икра лососевая,"Молоко, сыр, яйца" 729 | бычки,"Соусы, орехи, консервы" 730 | коктейль молочный,"Молоко, сыр, яйца" 731 | пюре картофельное,Подборки и готовые блюда 732 | борщ,Подборки и готовые блюда 733 | саше,Товары для дома и дачи 734 | печень,"Соусы, орехи, консервы" 735 | шампунь детский,Товары для мам и детей 736 | спрей для волос,"Красота, гигиена, бытовая химия" 737 | щепа,Товары для дома и дачи 738 | пилка,"Красота, гигиена, бытовая химия" 739 | спички,Товары для дома и дачи 740 | морская капуста,Подборки и готовые блюда 741 | мини-круассаны,"Хлеб, сладости, снеки" 742 | батончик-мюсли,"Макароны, крупы, специи" 743 | напиток чайный,"Чай, кофе, сахар" 744 | соломка,"Хлеб, сладости, снеки" 745 | чистящее,Товары для дома и дачи 746 | насадка для швабры,Товары для дома и дачи 747 | миска,Товары для дома и дачи 748 | ролик,Товары для дома и дачи 749 | стельки,Товары для дома и дачи 750 | мясо,Подборки и готовые блюда 751 | сухой шампунь,"Красота, гигиена, бытовая химия" 752 | изделия макаронные,"Макароны, крупы, специи" 753 | кешью,"Соусы, орехи, консервы" 754 | финики,"Соусы, орехи, консервы" 755 | прокладки ежедневные,"Красота, гигиена, бытовая химия" 756 | средство чистящее,"Красота, гигиена, бытовая химия" 757 | продукт йогуртный,"Молоко, сыр, яйца" 758 | ошейник,Зоотовары 759 | тушь для ресниц,"Красота, гигиена, бытовая химия" 760 | набор игровой,Товары для мам и детей 761 | орех,"Соусы, орехи, консервы" 762 | филе цыпленка,"Птица, мясо, деликатесы" 763 | сырок глазированный,"Молоко, сыр, яйца" 764 | полотенца бумажные,"Красота, гигиена, бытовая химия" 765 | цыпленок,"Птица, мясо, деликатесы" 766 | семечки,"Соусы, орехи, консервы" 767 | винный напиток,Алкоголь 768 | носки,"Красота, гигиена, бытовая химия" 769 | квас,"Воды, соки, напитки" 770 | сухарики ржаные,"Хлеб, сладости, снеки" 771 | крабовые палочки,"Рыба, икра" 772 | лак для ногтей,"Красота, гигиена, бытовая химия" 773 | удобрение,Товары для дома и дачи 774 | гренки,"Хлеб, сладости, снеки" 775 | хлебушек,"Хлеб, сладости, снеки" 776 | поильник,Товары для мам и детей 777 | средство моющее,"Красота, гигиена, бытовая химия" 778 | текила,Алкоголь 779 | масло моторное,"Красота, гигиена, бытовая химия" 780 | крупа рис,"Макароны, крупы, специи" 781 | творог мягкий,"Молоко, сыр, яйца" 782 | игрушка для кошек,Зоотовары 783 | средство для мытья посуды,"Красота, гигиена, бытовая химия" 784 | полотенце махровое,Товары для дома и дачи 785 | изюм,"Соусы, орехи, консервы" 786 | пенка для умывания,"Красота, гигиена, бытовая химия" 787 | тоник для лица,"Красота, гигиена, бытовая химия" 788 | тушь,"Красота, гигиена, бытовая химия" 789 | лаваш,"Хлеб, сладости, снеки" 790 | сельдь филе,"Рыба, икра" 791 | продукт кисломолочный,Товары для мам и детей 792 | печь,Бытовая техника 793 | лента,Товары для дома и дачи 794 | молочный,Товары для мам и детей 795 | корм для,Зоотовары 796 | салат из морской капусты,"Макароны, крупы, специи" 797 | порошок стиральный,"Красота, гигиена, бытовая химия" 798 | молоко сгущенное,"Молоко, сыр, яйца" 799 | кета,"Рыба, икра" 800 | цикорий,"Чай, кофе, сахар" 801 | семга,"Рыба, икра" 802 | гуляш,"Птица, мясо, деликатесы" 803 | соус майонезный,"Соусы, орехи, консервы" 804 | полотенце,Товары для дома и дачи 805 | кусачки,"Красота, гигиена, бытовая химия" 806 | вермут,Алкоголь 807 | мед,"Соусы, орехи, консервы" 808 | масса творожная,"Молоко, сыр, яйца" 809 | ром,Алкоголь 810 | пряник,"Хлеб, сладости, снеки" 811 | консервы для собак,Зоотовары 812 | минтай,"Рыба, икра" 813 | комплект пылесборников,Бытовая техника 814 | соска,Товары для мам и детей 815 | бутылочка,Товары для мам и детей 816 | фундук,"Соусы, орехи, консервы" 817 | молочко для тела,"Красота, гигиена, бытовая химия" 818 | настойка,Алкоголь 819 | игрушка для собак,Зоотовары 820 | карандаш для глаз,"Красота, гигиена, бытовая химия" 821 | джин,Алкоголь 822 | скраб для лица,"Красота, гигиена, бытовая химия" 823 | сырок творожный,"Молоко, сыр, яйца" 824 | резинка для волос,"Красота, гигиена, бытовая химия" 825 | лепешки,"Хлеб, сладости, снеки" 826 | фольга,Другое 827 | антифриз,"Красота, гигиена, бытовая химия" 828 | нагрудник,Товары для мам и детей 829 | голень цыпленка,"Птица, мясо, деликатесы" 830 | жидкость для розжига,Дача и гриль 831 | горшок для цветов,Товары для мам и детей 832 | доска гладильная,"Красота, гигиена, бытовая химия" 833 | плед,Товары для дома и дачи 834 | пластины,Дача и гриль 835 | одеяло,Товары для дома и дачи 836 | помада,"Красота, гигиена, бытовая химия" 837 | штора,"Красота, гигиена, бытовая химия" 838 | покрывало,Товары для дома и дачи 839 | чехол,Товары для дома и дачи 840 | туалетная вода,"Красота, гигиена, бытовая химия" 841 | пустышка,Товары для мам и детей 842 | книжка,Товары для мам и детей 843 | пакеты,Дача и гриль 844 | гольфы,"Красота, гигиена, бытовая химия" 845 | корм для щенков,Зоотовары 846 | биопродукт кисломолочный,"Молоко, сыр, яйца" 847 | сидр,Алкоголь 848 | тефтели,"Соусы, орехи, консервы" 849 | напиток безалкогольный,Алкоголь 850 | лакомство для грызунов,Зоотовары 851 | резинки,Другое 852 | попкорн,"Хлеб, сладости, снеки" 853 | концентрат,Товары для дома и дачи 854 | огурец,"Соусы, орехи, консервы" 855 | пластырь,Дача и гриль 856 | украшение новогоднее,Товары для дома и дачи 857 | расческа,"Красота, гигиена, бытовая химия" 858 | антиперспирант-карандаш,"Красота, гигиена, бытовая химия" 859 | какао-напиток,"Чай, кофе, сахар" 860 | масло сливочное,"Молоко, сыр, яйца" 861 | антиперспирант шариковый,"Красота, гигиена, бытовая химия" 862 | кофе молотый,"Чай, кофе, сахар" 863 | кассеты для бритья,"Красота, гигиена, бытовая химия" 864 | крем-мыло жидкое,"Красота, гигиена, бытовая химия" 865 | сменный баллон для,"Красота, гигиена, бытовая химия" 866 | щетка для волос,Товары для дома и дачи 867 | крем детский,Товары для мам и детей 868 | краска-уход для волос,"Красота, гигиена, бытовая химия" 869 | бальзам-ополаскиватель для волос,"Красота, гигиена, бытовая химия" 870 | сыр творожный,"Молоко, сыр, яйца" 871 | гель-уход для душа,"Красота, гигиена, бытовая химия" 872 | гель для умывания,"Красота, гигиена, бытовая химия" 873 | био,"Молоко, сыр, яйца" 874 | творог детский,"Молоко, сыр, яйца" 875 | чай детский,Товары для мам и детей 876 | сухая смесь,"Макароны, крупы, специи" 877 | масло сладко-сливочное,"Молоко, сыр, яйца" 878 | чай травяной,"Чай, кофе, сахар" 879 | батончик мюсли,"Хлеб, сладости, снеки" 880 | палочки ватные,"Красота, гигиена, бытовая химия" 881 | напиток соевый,"Воды, соки, напитки" 882 | чулки,Товары для дома и дачи 883 | ароматизатор автомобильный,Товары для дома и дачи 884 | трусы женские,Товары для дома и дачи 885 | соска-пустышка,Товары для мам и детей 886 | донат,"Хлеб, сладости, снеки" 887 | подследники,Товары для дома и дачи 888 | обувь домашняя,Товары для дома и дачи 889 | запеканка творожная,"Молоко, сыр, яйца" 890 | заменитель сахара,"Макароны, крупы, специи" 891 | трусы мужские,Товары для дома и дачи 892 | стики табачные,Товары для дома и дачи 893 | сыворотка для лица,"Красота, гигиена, бытовая химия" 894 | поводок для собак,Зоотовары 895 | шлейка для собак,Зоотовары 896 | ошейник для собак,Зоотовары 897 | дождевик для собак,Зоотовары 898 | толстовка для собак,Зоотовары 899 | витамины,Зоотовары 900 | шампунь-кондиционер,Зоотовары 901 | подстилки,Зоотовары 902 | лакомство для птиц,Зоотовары 903 | корм для птиц,Зоотовары 904 | миска для животных,Зоотовары 905 | корм для грызунов,Зоотовары 906 | паста ореховая,"Хлеб, сладости, снеки" 907 | украшение,Товары для дома и дачи 908 | зубная щетка детская,Товары для мам и детей 909 | мыло туалетное,"Красота, гигиена, бытовая химия" 910 | мешки,"Красота, гигиена, бытовая химия" 911 | наматрасник,Товары для мам и детей 912 | тональный крем,"Красота, гигиена, бытовая химия" 913 | виши,"Красота, гигиена, бытовая химия" 914 | капли,Зоотовары 915 | librederm,"Красота, гигиена, бытовая химия" 916 | vichy,"Красота, гигиена, бытовая химия" 917 | корм для рыб,Зоотовары 918 | щетка зубная,Товары для мам и детей 919 | хлебцы хрустящие,"Хлеб, сладости, снеки" 920 | чашка-поильник,Товары для мам и детей 921 | изд :,Товары для мам и детей 922 | мед-суфле,"Соусы, орехи, консервы" 923 | формовая игрушка,Товары для дома и дачи 924 | лакомые,Зоотовары 925 | "изд ""мозаика-синтез""",Товары для мам и детей 926 | полотенце банное,Товары для дома и дачи 927 | цветы георгина,Товары для дома и дачи 928 | развивающая,Товары для мам и детей 929 | оттеночный бальзам,"Красота, гигиена, бытовая химия" 930 | читаем детям,Товары для мам и детей 931 | новогоднее подвесное украшение,Товары для дома и дачи 932 | "изд ""росмэн""",Товары для мам и детей 933 | мягкая игрушка,Товары для мам и детей 934 | аппликация,Товары для мам и детей 935 | игрушка мягкая,Товары для мам и детей 936 | напиток газированный,"Воды, соки, напитки" 937 | изд,Товары для дома и дачи 938 | керамический горшок,Товары для дома и дачи 939 | набор для двоих,Подборки и готовые блюда 940 | календарь 2019,Товары для дома и дачи 941 | набор шаров,Товары для дома и дачи 942 | новогодний,"Хлеб, сладости, снеки" 943 | кашпо-горшок,Товары для дома и дачи 944 | игра настольная,Товары для мам и детей 945 | колготки детские,Товары для мам и детей 946 | коврик-раскраска,Товары для мам и детей 947 | "изд ""клевер""",Товары для мам и детей 948 | cыр,"Молоко, сыр, яйца" 949 | корм влажный,Зоотовары 950 | смесь молочная сухая,Товары для мам и детей 951 | картина,Товары для мам и детей 952 | халат махровый,Товары для дома и дачи 953 | вазон,Товары для дома и дачи 954 | фигурка,Товары для мам и детей 955 | носки детские,Товары для мам и детей 956 | ломтики,"Соусы, орехи, консервы" 957 | одежда для куклы,Товары для мам и детей 958 | краюшки,"Хлеб, сладости, снеки" 959 | пакет,Другое 960 | плюшка,"Хлеб, сладости, снеки" 961 | каравай,"Хлеб, сладости, снеки" 962 | винегрет,Подборки и готовые блюда 963 | диски ватные,"Красота, гигиена, бытовая химия" 964 | судак,"Рыба, икра" 965 | паприка,"Макароны, крупы, специи" 966 | мандарины,"Овощи, фрукты, ягоды" 967 | пломбир,Замороженные продукты 968 | груши,"Овощи, фрукты, ягоды" 969 | журнал,Другое 970 | сельдерей,"Овощи, фрукты, ягоды" 971 | груша,"Овощи, фрукты, ягоды" 972 | кинза,"Овощи, фрукты, ягоды" 973 | лавровый лист,"Макароны, крупы, специи" 974 | зелень,"Овощи, фрукты, ягоды" 975 | резинка,Другое 976 | лимонная кислота,"Макароны, крупы, специи" 977 | авокадо,"Овощи, фрукты, ягоды" 978 | айва,"Овощи, фрукты, ягоды" 979 | газета,Другое 980 | голубика,"Овощи, фрукты, ягоды" 981 | закусочка,"Хлеб, сладости, снеки" 982 | земля,Товары для дома и дачи 983 | кабачок,"Овощи, фрукты, ягоды" 984 | киндер,"Хлеб, сладости, снеки" 985 | коре,"Птица, мясо, деликатесы" 986 | купон,Другое 987 | куркума,"Макароны, крупы, специи" 988 | лепешка,"Хлеб, сладости, снеки" 989 | лимон,"Овощи, фрукты, ягоды" 990 | мята,"Овощи, фрукты, ягоды" 991 | окорочек,"Птица, мясо, деликатесы" 992 | оладьи,Подборки и готовые блюда 993 | пирожное,"Хлеб, сладости, снеки" 994 | пленка пищевая,Другое 995 | посыпка,Другое 996 | рожок,"Хлеб, сладости, снеки" 997 | свечи,Другое 998 | сигареты,Другое 999 | слива,"Овощи, фрукты, ягоды" 1000 | смородина,"Овощи, фрукты, ягоды" 1001 | снежок,"Молоко, сыр, яйца" 1002 | снек,"Хлеб, сладости, снеки" 1003 | сода,"Чай, кофе, сахар" 1004 | спаржа,"Овощи, фрукты, ягоды" 1005 | стик,Другое 1006 | стики,Другое 1007 | сушка,"Хлеб, сладости, снеки" 1008 | телятина,"Птица, мясо, деликатесы" 1009 | томат,"Овощи, фрукты, ягоды" 1010 | черешня,"Овощи, фрукты, ягоды" 1011 | чиабатта,"Хлеб, сладости, снеки" 1012 | апельсины,"Овощи, фрукты, ягоды" 1013 | томаты,"Овощи, фрукты, ягоды" 1014 | cахар,"Чай, кофе, сахар" 1015 | cухарики,"Хлеб, сладости, снеки" 1016 | баранка,"Хлеб, сладости, снеки" 1017 | киви,"Овощи, фрукты, ягоды" 1018 | вобла,"Рыба, икра" 1019 | яблоки,"Овощи, фрукты, ягоды" 1020 | виноград,"Овощи, фрукты, ягоды" 1021 | тыква,"Овощи, фрукты, ягоды" 1022 | ядра орехов,"Соусы, орехи, консервы" 1023 | гематоген,Другое 1024 | мини-морковь,"Овощи, фрукты, ягоды" 1025 | ваза,Другое 1026 | ребра,"Птица, мясо, деликатесы" 1027 | сахар-песок,"Чай, кофе, сахар" 1028 | изделие макаронные,"Макароны, крупы, специи" 1029 | изделие кондитерское,"Хлеб, сладости, снеки" 1030 | изделие хлебобулочное,"Хлеб, сладости, снеки" 1031 | хлебец,"Хлеб, сладости, снеки" 1032 | наклейка,Другое 1033 | улитка,"Хлеб, сладости, снеки" 1034 | бананы,"Овощи, фрукты, ягоды" 1035 | лист лавровый,"Макароны, крупы, специи" 1036 | сумка,Другое 1037 | открытка,Другое 1038 | поджарка,"Птица, мясо, деликатесы" 1039 | белок,"Молоко, сыр, яйца" 1040 | плитка,"Хлеб, сладости, снеки" 1041 | окорочка,"Птица, мясо, деликатесы" 1042 | гвоздика,"Макароны, крупы, специи" 1043 | редька,"Овощи, фрукты, ягоды" 1044 | рассол,"Соусы, орехи, консервы" 1045 | пасха,"Молоко, сыр, яйца" 1046 | кислота лимонная,"Соусы, орехи, консервы" 1047 | -вилка,Посуда 1048 | -кабель,Товары для дома и дачи 1049 | -мыло,"Красота, гигиена, бытовая химия" 1050 | aнанас,"Овощи, фрукты, ягоды" 1051 | cалфетки,Товары для дома и дачи 1052 | cельдь,"Рыба, икра" 1053 | cмесь,Другое 1054 | cок,"Воды, соки, напитки" 1055 | cоль,"Чай, кофе, сахар" 1056 | cпагеттини,"Макароны, крупы, специи" 1057 | cпрайт,"Воды, соки, напитки" 1058 | kофе,"Чай, кофе, сахар" 1059 | moлоко,"Молоко, сыр, яйца" 1060 | абрикос,"Овощи, фрукты, ягоды" 1061 | авокад,"Овощи, фрукты, ягоды" 1062 | автомат,Другое 1063 | акварель,Другое 1064 | активиа,"Молоко, сыр, яйца" 1065 | актимель,"Молоко, сыр, яйца" 1066 | антинакипин,Другое 1067 | антиржавчина,Другое 1068 | анчоус,"Рыба, икра" 1069 | апельсин,"Овощи, фрукты, ягоды" 1070 | арбуз,"Овощи, фрукты, ягоды" 1071 | ацидобифилин,"Молоко, сыр, яйца" 1072 | бабочки,Другое 1073 | баклажан,"Овощи, фрукты, ягоды" 1074 | банан,"Овощи, фрукты, ягоды" 1075 | бананы-мини,"Овощи, фрукты, ягоды" 1076 | бантики,Другое 1077 | бастурма,"Птица, мясо, деликатесы" 1078 | батареи,Другое 1079 | батоны,"Хлеб, сладости, снеки" 1080 | белизна,Другое 1081 | биокефир,"Молоко, сыр, яйца" 1082 | бисквит,"Хлеб, сладости, снеки" 1083 | бифидок,"Молоко, сыр, яйца" 1084 | бифидокефир,"Молоко, сыр, яйца" 1085 | бифилайф,"Молоко, сыр, яйца" 1086 | блин,Подборки и готовые блюда 1087 | блины,Подборки и готовые блюда 1088 | бренди,Алкоголь 1089 | бриджи,Другое 1090 | брокколи,"Овощи, фрукты, ягоды" 1091 | бублик,"Хлеб, сладости, снеки" 1092 | ванилин,"Макароны, крупы, специи" 1093 | ванилин-интенсив,"Макароны, крупы, специи" 1094 | ватрушка,"Хлеб, сладости, снеки" 1095 | венчик,Другое 1096 | вешалка-зажим,Другое 1097 | вилки,Посуда 1098 | водоросль,"Овощи, фрукты, ягоды" 1099 | воды,"Воды, соки, напитки" 1100 | гель-крем,"Красота, гигиена, бытовая химия" 1101 | гель-уход,"Красота, гигиена, бытовая химия" 1102 | глина,Другое 1103 | горшки,Другое 1104 | гранат,"Овощи, фрукты, ягоды" 1105 | гранаты,"Овощи, фрукты, ягоды" 1106 | грейпфрут,"Овощи, фрукты, ягоды" 1107 | грейпфруты,"Овощи, фрукты, ягоды" 1108 | дезинфектор,Другое 1109 | джин-тоник,Алкоголь 1110 | дип-соус,"Соусы, орехи, консервы" 1111 | диски,Другое 1112 | дуршлаг,Другое 1113 | ежевика,"Овощи, фрукты, ягоды" 1114 | ессентуки,"Воды, соки, напитки" 1115 | желудки,"Птица, мясо, деликатесы" 1116 | жимолость,"Овощи, фрукты, ягоды" 1117 | жироудалитель,Другое 1118 | завтраки,Подборки и готовые блюда 1119 | загуститель,Другое 1120 | заколка,Другое 1121 | зубная щётка,"Красота, гигиена, бытовая химия" 1122 | зубочистки,Другое 1123 | изделие,Другое 1124 | изделия,Другое 1125 | кабачки,"Овощи, фрукты, ягоды" 1126 | какао-порошок,"Чай, кофе, сахар" 1127 | камбала,"Рыба, икра" 1128 | карпаччо,Подборки и готовые блюда 1129 | карри,"Макароны, крупы, специи" 1130 | картофель-фри,Подборки и готовые блюда 1131 | картошка,"Овощи, фрукты, ягоды" 1132 | кислота,"Соусы, орехи, консервы" 1133 | кисточка,Другое 1134 | кокос,"Овощи, фрукты, ягоды" 1135 | кокосы,"Овощи, фрукты, ягоды" 1136 | корица-сахар,"Макароны, крупы, специи" 1137 | красители,Другое 1138 | круасаны,"Хлеб, сладости, снеки" 1139 | круггетсы,Замороженные продукты 1140 | крыло,"Птица, мясо, деликатесы" 1141 | крылышки,"Птица, мясо, деликатесы" 1142 | крылышко,"Птица, мясо, деликатесы" 1143 | крылья,"Птица, мясо, деликатесы" 1144 | кумин,"Макароны, крупы, специи" 1145 | кунжут,"Макароны, крупы, специи" 1146 | курица,"Птица, мясо, деликатесы" 1147 | кус-кус,"Макароны, крупы, специи" 1148 | кускус,"Макароны, крупы, специи" 1149 | лайм,"Овощи, фрукты, ягоды" 1150 | лаймы,"Овощи, фрукты, ягоды" 1151 | лакомcтво,"Хлеб, сладости, снеки" 1152 | леденец,"Хлеб, сладости, снеки" 1153 | лимоны,"Овощи, фрукты, ягоды" 1154 | ломтерезка,Другое 1155 | лук-порей,"Овощи, фрукты, ягоды" 1156 | лютик,"Макароны, крупы, специи" 1157 | макадамия,"Соусы, орехи, консервы" 1158 | маракуйя,"Овощи, фрукты, ягоды" 1159 | марганцовка,"Красота, гигиена, бытовая химия" 1160 | маршмеллоу,"Хлеб, сладости, снеки" 1161 | масса,"Молоко, сыр, яйца" 1162 | мини-багет,"Хлеб, сладости, снеки" 1163 | мини-пирожные,"Хлеб, сладости, снеки" 1164 | мини-шпинат,"Овощи, фрукты, ягоды" 1165 | минибагет,"Хлеб, сладости, снеки" 1166 | моцарелла,"Молоко, сыр, яйца" 1167 | моццарелла,"Молоко, сыр, яйца" 1168 | мочалка-варежка,"Красота, гигиена, бытовая химия" 1169 | наклейки-стразы,Другое 1170 | невидимка,Другое 1171 | нектарины,"Овощи, фрукты, ягоды" 1172 | нуга,"Хлеб, сладости, снеки" 1173 | обед,Подборки и готовые блюда 1174 | облепиха,"Овощи, фрукты, ягоды" 1175 | овощи-гриль,"Овощи, фрукты, ягоды" 1176 | овёс,"Макароны, крупы, специи" 1177 | окунь,"Рыба, икра" 1178 | орехи,"Соусы, орехи, консервы" 1179 | осьминог,"Рыба, икра" 1180 | пазлы,Товары для мам и детей 1181 | пакетики,Другое 1182 | пампушки,"Хлеб, сладости, снеки" 1183 | панкейк,"Хлеб, сладости, снеки" 1184 | пантолеты,"Хлеб, сладости, снеки" 1185 | папайя,"Овощи, фрукты, ягоды" 1186 | пеленка,Товары для мам и детей 1187 | пергамент,Другое 1188 | персик,"Овощи, фрукты, ягоды" 1189 | перья,Другое 1190 | пижама,Товары для дома и дачи 1191 | пластыри,Другое 1192 | платки,Другое 1193 | платочки,Другое 1194 | плащ,Другое 1195 | пленка,Другое 1196 | плоды,"Овощи, фрукты, ягоды" 1197 | повязка,Другое 1198 | подсластитель,"Чай, кофе, сахар" 1199 | полосатик,"Рыба, икра" 1200 | помело,Другое 1201 | пончик,"Хлеб, сладости, снеки" 1202 | пончики,"Хлеб, сладости, снеки" 1203 | разрыхлитель,Другое 1204 | ракушки,Другое 1205 | рататуй,Подборки и готовые блюда 1206 | редис,"Овощи, фрукты, ягоды" 1207 | репа,"Овощи, фрукты, ягоды" 1208 | решетка,Другое 1209 | розмарин,"Макароны, крупы, специи" 1210 | рукав,Другое 1211 | салями,"Птица, мясо, деликатесы" 1212 | сахар-рафинад,"Чай, кофе, сахар" 1213 | свёкла,"Овощи, фрукты, ягоды" 1214 | сервелат,"Птица, мясо, деликатесы" 1215 | сердце,"Птица, мясо, деликатесы" 1216 | скретч-карта,Другое 1217 | скретч карта,Другое 1218 | сливы,"Овощи, фрукты, ягоды" 1219 | соус-крем,"Соусы, орехи, консервы" 1220 | спинка,"Птица, мясо, деликатесы" 1221 | стейки,"Птица, мясо, деликатесы" 1222 | стикеры,Другое 1223 | стрипсы,"Птица, мясо, деликатесы" 1224 | сумка-переноска,Другое 1225 | суфле-маршмеллоу,"Хлеб, сладости, снеки" 1226 | сухари-гренки,"Хлеб, сладости, снеки" 1227 | сухарики-гренки,"Хлеб, сладости, снеки" 1228 | сыр-мусс,"Молоко, сыр, яйца" 1229 | сырки,"Молоко, сыр, яйца" 1230 | таблетка,Другое 1231 | тархун,"Воды, соки, напитки" 1232 | тимьян,"Макароны, крупы, специи" 1233 | тростник,"Макароны, крупы, специи" 1234 | трусы,Другое 1235 | трусы-шорты,Другое 1236 | тряпки,Другое 1237 | уголь,Другое 1238 | утяжелители,Другое 1239 | фрукты,"Овощи, фрукты, ягоды" 1240 | фунчoза,"Макароны, крупы, специи" 1241 | "хлеб""бородинский""","Хлеб, сладости, снеки" 1242 | "хлеб""митава""","Хлеб, сладости, снеки" 1243 | "хлеб""митва""нарезка","Хлеб, сладости, снеки" 1244 | хлоргексидина,Другое 1245 | хмели-сунели,"Макароны, крупы, специи" 1246 | цедра,Другое 1247 | цукаты,Другое 1248 | цыпл-бройлер,"Птица, мясо, деликатесы" 1249 | цыпленок-гриль,"Птица, мясо, деликатесы" 1250 | цыплята,"Птица, мясо, деликатесы" 1251 | чак-чак,"Хлеб, сладости, снеки" 1252 | чебурек,Подборки и готовые блюда 1253 | чебурешки,Подборки и готовые блюда 1254 | чипсы-нори,"Хлеб, сладости, снеки" 1255 | чудо-молоко,"Молоко, сыр, яйца" 1256 | чурчхела,"Хлеб, сладости, снеки" 1257 | шаверма,Подборки и готовые блюда 1258 | шарики,Другое 1259 | шиповник,"Овощи, фрукты, ягоды" 1260 | щавель,"Овощи, фрукты, ягоды" 1261 | эклеры,"Хлеб, сладости, снеки" 1262 | электроды,Другое 1263 | яблочки,"Овощи, фрукты, ягоды" 1264 | яйцеварка,Другое 1265 | антиперспант,"Красота, гигиена, бытовая химия" 1266 | дыня,"Овощи, фрукты, ягоды" 1267 | краситель,"Красота, гигиена, бытовая химия" 1268 | cалат,"Овощи, фрукты, ягоды" 1269 | коржик,"Хлеб, сладости, снеки" 1270 | сгущенка,"Молоко, сыр, яйца" 1271 | cтейк,"Птица, мясо, деликатесы" 1272 | инструмент,Товары для дома и дачи 1273 | яблоко,"Овощи, фрукты, ягоды" 1274 | долма,"Соусы, орехи, консервы" 1275 | -------------------------------------------------------------------------------- /receipt_parser/dicts.py: -------------------------------------------------------------------------------- 1 | """Most common abbreviations for product names.""" 2 | from typing import Dict 3 | 4 | 5 | PRODUCTS: Dict[str, str] = { 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 | "бум": "бумага", 31 | "прян": "пряники", 32 | "пломб": "пломбир", 33 | "проклад": "прокладки", 34 | "кукур": "кукуруза", 35 | "йог": "йогурт", 36 | "груд": "грудка", 37 | "упаков": "упаковка", 38 | "батонч": "батончик", 39 | "бат": "батон", 40 | "глаз": "глазурь", 41 | "свин": "свинина", 42 | "круас": "круасаны", 43 | "базил": "базилик", 44 | "пос": "посуда", 45 | "трюф": "трюфель", 46 | "вар": "варенье", 47 | "жев": "жевательная резинка", 48 | "рез": "резинка", 49 | "лик": "ликер", 50 | "бифшт": "бифштекс", 51 | "сосис": "сосиски", 52 | "твор": "творог", 53 | "биск": "бисквит", 54 | "лаком": "лакомство", 55 | "кетч": "кетчуп", 56 | "пирож": "пирожное", 57 | "подуш": "подушечки", 58 | "колб": "колбаса", 59 | "наб": "набор", 60 | "tea": "чай", 61 | "шамп": "шампанское", 62 | "осв": "освежитель", 63 | "воз": "воздух", 64 | "возд": "воздух", 65 | "филе-кусок": "филе", 66 | "крем-мыло": "мыло", 67 | "мини-сушки": "сушки", 68 | "лук-репка": "лук", 69 | "отбел": "отбеливатель", 70 | "биотвор": "биотворог", 71 | "биойог": "биойогурт", 72 | "вин": "вино", 73 | "сард": "сардельки", 74 | "припр": "приправа", 75 | "мол": "молоко", 76 | "шок": "шоколад", 77 | "пакет-майка": "пакет", 78 | "печен": "печенье", 79 | "штруд": "штрудель", 80 | "пашт": "паштет", 81 | "марм": "мармелад", 82 | "дес": "десерт", 83 | "жеврезинка": "жевательная резинка", 84 | "лим кислота": "лимонная кислота", 85 | "том.паста": "томатная паста", 86 | "ваф": "вафли", 87 | "спагет": "спагетти", 88 | "мюс": "мюсли", 89 | "кор": "коре", 90 | "наполн": "наполнитель", 91 | "макар": "макароны", 92 | "порош": "порошок", 93 | "tоматы": "томаты", 94 | "сред": "средство", 95 | "свечи-мини": "свечи", 96 | "шам": "шампунь", 97 | "нект": "нектар", 98 | "зп": "зубная паста", 99 | "сухар": "сухари", 100 | "приправ": "приправа", 101 | "котл": "котлета", 102 | "прокл": "прокладки", 103 | "салфет": "салфетки", 104 | "мармел": "мармелад", 105 | "багета": "багет", 106 | "горош": "горошек", 107 | "семеч": "семечки", 108 | "кренд": "крендели", 109 | "пасх": "пасха", 110 | "зажиг": "зажигалка", 111 | "сырчечилспаг": "сыр", 112 | "антиперсп": "антиперспант", 113 | "бальз-опол": "бальзам", 114 | "бзмжсыр": "сыр", 115 | "био-йогу": "биойогурт", 116 | "био-йогурт": "биойогурт", 117 | "биотв": "биотворог", 118 | "бумаг": "бумага", 119 | "вермиш": "вермишель", 120 | "гель-эксп": "гель", 121 | "зажигалок": "зажигалка", 122 | "кнофеты": "конфеты", 123 | "крем-мыл": "мыло", 124 | "кэфир": "кефир", 125 | "лосьон-тон": "лосьон", 126 | "макарон": "макароны", 127 | "манг": "манго", 128 | "морожен": "мороженое", 129 | "пирожн": "пирожное", 130 | "подг": "подгузники", 131 | "презерв": "презервативы", 132 | "сосиск": "сосиски", 133 | "тамп": "тампоны", 134 | "разрыхлит": "разрыхлитель", 135 | "красит": "краситель", 136 | } 137 | 138 | """Вot brands, anglicisms and acronyms.""" 139 | BRANDS: Dict[str, str] = { 140 | "д.в д": "домик в деревне", 141 | "д.в.д": "домик в деревне", 142 | "хл.сп": "хлебный спас", 143 | "к.ц.": "красная цена", 144 | "кр.ц.": "красная цена", 145 | "кр.цена": "красная цена", 146 | "пр!ст": "просто", 147 | "орео": "oreo", 148 | "п.св": "первая свежесть", 149 | "раст": "растишка", 150 | "гранд дуэт": "grand duet", 151 | "прост.": "простоквашино", 152 | "прост ": "простоквашино", 153 | "mil gen": "miller genuine", 154 | "шарл": "шарлиз", 155 | "домашняя кухн": "домашняя кухня", 156 | "крикет": "cricket", 157 | "домик в дерев": "домик в деревне", 158 | "акт.": "актвия", 159 | "озор.пчел": "озорная пчелка", 160 | "вор.сух": "воронцовские сухарики", 161 | "mr.ricco": "mr. ricco", 162 | "geese": "geese", 163 | "dr.korner": "dr. korner", 164 | "dr.oetker": "dr. oetker", 165 | "alp.gold": "alpen gold", 166 | "mil.gen.dr": "miller", 167 | "био-бал.": "bio balance", 168 | "epica": "epica", 169 | "с.прид": "сады придонья", 170 | "g.gold": "gourmet gold", 171 | "colg.з/п": "colgate", 172 | "с.пудовъ": "с. пудовъ", 173 | "сибирск.коллекция": "сибирская коллекция", 174 | "(данон)": "danone", 175 | "lay`": "lays", 176 | "кока-кола": "coca-cola", 177 | "марс": "mars", 178 | "нестле": "nestle", 179 | "брест-литовск": "брест-литовский", 180 | "lay's": "lays", 181 | "хенкель": "henkel", 182 | "gl.vil": "global village", 183 | "bar": "barilla", 184 | "tol": "tolli", 185 | "юбил.": "юбилейное", 186 | "астор": "astoria", 187 | "бар.": "барни", 188 | "рот фронт": "ротфронт", 189 | "роллт": "роллтон", 190 | "верхов.": "верховье", 191 | "мист.": "мистраль", 192 | "иммун": "иммунеле", 193 | "рест.": "рестория", 194 | "зд.мен.": "здоровое меню", 195 | " вв ": "вкусвилл", 196 | "зелен. линия": "зеленая линия", 197 | "комм.": "коммунарка", 198 | "волоконовско": "волоконовское", 199 | "олвейс": "always", 200 | "пантин": "pantene", 201 | "палмолив": "palmolive", 202 | "жатецкий гусь": "zatecky gus", 203 | "советские традиц": "советские традиции", 204 | "президент": "president", 205 | "стародв": "стародворские", 206 | "альметте": "almette", 207 | "дом в дер": "домик в деревне", 208 | "дом дер": "домик в деревне", 209 | "дом.в дер.": "домик в деревне", 210 | "виола виттр": "виола виттрока", 211 | "золот ларец": "золотой ларец", 212 | "клинск": "клинский", 213 | "добр ": "добрый", 214 | "добр. ": "добрый", 215 | "хл.д.": "хлебный дом", 216 | "рублев": "рублевский", 217 | "фрутм.": "фрутмотив", 218 | "черкизов.": "черкизовский", 219 | "пилигр.": "пилигрим", 220 | "кремлев": "кремлевский", 221 | "гессер": "gosser", 222 | "воронц": "воронцовские", 223 | "гранола": "granola", 224 | "r.sp.": "ritter sport", 225 | "sиб.кол.": "сибирская коллекция", 226 | "яр.бройл.": "ярославский бройлер", 227 | "кузм.": "кузмино", 228 | "виола": "viola", 229 | "чупа чупс": "chupa chups", 230 | "наше солнышк": "наше солнышко", 231 | "ролл": "роллтон", 232 | "зева": "zewa", 233 | "б.ю.алекс.": "б.ю.александров", 234 | "мамба": "mamba", 235 | "эком.": "ecomilk", 236 | "савушк.": "савушкин хуторок", 237 | "милка": "milka", 238 | "виолетта": "Violette", 239 | "марк.пер.": "маркет перекресток", 240 | "национ.": "националь", 241 | "пр.кав.": "предгорье кавказа", 242 | "велк.": "велком", 243 | "байс.": "baisad", 244 | "ростагроэкс": "ростагроэкспорт", 245 | "слав.": "славянка", 246 | "варен.": "варенька", 247 | "хейнек.": "heineken", 248 | "гринфилдголден": "greenfield", 249 | "стр.вас.": "страна васильки", 250 | "бархатные ручк": "бархатные ручки", 251 | "велле": "velle", 252 | "терре аллегре": "terre allegre", 253 | "зелен.линия": "зеленая линия", 254 | "ильина": "от ильиной", 255 | "данон": "danone", 256 | "юби.": "юбилейное", 257 | "больш.кружка": "большая кружка", 258 | "рязан.": "рязаночка", 259 | "вдох.": "вдохновение", 260 | "озби": "семейка озби", 261 | "ландлибе": "landliebe", 262 | "асен.фер.": "асеньевская ферма", 263 | "приос.": "приосколье", 264 | "б.ю. алексан": "б.ю. александров", 265 | "б.ю.алексан": "б.ю. александров", 266 | "б.ю.александров": "б.ю. александров", 267 | "б.ю. александров": "б.ю. александров", 268 | "азов.кф": "азовская кф", 269 | "феликс": "felix", 270 | "з.боч": "золотая бочка", 271 | "пр.ява": "принцесса ява", 272 | "золот.ярлык": "золотой ярлык", 273 | "село зел.": "село зеленое", 274 | "оral-в": "оral-b", 275 | "рог.": "рогачевъ", 276 | "золотая симф.": "золотая симфония", 277 | "золот стан": "золотой стандарт", 278 | "mентос": "mentos", 279 | "men.": "mentos", 280 | "дан.": "danone", 281 | "петр ": "петр I", 282 | "ява ": "ява", 283 | "лд ": "LD", 284 | "люб.": "любимый", 285 | "act.": "activia", 286 | "сенатор": "senator", 287 | "мальборо": "marlboro", 288 | "филип моррис": "philip morris", 289 | "филлип моррис": "philip morris", 290 | "винстон": "winston", 291 | "бонд": "bond", 292 | "ротманс": "rothmans", 293 | "эссе": "esse", 294 | "bon": "bonduelle", 295 | "магги": "maggi", 296 | "чист.лин.": "чистая линия", 297 | "рост.": "ростагроэкспорт", 298 | "залесск.фермер": "залесский фермер", 299 | "kаmis": "kamis", 300 | "мистер пропер": "mr. proper", 301 | "лейз": "lays", 302 | "natur.": "natural", 303 | "alw.": "always", 304 | "tur.dies.": "turbo diesel", 305 | "мдб": "мясной дом бородина", 306 | "три короч.": "три корочки", 307 | "pur.": "purina", 308 | "nest.": "nestle", 309 | "bio баланс": "bio balance", 310 | "посид.": "посиделкино", 311 | "сл.слоб.": "сладкая слобода", 312 | "ост.": "останкино", 313 | "ког.я выр.": "когда я вырасту", 314 | "ког.я.выр.": "когда я вырасту", 315 | "coca-c.": "coca-cola", 316 | "хугар.": "hoegaarden", 317 | "хугарден": "hoegaarden", 318 | "черног.": "черноголовка", 319 | "мерси": "mersi", 320 | "чоко пай": "chocopie", 321 | "мос.кар.": "московский картофель", 322 | "maxib.": "maxibon", 323 | "р.хол.": "русский холод", 324 | "extr.": "extreme", 325 | "свитл.": "свитлогорье", 326 | "дом.кух.": "домашняя кухня", 327 | "дом кух": "домашняя кухня", 328 | "китекат": "kitekat", 329 | "контекс": "contex", 330 | "лукоил": "lukoil", 331 | "орбит": "orbit", 332 | "пепси": "pepsi", 333 | "простокваш": "простоквашино", 334 | "простоквашино": "простоквашино", 335 | "раули мерло": "rauli merlot", 336 | "ядрена коп": "ядрена копоть", 337 | "ядрена": "ядрена копоть", 338 | "тетри": "tetri", 339 | "зол.капли": "золотые капли", 340 | "мария": "любятово", 341 | "нев.косм.": "невская косметика", 342 | "шато тамань": "chateau tamagne", 343 | "DR.OETK.": "dr. oetker", 344 | "баб.": "бабаевский", 345 | "меллер": "meller", 346 | "кр.лин.": "красная линия", 347 | "мос.пр.": "московский провансаль", 348 | "м.к.": "морской котик", 349 | "обер.": "обережье", 350 | "б.бер.": "балтийский берег", 351 | "ясно солны": "ясно солнышко", 352 | "коров.": "коровка", 353 | "любят.": "любятово", 354 | "рус.нива": "русская нива", 355 | "алекс.": "б. ю. александров", 356 | "спрайт": "sprite", 357 | "фанта": "fanta", 358 | "фр.сад": "фруктовый сад", 359 | "дюарс": "dewars", 360 | "вологод.пломбир": "вологодский пломбир", 361 | "greenf": "greenfield", 362 | "hochl": "hochland", 363 | "nesc": "nescafe", 364 | "ч лин": "чистая линия", 365 | "pres": "president", 366 | "бабаев": "бабаевский", 367 | "дош": "доширак", 368 | "бр-лит": "брест-литовский", 369 | "lor.": "lorenz naturals", 370 | "dan": "danone", 371 | "bond": "bonduelle", 372 | "colg": "colgate", 373 | "сады прид.": "сады придонья", 374 | "ч.линия": "чистая линия", 375 | "кп/": "красная птица", 376 | "кд/": "каждый день", 377 | "lay`s/": "lays", 378 | " я/": "я", 379 | "/я ": "я", 380 | "кр/цена": "красня цена", 381 | "д/ваня": "дядя ваня", 382 | "дом.в/дер.": "домик в деревне", 383 | } 384 | 385 | SLASH_PRODUCTS: Dict[str, str] = { 386 | "б/йог": "биойогурт", 387 | "сух/завтрак": "сухой завтрак", 388 | "з/щ": "зубная щётка", 389 | "йогурт/смусси": "йогурт", 390 | "т/бумага": "туалетная бумага", 391 | "укроп/петрушк": "зелень", 392 | "б/полотенца": "бумажные полотенца", 393 | "з/паста": "зубная паста", 394 | "т/мыло": "мыло", 395 | "ж/мыло": "жидкое мыло", 396 | "бум/салф": "бумажные полотенца", 397 | "з/п": "зубная паста", 398 | "шамп/бал": "шампунь", 399 | "с/порошок": "порошок", 400 | "ш/р": "шариковая ручка", 401 | "т/бум": "тулетная бумага", 402 | } 403 | 404 | BRANDS_WITH_NUMBERS: Dict[str, str] = { 405 | "кр.с 1901г": "чай краснодарский с 1901 года", 406 | "365 дней": "365 дней", 407 | "365дней": "365 дней", 408 | "7 days": "7 days", 409 | "7days": "7 days", 410 | "7day": "7 days", 411 | "6 соток": "6 соток", 412 | "6соток": "6 соток", 413 | "5 морей": "5 морей", 414 | "5морей": "5 морей", 415 | "4 сезона": "", 416 | "7 up": "7 up", 417 | "7up": "7 up", 418 | "3 glocken": "3 glocken", 419 | "3glocken": "3 glocken", 420 | "1 toy": "1 toy", 421 | "1toy": "1 toy", 422 | "48 копеек": "48 копеек", 423 | "48копеек": "48 копеек", 424 | "48 коп": "48 копеек", 425 | "48коп": "48 копеек", 426 | "got2b": "got2b", 427 | "хлебозавод 28": "", 428 | "хлебозавод28": "", 429 | "4life": "4 life", 430 | "4 life": "4 life", 431 | "36 копеек": "36 копеек", 432 | "36копеек": "36 копеек", 433 | "36 коп": "36 копеек", 434 | "36коп": "36 копеек", 435 | "101 зерно": "101 зерно", 436 | "101зерно": "101 зерно", 437 | "5 океанов": "5 океанов", 438 | "5океанов": "5 океанов", 439 | "j-7": "j-7", 440 | "j7": "j-7", 441 | "петр I": "петр I", 442 | "петрI": "петр I", 443 | "петр 1": "петр I", 444 | "петр1": "петр I", 445 | "7 злаков": "7 злаков", 446 | "7злаков": "7 злаков", 447 | } 448 | -------------------------------------------------------------------------------- /receipt_parser/finder.py: -------------------------------------------------------------------------------- 1 | """ 2 | Search and recognize the name, category and 3 | brand of a product from its description. 4 | """ 5 | from typing import Optional, List, Union, Dict 6 | from itertools import combinations 7 | import pandas as pd # type: ignore 8 | from pymystem3 import Mystem # type: ignore 9 | 10 | try: 11 | from cat_model import PredictCategory # type: ignore 12 | except ImportError: 13 | from receipt_parser.cat_model import PredictCategory # type: ignore 14 | 15 | # pylint: disable=C1801 16 | 17 | 18 | def df_apply(data: pd.DataFrame, func, axis: int = 1) -> pd.DataFrame: 19 | """ 20 | User define the `apply` function from pd.DataFrame. 21 | Use only for 2-column and 3-column data. 22 | 23 | Parameters 24 | ---------- 25 | data : pd.DataFrame 26 | The data on which the `func` function will be applied. 27 | func : function 28 | Function to apply to each column or row. 29 | axis : {0 or 'index', 1 or 'columns'}, default=1 30 | Axis along which the function is applied. 31 | 32 | Returns 33 | ------- 34 | pd.DataFrame 35 | Result of applying ``func`` along the given axis of the 36 | DataFrame. 37 | 38 | Examples 39 | -------- 40 | >>> from pandas import DataFrame 41 | 42 | >>> DataFrame.my_apply = df_apply 43 | >>> df[['name', 'brand']].my_apply(foo) 44 | """ 45 | 46 | _cols = data.columns 47 | _len = len(_cols) 48 | 49 | if _len == 2: 50 | return data.apply(lambda x: func(x[_cols[0]], x[_cols[1]]), axis=axis) 51 | return data.apply(lambda x: func(x[_cols[0]], x[_cols[1]], x[_cols[2]]), axis=axis) 52 | 53 | 54 | class Finder: 55 | """ 56 | Search and recognize the name, category and brand of a product 57 | from its description. 58 | Search is carried out in the collected datasets: `brands_ru.csv`, 59 | `products.csv`, `all_clean.csv`. 60 | 61 | Parameters 62 | ---------- 63 | pathes: Optional[Dict[str, str]], (default=None) 64 | Dictionary with paths to required files. 65 | 66 | Attributes 67 | ---------- 68 | mystem : Mystem 69 | A Python wrapper of the Yandex Mystem 3.1 morphological 70 | analyzer (http://api.yandex.ru/mystem). 71 | See aslo `https://github.com/nlpub/pymystem3`. 72 | cat_model: PredictCategory 73 | Class for predicting a category by product description 74 | using a neural network written in PyTorch. 75 | brands_ru : np.ndarray 76 | List of Russian brands. 77 | products : pd.DataFrame 78 | DataFrame of product names and categories. 79 | all_clean : pd.DataFrame 80 | General dataset with all product information. 81 | data: pd.DataFrame 82 | Text column with a description of the products to parse. 83 | Products description should be normalized by Normalizer. 84 | See `receipt_parser.normalize.Normalizer`. 85 | 86 | Examples 87 | -------- 88 | >>> product = 'Майонез MR.RICCO Провансаль 67% д/п 400' 89 | >>> finder = Finder() 90 | >>> finder.find_all(product) 91 | 92 | Notes 93 | ----- 94 | You may be comfortable with the following resource: 95 | 'https://receiptnlp.tinkoff.ru/'. 96 | See also `receipt_parser.parsers.tinkoff`. 97 | """ 98 | 99 | def __init__(self, pathes: Optional[Dict[str, str]] = None): 100 | pathes = pathes or {} 101 | self.mystem = Mystem() 102 | pd.DataFrame.appl = df_apply 103 | 104 | # Init model: 105 | model_params = {"num_class": 21, "embed_dim": 50, "vocab_size": 500} 106 | bpe_model = pathes.get("cat_bpe_model", "models/cat_bpe_model.yttm") 107 | cat_model = pathes.get("cat_model", "models/cat_model.pth") 108 | self.cat_model = PredictCategory(bpe_model, cat_model, model_params) 109 | 110 | # Read DataFrames: 111 | brands = pathes.get("brands_ru", "data/cleaned/brands_ru.csv") 112 | products = pathes.get("products", "data/cleaned/products.csv") 113 | all_clean = pathes.get("all_clean", "data/cleaned/all_clean.csv") 114 | self.brands_ru = pd.read_csv(brands)["brand"].values 115 | self.products = pd.read_csv(products) 116 | self.all_clean = pd.read_csv(all_clean) 117 | self.data = pd.DataFrame() 118 | 119 | def find_brands(self, name: str, brand: Optional[str] = None) -> pd.Series: 120 | """ 121 | Find Russian brands using the dataset `brands_ru.csv`. 122 | For more accurate recognition, a combination of words in a 123 | different order is used. 124 | 125 | Parameters 126 | ---------- 127 | name : str 128 | Product name. 129 | brand : str, optional (default=None) 130 | Product category. 131 | 132 | Returns 133 | ------- 134 | pd.Series 135 | pd.Series([name, brand]) 136 | """ 137 | 138 | if name and not brand: 139 | names = set( 140 | [f"{comb[0]} {comb[1]}" for comb in combinations(name.split(), 2)] 141 | + name.split() 142 | ) 143 | for rus_brand in self.brands_ru: 144 | if rus_brand in names: 145 | name = name.replace(rus_brand, "").replace(" ", " ").strip() 146 | return pd.Series([name, rus_brand]) 147 | return pd.Series([name, brand]) 148 | 149 | @staticmethod 150 | def __remove_duplicate_word(arr: List[str]) -> List[str]: 151 | """ 152 | Remove duplicates in words when one name is a continuation 153 | of another: ['вода', 'вода питьевая'] --> ['вода питьевая']. 154 | 155 | Parameters 156 | ---------- 157 | arr : List[str] 158 | List description of products in different variants. 159 | 160 | Returns 161 | ------- 162 | arr : List[str] 163 | List description of products without duplicates. 164 | """ 165 | 166 | if max([len(x.split()) for x in arr]) > 1: 167 | arr = sorted(arr, key=lambda x: len(x.split())) 168 | one_words = [] 169 | for product in arr.copy(): 170 | if len(product.split()) == 1: 171 | one_words.append(product) 172 | else: 173 | for word in one_words: 174 | if word in product and word in arr: 175 | arr.remove(word) 176 | return arr 177 | 178 | # pylint: disable=bad-continuation 179 | def find_product( 180 | self, name: str, product: str, category: Optional[str] = None 181 | ) -> pd.Series: 182 | """ 183 | Find products name using the dataset `products.csv`. 184 | For more accurate recognition, a combination of words in a 185 | different order is used. 186 | 187 | Parameters 188 | ---------- 189 | name : str 190 | Product name. 191 | product : str 192 | Product description. 193 | category : str, optional (default=None) 194 | Product category. 195 | 196 | Returns 197 | ------- 198 | pd.Series 199 | pd.Series([name, product, category]) 200 | """ 201 | 202 | if name and not product: 203 | names = pd.DataFrame( 204 | set( 205 | [f"{comb[0]} {comb[1]}" for comb in combinations(name.split(), 2)] 206 | + name.split() 207 | ), 208 | columns=["product"], 209 | ) 210 | merge = self.products.merge(names) 211 | if len(merge): 212 | product = ", ".join( 213 | self.__remove_duplicate_word(merge["product"].values) 214 | ) 215 | if len(merge) == 1: 216 | category = merge["category"].values[0] 217 | else: 218 | category = self.cat_model.predict(name) 219 | return pd.Series([name, product, category]) 220 | 221 | def _use_mystem(self, name: str, product: str) -> str: 222 | """ 223 | Use Yandex pymystem3 library to lemmatize words in product descriptions. 224 | I tried to use pymorphy, but the recognition quality got worse. 225 | 226 | Parameters 227 | ---------- 228 | name : str 229 | Product name. 230 | product : str 231 | Product description. 232 | 233 | Returns 234 | ------- 235 | str 236 | Product description after lemmatization. 237 | 238 | Notes 239 | ----- 240 | See also `https://github.com/nlpub/pymystem3`. 241 | """ 242 | 243 | if name and not product: 244 | name = "".join(self.mystem.lemmatize(name)[:-1]) 245 | return name 246 | 247 | def find_category(self, name: str, product: str, category: str) -> pd.Series: 248 | """ 249 | Find a product category using the dataset `products.csv`. 250 | 251 | Parameters 252 | ---------- 253 | name : str 254 | Product name. 255 | product : str 256 | Product description. 257 | category : str 258 | Product category. 259 | 260 | Returns 261 | ------- 262 | pd.Series 263 | pd.Series([product, category]) 264 | """ 265 | 266 | if product and not category: 267 | tmp = self.products[self.products["product"] == product] 268 | if len(tmp): 269 | category = tmp["category"].values[0] 270 | else: 271 | category = self.cat_model.predict(name) 272 | 273 | return pd.Series([product, category]) 274 | 275 | def find_product_by_brand( 276 | self, product: str, brand: str, category: str 277 | ) -> pd.Series: 278 | """ 279 | If we were able to recognize the product brand, 280 | but could not recongize the product name, 281 | we can assign the most common product name for this brand. 282 | 283 | Parameters 284 | ---------- 285 | product : str 286 | Product description. 287 | brand : str 288 | Product brand. 289 | category : str 290 | Product category. 291 | 292 | Returns 293 | ------- 294 | pd.Series 295 | pd.Series([product, brand, category]) 296 | """ 297 | 298 | if brand and not product: 299 | single_brand_goods = self.all_clean[self.all_clean["Бренд"] == brand] 300 | if len(single_brand_goods): 301 | product = single_brand_goods["Продукт"].value_counts().index[0] 302 | category = single_brand_goods["Категория"].value_counts().index[0] 303 | 304 | return pd.Series([product, brand, category]) 305 | 306 | def __print_logs(self, message: str, verbose: int) -> None: 307 | """ 308 | Print the number of recognized brands, 309 | categories and names of goods. 310 | """ 311 | 312 | if verbose: 313 | _len = len(self.data) 314 | print(message) 315 | print( 316 | "Recognized brands: " 317 | f"{len(self.data['brand_norm'].dropna())}/{_len}, " 318 | f"products: {len(self.data['product_norm'].dropna())}/{_len}, " 319 | f"categories: {len(self.data['cat_norm'].dropna())}/{_len}", 320 | "-" * 80, 321 | sep="\n", 322 | end="\n\n", 323 | ) 324 | 325 | @staticmethod 326 | def __transform_data(data: Union[pd.DataFrame, str]) -> pd.DataFrame: 327 | """Transform pd.Series or str to pd.DataFrame.""" 328 | 329 | columns = ["product_norm", "brand_norm", "cat_norm"] 330 | 331 | if isinstance(data, str): 332 | data = pd.DataFrame([data], columns=["name_norm"]) 333 | else: 334 | if "name_norm" not in data.columns: 335 | raise ValueError( 336 | "Столбец с описанием товара должен иметь название `name_norm`." 337 | ) 338 | 339 | for col in columns: 340 | if col not in data.columns: 341 | data[col] = None 342 | return data 343 | 344 | def __find_all(self, verbose: int) -> None: 345 | self.__print_logs("Before:", verbose) 346 | 347 | # Find brands: 348 | self.data[["name_norm", "brand_norm"]] = self.data[ 349 | ["name_norm", "brand_norm"] 350 | ].appl(self.find_brands) 351 | self.__print_logs("Find brands:", verbose) 352 | 353 | # Find product and category: 354 | self.data[["name_norm", "product_norm", "cat_norm"]] = self.data[ 355 | ["name_norm", "product_norm"] 356 | ].appl(self.find_product) 357 | self.__print_logs("Find product and category:", verbose) 358 | 359 | # Remove `-`: 360 | self.data["name_norm"] = self.data["name_norm"].str.replace("-", " ") 361 | self.data[["name_norm", "product_norm", "cat_norm"]] = self.data[ 362 | ["name_norm", "product_norm", "cat_norm"] 363 | ].appl(self.find_product) 364 | self.__print_logs( 365 | "Remove `-` and the second attempt to find a product:", verbose 366 | ) 367 | 368 | # Use Mystem: 369 | self.data["name_norm"] = self.data[["name_norm", "product_norm"]].appl( 370 | self._use_mystem 371 | ) 372 | self.data[["name_norm", "product_norm", "cat_norm"]] = self.data[ 373 | ["name_norm", "product_norm", "cat_norm"] 374 | ].appl(self.find_product) 375 | self.__print_logs( 376 | "Use Mystem for lemmatization and the third attempt to find a product:", 377 | verbose, 378 | ) 379 | 380 | # Find category: 381 | self.data[["product_norm", "cat_norm"]] = self.data[ 382 | ["name_norm", "product_norm", "cat_norm"] 383 | ].appl(self.find_category) 384 | self.__print_logs("Find the remaining categories:", verbose) 385 | 386 | # Find product by brand: 387 | self.data[["product_norm", "brand_norm", "cat_norm"]] = self.data[ 388 | ["product_norm", "brand_norm", "cat_norm"] 389 | ].appl(self.find_product_by_brand) 390 | self.__print_logs("Find product by brand:", verbose) 391 | 392 | def find_all( 393 | self, data: Union[pd.DataFrame, str], verbose: int = 0 394 | ) -> pd.DataFrame: 395 | """ 396 | Start search and recognition search processes in `data`. 397 | 398 | Parameters 399 | ---------- 400 | data : Union[pd.DataFrame, str] 401 | Text column with a description of the products to parse. 402 | Products description should be normalized by Normalizer. 403 | See `receipt_parser.normalize.Normalizer`. 404 | verbose: int (default=0) 405 | Set verbose to any positive number for verbosity. 406 | 407 | Returns 408 | ------- 409 | pd.DataFrame 410 | Recognized product names, brands and product categories. 411 | """ 412 | 413 | self.data = self.__transform_data(data) 414 | self.__find_all(verbose) 415 | 416 | return self.data 417 | -------------------------------------------------------------------------------- /receipt_parser/models/cat_bpe_model.yttm: -------------------------------------------------------------------------------- 1 | 73 423 2 | 1076 22 3 | 34 30 4 | 1089 12 5 | 102 53 6 | 1102 34 7 | 115 48 8 | 1081 17 9 | 39 37 10 | 1094 32 11 | 107 55 12 | 1073 23 13 | 120 64 14 | 1086 5 15 | 99 43 16 | 1099 20 17 | 112 59 18 | 1078 29 19 | 1091 19 20 | 104 52 21 | 117 60 22 | 1083 13 23 | 96 54 24 | 1096 28 25 | 109 46 26 | 1075 25 27 | 8217 68 28 | 122 65 29 | 1088 11 30 | 101 41 31 | 8230 61 32 | 1101 36 33 | 114 44 34 | 59 74 35 | 1080 9 36 | 38 38 37 | 1093 31 38 | 106 72 39 | 1072 6 40 | 119 75 41 | 1085 8 42 | 98 58 43 | 187 62 44 | 1098 40 45 | 111 45 46 | 1077 7 47 | 35 76 48 | 1090 14 49 | 103 66 50 | 8211 69 51 | 171 63 52 | 1103 21 53 | 116 47 54 | 1082 10 55 | 9601 4 56 | 1095 26 57 | 108 50 58 | 1074 16 59 | 121 57 60 | 1087 18 61 | 100 56 62 | 1100 24 63 | 113 70 64 | 1079 27 65 | 92 73 66 | 1092 33 67 | 105 49 68 | 173 71 69 | 1105 39 70 | 118 67 71 | 1084 15 72 | 97 42 73 | 1097 35 74 | 110 51 75 | 4 10 77 76 | 4 12 78 77 | 4 18 79 78 | 8 6 80 79 | 4 15 81 80 | 8 20 82 81 | 11 5 83 82 | 8 5 84 83 | 10 6 85 84 | 5 13 86 85 | 11 6 87 86 | 13 24 88 87 | 12 14 89 88 | 10 9 90 89 | 10 5 91 90 | 4 16 92 91 | 7 11 93 92 | 8 9 94 93 | 77 5 95 94 | 11 7 96 95 | 4 23 97 96 | 82 17 98 97 | 7 14 99 98 | 13 6 100 99 | 16 6 101 100 | 80 21 102 101 | 4 25 103 102 | 13 9 104 103 | 4 14 105 104 | 13 5 106 105 | 11 9 107 106 | 7 8 108 107 | 4 5 109 108 | 16 5 110 109 | 4 22 111 110 | 13 7 112 111 | 4 30 113 112 | 14 5 114 113 | 7 88 115 114 | 4 26 116 115 | 11 19 117 116 | 86 5 118 117 | 81 6 119 118 | 4 6 120 119 | 14 9 121 120 | 7 12 122 121 | 4 28 123 122 | 4 27 124 123 | 4 80 125 124 | 5 22 126 125 | 14 6 127 126 | 5 11 128 127 | 8 7 129 128 | 15 9 130 129 | 82 7 131 130 | 15 6 132 131 | 5 10 133 132 | 84 7 134 133 | 7 17 135 134 | 4 9 136 135 | 16 20 137 136 | 77 6 138 137 | 4 19 139 138 | 4 33 140 139 | 12 90 141 140 | 19 12 142 141 | 84 17 143 142 | 78 5 144 143 | 23 6 145 144 | 6 15 146 145 | 79 6 147 146 | 29 7 148 147 | 7 10 149 148 | 9 8 150 149 | 97 6 151 150 | 11 15 152 151 | 12 85 153 152 | 19 23 154 153 | 25 5 155 154 | 79 5 156 155 | 86 6 157 156 | 32 7 158 157 | 9 14 159 158 | 5 15 160 159 | 4 21 161 160 | 101 8 162 161 | 12 6 163 162 | 22 9 164 163 | 92 118 165 164 | 95 152 166 165 | 137 17 167 166 | 165 12 168 167 | 18 9 169 168 | 25 19 170 169 | 7 27 171 170 | 5 8 172 171 | 78 19 173 172 | 78 6 174 173 | 7 15 175 174 | 28 6 176 175 | 116 93 177 176 | 95 8 178 177 | 4 31 179 178 | 5 16 180 179 | 12 9 181 180 | 29 9 182 181 | 4 104 183 182 | 6 17 184 183 | 157 22 185 184 | 89 6 186 185 | 91 17 187 186 | 5 83 188 187 | 133 185 189 188 | 77 19 190 189 | 123 189 191 190 | 25 6 192 191 | 35 9 193 192 | 81 118 194 193 | 4 36 195 194 | 77 96 196 195 | 78 20 197 196 | 26 9 198 197 | 23 128 199 198 | 16 7 200 199 | 32 9 201 200 | 32 6 202 201 | 79 83 203 202 | 24 7 204 203 | 116 184 205 204 | 91 15 206 205 | 19 8 207 206 | 5 17 208 207 | 101 21 209 208 | 94 21 210 209 | 22 6 211 210 | 79 9 212 211 | 78 15 213 212 | 18 6 214 213 | 16 99 215 214 | 125 199 216 215 | 4 100 217 216 | 31 6 218 217 | 4 32 219 218 | 110 17 220 219 | 7 26 221 220 | 81 122 222 221 | 11 14 223 222 | 92 9 224 223 | 81 21 225 224 | 92 6 226 225 | 83 162 227 226 | 87 8 228 227 | 16 9 229 228 | 28 149 230 229 | 158 16 231 230 | 8 21 232 231 | 12 5 233 232 | 196 15 234 233 | 94 7 235 234 | 86 24 236 235 | 222 21 237 236 | 237 231 238 237 | 4 129 239 238 | 79 93 240 239 | 124 6 241 240 | 82 31 242 241 | 96 22 243 242 | 95 230 244 243 | 10 14 245 244 | 100 29 246 245 | 141 17 247 246 | 5 170 248 247 | 13 21 249 248 | 79 126 250 249 | 89 110 251 250 | 23 106 252 251 | 13 99 253 252 | 81 9 254 253 | 123 146 255 254 | 77 86 256 255 | 27 6 257 256 | 22 19 258 257 | 24 21 259 258 | 111 99 260 259 | 27 9 261 260 | 28 85 262 261 | 33 99 263 262 | 161 252 264 263 | 255 18 265 264 | 78 243 266 265 | 20 17 267 266 | 119 12 268 267 | 248 223 269 268 | 4 83 270 269 | 265 207 271 270 | 29 6 272 271 | 271 24 273 272 | 78 14 274 273 | 103 115 275 274 | 4 87 276 275 | 153 21 277 276 | 77 87 278 277 | 79 107 279 278 | 26 122 280 279 | 26 7 281 280 | 4 17 282 281 | 10 114 283 282 | 26 98 284 283 | 92 5 285 284 | 114 10 286 285 | 84 15 287 286 | 116 9 288 287 | 124 154 289 288 | 100 22 290 289 | 14 115 291 290 | 4 117 292 291 | 10 142 293 292 | 87 88 294 293 | 136 25 295 294 | 91 16 296 295 | 22 7 297 296 | 89 24 298 297 | 15 5 299 298 | 144 142 300 299 | 256 145 301 300 | 23 9 302 301 | 169 286 303 302 | 125 303 304 303 | 190 107 305 304 | 77 13 306 305 | 144 145 307 306 | 79 34 308 307 | 140 9 309 308 | 308 96 310 309 | 289 102 311 310 | 193 17 312 311 | 97 7 313 312 | 197 11 314 313 | 13 20 315 314 | 5 26 316 315 | 282 269 317 316 | 266 251 318 317 | 89 9 319 318 | 4 84 320 319 | 28 7 321 320 | 109 23 322 321 | 79 221 323 322 | 8 121 324 323 | 97 171 325 324 | 92 293 326 325 | 4 148 327 326 | 94 85 328 327 | 11 20 329 328 | 108 98 330 329 | 194 26 331 330 | 81 20 332 331 | 307 10 333 332 | 16 93 334 333 | 173 31 335 334 | 14 7 336 335 | 78 9 337 336 | 228 14 338 337 | 180 21 339 338 | 27 5 340 339 | 103 339 341 340 | 4 112 342 341 | 4 96 343 342 | 177 98 344 343 | 213 122 345 344 | 35 6 346 345 | 195 10 347 346 | 14 19 348 347 | 4 106 349 348 | 35 7 350 349 | 345 24 351 350 | 84 155 352 351 | 323 108 353 352 | 105 5 354 353 | 4 107 355 354 | 85 21 356 355 | 27 146 357 356 | 126 6 358 357 | 85 130 359 358 | 12 19 360 359 | 33 7 361 360 | 83 132 362 361 | 219 215 363 362 | 10 19 364 363 | 147 186 365 364 | 4 182 366 365 | 301 163 367 366 | 78 121 368 367 | 264 91 369 368 | 120 362 370 369 | 178 263 371 370 | 82 15 372 371 | 260 135 373 372 | 111 19 374 373 | 8 22 375 374 | 34 312 376 375 | 29 108 377 376 | 306 154 378 377 | 159 115 379 378 | 261 227 380 379 | 90 17 381 380 | 105 7 382 381 | 25 9 383 382 | 109 14 384 383 | 86 9 385 384 | 268 106 386 385 | 18 93 387 386 | 140 117 388 387 | 120 324 389 388 | 13 34 390 389 | 10 87 391 390 | 341 164 392 391 | 110 7 393 392 | 91 7 394 393 | 89 87 395 394 | 22 5 396 395 | 97 19 397 396 | 10 7 398 397 | 97 9 399 398 | 110 83 400 399 | 335 208 401 400 | 113 30 402 401 | 13 108 403 402 | 94 10 404 403 | 109 12 405 404 | 78 104 406 405 | 93 9 407 406 | 326 160 408 407 | 81 19 409 408 | 105 19 410 409 | 81 7 411 410 | 201 172 412 411 | 151 88 413 412 | 114 15 414 413 | 18 281 415 414 | 101 291 416 415 | 374 176 417 416 | 77 9 418 417 | 33 9 419 418 | 161 25 420 419 | 413 357 421 420 | 4 35 422 421 | 353 204 423 422 | 32 20 424 423 | 14 20 425 424 | 12 21 426 425 | 89 5 427 426 | 78 200 428 427 | 81 188 429 428 | 22 20 430 429 | 31 5 431 430 | 295 117 432 431 | 77 117 433 432 | 95 361 434 433 | 197 83 435 434 | 371 20 436 435 | 112 23 437 436 | 103 100 438 437 | 84 16 439 438 | 103 117 440 439 | 111 5 441 440 | 137 7 442 441 | 11 21 443 442 | 89 21 444 443 | 10 12 445 444 | 28 9 446 445 | 79 159 447 446 | 347 395 448 447 | 447 204 449 448 | 120 18 450 449 | 141 7 451 450 | 96 17 452 451 | 183 202 453 452 | 87 101 454 453 | 4 37 455 454 | 128 14 456 455 | 125 348 457 456 | 138 176 458 457 | 136 27 459 458 | 138 11 460 459 | 156 83 461 460 | 179 437 462 461 | 432 262 463 462 | 81 5 464 463 | 14 259 465 464 | 78 18 466 465 | 147 10 467 466 | 121 16 468 467 | 22 108 469 468 | 278 153 470 469 | 332 106 471 470 | 123 6 472 471 | 109 110 473 472 | 31 246 474 473 | 109 474 475 474 | 428 148 476 475 | 164 412 477 476 | 77 94 478 477 | 119 85 479 478 | 224 28 480 479 | 388 283 481 480 | 126 5 482 481 | 294 98 483 482 | 14 87 484 483 | 95 182 485 484 | 139 94 486 485 | 127 11 487 486 | 346 21 488 487 | 119 104 489 488 | 95 15 490 489 | 105 6 491 490 | 11 90 492 491 | 109 16 493 492 | 111 171 494 493 | 92 358 495 494 | 81 128 496 495 | 429 148 497 496 | 12 12 498 497 | 18 112 499 498 | 1 0 2 3 499 | -------------------------------------------------------------------------------- /receipt_parser/models/cat_model.pth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slgero/receipt_parser/7419da2d6ce99a7a9d7b666072b138059ad856d4/receipt_parser/models/cat_model.pth -------------------------------------------------------------------------------- /receipt_parser/normalizer.py: -------------------------------------------------------------------------------- 1 | """Normalize product description.""" 2 | import re 3 | from typing import Optional, Union, Dict 4 | import pandas as pd # type: ignore 5 | from pandarallel import pandarallel # type: ignore 6 | 7 | pandarallel.initialize(progress_bar=False, verbose=0) 8 | 9 | try: 10 | # pylint: disable=line-too-long 11 | from receipt_parser.dicts import PRODUCTS, BRANDS, SLASH_PRODUCTS, BRANDS_WITH_NUMBERS # type: ignore 12 | except ModuleNotFoundError: 13 | from dicts import PRODUCTS, BRANDS, SLASH_PRODUCTS, BRANDS_WITH_NUMBERS # type: ignore 14 | 15 | 16 | # pylint: disable=bad-continuation 17 | class Apply: 18 | """User define the `apply` function from pd.Series and pd.DataFrame""" 19 | 20 | @staticmethod 21 | def series_apply(data: pd.Series, func, use_parallel: Optional[bool] = None): 22 | """ 23 | User define the `apply` function from pd.Series. 24 | 25 | Parameters 26 | ---------- 27 | data : pd.Series 28 | The data on which the `func` function will be applied. 29 | func : function 30 | Function to apply to each column or row. 31 | use_parallel : Optional[bool], default=None 32 | Multiprocessing will be used if the data size is greater than 30000. 33 | 34 | Returns 35 | ------- 36 | pd.DataFrame 37 | Result of applying ``func`` on the Series. 38 | 39 | Examples 40 | -------- 41 | >>> from pandas import Series 42 | 43 | >>> Series.my_apply = series_apply 44 | >>> df['name'].my_apply(foo) 45 | """ 46 | 47 | if use_parallel is None: 48 | use_parallel = len(data) >= 10000 49 | if use_parallel: 50 | return data.parallel_apply(func) 51 | return data.apply(func) 52 | 53 | @staticmethod 54 | def df_apply( 55 | data: pd.DataFrame, func, use_parallel: Optional[bool] = None, axis: int = 1 56 | ) -> pd.DataFrame: 57 | """ 58 | User define the `apply` function from pd.DataFrame. 59 | Use only for 2-column data. 60 | 61 | Parameters 62 | ---------- 63 | data : pd.DataFrame 64 | The data on which the `func` function will be applied. 65 | func : function 66 | Function to apply to each column or row. 67 | use_parallel : Optional[bool], default=None 68 | Multiprocessing will be used if the data size is greater than 30000. 69 | axis : {0 or 'index', 1 or 'columns'}, default=1 70 | Axis along which the function is applied. 71 | 72 | Returns 73 | ------- 74 | pd.DataFrame 75 | Result of applying ``func`` along the given axis of the DataFrame. 76 | 77 | Examples 78 | -------- 79 | >>> from pandas import DataFrame 80 | 81 | >>> DataFrame.my_apply = df_apply 82 | >>> df[['name', 'brand']].my_apply(foo) 83 | """ 84 | 85 | _cols = data.columns 86 | 87 | if use_parallel is None: 88 | use_parallel = len(data) >= 10000 89 | 90 | if use_parallel: 91 | return data.parallel_apply( 92 | lambda x: func(x[_cols[0]], x[_cols[1]]), axis=axis 93 | ) 94 | return data.apply(lambda x: func(x[_cols[0]], x[_cols[1]]), axis=axis) 95 | 96 | 97 | class Normalizer: 98 | """ 99 | Normalize product description: expand abbreviations, 100 | delete garbage words and characters for further recognition, 101 | remove english worlds, etc. 102 | Steps: 103 | 1. Convert to lowercase; 104 | 2. Delete all words including numbers; 105 | 3. Delete all service characters; 106 | 4. Delete words consisting of 1 or 2 characters; 107 | 5. Find English brands using the dataset `brands_en.csv`; 108 | 6. Delete words from blacklist and words in English; 109 | 7. Replace words using `dicts.PRODUCTS`. 110 | 111 | Parameters 112 | ---------- 113 | pathes: Optional[Dict[str, str]], (default=None) 114 | Dictionary with paths to *.csv files. 115 | 116 | Attributes 117 | ---------- 118 | blacklist: np.ndarray 119 | Stop word list. 120 | brands: np.ndarray 121 | List with most common English brands. 122 | 123 | Examples 124 | -------- 125 | >>> product = 'Майонез MR.RICCO Провансаль 67% д/п 400' 126 | >>> norm = Normalizer() 127 | >>> norm.normalize(product) 128 | """ 129 | 130 | def __init__(self, pathes: Optional[Dict[str, str]] = None): 131 | pathes = pathes or {} 132 | self.blacklist = pd.read_csv(pathes.get("blacklist", "data/blacklist.csv"))[ 133 | "name" 134 | ].values 135 | self.brands = pd.read_csv( 136 | pathes.get("brands_en", "data/cleaned/brands_en.csv") 137 | )["brand"].values 138 | 139 | # Init user define apply function: 140 | pd.DataFrame.appl = Apply.df_apply 141 | pd.Series.appl = Apply.series_apply 142 | 143 | @staticmethod 144 | def _remove_numbers(name: str) -> pd.Series: 145 | """Remove all words in product description which contain numbers.""" 146 | 147 | brand = None 148 | # Find brands with numbers: 149 | for key, value in BRANDS_WITH_NUMBERS.items(): 150 | if key in name: 151 | brand = value 152 | name = name.replace(key, "") 153 | break 154 | 155 | name = " ".join(re.sub(r"\w*\d\w*", "", word) for word in name.split()) 156 | return pd.Series([name, brand]) 157 | 158 | @staticmethod 159 | def _remove_punctuation(name: str, brand: Optional[str]) -> pd.Series: 160 | """Remove all service characters in product description.""" 161 | 162 | # Find abbreviations: 163 | for key, value in BRANDS.items(): 164 | if key in name: 165 | brand = value 166 | name = name.replace(key, "") 167 | break 168 | 169 | product = None 170 | for key, value in SLASH_PRODUCTS.items(): 171 | if key in name: 172 | product = value 173 | name = name.replace(key, " ") 174 | break 175 | 176 | # Pattern: remove `-` after the sentence and remove almost all service chars 177 | pattern = r"((?<=\w)-+(?!\w))|([.,+!?%:№*/\(|\)])" 178 | name = re.sub(pattern, " ", name).replace(" ", " ") 179 | return pd.Series([name, product, brand]) 180 | 181 | def find_en_brands(self, name: str, brand: Optional[str]) -> pd.Series: 182 | """Find English brands using the dataset `brands_en.csv`.""" 183 | 184 | if not brand: 185 | for brand_en in self.brands: 186 | if brand_en in name: 187 | brand = brand_en 188 | name = name.replace(brand_en, "") 189 | break 190 | 191 | return pd.Series([name, brand]) 192 | 193 | @staticmethod 194 | def _remove_one_and_two_chars(name: str) -> str: 195 | """Remove words consisting of 1 or 2 characters.""" 196 | 197 | return " ".join(x for x in name.split() if len(x) > 2) 198 | 199 | def _remove_words_in_blacklist(self, name: str) -> str: 200 | """Remove words from blacklist.""" 201 | 202 | return " ".join(word for word in name.split() if word not in self.blacklist) 203 | 204 | @staticmethod 205 | def _replace_with_product_dict(name: str) -> str: 206 | """Replace words using `dicts.PRODUCTS`.""" 207 | 208 | return " ".join(PRODUCTS.get(word, word) for word in name.split()) 209 | 210 | @staticmethod 211 | def _remove_all_english_words(name: str, brand: Optional[str]) -> pd.Series: 212 | """ 213 | Remove all English words in the product description. 214 | We make the assumption that these words are a brand. 215 | """ 216 | 217 | eng_brands = " ".join(re.findall(r"\b([a-z]+)\b", name)) 218 | name = re.sub(r"\b([a-z]+)\b", "", name) 219 | 220 | if eng_brands and not brand: 221 | return pd.Series([name, eng_brands]) 222 | return pd.Series([name, brand]) 223 | 224 | @staticmethod 225 | def __transform_data(data: Union[pd.Series, str]) -> pd.DataFrame: 226 | """Transform pd.Series or str to pd.DataFrame.""" 227 | 228 | columns = ["name", "name_norm", "product_norm", "brand_norm"] 229 | 230 | if isinstance(data, pd.Series): 231 | return pd.DataFrame(data, columns=columns) 232 | return pd.DataFrame([[data, None, None, None]], columns=columns) 233 | 234 | def normalize(self, data: Union[pd.Series, str]) -> pd.DataFrame: 235 | """ 236 | Normalize the description of the product: expand abbreviations, 237 | delete garbage words and characters for further recognition, 238 | remove english worlds, etc. 239 | 240 | Parameters 241 | ---------- 242 | data : Union[pd.Series, str] 243 | Text column with a description of the products to normalize. 244 | 245 | Returns 246 | ------- 247 | pd.DataFrame 248 | Normalized description dataframe. 249 | """ 250 | 251 | data = self.__transform_data(data) 252 | data["name_norm"] = data["name"].str.lower() 253 | data[["name_norm", "brand_norm"]] = data["name_norm"].appl(self._remove_numbers) 254 | data[["name_norm", "product_norm", "brand_norm"]] = data[ 255 | ["name_norm", "brand_norm"] 256 | ].appl(self._remove_punctuation) 257 | data["name_norm"] = data["name_norm"].appl(self._remove_one_and_two_chars) 258 | data[["name_norm", "brand_norm"]] = data[["name_norm", "brand_norm"]].appl( 259 | self.find_en_brands 260 | ) 261 | data["name_norm"] = data["name_norm"].appl(self._remove_words_in_blacklist) 262 | data["name_norm"] = data["name_norm"].appl(self._replace_with_product_dict) 263 | data[["name_norm", "brand_norm"]] = data[["name_norm", "brand_norm"]].appl( 264 | self._remove_all_english_words 265 | ) 266 | return data 267 | -------------------------------------------------------------------------------- /receipt_parser/notebooks/cat_model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Create the first baseline model to classify categories" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 84, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "The autoreload extension is already loaded. To reload it, use:\n", 20 | " %reload_ext autoreload\n" 21 | ] 22 | } 23 | ], 24 | "source": [ 25 | "import pandas as pd\n", 26 | "import re\n", 27 | "from typing import Optional, Union, Dict, Any, Tuple, List\n", 28 | "from receipt_parser import RuleBased\n", 29 | "import numpy as np\n", 30 | "import matplotlib.pyplot as plt\n", 31 | "from sklearn.preprocessing import LabelEncoder\n", 32 | "\n", 33 | "import youtokentome as yttm\n", 34 | "import torch\n", 35 | "from torch import nn\n", 36 | "from torch.nn import functional as F\n", 37 | "from torch.utils.data import Dataset, DataLoader\n", 38 | "import torch.optim as optim\n", 39 | "import random\n", 40 | "\n", 41 | "\n", 42 | "%matplotlib inline\n", 43 | "%load_ext autoreload\n", 44 | "%autoreload 2" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 3, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "SEED = 1234\n", 54 | "\n", 55 | "random.seed(SEED)\n", 56 | "torch.manual_seed(SEED)\n", 57 | "torch.backends.cudnn.deterministic = True" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 4, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "class CategoryDataset(Dataset):\n", 67 | " def __init__(self, cat_df: pd.DataFrame, vocab_size: int = 500, use_padding: bool = False, chunk_length: int = 50, pad_idx: int = 0):\n", 68 | " self.use_padding = use_padding\n", 69 | " self.chunk_length = chunk_length\n", 70 | " self.pad_idx = pad_idx\n", 71 | "\n", 72 | " # Prepare DataFrame\n", 73 | " self.le = LabelEncoder()\n", 74 | " self.cat_df = self.__transfrom_df(cat_df)\n", 75 | " self.train, self.val, self.test = self.split_df(self.cat_df)\n", 76 | " self._lookup_dict = {'train': self.train,\n", 77 | " 'val': self.val,\n", 78 | " 'test': self.test}\n", 79 | " \n", 80 | " # Train BPE model:\n", 81 | " self.vocab_size = vocab_size\n", 82 | " self.path_to_bpe = 'data_cat/train_bpe_model.yttm'\n", 83 | " self.path_to_train = 'data_cat/train.txt'\n", 84 | " self.save_texts_to_file(self.train['name_norm'], self.path_to_train)\n", 85 | " self.tokenizer = self.build_bpe_model()\n", 86 | " \n", 87 | " self.set_split('train')\n", 88 | " \n", 89 | " def set_split(self, split=\"train\"):\n", 90 | " \"\"\"\n", 91 | " Selects the splits in the dataset.\n", 92 | " split (str): one of \"train\", \"val\", or \"test\"\n", 93 | " \"\"\"\n", 94 | "\n", 95 | " self._target_df = self._lookup_dict[split]\n", 96 | " \n", 97 | " def build_bpe_model(self) -> yttm.BPE:\n", 98 | " yttm.BPE.train(\n", 99 | " data=self.path_to_train,\n", 100 | " vocab_size=self.vocab_size,\n", 101 | " model=self.path_to_bpe,\n", 102 | " pad_id=self.pad_idx\n", 103 | " )\n", 104 | " return yttm.BPE(self.path_to_bpe)\n", 105 | " \n", 106 | " @staticmethod\n", 107 | " def ensure_length(txt: List[int], out_len: int, pad_value: int) -> List[int]:\n", 108 | " \"\"\"Add PAD-indices to a `out_len` length.\"\"\"\n", 109 | "\n", 110 | " if len(txt) < out_len:\n", 111 | " txt = list(txt) + [pad_value] * (out_len - len(txt))\n", 112 | " else:\n", 113 | " txt = txt[:out_len]\n", 114 | " return txt\n", 115 | " \n", 116 | " def __transfrom_df(self, df: pd.DataFrame) -> pd.DataFrame:\n", 117 | " \"\"\"\n", 118 | " Drop duplicates and NAN objects. Rename `category` column.\n", 119 | " Encode category to idx using `LabelEncoder`.\n", 120 | " \"\"\"\n", 121 | " \n", 122 | " \n", 123 | " df = df.drop_duplicates('name_norm').dropna()\n", 124 | " df = df.rename(columns = {'Категория': 'category'})\n", 125 | " df['target'] = self.le.fit_transform(df['category'])\n", 126 | " return df\n", 127 | "\n", 128 | " @staticmethod\n", 129 | " def save_texts_to_file(texts: pd.Series, out_file: str) -> None:\n", 130 | " \"\"\"Save text to .txt fromat for BPE model.\"\"\"\n", 131 | " \n", 132 | " with open(out_file, 'w') as outf:\n", 133 | " outf.write('\\n'.join(texts))\n", 134 | " \n", 135 | " @staticmethod\n", 136 | " def split_df(df: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:\n", 137 | " \"\"\"\n", 138 | " 60% - train set,\n", 139 | " 20% - validation set,\n", 140 | " 20% - test set\n", 141 | " \n", 142 | " Return train, validation, test.\n", 143 | " \"\"\"\n", 144 | "\n", 145 | " return np.split(df.sample(frac=1), [int(.6*len(df)), int(.8*len(df))])\n", 146 | " \n", 147 | " def get_labels(self) -> List[str]:\n", 148 | " \"\"\"Return all unique categories in dataframe.\"\"\"\n", 149 | "\n", 150 | " return self.train['category'].unique()\n", 151 | " \n", 152 | " def decode_target(self, target: int) -> str:\n", 153 | " return self.le.inverse_transform([target])[0]\n", 154 | " \n", 155 | " def __len__(self) -> int:\n", 156 | " return len(self._target_df)\n", 157 | "\n", 158 | " def __getitem__(self, index: int) -> Dict[str, Any]:\n", 159 | " row = self._target_df.iloc[index]\n", 160 | " cat_vector = self.tokenizer.encode(row.name_norm)\n", 161 | " if self.use_padding:\n", 162 | " cat_vector = self.ensure_length(cat_vector, self.chunk_length, self.pad_idx)\n", 163 | " cat_vector = torch.tensor(cat_vector)\n", 164 | " target = row.target\n", 165 | " \n", 166 | " return {'x_data': cat_vector,\n", 167 | " 'y_target': target}\n", 168 | " \n", 169 | "\n", 170 | "def generate_batch(batch):\n", 171 | " label = torch.tensor([entry['y_target'] for entry in batch])\n", 172 | " text = [entry['x_data'] for entry in batch]\n", 173 | " offsets = [0] + [len(entry) for entry in text]\n", 174 | " # torch.Tensor.cumsum returns the cumulative sum\n", 175 | " # of elements in the dimension dim.\n", 176 | "\n", 177 | " offsets = torch.tensor(offsets[:-1]).cumsum(dim=0)\n", 178 | " text = torch.cat(text)\n", 179 | " \n", 180 | " return text, offsets, label" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "## Model" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 97, 193 | "metadata": {}, 194 | "outputs": [], 195 | "source": [ 196 | "class CategoryClassifier(nn.Module):\n", 197 | " \"\"\"A simple perceptron baseline moedel.\"\"\"\n", 198 | "\n", 199 | " def __init__(self, vocab_size: int, embed_dim: int, num_class: int, pad_idx: int = 0):\n", 200 | " super(CategoryClassifier, self).__init__()\n", 201 | " self.embedding = nn.EmbeddingBag(vocab_size, embed_dim)\n", 202 | "# self.embedding = nn.Embedding(vocab_size, embedding_dim=embed_dim, padding_idx=pad_idx)\n", 203 | " self.fc = nn.Linear(in_features=embed_dim, \n", 204 | " out_features=num_class)\n", 205 | " self.init_weights()\n", 206 | "\n", 207 | " def init_weights(self):\n", 208 | " initrange = 0.5\n", 209 | " self.embedding.weight.data.uniform_(-initrange, initrange)\n", 210 | " self.fc.weight.data.uniform_(-initrange, initrange)\n", 211 | " self.fc.bias.data.zero_()\n", 212 | " \n", 213 | " def forward(self, x_in, offsets, apply_sigmoid=False):\n", 214 | " \"\"\"The forward pass of the classifier.\"\"\"\n", 215 | " \n", 216 | " embedded = self.embedding(x_in, offsets)\n", 217 | " y_out = self.fc(embedded)\n", 218 | " if apply_sigmoid:\n", 219 | " y_out = torch.sigmoid(y_out)\n", 220 | " return y_out" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 6, 226 | "metadata": {}, 227 | "outputs": [], 228 | "source": [ 229 | "VOCAB_SIZE = 500\n", 230 | "USE_PADDING = False\n", 231 | "CHUNK_LENGHT = 50\n", 232 | "PAD_IDX = 0\n", 233 | "\n", 234 | "# Init dataset:\n", 235 | "df = pd.read_csv('train_cat.csv')\n", 236 | "df.shape\n", 237 | "dataset = CategoryDataset(df, VOCAB_SIZE, USE_PADDING, CHUNK_LENGHT, PAD_IDX)\n", 238 | "\n", 239 | "# Init model:\n", 240 | "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", 241 | "NUN_CLASS = len(dataset.get_labels())\n", 242 | "EMBED_DIM = 50\n", 243 | "BATCH_SIZE = 32\n", 244 | "\n", 245 | "model = CategoryClassifier(VOCAB_SIZE, EMBED_DIM, NUN_CLASS)" 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [ 252 | "## Train Loop" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 7, 258 | "metadata": {}, 259 | "outputs": [ 260 | { 261 | "name": "stdout", 262 | "output_type": "stream", 263 | "text": [ 264 | "Epoch: 1 | time in 0 minutes, 13 seconds\n", 265 | "\tLoss: 0.0675(train)\t|\tAcc: 39.5%(train)\n", 266 | "\tLoss: 0.0003(valid)\t|\tAcc: 58.8%(valid)\n", 267 | "Epoch: 2 | time in 0 minutes, 13 seconds\n", 268 | "\tLoss: 0.0414(train)\t|\tAcc: 65.1%(train)\n", 269 | "\tLoss: 0.0002(valid)\t|\tAcc: 68.4%(valid)\n", 270 | "Epoch: 3 | time in 0 minutes, 13 seconds\n", 271 | "\tLoss: 0.0329(train)\t|\tAcc: 71.1%(train)\n", 272 | "\tLoss: 0.0001(valid)\t|\tAcc: 71.9%(valid)\n", 273 | "Epoch: 4 | time in 0 minutes, 13 seconds\n", 274 | "\tLoss: 0.0290(train)\t|\tAcc: 73.7%(train)\n", 275 | "\tLoss: 0.0001(valid)\t|\tAcc: 73.8%(valid)\n", 276 | "Epoch: 5 | time in 0 minutes, 13 seconds\n", 277 | "\tLoss: 0.0267(train)\t|\tAcc: 75.3%(train)\n", 278 | "\tLoss: 0.0001(valid)\t|\tAcc: 74.4%(valid)\n" 279 | ] 280 | } 281 | ], 282 | "source": [ 283 | "def transform_target(target):\n", 284 | " res = []\n", 285 | " for i in target:\n", 286 | " t_ = np.zeros(NUN_CLASS)\n", 287 | " t_[i] = 1\n", 288 | " res.append(t_)\n", 289 | " return res\n", 290 | "\n", 291 | "import time\n", 292 | "\n", 293 | "N_EPOCHS = 5\n", 294 | "model = model.to(device)\n", 295 | "loss_func = nn.CrossEntropyLoss().to(device)\n", 296 | "\n", 297 | "learning_rate=0.001\n", 298 | "optimizer = optim.Adam(model.parameters(), lr=learning_rate)\n", 299 | "scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer=optimizer,\n", 300 | " mode='min', factor=0.5,\n", 301 | " patience=1)\n", 302 | "\n", 303 | "# optimizer = torch.optim.SGD(model.parameters(), lr=4.0)\n", 304 | "# scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.9)\n", 305 | "\n", 306 | "for epoch in range(N_EPOCHS):\n", 307 | " start_time = time.time()\n", 308 | " \n", 309 | " \n", 310 | " # Train the model\n", 311 | " dataset.set_split('train')\n", 312 | " train_loss = 0\n", 313 | " train_acc = 0\n", 314 | " model.train()\n", 315 | " data = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True,\n", 316 | " collate_fn=generate_batch)\n", 317 | " for i, (text, offsets, target) in enumerate(data):\n", 318 | " optimizer.zero_grad()\n", 319 | " text, offsets, target = text.to(device), offsets.to(device), target.to(device)\n", 320 | " output = model(text, offsets)\n", 321 | "# target = torch.tensor(transform_target(target)).long().to(device)\n", 322 | " loss = loss_func(output, target)\n", 323 | " train_loss += loss.item()\n", 324 | " loss.backward()\n", 325 | " optimizer.step()\n", 326 | " train_acc += (output.argmax(1) == target).sum().item()\n", 327 | "\n", 328 | " # Adjust the learning rate\n", 329 | " train_loss, train_acc = train_loss / len(dataset), train_acc / len(dataset)\n", 330 | " scheduler.step(train_loss)\n", 331 | " \n", 332 | " # Eval the model\n", 333 | " dataset.set_split('val')\n", 334 | " loss = 0\n", 335 | " acc = 0\n", 336 | " model.eval()\n", 337 | " data = DataLoader(dataset, batch_size=BATCH_SIZE, collate_fn=generate_batch)\n", 338 | " \n", 339 | " for i, (text, offsets, target) in enumerate(data):\n", 340 | " text, offsets, target = text.to(device), offsets.to(device), target.to(device)\n", 341 | "# target = torch.tensor(transform_target(target)).long().to(device)\n", 342 | " output = model(text, offsets)\n", 343 | " loss = loss_func(output, target)\n", 344 | " loss += loss.item()\n", 345 | " acc += (output.argmax(1) == target).sum().item()\n", 346 | " \n", 347 | " valid_loss, valid_acc = loss / len(dataset), acc / len(dataset)\n", 348 | " \n", 349 | " \n", 350 | " secs = int(time.time() - start_time)\n", 351 | " mins = secs / 60\n", 352 | " secs = secs % 60\n", 353 | "\n", 354 | " print('Epoch: %d' %(epoch + 1), \" | time in %d minutes, %d seconds\" %(mins, secs))\n", 355 | " print(f'\\tLoss: {train_loss:.4f}(train)\\t|\\tAcc: {train_acc * 100:.1f}%(train)')\n", 356 | " print(f'\\tLoss: {valid_loss:.4f}(valid)\\t|\\tAcc: {valid_acc * 100:.1f}%(valid)')" 357 | ] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "metadata": {}, 362 | "source": [ 363 | "## Test the model:" 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": 8, 369 | "metadata": {}, 370 | "outputs": [], 371 | "source": [ 372 | "def predict(name_norm: str) -> str:\n", 373 | " with torch.no_grad():\n", 374 | " text = dataset.tokenizer.encode(name_norm)\n", 375 | " text = torch.tensor(text).to(device)\n", 376 | " output = model(text, torch.tensor([0]).to(device))\n", 377 | " return dataset.decode_target(output.argmax(1).item())" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": 10, 383 | "metadata": {}, 384 | "outputs": [ 385 | { 386 | "data": { 387 | "text/html": [ 388 | "
\n", 389 | "\n", 402 | "\n", 403 | " \n", 404 | " \n", 405 | " \n", 406 | " \n", 407 | " \n", 408 | " \n", 409 | " \n", 410 | " \n", 411 | " \n", 412 | " \n", 413 | " \n", 414 | " \n", 415 | " \n", 416 | " \n", 417 | " \n", 418 | " \n", 419 | " \n", 420 | " \n", 421 | " \n", 422 | " \n", 423 | " \n", 424 | " \n", 425 | " \n", 426 | " \n", 427 | " \n", 428 | " \n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 466 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | "
name_normcategorytarget
27600биойогурт питьевой черная смородинаМолоко, сыр, яйца9
29972зефир \"сладкие истории\" вкусом крем брюле негл...Хлеб, сладости, снеки19
27110зубная паста морские минералыКрасота, гигиена, бытовая химия7
21664джем крыжовниковыйСоусы, орехи, консервы16
9461колбаса охотничья сырокопченаяПтица, мясо, деликатесы14
28063корм собак вкусные потрошки говядина сердцеЗоотовары6
24537хлопья микс органическиеМакароны, крупы, специи8
16145краска волос карамельКрасота, гигиена, бытовая химия7
22289био йогурт питьевой злакамиМолоко, сыр, яйца9
44658подарочный набор совершенствоКрасота, гигиена, бытовая химия7
\n", 474 | "
" 475 | ], 476 | "text/plain": [ 477 | " name_norm \\\n", 478 | "27600 биойогурт питьевой черная смородина \n", 479 | "29972 зефир \"сладкие истории\" вкусом крем брюле негл... \n", 480 | "27110 зубная паста морские минералы \n", 481 | "21664 джем крыжовниковый \n", 482 | "9461 колбаса охотничья сырокопченая \n", 483 | "28063 корм собак вкусные потрошки говядина сердце \n", 484 | "24537 хлопья микс органические \n", 485 | "16145 краска волос карамель \n", 486 | "22289 био йогурт питьевой злаками \n", 487 | "44658 подарочный набор совершенство \n", 488 | "\n", 489 | " category target \n", 490 | "27600 Молоко, сыр, яйца 9 \n", 491 | "29972 Хлеб, сладости, снеки 19 \n", 492 | "27110 Красота, гигиена, бытовая химия 7 \n", 493 | "21664 Соусы, орехи, консервы 16 \n", 494 | "9461 Птица, мясо, деликатесы 14 \n", 495 | "28063 Зоотовары 6 \n", 496 | "24537 Макароны, крупы, специи 8 \n", 497 | "16145 Красота, гигиена, бытовая химия 7 \n", 498 | "22289 Молоко, сыр, яйца 9 \n", 499 | "44658 Красота, гигиена, бытовая химия 7 " 500 | ] 501 | }, 502 | "execution_count": 10, 503 | "metadata": {}, 504 | "output_type": "execute_result" 505 | } 506 | ], 507 | "source": [ 508 | "tmp = dataset.test.sample(10)\n", 509 | "tmp" 510 | ] 511 | }, 512 | { 513 | "cell_type": "code", 514 | "execution_count": 11, 515 | "metadata": {}, 516 | "outputs": [ 517 | { 518 | "name": "stdout", 519 | "output_type": "stream", 520 | "text": [ 521 | "биойогурт питьевой черная смородина --> Молоко, сыр, яйца\n", 522 | "зефир \"сладкие истории\" вкусом крем брюле неглазированный --> Хлеб, сладости, снеки\n", 523 | "зубная паста морские минералы --> Красота, гигиена, бытовая химия\n", 524 | "джем крыжовниковый --> Товары для дома и дачи\n", 525 | "колбаса охотничья сырокопченая --> Птица, мясо, деликатесы\n", 526 | "корм собак вкусные потрошки говядина сердце --> Зоотовары\n", 527 | "хлопья микс органические --> Макароны, крупы, специи\n", 528 | "краска волос карамель --> Красота, гигиена, бытовая химия\n", 529 | "био йогурт питьевой злаками --> Молоко, сыр, яйца\n", 530 | "подарочный набор совершенство --> Красота, гигиена, бытовая химия\n" 531 | ] 532 | } 533 | ], 534 | "source": [ 535 | "for name, pred in zip(tmp['name_norm'], tmp['name_norm'].apply(predict)):\n", 536 | " print(f'{name} --> {pred}')" 537 | ] 538 | }, 539 | { 540 | "cell_type": "code", 541 | "execution_count": 14, 542 | "metadata": {}, 543 | "outputs": [ 544 | { 545 | "data": { 546 | "text/plain": [ 547 | "array(['Алкоголь', 'Бытовая техника', 'Воды, соки, напитки',\n", 548 | " 'Дача и гриль', 'Другое', 'Замороженные продукты', 'Зоотовары',\n", 549 | " 'Красота, гигиена, бытовая химия', 'Макароны, крупы, специи',\n", 550 | " 'Молоко, сыр, яйца', 'Овощи, фрукты, ягоды',\n", 551 | " 'Подборки и готовые блюда', 'Постные продукты', 'Посуда',\n", 552 | " 'Птица, мясо, деликатесы', 'Рыба, икра', 'Соусы, орехи, консервы',\n", 553 | " 'Товары для дома и дачи', 'Товары для мам и детей',\n", 554 | " 'Хлеб, сладости, снеки', 'Чай, кофе, сахар'], dtype=object)" 555 | ] 556 | }, 557 | "execution_count": 14, 558 | "metadata": {}, 559 | "output_type": "execute_result" 560 | } 561 | ], 562 | "source": [ 563 | "dataset.le.classes_" 564 | ] 565 | }, 566 | { 567 | "cell_type": "markdown", 568 | "metadata": {}, 569 | "source": [ 570 | "## Save model" 571 | ] 572 | }, 573 | { 574 | "cell_type": "code", 575 | "execution_count": 25, 576 | "metadata": {}, 577 | "outputs": [], 578 | "source": [ 579 | "torch.save(model.state_dict(), 'receipt_parser/models/baseline_model.pth')" 580 | ] 581 | }, 582 | { 583 | "cell_type": "markdown", 584 | "metadata": {}, 585 | "source": [ 586 | "## Load to use" 587 | ] 588 | }, 589 | { 590 | "cell_type": "code", 591 | "execution_count": 98, 592 | "metadata": {}, 593 | "outputs": [], 594 | "source": [ 595 | "class PredictCategory:\n", 596 | " def __init__(self, path_to_bpe: str, path_to_model: str, model_params: Dict[str, int]):\n", 597 | " self.bpe_model = yttm.BPE(path_to_bpe)\n", 598 | " self.categories: List[str] = [\n", 599 | " 'Алкоголь', 'Бытовая техника', 'Воды, соки, напитки',\n", 600 | " 'Дача и гриль', 'Другое', 'Замороженные продукты', 'Зоотовары',\n", 601 | " 'Красота, гигиена, бытовая химия', 'Макароны, крупы, специи',\n", 602 | " 'Молоко, сыр, яйца', 'Овощи, фрукты, ягоды',\n", 603 | " 'Подборки и готовые блюда', 'Постные продукты', 'Посуда',\n", 604 | " 'Птица, мясо, деликатесы', 'Рыба, икра', 'Соусы, орехи, консервы',\n", 605 | " 'Товары для дома и дачи', 'Товары для мам и детей',\n", 606 | " 'Хлеб, сладости, снеки', 'Чай, кофе, сахар'\n", 607 | " ]\n", 608 | " self.device = torch.device(\"cpu\")\n", 609 | " self.model = CategoryClassifier(**model_params)\n", 610 | " self.model.load_state_dict(torch.load(path_to_model, map_location=self.device))\n", 611 | " self.model.eval()\n", 612 | " \n", 613 | " def predict(self, name_norm: str) -> str:\n", 614 | " \"\"\"Predict category by name norm.\"\"\"\n", 615 | " \n", 616 | " text = self.bpe_model.encode(name_norm)\n", 617 | " text = torch.tensor(text).to(self.device)\n", 618 | " output = self.model(text, torch.tensor([0]).to(self.device))\n", 619 | " return self.categories[output.argmax(1).item()]\n", 620 | " " 621 | ] 622 | }, 623 | { 624 | "cell_type": "code", 625 | "execution_count": 102, 626 | "metadata": {}, 627 | "outputs": [ 628 | { 629 | "data": { 630 | "text/plain": [ 631 | "'Хлеб, сладости, снеки'" 632 | ] 633 | }, 634 | "execution_count": 102, 635 | "metadata": {}, 636 | "output_type": "execute_result" 637 | } 638 | ], 639 | "source": [ 640 | "# Usage\n", 641 | "\n", 642 | "params = {\n", 643 | " 'num_class': 21,\n", 644 | " 'embed_dim': 50,\n", 645 | " 'vocab_size': 500\n", 646 | "}\n", 647 | "\n", 648 | "predictor = PredictCategory('receipt_parser/models/train_bpe_model.yttm', 'receipt_parser/models/baseline_model.pth', params)\n", 649 | "predictor.predict('джем')" 650 | ] 651 | }, 652 | { 653 | "cell_type": "code", 654 | "execution_count": null, 655 | "metadata": {}, 656 | "outputs": [], 657 | "source": [] 658 | } 659 | ], 660 | "metadata": { 661 | "kernelspec": { 662 | "display_name": "Python 3", 663 | "language": "python", 664 | "name": "python3" 665 | }, 666 | "language_info": { 667 | "codemirror_mode": { 668 | "name": "ipython", 669 | "version": 3 670 | }, 671 | "file_extension": ".py", 672 | "mimetype": "text/x-python", 673 | "name": "python", 674 | "nbconvert_exporter": "python", 675 | "pygments_lexer": "ipython3", 676 | "version": "3.7.3" 677 | } 678 | }, 679 | "nbformat": 4, 680 | "nbformat_minor": 4 681 | } 682 | -------------------------------------------------------------------------------- /receipt_parser/parsers/README.md: -------------------------------------------------------------------------------- 1 | Для создания размеченного датасета я использовал этот [ресурс](https://xmldatafeed.com/catalog/) *(не реклама)*. Там можно скачать уже готовые спаршенные данные с разных магазинов. Однако была проблема, что информация уже немного устаревшая, поэтому пришлось писать свои парсеры, чтобы обогатить данные. 2 | 3 | ## Описание парсеров: 4 | * [Перекрёсток](https://www.perekrestok.ru) - собраны все актуальные товары. 5 | * [Магнит](https://edadeal.ru/) - к сожалению, у них не представлен список товаров на сайте, поэтому пришлось парсить едадил, но данных там мало. 6 | * [Пятёрочка](http://pyaterochkaakcii.ru/) - аналогично магниту, для неё нет списка товаров, единственное, что мне удалось найти, так это сайт с акциями, вот его и парсил. 7 | * [Тинькофф](https://receiptnlp.tinkoff.ru/) - это сервис для нормализации чеков, он достаточно не плох и в нём используются нейронки. Я порой использовал его для разметки данных. Для скрапинга используется selenium, а чтобы запустить его, нужно из команжной строки передать 3 аргумента: 8 | * Путь до датасета в формате .csv, который нужно разметить. Колонка с описанием товара должна называться `Название`. 9 | * Путь, куда будет сохраняться размеченный датасет. 10 | * Количество ядер CPU, для мультипроцессинга 11 | 12 | Пример использования: 13 | ```bash 14 | $ python3 tinkoff.py magnit.csv magnit_clean.csv 4 15 | ``` -------------------------------------------------------------------------------- /receipt_parser/parsers/magnit.py: -------------------------------------------------------------------------------- 1 | """Parse info about Magnit supremarket in Edadil story: `https://edadeal.ru/`.""" 2 | from time import sleep 3 | from typing import List 4 | from selenium import webdriver # type: ignore 5 | 6 | 7 | class Magnit: 8 | """Parse olnly product's names.""" 9 | 10 | def __init__(self): 11 | self.url = "https://edadeal.ru/moskva/retailers/magnit-univer?page={}" 12 | self.driver = webdriver.Chrome() 13 | self.max_pages = 30 14 | 15 | def basic_auth(self) -> None: 16 | """Prepare the page for parsing.""" 17 | 18 | self.driver.get(self.url.format(0)) 19 | sleep(2) 20 | button = self.driver.find_element_by_class_name("b-rollout__cancel") 21 | button.click() 22 | sleep(2) 23 | self.driver.maximize_window() 24 | 25 | def parse(self) -> List[str]: 26 | """Parse all product in `https://edadeal.ru`.""" 27 | 28 | self.basic_auth() 29 | 30 | names = [] 31 | for page in range(self.max_pages): 32 | self.driver.get(self.url.format(page)) 33 | sleep(2) 34 | products = self.driver.find_elements_by_class_name("b-offer__description") 35 | if len(products) == 0: 36 | print(f"End on page = {page}.") 37 | break 38 | for name in products: 39 | names.append(name.text) 40 | 41 | return names 42 | -------------------------------------------------------------------------------- /receipt_parser/parsers/perecrestok.py: -------------------------------------------------------------------------------- 1 | """Parse info about Perecrestok supremarket: `https://www.perekrestok.ru`.""" 2 | from time import sleep 3 | from typing import Dict, Optional, NamedTuple, Set, NoReturn 4 | import requests as req 5 | from selenium import webdriver # type: ignore 6 | from bs4 import BeautifulSoup # type: ignore 7 | import pandas as pd # type: ignore 8 | 9 | Category = NamedTuple("Category", [("name", str), ("url", str)]) 10 | 11 | 12 | class Perecrestok: 13 | """Allow to parse all data about products from the Perecrestok.""" 14 | 15 | def __init__(self): 16 | self.main_url = "https://www.perekrestok.ru" 17 | self.url_catalog = "https://www.perekrestok.ru/catalog" 18 | self.columns = [ 19 | "Название", 20 | "Категория", 21 | "Производитель", 22 | "Торговая марка", 23 | "Вес", 24 | "Жирность", 25 | ] 26 | self.result = pd.DataFrame(columns=self.columns) 27 | 28 | @staticmethod 29 | def __get_html(url: str) -> str: 30 | """Scroll down a HTML page and return the HTML-code.""" 31 | 32 | driver = webdriver.Chrome() 33 | driver.get(url) 34 | for i in range(0, 50000, 1080): 35 | driver.execute_script(f"window.scrollTo({i}, {i+1080})") 36 | sleep(3) 37 | page: str = driver.page_source 38 | driver.close() 39 | return page 40 | 41 | @staticmethod 42 | def __get_product_name(soup: BeautifulSoup) -> Optional[str]: 43 | """Return the porduct name.""" 44 | 45 | name = soup.find(class_="js-product__title xf-product-card__title") 46 | if name: 47 | name = name.text.split("\n")[0] 48 | return name 49 | 50 | @staticmethod 51 | def __raise_error(function_name: str) -> NoReturn: 52 | """Raise error if status code not equal 200.""" 53 | 54 | raise ValueError(f"Проблема с подключением к сети в функции {function_name}.") 55 | 56 | def get_catalog(self) -> Set[Category]: 57 | """Return set of namedtuples about all categories in the catalog.""" 58 | 59 | result: Set[Category] = set() 60 | resp = req.get(self.url_catalog) 61 | if resp.status_code != 200: 62 | self.__raise_error(self.get_catalog.__name__) 63 | soup = BeautifulSoup(resp.text, "lxml") 64 | for cat in soup.find_all(class_="xf-catalog-categories__item"): 65 | href = cat.find(class_="xf-catalog-categories__link").get("href") 66 | name = cat.text.strip() 67 | result.add(Category(name, self.main_url + href)) 68 | 69 | return result 70 | 71 | def __parse_good(self, url: str) -> Dict[str, Optional[str]]: 72 | """Parse information about the product.""" 73 | 74 | product: Dict[str, Optional[str]] = {} 75 | _ = [product.setdefault(key, None) for key in self.columns] 76 | resp = req.get(url) 77 | if resp.status_code != 200: 78 | self.__raise_error(self.__parse_good.__name__) 79 | soup = BeautifulSoup(resp.text, "lxml") 80 | product["Название"] = self.__get_product_name(soup) 81 | table = soup.find( 82 | "table", attrs={"class": "xf-product-info__table xf-product-table"} 83 | ) 84 | if table: 85 | rows = table.find_all("tr") 86 | for row in rows: 87 | key = row.find_all(class_="xf-product-table__col-header")[ 88 | 0 89 | ].text.strip() 90 | value = row.find_all("td")[0].text.strip() 91 | if key == "Объём": 92 | key = "Вес" 93 | if key in self.columns: 94 | product[key] = value 95 | return product 96 | 97 | def __parse_category(self, category: Category) -> pd.DataFrame: 98 | """Parse all products in the categoty.""" 99 | 100 | print(f"Start parsing {category.name}.") 101 | page = self.__get_html(category.url) 102 | soup = BeautifulSoup(page, "lxml") 103 | goods = soup.find_all(class_="js-catalog-product _additionals xf-catalog__item") 104 | for good in goods: 105 | url = good.find(class_="xf-product-picture__link js-product__image").get( 106 | "href" 107 | ) 108 | url = self.main_url + url 109 | product = self.__parse_good(url) 110 | product["Категория"] = category.name 111 | self.result = pd.concat( 112 | [self.result, pd.DataFrame.from_dict(product, orient="index").T] 113 | ) 114 | 115 | self.result = self.result.dropna(subset=["Название"]).drop_duplicates( 116 | subset=["Название"] 117 | ) 118 | return self.result 119 | 120 | def parse(self) -> pd.DataFrame: 121 | """Parse all products descriptions from `https://www.perekrestok.ru`.""" 122 | 123 | for category in self.get_catalog(): 124 | self.__parse_category(category) 125 | return self.result 126 | 127 | 128 | if __name__ == "__main__": 129 | parser = Perecrestok() 130 | data = parser.parse() 131 | data.to_csv("perecrestok_goods.csv") 132 | -------------------------------------------------------------------------------- /receipt_parser/parsers/peterochka.py: -------------------------------------------------------------------------------- 1 | """Parse info about Peterochka supremarket: `http://pyaterochkaakcii.ru/`.""" 2 | import re 3 | from typing import List, Optional, NoReturn 4 | import requests as req 5 | from bs4 import BeautifulSoup # type: ignore 6 | import pandas as pd # type: ignore 7 | 8 | 9 | class ParseProductName: 10 | """Find brand, product and weight if products desription.""" 11 | 12 | def __init__(self, products: pd.DataFrame): 13 | self.products = products.dropna().drop_duplicates() 14 | 15 | @staticmethod 16 | def find_brand(name: str) -> Optional[str]: 17 | """Find brand in product name.""" 18 | 19 | pattern = r'"(.*)"' 20 | brand = re.findall(pattern, name) 21 | return brand[0] if brand else None 22 | 23 | @staticmethod 24 | def remove_brand(name: str, brand: str) -> str: 25 | """Remove brand from product name.""" 26 | 27 | return name.replace(f'"{brand}"', "") 28 | 29 | @staticmethod 30 | def find_weight_or_volume(name: str) -> Optional[str]: 31 | """Find weight or volume in product name.""" 32 | 33 | pattern = r"\d+((\.|\,|x|X|х|Х)?)\d* ?([а-я]|[А-Я])*$" 34 | result = re.search(pattern, name) 35 | return result.group(0) if result else None 36 | 37 | @staticmethod 38 | def find_product(name: str) -> str: 39 | """Find main product.""" 40 | 41 | return name[: name.find(' "')] 42 | 43 | def run_parse(self) -> pd.DataFrame: 44 | """Find brand, product and weight of goods.""" 45 | 46 | self.products["Бренд"] = self.products["Название"].apply(self.find_brand) 47 | self.products = self.products.dropna() 48 | self.products["Вес"] = self.products["Название"].apply( 49 | self.find_weight_or_volume 50 | ) 51 | self.products["Продукт"] = self.products["Название"].apply(self.find_product) 52 | return self.products 53 | 54 | 55 | class Peterochka: 56 | """Allow to parse all data about products from the Peterochka.""" 57 | 58 | def __init__(self): 59 | self.katalog = { 60 | "bakaleya": "Соусы, орехи, консервы.", 61 | "bytovaya-himiya": "Бытовая химия.", 62 | "zamorozhennye-produkty": "Замороженные продукты.", 63 | "kolbasa-kopchennosti-myasnye-delikatesy": "Птица, мясо, деликатесы.", 64 | "konditerskie-izdeliya": "Хлеб, сладости, снеки.", 65 | "chay-kofe-kakao": "Чай, кофе, сахар.", 66 | "hlebobulochnye-izdeliya": "Хлеб, сладости, снеки.", 67 | "tovary-dlya-zhivotnyh": "Зоотовары.", 68 | "tovary-dlya-detey": "Товары для мам и детей.", 69 | "specialnoe-pitanie": "Скидки месяца.", 70 | "sladosti-i-konfety": "Хлеб, сладости, снеки.", 71 | "ryba-i-moreprodukty": "Рыба, икра.", 72 | "napitki-soki-vody": "Воды, соки, напитки.", 73 | "myaso-i-ptica": "Птица, мясо, деликатесы.", 74 | "kosmetika-i-lichnaya-gigiena": "Красота, гигиена, бытовая химия.", 75 | } 76 | self.url = "http://pyaterochkaakcii.ru/katalog/{category}?page={page}" 77 | self.pages = 20 78 | 79 | @staticmethod 80 | def __raise_error(function_name: str) -> NoReturn: 81 | """Raise error if status code not equal 200.""" 82 | 83 | raise ValueError(f"Проблема с подключением к сети в функции {function_name}.") 84 | 85 | def parse_category(self, category: tuple) -> List[str]: 86 | """Parse all products in the categoty.""" 87 | 88 | goods = [] 89 | for page in range(self.pages): 90 | resp = req.get(self.url.format(category=category[0], page=page)) 91 | if resp.status_code != 200: 92 | self.__raise_error(self.parse_category.__name__) 93 | soup = BeautifulSoup(resp.text, "lxml") 94 | names = soup.find_all(class_="name") 95 | if not names: # it was the last page 96 | break 97 | for name in names: 98 | goods.append(name.text) 99 | return goods 100 | 101 | def _parse_all(self) -> pd.DataFrame: 102 | """Parse all product in `http://pyaterochkaakcii.ru/`.""" 103 | 104 | result = pd.DataFrame(columns=["Название", "Категория"]) 105 | for category in self.katalog.items(): 106 | print(f"Start parsing {category[1]}") 107 | goods = self.parse_category(category) 108 | tmp = pd.DataFrame(goods, columns=["Название"]) 109 | tmp["Категория"] = category[1] 110 | result = pd.concat([result, tmp]) 111 | return result 112 | 113 | def parse(self) -> pd.DataFrame: 114 | """ 115 | Parse all product in `http://pyaterochkaakcii.ru/` 116 | and transform data with `ParseProductName` class. 117 | """ 118 | 119 | parse_data: pd.DataFrame = self._parse_all() 120 | product_parser = ParseProductName(parse_data) 121 | clean_data: pd.DataFrame = product_parser.run_parse() 122 | return clean_data 123 | 124 | 125 | if __name__ == "__main__": 126 | parser = Peterochka() 127 | data = parser.parse() 128 | data.to_csv("peterochka_goods.csv") 129 | -------------------------------------------------------------------------------- /receipt_parser/parsers/tinkoff.py: -------------------------------------------------------------------------------- 1 | """Use Tinkoff(`https://receiptnlp.tinkoff.ru/`) to parse product goods.""" 2 | import sys 3 | from time import sleep 4 | from typing import List, Dict 5 | from multiprocessing import Pool 6 | import pandas as pd # type: ignore 7 | from selenium import webdriver # type: ignore 8 | from selenium.webdriver.common.keys import Keys # type: ignore 9 | 10 | # pylint: disable=unbalanced-tuple-unpacking 11 | 12 | 13 | class Tinkoff: 14 | """Use Tinkoff server to parse product goods. Use Chrome browser.""" 15 | 16 | def __init__(self): 17 | self.url = "https://receiptnlp.tinkoff.ru/" 18 | self.driver = None 19 | 20 | def get_session(self) -> None: 21 | """Open browser.""" 22 | 23 | self.driver = webdriver.Chrome() 24 | self.driver.get(self.url) 25 | 26 | def fil_fields(self, name: str) -> None: 27 | """Delete last text in the field and add new one.""" 28 | 29 | window = self.driver.find_element_by_class_name("_3gY2s") 30 | window.send_keys(Keys.CONTROL + "a") 31 | window.send_keys(Keys.DELETE) 32 | window.send_keys(name) 33 | button = self.driver.find_element_by_xpath("//button[@type='submit']") 34 | button.click() 35 | sleep(0.5) 36 | 37 | def parse_table(self) -> Dict[str, str]: 38 | """Parse information on the check.""" 39 | 40 | table = self.driver.find_element_by_class_name("Mnf-LQLHUyIQdT1VvKhCE") 41 | result = {} 42 | for row in table.text.split("\n"): 43 | result[row.split()[0]] = " ".join(row.split()[1:]) 44 | return result 45 | 46 | def parse_data(self, products: List[str]) -> List[Dict[str, str]]: 47 | """Start parsing data.""" 48 | 49 | self.get_session() 50 | result = [] 51 | for name in products: 52 | self.fil_fields(name) 53 | result.append(self.parse_table()) 54 | self.driver.close() 55 | return result 56 | 57 | 58 | def get_batches(products: List[str], processes_count: int) -> List[List[str]]: 59 | """Split List to List[List] for using the multiprocessing.""" 60 | 61 | result = [] 62 | bath_size = round(len(products) / processes_count) 63 | for i in range(0, len(products), bath_size): 64 | result.append(products[i : i + bath_size]) 65 | return result 66 | 67 | 68 | def prepare_data_to_parse(path: str, processes_count: int) -> List[List[str]]: 69 | """Read data and prepare it to use in a multiprocessing.""" 70 | 71 | data = pd.read_csv(path) 72 | target_column = "Название" 73 | if target_column not in data.columns: 74 | raise KeyError('Dataset must have a column with name "Название"') 75 | data = data["Название"].apply(lambda x: [x]).values.tolist() 76 | return get_batches(data, processes_count) 77 | 78 | 79 | def transform_and_save(data: List[List[Dict[str, str]]], path_to_save: str) -> None: 80 | """Expand list if lists and save as csv file.""" 81 | 82 | # Expand list: List[List[dict]] -> List[dict] 83 | data = [item for elem in data for item in elem] # type: ignore 84 | pd.DataFrame.from_dict(data).to_csv(path_to_save, index=False) 85 | 86 | 87 | if __name__ == "__main__": 88 | _, PATH_TO_DB, PATH_TO_SAVE, PROCESSES_COUNT = sys.argv 89 | prepared_data = prepare_data_to_parse(PATH_TO_DB, int(PROCESSES_COUNT)) 90 | tinkoff = Tinkoff() 91 | pool = Pool(int(PROCESSES_COUNT)) 92 | res = pool.map(tinkoff.parse_data, prepared_data) 93 | transform_and_save(res, PATH_TO_SAVE) 94 | pool.close() 95 | -------------------------------------------------------------------------------- /receipt_parser/receipt_parser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provide various types of technologies for recognition 3 | and normalization product descriptions. 4 | """ 5 | import os 6 | from typing import Union, Optional, Dict 7 | import wget # type: ignore 8 | import pandas as pd # type: ignore 9 | 10 | try: 11 | from receipt_parser.finder import Finder # type: ignore 12 | from receipt_parser.normalizer import Normalizer # type: ignore 13 | except ImportError: 14 | from finder import Finder # type: ignore 15 | from normalizer import Normalizer # type: ignore 16 | 17 | 18 | class DownloadData: 19 | """Download some data that i can't add to PyPi.""" 20 | 21 | def __init__(self): 22 | # pylint: disable=line-too-long 23 | self.base_url = "https://raw.githubusercontent.com/slgero/receipt_parser/master/receipt_parser" 24 | self.files: Dict[str, str] = { 25 | "cleaned/all_clean.csv": f"{self.base_url}/data/cleaned/all_clean.csv", 26 | "cleaned/brands_en.csv": f"{self.base_url}/data/cleaned/brands_en.csv", 27 | "cleaned/brands_ru.csv": f"{self.base_url}/data/cleaned/brands_ru.csv", 28 | "cleaned/products.csv": f"{self.base_url}/data/cleaned/products.csv", 29 | "blacklist.csv": f"{self.base_url}/data/blacklist.csv", 30 | "cat_bpe_model.yttm": f"{self.base_url}/models/cat_bpe_model.yttm", 31 | "cat_model.pth": f"{self.base_url}/models/cat_model.pth", 32 | } 33 | self.home_folder: str = os.path.join(os.getcwd(), "data") 34 | 35 | @staticmethod 36 | def _is_folder_exists() -> bool: 37 | """Check if folder `data` exists.""" 38 | 39 | pwd: str = os.getcwd() 40 | data_folder: str = os.path.join(pwd, "data") 41 | return os.path.isdir(data_folder) 42 | 43 | @staticmethod 44 | def _make_dirs() -> None: 45 | """Make 3 dirs: `data/`, `models` and `data/cleaned`.""" 46 | 47 | os.makedirs("data") 48 | os.makedirs(os.path.join("data", "cleaned")) 49 | os.makedirs(os.path.join("data", "models")) 50 | 51 | def download_files(self) -> None: 52 | """Download files from my github account.""" 53 | 54 | for name, url in self.files.items(): 55 | print(f"Download {name.split('/')[-1]}") 56 | wget.download(url, os.path.join("data", name)) 57 | 58 | def get_pathes(self) -> Dict[str, str]: 59 | """Return new pathes to files.""" 60 | 61 | pathes: Dict[str, str] = {} 62 | 63 | for path in self.files: 64 | name = path.split("/")[-1].split(".")[0] 65 | pathes[name] = os.path.join(self.home_folder, path) 66 | return pathes 67 | 68 | def download(self) -> Dict[str, str]: 69 | """Download files and return new pathes.""" 70 | 71 | if not self._is_folder_exists(): 72 | print("It's need to download some data...") 73 | self._make_dirs() 74 | self.download_files() 75 | return self.get_pathes() 76 | 77 | 78 | # pylint: disable=too-few-public-methods 79 | class RuleBased: 80 | """ 81 | Use rules based on regular expressions and 82 | keyword selective tools on marked datasets to 83 | recognize product descriptions. 84 | 85 | Parameters 86 | ---------- 87 | pathes: Optional[Dict[str, str]] (default=None) 88 | Dictionary with paths to *.csv files. 89 | 90 | Attributes 91 | ---------- 92 | norm: Normalizer 93 | Normalize product description: expand abbreviations, 94 | delete garbage words and characters for further recognition, 95 | remove english worlds, etc. 96 | find : Finder 97 | Search and recognize the name, category and brand of a product 98 | from its description. 99 | 100 | Examples 101 | -------- 102 | >>> rules = RuleBased() 103 | >>> rules.parse(df['name']) 104 | """ 105 | 106 | def __init__(self, pathes: Optional[Dict[str, str]] = None): 107 | download_pathes: Dict[str, str] = DownloadData().download() 108 | pathes = pathes or download_pathes 109 | 110 | self.norm = Normalizer(pathes) 111 | self.find = Finder(pathes) 112 | 113 | @staticmethod 114 | def __transform_data(data: Union[pd.DataFrame, pd.Series, str]) -> pd.Series: 115 | """Transform data to pd.Series into the desired format.""" 116 | 117 | if isinstance(data, pd.DataFrame): 118 | if "name" not in data.columns: 119 | raise ValueError( 120 | "Столбец с описанием товара должен иметь название `name`." 121 | ) 122 | return data["name"] 123 | return pd.Series(data, name="name") 124 | 125 | # pylint: disable=bad-continuation 126 | def parse( 127 | self, data: Union[pd.DataFrame, pd.Series, str], verbose: int = 0 128 | ) -> pd.DataFrame: 129 | """ 130 | Start the parsing process. 131 | 132 | Parameters 133 | ---------- 134 | data : Union[pd.DataFrame, pd.Series, str] 135 | Text column with a description of the products to parse. 136 | verbose: int (default=0) 137 | Set verbose to any positive number for verbosity. 138 | 139 | Returns 140 | ------- 141 | pd.DataFrame 142 | Recognized product names, brands and product categories. 143 | """ 144 | 145 | data = self.__transform_data(data) 146 | data = self.norm.normalize(data) 147 | data = self.find.find_all(data, verbose) 148 | data = data.drop("name_norm", axis=1) 149 | return data 150 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy >= 1.18.3 2 | pandas >= 1.0.3 3 | pandarallel >= 1.4.8 4 | pymystem3 >= 0.2.0 5 | setuptools 6 | torch 7 | torchvision 8 | wget >= 3.2 9 | youtokentome >= 1.0.6 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """Setup file.""" 2 | import os.path 3 | import re 4 | import setuptools 5 | 6 | 7 | def get_package_variable(name: str, rel_path: str = "receipt_parser/__init__.py"): 8 | """Read pakage variable from `__init__.py`""" 9 | 10 | path = os.path.join(os.path.abspath(os.path.dirname(name)), rel_path) 11 | 12 | pattern = re.compile(r'^{}.*?([\'"])(?P.+)\1.*$'.format(re.escape(name))) 13 | 14 | with open(path, "r", encoding="UTF-8") as file: 15 | for line in file: 16 | match = pattern.match(line) 17 | 18 | if match: 19 | return match.group("value") 20 | raise RuntimeError("Unable to find variable: " + name) 21 | 22 | 23 | __version__ = get_package_variable("__version__") 24 | 25 | 26 | with open("README.md", "r", encoding="UTF-8") as readme: 27 | LONG_DESCRIPTION = readme.read() 28 | 29 | with open("requirements.txt", "r", encoding="UTF-8") as requires: 30 | INSTALL_REQUIRES = requires.read() 31 | 32 | 33 | setuptools.setup( 34 | name="receipt_parser", 35 | version=__version__, 36 | author="Savvov Sergey", 37 | author_email="sersavvov@yandex.ru", 38 | description="Allow receipt parsing", 39 | long_description=LONG_DESCRIPTION, 40 | long_description_content_type="text/markdown", 41 | url="https://github.com/slgero/receipt_parser", 42 | packages=setuptools.find_packages(include=["receipt_parser", "receipt_parser.*"]), 43 | classifiers=[ 44 | "Programming Language :: Python :: 3", 45 | "License :: OSI Approved :: MIT License", 46 | "Operating System :: OS Independent", 47 | "Intended Audience :: Science/Research", 48 | "Topic :: Scientific/Engineering :: Information Analysis", 49 | "Natural Language :: Russian", 50 | ], 51 | install_requires=INSTALL_REQUIRES, 52 | python_requires=">=3.6", 53 | keywords=["receipt parser", "product parser", "nlp"], 54 | ) 55 | --------------------------------------------------------------------------------