├── .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 |
5 |
6 |
8 |
9 | 
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 | " shop_name | \n",
68 | " Название | \n",
69 | " product | \n",
70 | " brand | \n",
71 | " category | \n",
72 | "
\n",
73 | " \n",
74 | " \n",
75 | " \n",
76 | " 0 | \n",
77 | " АТАК | \n",
78 | " ЖЕЛУДКИ 500Г/ПОДЛ/ТР | \n",
79 | " желудки | \n",
80 | " троекурово | \n",
81 | " Птица, мясо, деликатесы | \n",
82 | "
\n",
83 | " \n",
84 | " 1 | \n",
85 | " АТАК | \n",
86 | " СОК ЯБЛ/БАНАН 6М 0,2 | \n",
87 | " сок | \n",
88 | " - | \n",
89 | " Воды, соки, напитки | \n",
90 | "
\n",
91 | " \n",
92 | "
\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 | " shop_name | \n",
78 | " name | \n",
79 | " quantity | \n",
80 | " price | \n",
81 | " sum | \n",
82 | "
\n",
83 | " \n",
84 | " \n",
85 | " \n",
86 | " 19 | \n",
87 | " ДИКСИ | \n",
88 | " МИНТАЙ ФИЛЕ Б/К СВ/МОР П/ПАК 8 | \n",
89 | " 1.0 | \n",
90 | " 199.90 | \n",
91 | " 199.90 | \n",
92 | "
\n",
93 | " \n",
94 | " 20 | \n",
95 | " ДИКСИ | \n",
96 | " БЗМЖ МОЛОКО СТРАНА ВАСИЛЬКИ У/ | \n",
97 | " 1.0 | \n",
98 | " 49.99 | \n",
99 | " 49.99 | \n",
100 | "
\n",
101 | " \n",
102 | " 21 | \n",
103 | " ДИКСИ | \n",
104 | " СУШКИ ТАРАЛЛИНИ С ЧЕСНОКОМ 180 | \n",
105 | " 1.0 | \n",
106 | " 29.99 | \n",
107 | " 29.99 | \n",
108 | "
\n",
109 | " \n",
110 | " 22 | \n",
111 | " ДИКСИ | \n",
112 | " ЯЙЦО КУРИНОЕ СТОЛОВОЕ 1КАТЕГОР | \n",
113 | " 1.0 | \n",
114 | " 63.99 | \n",
115 | " 63.99 | \n",
116 | "
\n",
117 | " \n",
118 | " 23 | \n",
119 | " ДИКСИ | \n",
120 | " ПЕЧЕНЬЕ ЮБИЛЕЙНОЕ ВИТАМИНИЗИРО | \n",
121 | " 2.0 | \n",
122 | " 29.99 | \n",
123 | " 59.98 | \n",
124 | "
\n",
125 | " \n",
126 | "
\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 | " shop_name | \n",
193 | " name | \n",
194 | "
\n",
195 | " \n",
196 | " \n",
197 | " \n",
198 | " 41261 | \n",
199 | " АТАК | \n",
200 | " ЖЕЛУДКИ 500Г/ПОДЛ/ТР | \n",
201 | "
\n",
202 | " \n",
203 | " 41677 | \n",
204 | " АТАК | \n",
205 | " СОК ЯБЛ/БАНАН 6М 0,2 | \n",
206 | "
\n",
207 | " \n",
208 | " 42169 | \n",
209 | " АТАК | \n",
210 | " ТОМАТ БАКИНСКИЙ ВЕС | \n",
211 | "
\n",
212 | " \n",
213 | " 41432 | \n",
214 | " АТАК | \n",
215 | " ЙОГ ВИШНЯ 250ГР | \n",
216 | "
\n",
217 | " \n",
218 | " 42541 | \n",
219 | " АТАК | \n",
220 | " СР-ВО АНТИЖИР 500МЛ | \n",
221 | "
\n",
222 | " \n",
223 | "
\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 | " shop_name | \n",
342 | " name | \n",
343 | " product | \n",
344 | " brand | \n",
345 | " category | \n",
346 | "
\n",
347 | " \n",
348 | " \n",
349 | " \n",
350 | " 41261 | \n",
351 | " АТАК | \n",
352 | " ЖЕЛУДКИ 500Г/ПОДЛ/ТР | \n",
353 | " желудки | \n",
354 | " троекурово | \n",
355 | " Птица, мясо, деликатесы | \n",
356 | "
\n",
357 | " \n",
358 | " 41677 | \n",
359 | " АТАК | \n",
360 | " СОК ЯБЛ/БАНАН 6М 0,2 | \n",
361 | " сок | \n",
362 | " - | \n",
363 | " Воды, соки, напитки | \n",
364 | "
\n",
365 | " \n",
366 | " 42169 | \n",
367 | " АТАК | \n",
368 | " ТОМАТ БАКИНСКИЙ ВЕС | \n",
369 | " томат | \n",
370 | " - | \n",
371 | " Овощи, фрукты, ягоды | \n",
372 | "
\n",
373 | " \n",
374 | " 41432 | \n",
375 | " АТАК | \n",
376 | " ЙОГ ВИШНЯ 250ГР | \n",
377 | " йогурт | \n",
378 | " - | \n",
379 | " Молоко, сыр, яйца | \n",
380 | "
\n",
381 | " \n",
382 | " 42541 | \n",
383 | " АТАК | \n",
384 | " СР-ВО АНТИЖИР 500МЛ | \n",
385 | " средство антижир | \n",
386 | " - | \n",
387 | " Красота, гигиена, бытовая химия | \n",
388 | "
\n",
389 | " \n",
390 | "
\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 | " name_norm | \n",
407 | " category | \n",
408 | " target | \n",
409 | "
\n",
410 | " \n",
411 | " \n",
412 | " \n",
413 | " 27600 | \n",
414 | " биойогурт питьевой черная смородина | \n",
415 | " Молоко, сыр, яйца | \n",
416 | " 9 | \n",
417 | "
\n",
418 | " \n",
419 | " 29972 | \n",
420 | " зефир \"сладкие истории\" вкусом крем брюле негл... | \n",
421 | " Хлеб, сладости, снеки | \n",
422 | " 19 | \n",
423 | "
\n",
424 | " \n",
425 | " 27110 | \n",
426 | " зубная паста морские минералы | \n",
427 | " Красота, гигиена, бытовая химия | \n",
428 | " 7 | \n",
429 | "
\n",
430 | " \n",
431 | " 21664 | \n",
432 | " джем крыжовниковый | \n",
433 | " Соусы, орехи, консервы | \n",
434 | " 16 | \n",
435 | "
\n",
436 | " \n",
437 | " 9461 | \n",
438 | " колбаса охотничья сырокопченая | \n",
439 | " Птица, мясо, деликатесы | \n",
440 | " 14 | \n",
441 | "
\n",
442 | " \n",
443 | " 28063 | \n",
444 | " корм собак вкусные потрошки говядина сердце | \n",
445 | " Зоотовары | \n",
446 | " 6 | \n",
447 | "
\n",
448 | " \n",
449 | " 24537 | \n",
450 | " хлопья микс органические | \n",
451 | " Макароны, крупы, специи | \n",
452 | " 8 | \n",
453 | "
\n",
454 | " \n",
455 | " 16145 | \n",
456 | " краска волос карамель | \n",
457 | " Красота, гигиена, бытовая химия | \n",
458 | " 7 | \n",
459 | "
\n",
460 | " \n",
461 | " 22289 | \n",
462 | " био йогурт питьевой злаками | \n",
463 | " Молоко, сыр, яйца | \n",
464 | " 9 | \n",
465 | "
\n",
466 | " \n",
467 | " 44658 | \n",
468 | " подарочный набор совершенство | \n",
469 | " Красота, гигиена, бытовая химия | \n",
470 | " 7 | \n",
471 | "
\n",
472 | " \n",
473 | "
\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 |
--------------------------------------------------------------------------------