#python #selenium #rest #parallel-processing #joblib
Вопрос:
Мне уже задавали этот вопрос ранее [неактивно, а теперь удалено], но я неправильно сформулировал его. И я пытаюсь улучшить это.
Любая помощь будет очень признательна.
ЧТО Я ПЫТАЮСЬ СДЕЛАТЬ: Автоматизировать определенные задачи [с помощью selenium] (Restful API)
- перейдите на веб-сайт
- найдите задачу и, если она найдена, откройте задачу на новой вкладке (переключитесь на новую вкладку — tab1)
- нажмите «просмотрено», а затем закройте вкладку (закрыть — переключиться на вкладку 0) [это ничего не возвращает]
- обновите первую вкладку (tab0), чтобы «просмотренные» автоматически обновлялись (я думаю, что это работает по сеансу/кешу)- есть боковой столбец, в котором отображаются все просмотренные задачи.
- И в конце всех задач (вкладка 0) нажмите «готово» в разделе «выполненные задачи», который возвращает код бронирования.
ТО, ЧТО У МЕНЯ ЕСТЬ, РАБОТАЕТ:
class SomeClass(SomeOtherClass):
def do_tasks(self, selections):
booking_code = None
task_done = 0
driver = self.connect() #spawns a chrome browser
#I want the below for loop to run in parallel
for task in tasks:
try:
#check_if_task_is_in_search_result_amp;_then_open_in_new_tab
#do_something
task_done = 1
#close_tab
except:
#handle_something
driver.close()
driver.switch_to.window(driver.window_handles[0])
driver.refresh()
try:
check = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CLASS_NAME, 'xxxx'))).click()
except NoSuchElementException as e:
log_error(str(e))
except TimeoutException as e:
log_error(str(e))
else:
booking_code = str(driver.find_element_by_class_name("number").text).split(':')[1]
driver.quit()
return task_done, booking_code
Это происходит последовательно и занимает примерно 5 минут для 5 задач.
ЗАСТАВИТЬ ЕГО РАБОТАТЬ ПАРАЛЛЕЛЬНО
WHAT I’VE TRIED SO FAR — bring out the for-loop section to a new method — do_task
.
Import: from joblib import Parallel, delayed
class SomeClass(SomeOtherClass):
def do_task(self, task):
driver = self.connect() #spawns a chrome browser
try:
#do_something
task_done = 1
except:
#handle_something
return task_done, driver
def get_booking_code(self, driver):
try:
check = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CLASS_NAME, 'xxxx'))).click()
except NoSuchElementException as e:
log_error(str(e))
else:
booking_code = str(driver.find_element_by_class_name("number").text).split(':')[1]
driver.quit()
return booking_code
if __name__ == '__main__':
tasks = [
['task1'],
['task2']
]
b = SomeClass(site='https://somesite.com/') #chrome connects to this via the self.connect()
completed_tasks, driver = Parallel(n_jobs=-1)(delayed(b.do_task)(task) for task in tasks)
booking_code = b.get_booking_code(driver)
print(completed_tasks, booking_code)
Он не работает. Он создает пустой браузер Chrome и немедленно закрывается.
Обратная связь, как показано ниже:
completed_tasks, driver = Parallel(n_jobs=-1)(delayed(b.do_task)(task) for task in tasks)
File "--envlibsite-packagesjoblibparallel.py", line 1054, in __call__
self.retrieve()
File "--envlibsite-packagesjoblibparallel.py", line 933, in retrieve
self._output.extend(job.get(timeout=self.timeout))
File "--envlibsite-packagesjoblib_parallel_backends.py", line 542, in wrap_future_result
return future.result(timeout=timeout)
File "--pythonpython38libconcurrentfutures_base.py", line 439, in result
return self.__get_result()
File "c:usersokwudappdatalocalprogramspythonpython38libconcurrentfutures_base.py", line 388, in __get_result
raise self._exception
selenium.common.exceptions.SessionNotCreatedException: Message: session not created
from disconnected: unable to connect to renderer
(Session info: chrome=91.0.4472.114)
Комментарии:
1. Самое простое объяснение, которое я видел, — параллельный запуск работников selenium. Ссылка
2. Вы не можете получить истинный параллелизм с Selenium. Попробуйте Pippeteer.
3. @pguardiario, спасибо, но я проверил пакет, и это потребовало бы, чтобы я переписал весь свой код (здесь показан 1 из 7 аналогичных кодов), поэтому я мог бы рассмотреть это в качестве последнего средства. Еще раз спасибо.
Ответ №1:
В идеале вы должны создать отдельный объект «SomeClass» для каждой задачи, а затем параллельно вызывать соответствующие функции, потому что в определениях, выполняемых параллельно, есть оператор self.connect() и driver.quit (). Пожалуйста, попробуйте либо создать отдельный объект класса для каждой параллельной задачи, либо создать общий сеанс и удалить driver.quit() из методов параллельного выполнения.
Комментарии:
1. Спасибо @Rohit, но я просто не могу собрать воедино ваше объяснение кода. Не могли бы вы объяснить немного подробнее?
Ответ №2:
Вчера я решил эту проблему(задачи выполняются параллельно), но сейчас я сталкиваюсь с совершенно новой задачей, для решения которой будет создана новая должность.
Как я заставил задачи выполняться параллельно (используя мой второй код — «то, что я пробовал до сих пор»):
import multiprocessing
#code excluded on purpose
if __name__ == '__main__':
tasks = [
['task1'],
['task2']
]
b = SomeClass()
with multiprocessing.Pool(processes=2) as p:
p.map(b.do_task, tasks)
do_task
Метод возвращает только количество выполненных задач (это произвольное возвращаемое значение).