pyaterochka-api 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Open Inflation
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.
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyaterochka_api
3
+ Version: 0.1.0
4
+ Summary: A Python API client for Pyaterochka store catalog
5
+ Home-page: https://github.com/Open-Inflation/pyaterochka_api
6
+ Author: Miskler
7
+ License: UNKNOWN
8
+ Platform: UNKNOWN
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+
16
+ # Pyaterochka API *(not official / не официальный)*
17
+
18
+ Pyaterochka (Пятёрочка) - https://5ka.ru/
19
+
20
+ ### Usage / Использование:
21
+ ```py
22
+ import pyaterochka_api
23
+ import asyncio
24
+
25
+
26
+ async def main():
27
+ # RUS: Выводит список всех категорий на сайте
28
+ # ENG: Outputs a list of all categories on the site
29
+ catalog = await pyaterochka_api.categories_list(subcategories=True)
30
+ print(f"Categories list output: {catalog!s:.100s}...\n")
31
+
32
+ # RUS: Выводит список всех товаров выбранной категории (ограничение 100 элементов, если превышает - запрашивайте через дополнительные страницы)
33
+ # ENG: Outputs a list of all items in the selected category (limiting to 100 elements, if exceeds - request through additional pages)
34
+ # Страниц не сущетвует, использовать желаемый лимит (до 499) / Pages do not exist, use the desired limit (up to 499)
35
+ print(f"Items list output: {await pyaterochka_api.products_list(catalog[0]['id'], limit=5)!s:.100s}...\n")
36
+
37
+ # RUS: Выводит основной конфиг сайта (очень долгая функция, рекомендую сохранять в файл и переиспользовать)
38
+ # ENG: Outputs the main config of the site (large function, recommend to save in a file and re-use it)
39
+ # RUS: Если требуется, можно настроить вывод логов в консоль
40
+ # ENG: If required, you can configure the output of logs in the console
41
+ print(f"Main config: {await pyaterochka_api.get_config(debug=True)!s:.100s}...\n")
42
+
43
+
44
+ if __name__ == '__main__':
45
+ asyncio.run(main())
46
+ ```
47
+
48
+ ### Report / Обратная связь
49
+
50
+ If you have any problems using it /suggestions, do not hesitate to write to the [project's GitHub](https://github.com/Open-Inflation/pyaterochka_api/issues)!
51
+
52
+ Если у вас возникнут проблемы в использовании / пожелания, не стесняйтесь писать на [GitHub проекта](https://github.com/Open-Inflation/pyaterochka_api/issues)!
53
+
54
+
@@ -0,0 +1,37 @@
1
+ # Pyaterochka API *(not official / не официальный)*
2
+
3
+ Pyaterochka (Пятёрочка) - https://5ka.ru/
4
+
5
+ ### Usage / Использование:
6
+ ```py
7
+ import pyaterochka_api
8
+ import asyncio
9
+
10
+
11
+ async def main():
12
+ # RUS: Выводит список всех категорий на сайте
13
+ # ENG: Outputs a list of all categories on the site
14
+ catalog = await pyaterochka_api.categories_list(subcategories=True)
15
+ print(f"Categories list output: {catalog!s:.100s}...\n")
16
+
17
+ # RUS: Выводит список всех товаров выбранной категории (ограничение 100 элементов, если превышает - запрашивайте через дополнительные страницы)
18
+ # ENG: Outputs a list of all items in the selected category (limiting to 100 elements, if exceeds - request through additional pages)
19
+ # Страниц не сущетвует, использовать желаемый лимит (до 499) / Pages do not exist, use the desired limit (up to 499)
20
+ print(f"Items list output: {await pyaterochka_api.products_list(catalog[0]['id'], limit=5)!s:.100s}...\n")
21
+
22
+ # RUS: Выводит основной конфиг сайта (очень долгая функция, рекомендую сохранять в файл и переиспользовать)
23
+ # ENG: Outputs the main config of the site (large function, recommend to save in a file and re-use it)
24
+ # RUS: Если требуется, можно настроить вывод логов в консоль
25
+ # ENG: If required, you can configure the output of logs in the console
26
+ print(f"Main config: {await pyaterochka_api.get_config(debug=True)!s:.100s}...\n")
27
+
28
+
29
+ if __name__ == '__main__':
30
+ asyncio.run(main())
31
+ ```
32
+
33
+ ### Report / Обратная связь
34
+
35
+ If you have any problems using it /suggestions, do not hesitate to write to the [project's GitHub](https://github.com/Open-Inflation/pyaterochka_api/issues)!
36
+
37
+ Если у вас возникнут проблемы в использовании / пожелания, не стесняйтесь писать на [GitHub проекта](https://github.com/Open-Inflation/pyaterochka_api/issues)!
@@ -0,0 +1,3 @@
1
+ from .manager import PurchaseMode, categories_list, products_list, get_config
2
+
3
+ __all__ = ['PurchaseMode', 'categories_list', 'products_list', 'get_config']
@@ -0,0 +1,116 @@
1
+ import aiohttp
2
+ from fake_useragent import UserAgent
3
+ from enum import Enum
4
+ import re
5
+ from tqdm.asyncio import tqdm
6
+
7
+
8
+ class Patterns(Enum):
9
+ JS = r'\s*let\s+n\s*=\s*({.*});\s*'
10
+ STR = r'(\w+)\s*:\s*"([^"\\]*(?:\\.[^"\\]*)*)"'
11
+ DICT = r'(\w+)\s*:\s*{(.*?)}'
12
+ LIST = r'(\w+)\s*:\s*\[([^\[\]]*(?:\[.*?\])*)\]'
13
+ FIND = r'\{.*?\}|\[.*?\]'
14
+
15
+
16
+
17
+ async def main_fetch(url: str, debug: bool = False, is_json: bool = True) -> tuple[bool, dict | None]:
18
+ async with aiohttp.ClientSession() as session:
19
+ if debug:
20
+ print(f"Requesting \"{url}\"...", flush=True)
21
+
22
+ async with session.get(
23
+ url=url,
24
+ headers={"User-Agent": UserAgent().random},
25
+ ) as response:
26
+ if debug:
27
+ print(f"Response status: {response.status}", flush=True)
28
+
29
+ if response.status == 200: # 200 OK
30
+ if debug:
31
+ print("Correct response", flush=True)
32
+ return True, await response.json() if is_json else await response.text()
33
+ elif response.status == 403: # 403 Forbidden (сервер воспринял как бота)
34
+ if debug:
35
+ print("Anti-bot protection. Use Russia IP address and try again.", flush=True)
36
+ return False, None
37
+ else:
38
+ if debug:
39
+ print(f"Please, create issue on GitHub", flush=True)
40
+ raise Exception(f"Response status: {response.status} (unknown error/status code)")
41
+
42
+ async def download_hardcode_config(config_url: str, debug: bool = False) -> dict | None:
43
+ is_success, js_code = await main_fetch(url=config_url, debug=debug, is_json=False)
44
+
45
+ if not is_success:
46
+ if debug:
47
+ print("Failed to fetch JS code")
48
+ return None
49
+ elif debug:
50
+ print("JS code fetched successfully")
51
+
52
+ # Регулярное выражение для извлечения определения переменной n
53
+ matches = re.finditer(Patterns.JS.value, js_code)
54
+
55
+ match_list = list(matches)
56
+ if debug:
57
+ print(f"Found matches {len(match_list)}")
58
+
59
+ if debug:
60
+ progress_bar = tqdm(total=33, desc="Parsing JS", position=0) # примерно 33 операции
61
+
62
+ async def parse_match(match: str) -> dict:
63
+ result = {}
64
+
65
+ if debug:
66
+ # Обновление описания прогресса
67
+ progress_bar.set_description("Parsing strings")
68
+
69
+ # Парсинг строк
70
+ string_matches = re.finditer(Patterns.STR.value, match)
71
+ for m in string_matches:
72
+ key, value = m.group(1), m.group(2)
73
+ result[key] = value.replace('\"', '"').replace('\\\\', '\\')
74
+
75
+ if debug:
76
+ progress_bar.update(1)
77
+ # Обновление описания прогресса
78
+ progress_bar.set_description("Parsing dictionaries")
79
+
80
+ # Парсинг словарей
81
+ dict_matches = re.finditer(Patterns.DICT.value, match)
82
+ for m in dict_matches:
83
+ key, value = m.group(1), m.group(2)
84
+ if not re.search(Patterns.STR.value, value):
85
+ result[key] = await parse_match(value)
86
+
87
+ if debug:
88
+ progress_bar.update(1)
89
+ # Обновление описания прогресса
90
+ progress_bar.set_description("Parsing lists")
91
+
92
+ # Парсинг списков
93
+ list_matches = re.finditer(Patterns.LIST.value, match)
94
+ for m in list_matches:
95
+ key, value = m.group(1), m.group(2)
96
+ if not re.search(Patterns.STR.value, value):
97
+ result[key] = [await parse_match(item.group(0)) for item in re.finditer(Patterns.FIND.value, value)]
98
+
99
+ if debug:
100
+ # Обновление прогресса
101
+ progress_bar.update(1)
102
+
103
+ return result
104
+
105
+ if match_list and len(match_list) >= 1: # нужная переменная идет второй из трех
106
+ if debug:
107
+ print("Starting to parse match")
108
+ result = await parse_match(match_list[1].group(0))
109
+ if debug:
110
+ progress_bar.close()
111
+ return result
112
+ else:
113
+ if debug:
114
+ progress_bar.close()
115
+ raise Exception("N variable in JS code not found")
116
+
@@ -0,0 +1,82 @@
1
+ from .api import main_fetch, download_hardcode_config
2
+ from enum import Enum
3
+
4
+ CATALOG_URL = "https://5d.5ka.ru/api/catalog/v2/stores"
5
+ HARDCODE_JS_CONFIG = "https://prod-cdn.5ka.ru/scripts/main.a0c039ea81eb8cf69492.js" # TODO сделать не хардкодным имя файла
6
+
7
+ class PurchaseMode(Enum):
8
+ STORE = "store"
9
+ DELIVERY = "delivery"
10
+
11
+
12
+ async def categories_list(
13
+ subcategories: bool = False,
14
+ mode: PurchaseMode = PurchaseMode.STORE,
15
+ sap_code_store_id: str = "Y232",
16
+ debug: bool = False
17
+ ) -> dict | None:
18
+ """
19
+ Asynchronously retrieves a list of categories from the Pyaterochka API.
20
+
21
+ Args:
22
+ subcategories (bool, optional): Whether to include subcategories in the response. Defaults to False.
23
+ mode (PurchaseMode, optional): The purchase mode to use. Defaults to PurchaseMode.STORE.
24
+ sap_code_store_id (str, optional): The store ID (official name in API is "sap_code") to use. Defaults to "Y232". This lib not support search ID stores.
25
+ debug (bool, optional): Whether to print debug information. Defaults to False.
26
+
27
+ Returns:
28
+ dict | None: A dictionary representing the categories list if the request is successful, None otherwise.
29
+
30
+ Raises:
31
+ Exception: If the response status is not 200 (OK) or 403 (Forbidden / Anti-bot).
32
+ """
33
+
34
+ request_url = f"{CATALOG_URL}/{sap_code_store_id}/categories?mode={mode.value}&include_subcategories={1 if subcategories else 0}"
35
+ is_success, response = await main_fetch(url=request_url, debug=debug)
36
+ return response
37
+
38
+
39
+ async def products_list(
40
+ category_id: int,
41
+ mode: PurchaseMode = PurchaseMode.STORE,
42
+ sap_code_store_id: str = "Y232",
43
+ limit: int = 30,
44
+ debug: bool = False
45
+ ) -> dict | None:
46
+ """
47
+ Asynchronously retrieves a list of products from the Pyaterochka API for a given category.
48
+
49
+ Args:
50
+ category_id (int): The ID of the category.
51
+ mode (PurchaseMode, optional): The purchase mode to use. Defaults to PurchaseMode.STORE.
52
+ sap_code_store_id (str, optional): The store ID (official name in API is "sap_code") to use. Defaults to "Y232". This lib not support search ID stores.
53
+ limit (int, optional): The maximum number of products to retrieve. Defaults to 30. Must be between 1 and 499.
54
+
55
+ Returns:
56
+ dict | None: A dictionary representing the products list if the request is successful, None otherwise.
57
+
58
+ Raises:
59
+ ValueError: If the limit is not between 1 and 499.
60
+ Exception: If the response status is not 200 (OK) or 403 (Forbidden / Anti-bot).
61
+ """
62
+
63
+ if limit < 1 or limit >= 500:
64
+ raise ValueError("Limit must be between 1 and 499")
65
+
66
+ request_url = f"{CATALOG_URL}/{sap_code_store_id}/categories/{category_id}/products?mode={mode.value}&limit={limit}"
67
+ is_success, response = await main_fetch(url=request_url, debug=debug)
68
+ return response
69
+
70
+
71
+ async def get_config(debug: bool = False) -> list | None:
72
+ """
73
+ Asynchronously retrieves the configuration from the hardcoded JavaScript file.
74
+
75
+ Args:
76
+ debug (bool, optional): Whether to print debug information. Defaults to False.
77
+
78
+ Returns:
79
+ list | None: A list representing the configuration if the request is successful, None otherwise.
80
+ """
81
+
82
+ return await download_hardcode_config(config_url=HARDCODE_JS_CONFIG, debug=debug)
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyaterochka-api
3
+ Version: 0.1.0
4
+ Summary: A Python API client for Pyaterochka store catalog
5
+ Home-page: https://github.com/Open-Inflation/pyaterochka_api
6
+ Author: Miskler
7
+ License: UNKNOWN
8
+ Platform: UNKNOWN
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+
16
+ # Pyaterochka API *(not official / не официальный)*
17
+
18
+ Pyaterochka (Пятёрочка) - https://5ka.ru/
19
+
20
+ ### Usage / Использование:
21
+ ```py
22
+ import pyaterochka_api
23
+ import asyncio
24
+
25
+
26
+ async def main():
27
+ # RUS: Выводит список всех категорий на сайте
28
+ # ENG: Outputs a list of all categories on the site
29
+ catalog = await pyaterochka_api.categories_list(subcategories=True)
30
+ print(f"Categories list output: {catalog!s:.100s}...\n")
31
+
32
+ # RUS: Выводит список всех товаров выбранной категории (ограничение 100 элементов, если превышает - запрашивайте через дополнительные страницы)
33
+ # ENG: Outputs a list of all items in the selected category (limiting to 100 elements, if exceeds - request through additional pages)
34
+ # Страниц не сущетвует, использовать желаемый лимит (до 499) / Pages do not exist, use the desired limit (up to 499)
35
+ print(f"Items list output: {await pyaterochka_api.products_list(catalog[0]['id'], limit=5)!s:.100s}...\n")
36
+
37
+ # RUS: Выводит основной конфиг сайта (очень долгая функция, рекомендую сохранять в файл и переиспользовать)
38
+ # ENG: Outputs the main config of the site (large function, recommend to save in a file and re-use it)
39
+ # RUS: Если требуется, можно настроить вывод логов в консоль
40
+ # ENG: If required, you can configure the output of logs in the console
41
+ print(f"Main config: {await pyaterochka_api.get_config(debug=True)!s:.100s}...\n")
42
+
43
+
44
+ if __name__ == '__main__':
45
+ asyncio.run(main())
46
+ ```
47
+
48
+ ### Report / Обратная связь
49
+
50
+ If you have any problems using it /suggestions, do not hesitate to write to the [project's GitHub](https://github.com/Open-Inflation/pyaterochka_api/issues)!
51
+
52
+ Если у вас возникнут проблемы в использовании / пожелания, не стесняйтесь писать на [GitHub проекта](https://github.com/Open-Inflation/pyaterochka_api/issues)!
53
+
54
+
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ pyaterochka_api/__init__.py
5
+ pyaterochka_api/api.py
6
+ pyaterochka_api/manager.py
7
+ pyaterochka_api.egg-info/PKG-INFO
8
+ pyaterochka_api.egg-info/SOURCES.txt
9
+ pyaterochka_api.egg-info/dependency_links.txt
10
+ pyaterochka_api.egg-info/requires.txt
11
+ pyaterochka_api.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ aiohttp
2
+ enum
3
+ fake-useragent
4
+ tqdm
@@ -0,0 +1 @@
1
+ pyaterochka_api
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,24 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name='pyaterochka_api',
5
+ version='0.1.0',
6
+ packages=find_packages(),
7
+ install_requires=[
8
+ 'aiohttp',
9
+ 'fake-useragent',
10
+ 'enum',
11
+ 'tqdm'
12
+ ],
13
+ author='Miskler',
14
+ description='A Python API client for Pyaterochka store catalog',
15
+ long_description=open('README.md').read(),
16
+ long_description_content_type='text/markdown',
17
+ url='https://github.com/Open-Inflation/pyaterochka_api',
18
+ classifiers=[
19
+ 'Programming Language :: Python :: 3',
20
+ 'License :: OSI Approved :: MIT License',
21
+ 'Operating System :: OS Independent',
22
+ ],
23
+ python_requires='>=3.10',
24
+ )