pyaterochka-api 0.1.9.1__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyaterochka_api/__init__.py +1 -0
- pyaterochka_api/api.py +12 -24
- pyaterochka_api/enums.py +0 -5
- pyaterochka_api/manager.py +0 -15
- pyaterochka_api/tools.py +5 -68
- {pyaterochka_api-0.1.9.1.dist-info → pyaterochka_api-0.2.0.dist-info}/METADATA +6 -17
- pyaterochka_api-0.2.0.dist-info/RECORD +10 -0
- pyaterochka_api-0.1.9.1.dist-info/RECORD +0 -10
- {pyaterochka_api-0.1.9.1.dist-info → pyaterochka_api-0.2.0.dist-info}/WHEEL +0 -0
- {pyaterochka_api-0.1.9.1.dist-info → pyaterochka_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {pyaterochka_api-0.1.9.1.dist-info → pyaterochka_api-0.2.0.dist-info}/top_level.txt +0 -0
pyaterochka_api/__init__.py
CHANGED
pyaterochka_api/api.py
CHANGED
@@ -2,7 +2,9 @@ import aiohttp
|
|
2
2
|
from fake_useragent import UserAgent
|
3
3
|
from camoufox import AsyncCamoufox
|
4
4
|
import logging
|
5
|
-
from
|
5
|
+
from typing import Union, Optional
|
6
|
+
from beartype import beartype
|
7
|
+
from .tools import parse_proxy, get_env_proxy
|
6
8
|
|
7
9
|
|
8
10
|
class PyaterochkaAPI:
|
@@ -10,16 +12,17 @@ class PyaterochkaAPI:
|
|
10
12
|
Класс для загрузки JSON/image и парсинга JavaScript-конфигураций из удаленного источника.
|
11
13
|
"""
|
12
14
|
|
15
|
+
@beartype
|
13
16
|
def __init__(self,
|
14
17
|
debug: bool = False,
|
15
18
|
proxy: str | None = None,
|
16
19
|
autoclose_browser: bool = False,
|
17
20
|
trust_env: bool = False,
|
18
21
|
timeout: float = 10.0
|
19
|
-
):
|
22
|
+
) -> None:
|
20
23
|
self._debug = debug
|
21
24
|
self._proxy = proxy
|
22
|
-
self._session = None
|
25
|
+
self._session: Optional[aiohttp.ClientSession] = None
|
23
26
|
self._autoclose_browser = autoclose_browser
|
24
27
|
self._browser = None
|
25
28
|
self._bcontext = None
|
@@ -33,7 +36,8 @@ class PyaterochkaAPI:
|
|
33
36
|
if not self._logger.hasHandlers():
|
34
37
|
self._logger.addHandler(handler)
|
35
38
|
|
36
|
-
|
39
|
+
@beartype
|
40
|
+
async def fetch(self, url: str) -> tuple[bool, Union[dict, list[dict], bytes, str, None], str]:
|
37
41
|
"""
|
38
42
|
Выполняет HTTP-запрос к указанному URL и возвращает результат.
|
39
43
|
|
@@ -63,26 +67,8 @@ class PyaterochkaAPI:
|
|
63
67
|
self._logger.error(f'Unexpected error: {response.status}')
|
64
68
|
raise Exception(f"Response status: {response.status} (unknown error/status code)")
|
65
69
|
|
66
|
-
|
67
|
-
|
68
|
-
Загружает и парсит JavaScript-конфигурацию с указанного URL.
|
69
|
-
|
70
|
-
:param config_url: URL для загрузки конфигурации.
|
71
|
-
:return: Распарсенные данные в виде словаря или None.
|
72
|
-
"""
|
73
|
-
is_success, js_code, _response_type = await self.fetch(url=config_url)
|
74
|
-
|
75
|
-
if not is_success:
|
76
|
-
if self._debug:
|
77
|
-
self._logger.error('Failed to fetch JS code')
|
78
|
-
return None
|
79
|
-
elif self._debug:
|
80
|
-
self._logger.debug('JS code fetched successfully')
|
81
|
-
|
82
|
-
return await parse_js(js_code=js_code, debug=self._debug, logger=self._logger)
|
83
|
-
|
84
|
-
|
85
|
-
async def browser_fetch(self, url: str, selector: str, state: str = 'attached') -> dict:
|
70
|
+
@beartype
|
71
|
+
async def browser_fetch(self, url: str, selector: str, state: str = 'attached') -> str:
|
86
72
|
if self._browser is None or self._bcontext is None:
|
87
73
|
await self.new_session(include_aiohttp=False, include_browser=True)
|
88
74
|
|
@@ -97,6 +83,7 @@ class PyaterochkaAPI:
|
|
97
83
|
await self.close(include_aiohttp=False, include_browser=True)
|
98
84
|
return content
|
99
85
|
|
86
|
+
@beartype
|
100
87
|
async def new_session(self, include_aiohttp: bool = True, include_browser: bool = False) -> None:
|
101
88
|
await self.close(include_aiohttp=include_aiohttp, include_browser=include_browser)
|
102
89
|
|
@@ -129,6 +116,7 @@ class PyaterochkaAPI:
|
|
129
116
|
self._bcontext = await self._browser.new_context()
|
130
117
|
self._logger.info(f"A new browser context has been opened.")
|
131
118
|
|
119
|
+
@beartype
|
132
120
|
async def close(
|
133
121
|
self,
|
134
122
|
include_aiohttp: bool = True,
|
pyaterochka_api/enums.py
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
|
3
3
|
class Patterns(Enum):
|
4
|
-
JS = r'\s*let\s+n\s*=\s*({.*});\s*' # let n = {...};
|
5
|
-
STR = r'(\w+)\s*:\s*"([^"\\]*(?:\\.[^"\\]*)*)"' # key: "value"
|
6
|
-
DICT = r'(\w+)\s*:\s*{(.*?)}' # key: {...}
|
7
|
-
LIST = r'(\w+)\s*:\s*\[([^\[\]]*(?:\[.*?\])*)\]' # key: [value]
|
8
|
-
FIND = r'\{.*?\}|\[.*?\]' # {} or []
|
9
4
|
# http(s)://user:pass@host:port
|
10
5
|
PROXY = r'^(?:(?P<scheme>https?:\/\/))?(?:(?P<username>[^:@]+):(?P<password>[^@]+)@)?(?P<host>[^:\/]+)(?::(?P<port>\d+))?$'
|
11
6
|
|
pyaterochka_api/manager.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
from .api import PyaterochkaAPI
|
2
|
-
from enum import Enum
|
3
2
|
import re
|
4
3
|
import json
|
5
4
|
from io import BytesIO
|
@@ -263,17 +262,3 @@ class Pyaterochka:
|
|
263
262
|
image.name = f'{url.split("/")[-1]}.{response_type.split("/")[-1]}'
|
264
263
|
|
265
264
|
return image
|
266
|
-
|
267
|
-
@beartype
|
268
|
-
async def get_config(self) -> dict:
|
269
|
-
"""
|
270
|
-
Asynchronously retrieves the configuration from the hardcoded JavaScript file.
|
271
|
-
|
272
|
-
Args:
|
273
|
-
debug (bool, optional): Whether to print debug information. Defaults to False.
|
274
|
-
|
275
|
-
Returns:
|
276
|
-
dict: A dictionary representing the configuration if the request is successful, error otherwise.
|
277
|
-
"""
|
278
|
-
|
279
|
-
return await self.api.download_config(config_url=self.HARDCODE_JS_CONFIG)
|
pyaterochka_api/tools.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
from .enums import Patterns
|
2
2
|
import os
|
3
3
|
import re
|
4
|
-
from
|
4
|
+
from beartype import beartype
|
5
|
+
import logging
|
5
6
|
|
7
|
+
@beartype
|
6
8
|
def get_env_proxy() -> str | None:
|
7
9
|
"""
|
8
10
|
Получает прокси из переменных окружения.
|
@@ -11,7 +13,8 @@ def get_env_proxy() -> str | None:
|
|
11
13
|
proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy") or os.environ.get("HTTP_PROXY") or os.environ.get("http_proxy")
|
12
14
|
return proxy if proxy else None
|
13
15
|
|
14
|
-
|
16
|
+
@beartype
|
17
|
+
def parse_proxy(proxy_str: str | None, trust_env: bool, logger: logging.Logger) -> dict | None:
|
15
18
|
logger.debug(f"Parsing proxy string: {proxy_str}")
|
16
19
|
|
17
20
|
if not proxy_str:
|
@@ -53,69 +56,3 @@ def parse_proxy(proxy_str: str | None, trust_env: bool, logger) -> dict | None:
|
|
53
56
|
|
54
57
|
logger.info(f"Proxy parsed as regex")
|
55
58
|
return proxy_dict
|
56
|
-
|
57
|
-
async def _parse_match(match: str, progress_bar: tqdm | None = None) -> dict:
|
58
|
-
result = {}
|
59
|
-
|
60
|
-
if progress_bar:
|
61
|
-
progress_bar.set_description("Parsing strings")
|
62
|
-
|
63
|
-
# Парсинг строк
|
64
|
-
string_matches = re.finditer(Patterns.STR.value, match)
|
65
|
-
for m in string_matches:
|
66
|
-
key, value = m.group(1), m.group(2)
|
67
|
-
result[key] = value.replace('\"', '"').replace('\\', '\\')
|
68
|
-
|
69
|
-
if progress_bar:
|
70
|
-
progress_bar.update(1)
|
71
|
-
progress_bar.set_description("Parsing dictionaries")
|
72
|
-
|
73
|
-
# Парсинг словарей
|
74
|
-
dict_matches = re.finditer(Patterns.DICT.value, match)
|
75
|
-
for m in dict_matches:
|
76
|
-
key, value = m.group(1), m.group(2)
|
77
|
-
if not re.search(Patterns.STR.value, value):
|
78
|
-
result[key] = await _parse_match(value, progress_bar)
|
79
|
-
|
80
|
-
if progress_bar:
|
81
|
-
progress_bar.update(1)
|
82
|
-
progress_bar.set_description("Parsing lists")
|
83
|
-
|
84
|
-
# Парсинг списков
|
85
|
-
list_matches = re.finditer(Patterns.LIST.value, match)
|
86
|
-
for m in list_matches:
|
87
|
-
key, value = m.group(1), m.group(2)
|
88
|
-
if not re.search(Patterns.STR.value, value):
|
89
|
-
result[key] = [await _parse_match(item.group(0), progress_bar) for item in re.finditer(Patterns.FIND.value, value)]
|
90
|
-
|
91
|
-
if progress_bar:
|
92
|
-
progress_bar.update(1)
|
93
|
-
|
94
|
-
return result
|
95
|
-
|
96
|
-
async def parse_js(js_code: str, debug: bool, logger) -> dict | None:
|
97
|
-
"""
|
98
|
-
Парсит JavaScript-код и извлекает данные из переменной "n".
|
99
|
-
|
100
|
-
:param js_code: JS-код в виде строки.
|
101
|
-
:return: Распарсенные данные в виде словаря или None.
|
102
|
-
"""
|
103
|
-
matches = re.finditer(Patterns.JS.value, js_code)
|
104
|
-
match_list = list(matches)
|
105
|
-
|
106
|
-
logger.debug(f'Found matches {len(match_list)}')
|
107
|
-
|
108
|
-
progress_bar = tqdm(total=33, desc="Parsing JS", position=0) if debug else None
|
109
|
-
|
110
|
-
if match_list and len(match_list) >= 1:
|
111
|
-
logger.info('Starting to parse match')
|
112
|
-
result = await _parse_match(match_list[1].group(0), progress_bar)
|
113
|
-
|
114
|
-
if progress_bar:
|
115
|
-
progress_bar.close()
|
116
|
-
logger.info('Complited parsing match')
|
117
|
-
return result
|
118
|
-
else:
|
119
|
-
if progress_bar:
|
120
|
-
progress_bar.close()
|
121
|
-
raise Exception("N variable in JS code not found")
|
@@ -1,15 +1,18 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pyaterochka_api
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: A Python API client for Pyaterochka store catalog
|
5
|
-
Home-page: https://github.com/Open-Inflation/pyaterochka_api
|
6
5
|
Author: Miskler
|
6
|
+
License-Expression: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/Open-Inflation/pyaterochka_api
|
8
|
+
Project-URL: Repository, https://github.com/Open-Inflation/pyaterochka_api
|
9
|
+
Project-URL: Documentation, https://open-inflation.github.io/pyaterochka_api/
|
10
|
+
Keywords: api,pyaterochka,store,catalog
|
7
11
|
Classifier: Programming Language :: Python :: 3
|
8
12
|
Classifier: Programming Language :: Python :: 3.10
|
9
13
|
Classifier: Programming Language :: Python :: 3.11
|
10
14
|
Classifier: Programming Language :: Python :: 3.12
|
11
15
|
Classifier: Programming Language :: Python :: 3.13
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
13
16
|
Classifier: Operating System :: Microsoft :: Windows
|
14
17
|
Classifier: Operating System :: POSIX :: Linux
|
15
18
|
Classifier: Intended Audience :: Developers
|
@@ -24,21 +27,11 @@ Requires-Dist: aiohttp
|
|
24
27
|
Requires-Dist: camoufox[geoip]
|
25
28
|
Requires-Dist: beartype
|
26
29
|
Requires-Dist: fake-useragent
|
27
|
-
Requires-Dist: tqdm
|
28
30
|
Provides-Extra: tests
|
29
31
|
Requires-Dist: pytest; extra == "tests"
|
30
32
|
Requires-Dist: pytest-asyncio; extra == "tests"
|
31
33
|
Requires-Dist: pytest-typed-schema-shot; extra == "tests"
|
32
|
-
Dynamic: author
|
33
|
-
Dynamic: classifier
|
34
|
-
Dynamic: description
|
35
|
-
Dynamic: description-content-type
|
36
|
-
Dynamic: home-page
|
37
34
|
Dynamic: license-file
|
38
|
-
Dynamic: provides-extra
|
39
|
-
Dynamic: requires-dist
|
40
|
-
Dynamic: requires-python
|
41
|
-
Dynamic: summary
|
42
35
|
|
43
36
|
# Pyaterochka API *(not official / не официальный)*
|
44
37
|
|
@@ -116,10 +109,6 @@ async def main():
|
|
116
109
|
news = await API.get_news(limit=5)
|
117
110
|
print(f"News output: {news!s:.100s}...\n")
|
118
111
|
|
119
|
-
# RUS: Выводит основной конфиг сайта (очень долгая функция, рекомендую сохранять в файл и переиспользовать)
|
120
|
-
# ENG: Outputs the main config of the site (large function, recommend to save in a file and re-use it)
|
121
|
-
print(f"Main config: {await API.get_config()!s:.100s}...\n")
|
122
|
-
|
123
112
|
# RUS: Если требуется, можно настроить вывод логов в консоль
|
124
113
|
# ENG: If required, you can configure the output of logs in the console
|
125
114
|
API.debug = True
|
@@ -0,0 +1,10 @@
|
|
1
|
+
pyaterochka_api/__init__.py,sha256=-rsfMGZCT77fmjEHQi4cyyErAZrsQQdbuaqlbOt-a08,130
|
2
|
+
pyaterochka_api/api.py,sha256=EsMsyNaFEWCif3iRK6GJCT1o4QHDY72rBgjfeu1BTSU,7204
|
3
|
+
pyaterochka_api/enums.py,sha256=H864-J-1VWiEsRxNaGLAkcH2kq92FEj7fhuxwqfRVaw,285
|
4
|
+
pyaterochka_api/manager.py,sha256=Wuugv3VuYOInyCBMfGyCCEn6lf4zdr3LwwZk1iT4R44,10261
|
5
|
+
pyaterochka_api/tools.py,sha256=fxBa-FKJfbKmup2ZrDTF1u3qyawiHODjG9ofq5ZCG0w,2178
|
6
|
+
pyaterochka_api-0.2.0.dist-info/licenses/LICENSE,sha256=Ee_P5XQUYoJuffzRL24j4GWpqgoWphUOKswpB2f9HcQ,1071
|
7
|
+
pyaterochka_api-0.2.0.dist-info/METADATA,sha256=7-bQZJW9MFgReD8e2hX1x0p3nVN2YcGixP7PrrpZUQc,9432
|
8
|
+
pyaterochka_api-0.2.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
9
|
+
pyaterochka_api-0.2.0.dist-info/top_level.txt,sha256=NTJa4yZBzfmC9B5FQuFETD9ngGd_KYZ3Hjfw_33aDTE,16
|
10
|
+
pyaterochka_api-0.2.0.dist-info/RECORD,,
|
@@ -1,10 +0,0 @@
|
|
1
|
-
pyaterochka_api/__init__.py,sha256=6XnalFPUumYqDJFyXw2puejJ612o-D1tYjZ_IjQ7Hx0,108
|
2
|
-
pyaterochka_api/api.py,sha256=KEr28n1aH69cVo0ztHCgB4ANVVZ-CezLWYTiPpfAmoc,7793
|
3
|
-
pyaterochka_api/enums.py,sha256=JnX4JiHzXyRo4se8sCFx0LyqcKlXXED0VcA0xI7r_ZI,621
|
4
|
-
pyaterochka_api/manager.py,sha256=yQw0njGCsropysT-_siuyNxXNpX-VJKOcVD8b-aV48I,10765
|
5
|
-
pyaterochka_api/tools.py,sha256=xFOThNRClX4u0cMfmQ5fVQKLM2Fn-rBAekzo_yBvRnQ,4447
|
6
|
-
pyaterochka_api-0.1.9.1.dist-info/licenses/LICENSE,sha256=Ee_P5XQUYoJuffzRL24j4GWpqgoWphUOKswpB2f9HcQ,1071
|
7
|
-
pyaterochka_api-0.1.9.1.dist-info/METADATA,sha256=ZQOhWOFzmF9nr2LfutfAVfgkzorB_OYb4sJmtpHtruI,9857
|
8
|
-
pyaterochka_api-0.1.9.1.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
9
|
-
pyaterochka_api-0.1.9.1.dist-info/top_level.txt,sha256=NTJa4yZBzfmC9B5FQuFETD9ngGd_KYZ3Hjfw_33aDTE,16
|
10
|
-
pyaterochka_api-0.1.9.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|