Как реализовать параллельную обработку селена

#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 Метод возвращает только количество выполненных задач (это произвольное возвращаемое значение).