Skip to content

Commit

Permalink
feat: рефактор и улучшения
Browse files Browse the repository at this point in the history
* парсинг рейтинга продавца
* проверка версии при запуске
* парсинг редиректов в каталог из поиска
fixes #9
  • Loading branch information
xob0t committed Dec 5, 2024
1 parent 26ba5ce commit d962f8d
Show file tree
Hide file tree
Showing 10 changed files with 823 additions and 930 deletions.
137 changes: 69 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,133 +1,134 @@
#
#

```
____ ___ ____ ___ ____ ____ _____________ _____
/ __ `__ \/ __ `__ \/ __ \/ __ `/ ___/ ___/ _ \/ ___/
/ / / / / / / / / / / /_/ / /_/ / / (__ ) __/ /
/_/ /_/ /_/_/ /_/ /_/ .___/\__,_/_/ /____/\___/_/
/ / / / / / / / / / / /_/ / /_/ / / (__ ) __/ /
/_/ /_/ /_/_/ /_/ /_/ .___/\__,_/_/ /____/\___/_/
/_/
```

![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/xob0t/mmparser/total)

## Сказать спасибо автору - [Yoomoney](https://yoomoney.ru/to/410018051351692)
Связь со мной [tg](https://t.me/mobate)

Связь со мной [tg](https://t.me/mobate) - Индивидуальной поддержкой бесплатно не занимаюсь

### Демо ускорено в 10 раз

[![asciicast](https://asciinema.org/a/fYFj0HVO16r16vaK1reEa4617.svg)](https://asciinema.org/a/fYFj0HVO16r16vaK1reEa4617)

<details>
<summary>Пример уведомления Telegram</summary>
<img src="media/tg_demo.jpg">
</details>

## Особенности
* Работа через api
* Парсинг карточек товаров при парсинге каталога/поиска
* Сохранение результатов в sqlite БД
* Запуск с конфигом и/или аргументами
* Интерактивное создание конфигов
* Поддержка прокси строкой или списком из файла
* Поддержка ссылок каталога, поиска, и карточек товара
* Парсинг одной ссылки в многопотоке, по потоку на прокси/соединение
* Импорт cookies экспортированних в формате Json с помошью [Cookie-Editor](https://chrome.google.com/webstore/detail/hlkenndednhfkekhgcdicdfddnkalmdm)
* Блеклист продавцов
* Regex фильтр по именам товаров
* Уведомления в телеграм по заданным параметрам
* Позволяет выставить время, через которое подходящий по параметрам уведомлений товар будет повторно отправлен в TG
* Использование блеклиста продавцов с ограничением на списание бонусов
* Сссылки на каталог супермаркетов не поддерживаются :(

- Работа через api
- Парсинг карточек товаров при парсинге каталога/поиска
- Сохранение результатов в sqlite БД
- Запуск с конфигом и/или аргументами
- Интерактивное создание конфигов
- Поддержка прокси строкой или списком из файла
- Поддержка ссылок каталога, поиска, и карточек товара
- Парсинг одной ссылки в многопотоке, по потоку на прокси/соединение
- Импорт cookies экспортированних в формате Json с помошью [Cookie-Editor](https://chrome.google.com/webstore/detail/hlkenndednhfkekhgcdicdfddnkalmdm)
- Блеклист продавцов
- Regex фильтр по именам товаров
- Уведомления в телеграм по заданным параметрам
- Позволяет выставить время, через которое подходящий по параметрам уведомлений товар будет повторно отправлен в TG
- Использование блеклиста продавцов с ограничением на списание бонусов
- Сссылки на каталог супермаркетов не поддерживаются :(

## Установка:
1. Установить [Python](https://www.python.org/downloads/), в установщике поставить галочку "Добавить в PATH"
2. [Скачать парсер](https://github.com/xob0t/mmparser/releases/latest/download/mmparser.zip)
3. Установить парсер: `pip install mmparser.zip -U`

1. Установить [Python](https://www.python.org/downloads/), в установщике поставить галочку "Добавить в PATH"
2. [Скачать парсер](https://github.com/xob0t/mmparser/releases/latest/download/mmparser.zip)
3. Установить парсер: `pip install mmparser.zip -U`

## Пример использования

### <span style="color:yellow">Кавычки обязательны!</span>

### Просто парсинг url

```
mmparser "https://megamarket.ru/catalog/?q=%D0%BD%D0%BE%D1%83%D1%82%D0%B1%D1%83%D0%BA&suggestionType=frequent_query#?filters=%7B%2288C83F68482F447C9F4E401955196697%22%3A%7B%22min%22%3A229028%2C%22max%22%3A307480%7D%2C%22A03364050801C25CD0A856C734F74FE9%22%3A%5B%221%22%5D%7D&sort=1"
```

### Парсинг url с cookie файлом

```
mmparser -cookies "cookies.json" "https://megamarket.ru/catalog/details/processor-amd-ryzen-5-5600-am4-oem-600008773764/"
```

### Без аргументов, создание конфига

```
mmparser
```

### Запуск с конфигом

```
mmparser -cfg "config.json"
mmparser -config "config.json"
```

## Чтение результатов

При запуске парсер создаст в рабочей директории файл storage.sqlite

Это sqlite база данных, очень удобно читается в [DB Browser for SQLite](https://sqlitebrowser.org/)

## Запуск по расписанию на windows:

[Планировщик заданий Windows для начинающих](https://remontka.pro/windows-task-scheduler/)

#

```
usage: mmparser [-h] [-job JOB_NAME] [-cfg CONFIG] [-i INCLUDE] [-e EXCLUDE] [-b BLACKLIST] [-ac] [-nc] [-c COOKIES]
[-aa ACCOUNT_ALERT] [-a ADDRESS] [-p PROXY] [-pl PROXY_LIST] [-ad] [-tc TG_CONFIG]
[-pva PRICE_VALUE_ALERT] [-pbva PRICE_BONUS_VALUE_ALERT] [-bva BONUS_VALUE_ALERT]
[-bpa BONUS_PERCENT_ALERT] [-mb] [-art ALERT_REPEAT_TIMEOUT] [-t THREADS] [-d DELAY] [-ed ERROR_DELAY]
[-log {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
mmparser [-h] [-job-name JOB_NAME] [-config CONFIG] [-include INCLUDE] [-exclude EXCLUDE] [-blacklist BLACKLIST] [-all-cards] [-no-cards] [-cookies COOKIES] [-account-alert ACCOUNT_ALERT] [-address ADDRESS] [-proxy PROXY] [-proxy-list PROXY_LIST] [-allow-direct] [-tg-config TG_CONFIG] [-price-value-alert PRICE_VALUE_ALERT]
[-price-bonus-value-alert PRICE_BONUS_VALUE_ALERT] [-bonus-value-alert BONUS_VALUE_ALERT] [-bonus-percent-alert BONUS_PERCENT_ALERT] [-use-merchant-blacklist] [-alert-repeat-timeout ALERT_REPEAT_TIMEOUT] [-threads THREADS] [-delay DELAY] [-error-delay ERROR_DELAY] [-log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
[url]
positional arguments:
url URL для парсинга
options:
-h, --help show this help message and exit
-job JOB_NAME, --job-name JOB_NAME
Название задачи, без этого параметра будет автоопределено
-cfg CONFIG, --config CONFIG
Путь к конфигу парсера
-i INCLUDE, --include INCLUDE
Парсить только товары, название которых совпадает с выражением
-e EXCLUDE, --exclude EXCLUDE
Пропускать товары, название которых совпадает с выражением
-b BLACKLIST, --blacklist BLACKLIST
Путь к файлу со списком игнорируемых продавцов
-ac, --all-cards Всегда парсить карточки товаров
-nc, --no-cards Не парсить карточки товаров
-c COOKIES, --cookies COOKIES
Путь к файлу с cookies в формате JSON (Cookie-Editor - Export Json)
-aa ACCOUNT_ALERT, --account-alert ACCOUNT_ALERT
-job-name JOB_NAME Название задачи, без этого параметра будет автоопределено
-config CONFIG Путь к конфигу парсера
-include INCLUDE Парсить только товары, название которых совпадает с выражением
-exclude EXCLUDE Пропускать товары, название которых совпадает с выражением
-blacklist BLACKLIST Путь к файлу со списком игнорируемых продавцов
-all-cards Всегда парсить карточки товаров
-no-cards Не парсить карточки товаров
-cookies COOKIES Путь к файлу с cookies в формате JSON (Cookie-Editor - Export Json)
-account-alert ACCOUNT_ALERT
Если вы используйте cookie, и вход в аккаунт не выполнен, присылать уведомление в TG
-a ADDRESS, --address ADDRESS
Адрес, будет использовано первое сопадение
-p PROXY, --proxy PROXY
Строка прокси в формате protocol://username:password@ip:port
-pl PROXY_LIST, --proxy-list PROXY_LIST
-address ADDRESS Адрес, будет использовано первое сопадение
-proxy PROXY Строка прокси в формате protocol://username:password@ip:port
-proxy-list PROXY_LIST
Путь к файлу с прокси в формате protocol://username:password@ip:port
-ad, --allow-direct Использовать прямое соединение параллельно с прокси для ускорения работы в многопотоке
-tc TG_CONFIG, --tg-config TG_CONFIG
Telegram Bot Token и Telegram Chat Id в формате token$id
-pva PRICE_VALUE_ALERT, --price-value-alert PRICE_VALUE_ALERT
-allow-direct Использовать прямое соединение параллельно с прокси для ускорения работы в многопотоке
-tg-config TG_CONFIG Telegram Bot Token и Telegram Chat Id в формате token$id
-price-value-alert PRICE_VALUE_ALERT
Если цена товара равна или ниже данного значения, уведомлять в TG
-pbva PRICE_BONUS_VALUE_ALERT, --price-bonus-value-alert PRICE_BONUS_VALUE_ALERT
-price-bonus-value-alert PRICE_BONUS_VALUE_ALERT
Если цена-бонусы товара равна или ниже данного значения, уведомлять в TG
-bva BONUS_VALUE_ALERT, --bonus-value-alert BONUS_VALUE_ALERT
-bonus-value-alert BONUS_VALUE_ALERT
Если количество бонусов товара равно или выше данного значения, уведомлять в TG
-bpa BONUS_PERCENT_ALERT, --bonus-percent-alert BONUS_PERCENT_ALERT
-bonus-percent-alert BONUS_PERCENT_ALERT
Если процент бонусов товара равно или выше данного значения, уведомлять в TG
-mb, --use-merchant-blacklist
Использовать черный список продавцов с ограничением на списание бонусов.
Для более эффективной работы рекомендуется установить парсер с поддержкой lxml:
pip install mmparser.zip[lxml] -U
-art ALERT_REPEAT_TIMEOUT, --alert-repeat-timeout ALERT_REPEAT_TIMEOUT
Если походящий по параметрам товар уже был отправлен в TG, повторно уведомлять по истечении
заданного времени, в часах
-t THREADS, --threads THREADS
Количество потоков. По умолчанию: 1 на каждое соединиение
-d DELAY, --delay DELAY
Задержка между запросами в секундах при работе в одном потоке. По умолчанию: 1.8
-ed ERROR_DELAY, --error-delay ERROR_DELAY
-use-merchant-blacklist
Использовать черный список продавцов с ограничением на списание бонусов
-alert-repeat-timeout ALERT_REPEAT_TIMEOUT
Если походящий по параметрам товар уже был отправлен в TG, повторно уведомлять по истечении заданного времени, в часах
-threads THREADS Количество потоков. По умолчанию: 1 на каждое соединиение
-delay DELAY Задержка между запросами в секундах при работе в одном потоке. По умолчанию: 1.8
-error-delay ERROR_DELAY
Задержка между запосами в секундах в случае ошибки при работе в одном потоке. По умолчанию: 5
-log {DEBUG,INFO,WARNING,ERROR,CRITICAL}, --log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
-log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
Уровень лога. По умолчанию: INFO
```
79 changes: 41 additions & 38 deletions core/db.py → core/db_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,24 @@ def new_job(job_name):
"""INSERT INTO
jobs
(name,started) VALUES (?,?)""",
(job_name,
now)
(job_name, now),
)
job_id = cursor.lastrowid
cursor.execute(f"""
CREATE TABLE "{job_name}_{job_id}" (
"goodsId" TEXT,
"merchantId" TEXT,
"goods_id" TEXT,
"merchant_id" TEXT,
"url" TEXT,
"title" TEXT,
"finalPrice" INTEGER,
"finalPriceBonus" INTEGER,
"bonusAmount" INTEGER,
"bonusPercent" INTEGER,
"availableQuantity" INTEGER,
"deliveryPossibilities" TEXT,
"merchantName" TEXT,
"scraped" DATETIME,
"price" INTEGER,
"price_bonus" INTEGER,
"bonus_amount" INTEGER,
"bonus_percent" INTEGER,
"available_quantity" INTEGER,
"delivery_date" TEXT,
"merchant_name" TEXT,
"merchant_rating" FLOAT,
"scraped_at" DATETIME,
"notified" BOOL
);
""")
Expand All @@ -58,17 +58,18 @@ def new_job(job_name):
def add_to_db(
job_id,
job_name,
goodsId,
merchantId,
goods_id,
merchant_id,
url,
title,
finalPrice,
finalPriceBonus,
bonusAmount,
bonusPercent,
availableQuantity,
deliveryPossibilities,
merchantName,
price,
price_bonus,
bonus_amount,
bonus_percent,
available_quantity,
delivery_date,
merchant_name,
merchant_rating,
notified,
):
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
Expand All @@ -77,30 +78,31 @@ def add_to_db(
cursor.execute(
f"""INSERT INTO
"{job_name}_{job_id}"
(goodsId,merchantId,url,title,finalPrice,finalPriceBonus,bonusAmount,
bonusPercent,availableQuantity,deliveryPossibilities,
merchantName,scraped,notified)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)""",
(goods_id,merchant_id,url,title,price,price_bonus,bonus_amount,
bonus_percent,available_quantity,delivery_date,
merchant_name,merchant_rating,scraped_at,notified)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
(
goodsId,
merchantId,
goods_id,
merchant_id,
url,
title,
finalPrice,
finalPriceBonus,
bonusAmount,
bonusPercent,
availableQuantity,
deliveryPossibilities,
merchantName,
price,
price_bonus,
bonus_amount,
bonus_percent,
available_quantity,
delivery_date,
merchant_name,
merchant_rating,
now,
notified,
),
)
sqlite_connection.commit()


def get_last_notified(goodsId, merchantId, finalPrice, bonusAmount):
def get_last_notified(goods_id, merchant_id, price, bonus_amount):
sqlite_connection = sqlite3.connect(FILENAME)
cursor = sqlite_connection.cursor()
last_notified_row = None
Expand All @@ -113,12 +115,12 @@ def get_last_notified(goodsId, merchantId, finalPrice, bonusAmount):
job_tables = [table[0] for table in cursor.fetchall()]

# Construct a union query to select from all job tables at once
union_query = " UNION ".join([f"SELECT scraped FROM '{table}' WHERE notified = 1 AND goodsId = ? AND merchantId = ? AND finalPrice = ? AND bonusAmount = ?" for table in job_tables])
union_query = " UNION ".join([f"SELECT scraped FROM '{table}' WHERE notified = 1 AND goods_id = ? AND merchant_id = ? AND price = ? AND bonus_amount = ?" for table in job_tables])

union_query+="ORDER BY scraped DESC LIMIT 1"
union_query += "ORDER BY scraped DESC LIMIT 1"

# Concatenate all parameters to be passed into the execute function
parameters = tuple([goodsId, merchantId, finalPrice, bonusAmount] * len(job_tables))
parameters = tuple([goods_id, merchant_id, price, bonus_amount] * len(job_tables))

cursor.execute(union_query, parameters)
row = cursor.fetchone()
Expand All @@ -128,6 +130,7 @@ def get_last_notified(goodsId, merchantId, finalPrice, bonusAmount):
cursor.close()
return last_notified_row


def finish_job(job_id):
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
sqlite_connection = sqlite3.connect(FILENAME)
Expand Down
10 changes: 10 additions & 0 deletions core/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class BaseException(Exception):
pass


class ConfigError(BaseException):
pass


class ApiError(BaseException):
pass
Loading

0 comments on commit d962f8d

Please sign in to comment.