|
1 | 1 | import re |
2 | 2 | import time |
3 | | -from typing import Optional |
| 3 | +import random |
| 4 | +from tqdm import tqdm |
| 5 | +from typing import Optional, Union, Literal |
4 | 6 | from playwright.sync_api import sync_playwright |
5 | 7 | from playwright._impl._errors import TimeoutError |
6 | | -from tqdm import tqdm |
7 | 8 | from helpers.parsers_helpers import open_scroller |
| 9 | +from getuseragent import UserAgent |
8 | 10 |
|
9 | 11 | from parsers_dataclasses import WildberriesProduct |
10 | 12 |
|
11 | 13 |
|
12 | 14 | class Wildberries: |
13 | 15 |
|
14 | | - __version__ = "0.1.1" |
| 16 | + __version__ = "0.1.2" |
15 | 17 |
|
16 | 18 | def __init__(self): |
17 | | - """version = 0.1.1""" |
| 19 | + """version = 0.2""" |
18 | 20 | self.page = None |
19 | | - self.pages_links: list[str | Optional[int]] = [] |
20 | | - self.goods_links: list[str] = [] |
| 21 | + self.goods_links: set[str] = set() |
21 | 22 | self.parsing_result: list[dict] = [] |
22 | | - self.base_link = "https://www.wildberries.ru/" |
| 23 | + self.base_link = "https://www.wildberries.ru" |
23 | 24 | self.scroller = open_scroller() |
24 | | - self.PAGE_SCROLL_SIZE = 15 |
25 | | - |
26 | | - def _get_goods_links(self, page_link: str) -> list[str]: |
27 | | - """Нахождение всех ссылок на товары на всей странице |
28 | | - version = 0.1 |
29 | | - """ |
30 | | - self.page.goto(page_link) |
31 | | - self.page.wait_for_selector(".catalog-page") |
32 | | - for _ in range(self.PAGE_SCROLL_SIZE): # прокрутка до конца страницы |
33 | | - self.page.evaluate(self.scroller) |
34 | | - time.sleep(1) # остановка для полной загрузки страницы |
35 | | - elements = self.page.query_selector_all('div.product-card-list a[draggable="false"]') |
36 | | - elements = list(map(lambda el: el.get_attribute('href'), elements)) |
37 | | - return elements |
38 | 25 |
|
39 | | - def _get_next_page_link(self, page_link: str) -> None: |
40 | | - """Получение ссылки на следующую страницу с текущей |
41 | | - version = 0.1 |
42 | | - """ |
43 | | - self.page.goto(page_link) |
44 | | - self.page.wait_for_selector(".catalog-page") |
45 | | - for _ in range(self.PAGE_SCROLL_SIZE): # прокрутка до конца страницы |
46 | | - self.page.evaluate(self.scroller) |
47 | | - try: |
48 | | - self.page.wait_for_selector('text="Следующая страница"', timeout=3_000) |
49 | | - element = self.page.get_by_text(text="Следующая страница") |
50 | | - self.pages_links.append(element.get_attribute('href')) |
51 | | - except TimeoutError: # если страницы для поиска закончились |
52 | | - self.pages_links.append(0) |
53 | | - |
54 | | - def _get_all_pages_links(self, number_of_pages): |
55 | | - """Добавление в список всех ссылок на страницы |
56 | | - version = 0.1 |
| 26 | + def _get_goods_links(self, number_of_goods: Union[Literal['max'], int] = 10) -> None: |
| 27 | + """Сбор всех ссылок на товары. Либо собирается максимальное количество товаров, либо явно |
| 28 | + указанное количество (по умолчанию=10). |
| 29 | + version = 0.2 |
57 | 30 | """ |
58 | | - self.pages_links.append(self.page.url) # ссылка на первую страницу каталога |
59 | | - if number_of_pages > 1: |
60 | | - for i in range(number_of_pages - 1): |
61 | | - page_link = self.pages_links[i] |
62 | | - if isinstance(page_link, str): |
63 | | - self._get_next_page_link(page_link) # получение ссылки на следующую страницу |
64 | | - else: # если страницы для поиска закончились (меньше заданного кол-ва) |
65 | | - self.pages_links.pop() # удаление нуля в конце |
| 31 | + tag_href = 'href' |
| 32 | + href_next_page = "Следующая страница" |
| 33 | + selector_next_page = f':text("{href_next_page}")' |
| 34 | + self.page.wait_for_selector('.catalog-page') |
| 35 | + while not self.page.is_visible('.custom-slider__list'): # пока не видно блок с рекомендуемыми товарами |
| 36 | + self.page.evaluate(self.scroller) # прокрутка до конца страницы |
| 37 | + links = self.page.query_selector_all('div.product-card-list a[draggable="false"]') |
| 38 | + links = set(map(lambda link: link.get_attribute(tag_href), links)) |
| 39 | + if number_of_goods == 'max': |
| 40 | + self.goods_links.update(links) |
| 41 | + if self.page.is_visible(selector_next_page): |
| 42 | + self.page.goto(self.page.get_by_text(text=href_next_page).get_attribute(tag_href)) |
| 43 | + self._get_goods_links(number_of_goods) |
| 44 | + else: |
| 45 | + for link in links: |
| 46 | + if len(self.goods_links) < number_of_goods: |
| 47 | + self.goods_links.add(link) |
| 48 | + else: |
66 | 49 | break |
| 50 | + if len(self.goods_links) < number_of_goods and self.page.is_visible(selector_next_page): |
| 51 | + self.page.goto(self.page.get_by_text(text=href_next_page).get_attribute(tag_href)) |
| 52 | + self._get_goods_links(number_of_goods) |
67 | 53 |
|
68 | 54 | @staticmethod |
69 | 55 | def __parse_seller_descr(descr: str) -> tuple: |
@@ -115,25 +101,23 @@ def _get_good_descr(self, page_link: str) -> None: |
115 | 101 | # создание итогового словаря по товару |
116 | 102 | self.parsing_result.append(product.dict()) |
117 | 103 |
|
118 | | - def find_all_goods(self, keyword: str, number_of_pages: int) -> None: |
| 104 | + def find_all_goods(self, keyword: str, number_of_goods: Union[Literal['max'], int] = 10) -> None: |
119 | 105 | """Поиск всех ссылок на товары по ключевому слову |
120 | | - version = 0.1.1 |
| 106 | + version = 0.1.2 |
121 | 107 | """ |
122 | 108 | with sync_playwright() as playwright: |
123 | | - browser = playwright.chromium.launch(headless=True) |
124 | | - context = browser.new_context() |
| 109 | + browser = playwright.chromium.launch(headless=True, args=['--disable-blink-features=AutomationControlled']) |
| 110 | + context = browser.new_context(user_agent=UserAgent().Random()) |
125 | 111 | self.page = context.new_page() |
126 | | - self.page.goto(self.base_link, timeout=1000, wait_until="load") # открытие ссылки сайта |
127 | | - self.page.wait_for_selector('input[id="searchInput"]') # поиск поля input |
128 | | - search_input = self.page.locator('input[id="searchInput"]') # помещение курсора в input |
129 | | - search_input.fill(keyword, timeout=300) |
130 | | - search_input.press('Enter') # запуск процесса поиска |
| 112 | + self.page.goto(self.base_link) |
| 113 | + time.sleep(1) |
| 114 | + input_field = self.page.get_by_placeholder("Найти на Wildberries").first |
| 115 | + input_field.type(keyword, delay=random.uniform(.1, .5)) |
| 116 | + input_field.press(key='Enter', delay=random.randint(100, 500)) # запуск процесса поиска |
131 | 117 | try: # проверка, что по запросу ничего не найдено |
132 | 118 | self.page.wait_for_selector('text="Попробуйте поискать по-другому или сократить запрос"', timeout=1_000) |
133 | 119 | except TimeoutError: # если по запросу найдены товары |
134 | | - self._get_all_pages_links(number_of_pages) # получение ссылок на все страницы по поиску |
135 | | - for link in self.pages_links: |
136 | | - self.goods_links.extend(self._get_goods_links(link)) |
| 120 | + self._get_goods_links(number_of_goods) # получение ссылок на все страницы по поиску |
137 | 121 | finally: |
138 | 122 | browser.close() |
139 | 123 |
|
|
0 commit comments