Skip to content

Commit c4067b4

Browse files
committed
Added "html_parsing/ranobehub_org/find_all_evolutions/"
1 parent dc0d23c commit c4067b4

File tree

2 files changed

+460
-0
lines changed

2 files changed

+460
-0
lines changed
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
__author__ = "ipetrash"
5+
6+
7+
import os
8+
import time
9+
10+
# pip install selenium
11+
from selenium import webdriver
12+
from selenium.webdriver.common.by import By
13+
from selenium.webdriver.firefox.options import Options
14+
from selenium.common.exceptions import NoSuchElementException
15+
16+
from utils import Tree, get_path_of_classes
17+
18+
19+
# TODO: Возможны какие-то варианты классов, что будут недоступны в прокачке т.к. уровень ещё недостаточен
20+
# "Безработный >> Сорокалетний девственник >> Fucking Slave >> Dungeon Master >> Boss of the gym"
21+
22+
URL = "https://ranobehub.org/user/19803/evolution"
23+
24+
SLEEP_SECS = 5
25+
26+
# NOTE: Вместо реализации с авторизацией используется профиль браузера, где сайт уже авторизован
27+
PROFILE_DIRECTORY = os.path.expandvars(
28+
r"%AppData%\Mozilla\Firefox\Profiles\6roves5p.selenium"
29+
)
30+
31+
options = Options()
32+
options.ignore_local_proxy_environment_variables() # NOTE: No connect to geckodriver
33+
options.profile = PROFILE_DIRECTORY
34+
35+
"""
36+
Алгоритм:
37+
1. Выбор первого класса.
38+
Путь классов: пустой
39+
Доступны кнопки:
40+
* Подтвердить выбор <имя класса>
41+
Ориентир: текст "Выбор класса"
42+
2. Выбор подкласса
43+
Путь классов: класса из шага 1.
44+
Доступны кнопки:
45+
* Сбросить
46+
* Подтвердить <имя подкласса>
47+
Ориентир: текст "Выбор подкласса"
48+
3. Выбор подкласса
49+
Если найден текст "Конец развития"
50+
Иначе, шаг 2
51+
"""
52+
53+
54+
def get_class_name(evolution_el) -> str:
55+
current_evolution_el = evolution_el.find_element(
56+
By.CSS_SELECTOR,
57+
".tns-slide-active .evolution-title"
58+
)
59+
return current_evolution_el.text.splitlines()[0]
60+
61+
62+
# TODO: Использование автоматического скачивания драйвера браузера через webdriver_manager
63+
driver = webdriver.Firefox(options=options)
64+
try:
65+
driver.implicitly_wait(SLEEP_SECS)
66+
driver.get(URL)
67+
68+
tree = Tree()
69+
70+
while True:
71+
try:
72+
try:
73+
evolution_el = driver.find_element(By.CSS_SELECTOR, ".evolution")
74+
except NoSuchElementException:
75+
print("Не найден класс эволюции")
76+
continue
77+
78+
text = evolution_el.text
79+
is_start = "Выбор класса" in text
80+
is_select_subclass = "Выбор подкласса" in text
81+
is_finish = "Конец развития" in text
82+
83+
class_path: list[str] = []
84+
try:
85+
classes_el = evolution_el.find_element(By.CSS_SELECTOR, "div > div.ui.message > p > div")
86+
class_path = get_path_of_classes(classes_el.text)
87+
print("Выбранные классы:", class_path)
88+
89+
except NoSuchElementException:
90+
pass
91+
92+
button_ok = button_cancel = None
93+
button_els = [button for button in evolution_el.find_elements(By.CSS_SELECTOR, "button") if button.is_enabled()]
94+
print("Доступные кнопки:", [button.text for button in button_els])
95+
for button in button_els:
96+
if "Подтвердить" in button.text:
97+
button_ok = button
98+
99+
if "Сбросить" in button.text:
100+
button_cancel = button
101+
102+
# Если не старт и дерево пустое, значит возвращаемся к началу
103+
if not is_start and not tree.root_node.children:
104+
print("Первый запуск и выбор классов не в начале, выполняю сброс")
105+
button_cancel.click()
106+
continue
107+
108+
current_node = None
109+
110+
# На первом шаге берем корневой, иначе пытаемся найти класс
111+
if is_start:
112+
current_node = tree.root_node
113+
else:
114+
current_node = tree.get_child(class_path)
115+
if not current_node:
116+
raise Exception(f"Не удалось найти текущий класс по {class_path}")
117+
118+
print("Текущий класс:", current_node if current_node.name else "Нет")
119+
120+
if is_finish:
121+
print(f'Класс отмечен как финальный: "{current_node.get_full_name()}"')
122+
current_node.is_last = True
123+
button_cancel.click()
124+
continue
125+
126+
variant_els = [
127+
el
128+
for el in evolution_el.find_elements(By.CSS_SELECTOR, "ul.slider-thumbnails > li")
129+
if el.is_displayed()
130+
]
131+
print("Доступные классы:", len(variant_els))
132+
133+
variant_by_el = dict()
134+
135+
# Просмотр всех доступных на выбор классов
136+
if variant_els:
137+
for variant_el in variant_els:
138+
variant_el.click()
139+
time.sleep(SLEEP_SECS)
140+
141+
# Элемент с названием и описание класса
142+
name = get_class_name(evolution_el) # TODO: Текст класса не присутствует, если окно свернуто
143+
variant_by_el[name] = variant_el
144+
145+
# Если вернулись к старту и классы уже были добавлены
146+
if is_start and tree.root_node.children:
147+
if not variant_by_el:
148+
raise Exception("Что-то пошло не так - нет классов на выбор при старте")
149+
150+
current_classes = list(variant_by_el.keys())
151+
print("Классы:", current_classes)
152+
153+
# Если все классы посещены
154+
if all(tree.get_child([name]).is_finished() for name in current_classes):
155+
break
156+
157+
# Добавление вариантов класса
158+
if variant_by_el:
159+
# Добавление всех классов на данном шаге
160+
for name, variant_el in variant_by_el.items():
161+
if not current_node.get_child([name]):
162+
child = current_node.add_child(name)
163+
print(f'Добавление класса "{child.get_full_name()}"')
164+
165+
# Выбираем класс, в котором еще не все узлы были посещены
166+
for name, variant_el in variant_by_el.items():
167+
child = current_node.get_child([name])
168+
169+
# Если нашли, то выбираем этот класс в UI
170+
if not child.is_finished():
171+
variant_el.click()
172+
break
173+
174+
# Если вариантов нет, добавление того, что сейчас есть
175+
else:
176+
# Элемент с названием и описание класса
177+
name = get_class_name(evolution_el)
178+
179+
if not current_node.get_child([name]):
180+
child = current_node.add_child(name)
181+
print(f'Добавление класса "{child.get_full_name()}"')
182+
183+
if is_finish:
184+
button_cancel.click()
185+
else:
186+
if not button_ok:
187+
name = get_class_name(evolution_el)
188+
print(f'[!] Класс "{name}" не доступен для выбора, похоже не хватает уровня')
189+
child = current_node.add_child(name).add_child("???", is_last=True)
190+
print(f'Текущая ветка классов отмечена как завершенная, но финальный класс неизвестен: "{child.get_full_name()}"')
191+
button_cancel.click()
192+
continue
193+
194+
button_ok.click()
195+
196+
finally:
197+
time.sleep(SLEEP_SECS)
198+
print()
199+
200+
print("\n" + "-" * 100 + "\n")
201+
202+
print("Все классы:")
203+
tree.print()
204+
205+
finally:
206+
driver.quit()
207+
208+
"""
209+
Все классы:
210+
🗡️ Воин
211+
🗡️ Воин >> ⚔️ Рыцарь
212+
🗡️ Воин >> ⚔️ Рыцарь >> 🛡️Феодал
213+
🗡️ Воин >> ⚔️ Рыцарь >> 🛡️Феодал >> 👑 Король
214+
🗡️ Воин >> ⚔️ Рыцарь >> 🥷 Мастер боевых искусств
215+
🗡️ Воин >> ⚔️ Рыцарь >> 🥷 Мастер боевых искусств >> 🥷🏽🗡️ Мастер меча
216+
🗡️ Воин >> ⚔️ Рыцарь >> 🥷 Мастер боевых искусств >> ⛩️ Монах
217+
🗡️ Воин >> ⚔️ Рыцарь >> 🛡️ Паладин
218+
🗡️ Воин >> ⚔️ Рыцарь >> 🛡️ Паладин >> ⚔️🛡️ Святой рыцарь
219+
🗡️ Воин >> ⚔️ Рыцарь >> 🛡️ Паладин >> 💀⚔️ Тёмный жнец
220+
🗡️ Воин >> 🗡️💰 Наемник
221+
🗡️ Воин >> 🗡️💰 Наемник >> 🗺️ Авантюрист
222+
🗡️ Воин >> 🗡️💰 Наемник >> 🗺️ Авантюрист >> 🏫 Глава гильдии
223+
🗡️ Воин >> 🗡️💰 Наемник >> 🗺️ Авантюрист >> 🦸 Герой
224+
🗡️ Воин >> 🗡️💰 Наемник >> 🗡️ Разбойник
225+
🗡️ Воин >> 🗡️💰 Наемник >> 🗡️ Разбойник >> Феодал
226+
🗡️ Воин >> 🗡️💰 Наемник >> Следопыт
227+
🗡️ Воин >> 🗡️💰 Наемник >> Следопыт >> 🏹 Охотник
228+
🗡️ Воин >> 🗡️💰 Наемник >> Следопыт >> Исследователь
229+
Юный даос
230+
Юный даос >> 🦓 Школа зебры
231+
Юный даос >> 🦓 Школа зебры >> Духовный владыка
232+
Юный даос >> 🦓 Школа зебры >> Духовный владыка >> Верховный даос
233+
Юный даос >> Школа тигра
234+
Юный даос >> Школа тигра >> Скала
235+
Юный даос >> Школа тигра >> Скала >> Высший культиватор
236+
Земледелец
237+
Земледелец >> Фермер
238+
Земледелец >> Фермер >> Владетель ранчо
239+
Земледелец >> Фермер >> Владетель ранчо >> Магнат
240+
Земледелец >> Торговец
241+
Земледелец >> Торговец >> Глава гильдии купцов
242+
Земледелец >> Торговец >> Глава гильдии купцов >> 🎩 Легендарный банкрот
243+
Земледелец >> Торговец >> Глава гильдии купцов >> 💰💰💰 Легендарный капиталист
244+
Практикант
245+
Практикант >> Волшебник
246+
Практикант >> Волшебник >> Некромант
247+
Практикант >> Волшебник >> Некромант >> Лич
248+
Практикант >> Волшебник >> Некромант >> Заклинатель духов
249+
Практикант >> Волшебник >> Целитель
250+
Практикант >> Волшебник >> Целитель >> Жрец
251+
Практикант >> Волшебник >> Целитель >> Пророк
252+
Практикант >> Волшебник >> Старший маг
253+
Практикант >> Волшебник >> Старший маг >> Архимаг
254+
Практикант >> Волшебник >> Старший маг >> Отступник
255+
Практикант >> Чародей
256+
Практикант >> Чародей >> Высший заклинатель
257+
Практикант >> Чародей >> Высший заклинатель >> Дьявольское отродье
258+
Практикант >> Чародей >> Высший заклинатель >> Демонопоклонник
259+
Практикант >> Чародей >> Пробудивший кровь
260+
Практикант >> Чародей >> Пробудивший кровь >> Обращённый
261+
Практикант >> Колдун
262+
Практикант >> Колдун >> Заключение договора: Гримуар
263+
Практикант >> Колдун >> Заключение договора: Гримуар >> Владыка заклинаний
264+
Практикант >> Колдун >> Заключение договора: Фея
265+
Практикант >> Колдун >> Заключение договора: Фея >> Владыка духов
266+
Практикант >> Колдун >> Заключение договора: Дракон
267+
Практикант >> Колдун >> Заключение договора: Дракон >> 🐉 Сношатель драконов
268+
Практикант >> Зов природы
269+
Практикант >> Зов природы >> Шаман
270+
Практикант >> Зов природы >> Шаман >> Хранитель астрала
271+
Практикант >> Зов природы >> Друид
272+
Практикант >> Зов природы >> Друид >> Защитник леса
273+
Дитя улиц
274+
Дитя улиц >> Карманник
275+
Дитя улиц >> Карманник >> Вор
276+
Дитя улиц >> Карманник >> Вор >> Политик
277+
Дитя улиц >> Карманник >> Вор >> Глава гильдии воров
278+
Дитя улиц >> Плут
279+
Дитя улиц >> Плут >> Убийца
280+
Дитя улиц >> Плут >> Убийца >> Ассасин
281+
Дитя улиц >> Плут >> Менестрель
282+
Дитя улиц >> Плут >> Менестрель >> Бард
283+
Дитя улиц >> Плут >> Менестрель >> Танцовщица
284+
Безработный
285+
Безработный >> Иссекай
286+
Безработный >> Иссекай >> Младшая сестра главного героя
287+
Безработный >> Иссекай >> Эльфийка–волшебница в гареме главного героя
288+
Безработный >> Сорокалетний девственник
289+
Безработный >> Сорокалетний девственник >> Fucking Slave
290+
Безработный >> Сорокалетний девственник >> Fucking Slave >> Dungeon Master
291+
Безработный >> Сорокалетний девственник >> Fucking Slave >> Dungeon Master >> Boss of the gym
292+
Безработный >> Сорокалетний девственник >> Fucking Slave >> Dungeon Master >> Boss of the gym >> ???
293+
"""

0 commit comments

Comments
 (0)