# VekParser: Расширяемый парсер для веб-сайтов
## Основные возможности
- Многоступенчатая обработка данных через YAML-конфигурацию
- Поддержка параллельного выполнения задач
- Встроенные механизмы для работы с динамическим контентом (через Playwright)
- Кастомные обработчики данных
- Автоматическое сохранение результатов в JSON
## Ключевые концепции
### Типы шагов обработки
| Тип | Назначение | Особенности |
|-----------|-------------------------------------|-------------------------------------|
| static | Инициализация статических значений | Задает начальный контекст выполнения|
| extract | Извлечение данных со страницы | Использует CSS/XPath селекторы |
| list | Параллельная обработка коллекций | Запускает вложенные цепочки шагов |
| save | Финализация и сохранение результата | Экспорт в JSON-файл |
### Жизненный цикл данных
1. Инициализация контекста (static)
2. Извлечение данных (extract)
3. Обработка списка (list)
4. Сохранение (save)
## Полная структура конфигурации
```yaml
steps:
- name: уникальное_имя_шага
type: [static|extract|list|save|свой тип]
# Параметры для static
values: { ключ: значение }
# Параметры для extract
url: "шаблон_URL"
data: { селекторы }
# Параметры для list
source: "ключ_в_контексте"
output: "выходной_ключ"
handler_name: [string|link|dict|свой обработчик]
# Общие параметры
next_steps:
- step: "имя_следующего_шага"
context_map: { ключ: "значение_из_контекста" }
```
## Расширенные возможности
### 1. Кастомные обработчики шагов
Регистрация пользовательской логики:
```python
class DataProcessor:
@staticmethod
def process_data(step_config: Dict, context: Dict) -> Dict:
return {"processed": True}
parser.register_step_handler('transform', DataProcessor.process_data)
```
Пример использования в конфиге:
```yaml
- name: advanced_transform
type: transform
next_steps: [...]
```
### 2. Обработчики элементов списка
Поддерживаемые типы:
- `string` - текстовые элементы
- `link` - URL-адреса
Регистрация обработчика для словарей:
```python
parser.register_item_handler('dict', lambda item, _: item)
```
Конфигурация:
```yaml
- name: process_items
type: list
handler_name: dict
steps: [...]
```
### 3. Механизм сохранения данных
Конфигурация шага сохранения:
```yaml
- name: save_results
type: save
fields:
- field1
- field2
```
Результат:
```json
[
{
"field1": "value1",
"field2": "value2"
},
{
"field1": "value3",
"field2": "value4"
}
]
```
## Полный пример парсера
### 1. Код обработчика
```python
class AreaCalculator:
@staticmethod
def calculate_dimensions(step_config: Dict, context: Dict) -> Dict:
# Логика вычислений
return {
'area': width * height,
'volume': width * height * depth
}
```
### 2. Конфигурация
```yaml
steps:
- name: product_processing
type: list
handler_name: dict
steps:
- name: extract_data
type: extract
url: "{link}"
data:
price: "#price::text"
dimensions: ".specs::text"
next_steps:
- step: calculate_metrics
- name: calculate_metrics
type: transform
next_steps:
- step: save_product
- name: save_product
type: save
fields:
- title
- price
- area
- volume
```
### 3. Инициализация парсера
```python
parser = WebParser(
config_path='config.yml',
base_url='https://example.com',
render_js=True
)
parser.register_step_handler('transform', AreaCalculator.calculate_dimensions)
parser.register_item_handler('dict', lambda item, _: item)
```
## Отладка и мониторинг
### Логирование
Уровни логирования:
- DEBUG: детальная информация о выполнении шагов
- INFO: основные этапы работы
- WARNING: проблемы с доступностью ресурсов
- ERROR: критические ошибки выполнения
Настройка:
```python
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
```
### Советы по оптимизации
1. Настройка параллелизма:
```python
WebParser(max_workers=10)
```
2. Интервал запросов:
```python
HttpClient(request_interval=1.0)
```
3. JS-рендеринг:
```python
WebParser(render_js=True)
```
Raw data
{
"_id": null,
"home_page": null,
"name": "vek-parser",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "web, parser, scraping, crawler",
"author": "Emil Markov",
"author_email": "Emil Markov <markovemil2@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/b9/05/4882802d9740354b305108bd00439e121e0c5f9b7683f9a39a8f708cc161/vek_parser-0.1.7.tar.gz",
"platform": null,
"description": "# VekParser: \u0420\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440 \u0434\u043b\u044f \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u043e\u0432\n\n## \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\n\n- \u041c\u043d\u043e\u0433\u043e\u0441\u0442\u0443\u043f\u0435\u043d\u0447\u0430\u0442\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 YAML-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e\n- \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\n- \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c (\u0447\u0435\u0440\u0435\u0437 Playwright)\n- \u041a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\n- \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0432 JSON\n\n## \u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u0438\n\n### \u0422\u0438\u043f\u044b \u0448\u0430\u0433\u043e\u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438\n\n| \u0422\u0438\u043f | \u041d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 | \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438 |\n|-----------|-------------------------------------|-------------------------------------|\n| static | \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 | \u0417\u0430\u0434\u0430\u0435\u0442 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f|\n| extract | \u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b | \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 CSS/XPath \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b |\n| list | \u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0439 | \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0448\u0430\u0433\u043e\u0432 |\n| save | \u0424\u0438\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 | \u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0432 JSON-\u0444\u0430\u0439\u043b |\n\n### \u0416\u0438\u0437\u043d\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0434\u0430\u043d\u043d\u044b\u0445\n1. \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 (static)\n2. \u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 (extract)\n3. \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043f\u0438\u0441\u043a\u0430 (list)\n4. \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 (save)\n\n## \u041f\u043e\u043b\u043d\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438\n\n```yaml\nsteps:\n - name: \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435_\u0438\u043c\u044f_\u0448\u0430\u0433\u0430\n type: [static|extract|list|save|\u0441\u0432\u043e\u0439 \u0442\u0438\u043f]\n \n # \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f static\n values: { \u043a\u043b\u044e\u0447: \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 }\n \n # \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f extract\n url: \"\u0448\u0430\u0431\u043b\u043e\u043d_URL\"\n data: { \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u044b }\n \n # \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f list\n source: \"\u043a\u043b\u044e\u0447_\u0432_\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435\"\n output: \"\u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0439_\u043a\u043b\u044e\u0447\"\n handler_name: [string|link|dict|\u0441\u0432\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a]\n \n # \u041e\u0431\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n next_steps:\n - step: \"\u0438\u043c\u044f_\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e_\u0448\u0430\u0433\u0430\"\n context_map: { \u043a\u043b\u044e\u0447: \"\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435_\u0438\u0437_\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430\" }\n```\n\n## \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\n\n### 1. \u041a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0448\u0430\u0433\u043e\u0432\n\n\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u0438:\n```python\nclass DataProcessor:\n @staticmethod\n def process_data(step_config: Dict, context: Dict) -> Dict:\n return {\"processed\": True}\n\nparser.register_step_handler('transform', DataProcessor.process_data)\n```\n\n\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435:\n```yaml\n- name: advanced_transform\n type: transform\n next_steps: [...]\n```\n\n### 2. \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u043f\u0438\u0441\u043a\u0430\n\n\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u0442\u0438\u043f\u044b:\n- `string` - \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b\n- `link` - URL-\u0430\u0434\u0440\u0435\u0441\u0430\n\n\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0434\u043b\u044f \u0441\u043b\u043e\u0432\u0430\u0440\u0435\u0439:\n```python\nparser.register_item_handler('dict', lambda item, _: item)\n```\n\n\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f:\n```yaml\n- name: process_items\n type: list\n handler_name: dict\n steps: [...]\n```\n\n### 3. \u041c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445\n\n\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0448\u0430\u0433\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f:\n```yaml\n- name: save_results\n type: save\n fields:\n - field1\n - field2\n```\n\n\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:\n```json\n[\n {\n \"field1\": \"value1\",\n \"field2\": \"value2\"\n },\n {\n \"field1\": \"value3\",\n \"field2\": \"value4\"\n }\n]\n```\n\n## \u041f\u043e\u043b\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u0430\u0440\u0441\u0435\u0440\u0430\n\n### 1. \u041a\u043e\u0434 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\n\n```python\nclass AreaCalculator:\n @staticmethod\n def calculate_dimensions(step_config: Dict, context: Dict) -> Dict:\n # \u041b\u043e\u0433\u0438\u043a\u0430 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439\n return {\n 'area': width * height,\n 'volume': width * height * depth\n }\n```\n\n### 2. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\n\n```yaml\nsteps:\n - name: product_processing\n type: list\n handler_name: dict\n steps:\n - name: extract_data\n type: extract\n url: \"{link}\"\n data:\n price: \"#price::text\"\n dimensions: \".specs::text\"\n next_steps:\n - step: calculate_metrics\n \n - name: calculate_metrics\n type: transform\n next_steps:\n - step: save_product\n \n - name: save_product\n type: save\n fields:\n - title\n - price\n - area\n - volume\n```\n\n### 3. \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0430\u0440\u0441\u0435\u0440\u0430\n\n```python\nparser = WebParser(\n config_path='config.yml',\n base_url='https://example.com',\n render_js=True\n)\nparser.register_step_handler('transform', AreaCalculator.calculate_dimensions)\nparser.register_item_handler('dict', lambda item, _: item)\n```\n\n## \u041e\u0442\u043b\u0430\u0434\u043a\u0430 \u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\n\n### \u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\n\u0423\u0440\u043e\u0432\u043d\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:\n- DEBUG: \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u0448\u0430\u0433\u043e\u0432\n- INFO: \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u044d\u0442\u0430\u043f\u044b \u0440\u0430\u0431\u043e\u0442\u044b\n- WARNING: \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c\u044e \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432\n- ERROR: \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f\n\n\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430:\n```python\nlogging.basicConfig(\n level=logging.DEBUG,\n format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'\n)\n```\n\n### \u0421\u043e\u0432\u0435\u0442\u044b \u043f\u043e \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438\n1. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c\u0430:\n```python\nWebParser(max_workers=10)\n```\n\n2. \u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432:\n```python\nHttpClient(request_interval=1.0)\n```\n\n3. JS-\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433:\n```python\nWebParser(render_js=True)\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "\u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u043e\u0432",
"version": "0.1.7",
"project_urls": {
"Documentation": "https://github.com/EmilMarkov/VekParser#readme",
"Homepage": "https://github.com/EmilMarkov/VekParser",
"Repository": "https://github.com/EmilMarkov/VekParser.git"
},
"split_keywords": [
"web",
" parser",
" scraping",
" crawler"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "dc153eb706185923eb7512728a7c0426a8b1ac1fbe64e968e8e275bd4aa213bf",
"md5": "a8c393aeaeeece78bb47ee684e9c7e23",
"sha256": "743213998cdbf51da3515beda9f9cd4f4b0f4bb2bad6eeace4c8eefda2431523"
},
"downloads": -1,
"filename": "vek_parser-0.1.7-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a8c393aeaeeece78bb47ee684e9c7e23",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 9559,
"upload_time": "2025-07-27T01:21:49",
"upload_time_iso_8601": "2025-07-27T01:21:49.847389Z",
"url": "https://files.pythonhosted.org/packages/dc/15/3eb706185923eb7512728a7c0426a8b1ac1fbe64e968e8e275bd4aa213bf/vek_parser-0.1.7-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "b9054882802d9740354b305108bd00439e121e0c5f9b7683f9a39a8f708cc161",
"md5": "be657a6c3d5ec563e43bc394662c375c",
"sha256": "089bcddcef169ccf3be711c842a596c6a661cdbc4c818e4de01ed83532c8057a"
},
"downloads": -1,
"filename": "vek_parser-0.1.7.tar.gz",
"has_sig": false,
"md5_digest": "be657a6c3d5ec563e43bc394662c375c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 10592,
"upload_time": "2025-07-27T01:21:50",
"upload_time_iso_8601": "2025-07-27T01:21:50.902011Z",
"url": "https://files.pythonhosted.org/packages/b9/05/4882802d9740354b305108bd00439e121e0c5f9b7683f9a39a8f708cc161/vek_parser-0.1.7.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-27 01:21:50",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "EmilMarkov",
"github_project": "VekParser#readme",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "requests",
"specs": [
[
">=",
"2.25.0"
]
]
},
{
"name": "selectorlib",
"specs": [
[
">=",
"0.16.0"
]
]
},
{
"name": "parsel",
"specs": [
[
">=",
"1.7.0"
]
]
},
{
"name": "PyYAML",
"specs": [
[
">=",
"5.4.0"
]
]
},
{
"name": "playwright",
"specs": [
[
"~=",
"1.50.0"
]
]
}
],
"lcname": "vek-parser"
}