-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
1,708 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
.vscode/settings.json | ||
.vscode/launch.json | ||
/core/__pycache__ | ||
*.sqlite | ||
*.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# mmparser | ||
## Купить | ||
Парсер открыт, но не бесплатен :) | ||
Купить - [Yoomoney](https://yoomoney.ru/fundraise/122C5TB8IKI.240412) | ||
|
||
## Особенности | ||
* Работа через 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` | ||
|
||
## Пример использования | ||
### <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"` | ||
### Без аргументов, создание конфига | ||
`mmparser` | ||
### Запуск с конфигом | ||
`mmparser -cfg "config.json"` | ||
### Запуск с конфигом и аргументами | ||
`mmparser -cfg "config.json" --allow-direct` | ||
|
||
## Чтение результатов | ||
При запуске парсер создаст в рабочей директории файл storage.sqlite | ||
|
||
Это sqlite база данных, очень удобно читается в [DB Browser for SQLite](https://sqlitebrowser.org/) | ||
|
||
## Запуск по расписанию на windows: | ||
[Планировщик заданий Windows для начинающих](https://remontka.pro/windows-task-scheduler/) | ||
|
||
# | ||
|
||
``` | ||
usage: main.py [-h] [-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] [-art ALERT_REPEAT_TIMEOUT] [-t THREADS] [-d DELAY] [-ed ERROR_DELAY] [-log {DEBUG,INFO,WARNING,ERROR,CRITICAL}] | ||
[url] | ||
positional arguments: | ||
url URL для парсинга | ||
options: | ||
-h, --help show this help message and exit | ||
-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 | ||
Если вы используйте cookie, и вход в аккаунт не выполнен, присылать уведомление в TG | ||
-a ADDRESS, --address ADDRESS | ||
Адрес, будет использовано первое сопадение | ||
-p PROXY, --proxy PROXY | ||
Строка прокси в формате protocol://username:password@ip:port | ||
-pl PROXY_LIST, --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 | ||
Если цена товара равна или ниже данного значения, уведомлять в TG | ||
-pbva PRICE_BONUS_VALUE_ALERT, --price-bonus-value-alert PRICE_BONUS_VALUE_ALERT | ||
Если цена-бонусы товара равна или ниже данного значения, уведомлять в TG | ||
-bva BONUS_VALUE_ALERT, --bonus-value-alert BONUS_VALUE_ALERT | ||
Если количество бонусов товара равно или выше данного значения, уведомлять в TG | ||
-bpa BONUS_PERCENT_ALERT, --bonus-percent-alert BONUS_PERCENT_ALERT | ||
Если процент бонусов товара равно или выше данного значения, уведомлять в TG | ||
-art ALERT_REPEAT_TIMEOUT, --alert-repeat-timeout ALERT_REPEAT_TIMEOUT | ||
Если походящий по параметрам товар уже был отправлен в TG, повторно уведомлять по истечении заданного времени, в часах | ||
-t THREADS, --threads THREADS | ||
Количество потоков. По умолчанию: 1 на каждое соединиение | ||
-d DELAY, --delay DELAY | ||
Задержка между запросами в секундах при работе в одном потоке. По умолчанию: 2 | ||
-ed ERROR_DELAY, --error-delay ERROR_DELAY | ||
Задержка между запосами в секундах в случае ошибки при работе в одном потоке. По умолчанию: 5 | ||
-log {DEBUG,INFO,WARNING,ERROR,CRITICAL}, --log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL} | ||
Уровень лога. По умолчанию: INFO | ||
``` |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import sqlite3 | ||
import datetime | ||
|
||
FILENAME = "storage.sqlite" | ||
|
||
|
||
def create_db(): | ||
sqlite_connection = sqlite3.connect(FILENAME) | ||
cursor = sqlite_connection.cursor() | ||
|
||
cursor.execute(""" | ||
CREATE TABLE "jobs" ( | ||
"id" INTEGER PRIMARY KEY AUTOINCREMENT, | ||
"name" TEXT, | ||
"started" DATETIME, | ||
"completed" DATETIME | ||
); | ||
""") | ||
|
||
sqlite_connection.commit() | ||
cursor.close() | ||
|
||
|
||
def new_job(job_name): | ||
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") | ||
sqlite_connection = sqlite3.connect(FILENAME) | ||
cursor = sqlite_connection.cursor() | ||
cursor.execute( | ||
"""INSERT INTO | ||
jobs | ||
(name,started) VALUES (?,?)""", | ||
(job_name, | ||
now) | ||
) | ||
job_id = cursor.lastrowid | ||
cursor.execute(f""" | ||
CREATE TABLE "{job_name}_{job_id}" ( | ||
"goodsId" TEXT, | ||
"merchantId" TEXT, | ||
"url" TEXT, | ||
"title" TEXT, | ||
"finalPrice" INTEGER, | ||
"finalPriceBonus" INTEGER, | ||
"bonusAmount" INTEGER, | ||
"bonusPercent" INTEGER, | ||
"availableQuantity" INTEGER, | ||
"deliveryPossibilities" TEXT, | ||
"merchantName" TEXT, | ||
"scraped" DATETIME, | ||
"notified" BOOL | ||
); | ||
""") | ||
sqlite_connection.commit() | ||
return job_id | ||
|
||
|
||
def add_to_db( | ||
job_id, | ||
job_name, | ||
goodsId, | ||
merchantId, | ||
url, | ||
title, | ||
finalPrice, | ||
finalPriceBonus, | ||
bonusAmount, | ||
bonusPercent, | ||
availableQuantity, | ||
deliveryPossibilities, | ||
merchantName, | ||
notified, | ||
): | ||
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") | ||
sqlite_connection = sqlite3.connect(FILENAME) | ||
cursor = sqlite_connection.cursor() | ||
cursor.execute( | ||
f"""INSERT INTO | ||
"{job_name}_{job_id}" | ||
(goodsId,merchantId,url,title,finalPrice,finalPriceBonus,bonusAmount, | ||
bonusPercent,availableQuantity,deliveryPossibilities, | ||
merchantName,scraped,notified) | ||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)""", | ||
( | ||
goodsId, | ||
merchantId, | ||
url, | ||
title, | ||
finalPrice, | ||
finalPriceBonus, | ||
bonusAmount, | ||
bonusPercent, | ||
availableQuantity, | ||
deliveryPossibilities, | ||
merchantName, | ||
now, | ||
notified, | ||
), | ||
) | ||
sqlite_connection.commit() | ||
|
||
|
||
def get_last_notified(goodsId, merchantId, finalPrice, bonusAmount): | ||
sqlite_connection = sqlite3.connect(FILENAME) | ||
cursor = sqlite_connection.cursor() | ||
last_notified_row = None | ||
|
||
# Get a list of all job tables | ||
# cursor.execute("SELECT id, name FROM jobs") | ||
# job_tables = cursor.fetchall() | ||
|
||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name != 'jobs' AND name != 'sqlite_sequence'") | ||
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+="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)) | ||
|
||
cursor.execute(union_query, parameters) | ||
row = cursor.fetchone() | ||
if row: | ||
last_notified_row = row[0] | ||
|
||
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) | ||
cursor = sqlite_connection.cursor() | ||
cursor.execute( | ||
"""UPDATE | ||
"jobs" | ||
SET completed = ? | ||
WHERE id = ? | ||
""", | ||
(now, job_id), | ||
) | ||
sqlite_connection.commit() | ||
cursor.close() | ||
if cursor.rowcount == 0: | ||
return False | ||
return True |
Oops, something went wrong.