Ускорить selenium webdriver
В моём проекте необходимо делать скриншот области страницы, я нашёл selenium wd, но он очень долго запускается, что может быть проблемой при использовании. Я думаю, что дело в том, что он создаёт при запуске инстанс хедлесс хрома, можно ли это делать до использования метода get()? Стоит учесть и то, что это всё должно работать при нескольких параллельных запросах. Я думаю, что можно создать пул из chromedriver , например, из 5и, а потом запускать их по-очереди. Но я не знаю, как это сделать Код:
def get(): chrome_options = Options() chrome_options.add_argument("--headless") chrome_options.add_argument('--no-sandbox') chrome_driver = "/mnt/e/pibot/old/chromedriver" brw = webdriver.Chrome(chrome_options=chrome_options, executable_path=chrome_driver) try: brw.get('https://ru.wikipedia.org/wiki/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%A1%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0') brw.set_window_size(1280, 5000) # правильная обрезка скрина try: location_toc = brw.find_element_by_id('toc').location print(brw.find_element_by_id('toc').text) except: location_toc = print(location_toc) location_content = brw.find_element_by_id('content').location size_content = brw.find_element_by_id('content').size brw.save_screenshot('/mnt/e/pibot/old/'+r+'a.png') im = Image.open('/mnt/e/pibot/old/'+r+'a.png') im = im.crop((location_content['x'], location_content['y'], location_content['x'] + size_content['width'], location_content['y'] + location_toc['y']-85)) im.save('/mnt/e/pibot/old/'+r+'a.png') print('Готово, выход') print(brw.current_url) except: raise Exception finally: brw.close()
Отслеживать
задан 6 мая 2018 в 16:07
695 1 1 золотой знак 8 8 серебряных знаков 28 28 бронзовых знаков
измерения показали, что строчка webdriver.Chrome() у вас наибольшее время занимает? Если да, руками service запустите и с помощью Remote к нему подключайтесь.
7 мая 2018 в 22:02
Я просто не знаю, сколько таких сервисов могу поднять? И как лучше всего реализовать «занятость» и «свободность» каждого из сервисов
8 мая 2018 в 11:12
цифры добавьте, которые показывают, что сколько времени у вас занимает и сколько хотите чтобы занимало.
Ускоряем Selenium-тесты
Все, кто использует Selenium тесты в своём билде, знают, что это достаточно дорогое удовольствие, потому что очень медленно. Из-за этого многие не запускают билд полностью перед коммитами или, вообще, запускают билд только по ночам. Итак, делаем наши тесты быстрее.
Отключаем native events
Примерная суть native events такова: когда тест натыкается на строку
driver.findElement(By.id(“someId”)).sendKeys(“hello”);
то операционке отправлятся некое событие типа “Введи в поле вооон с теми координатами слово ‘hello’”. Координаты расчитываются как-то сами собой и всё выглядит так, будто пользователь медленно вводит текст с клавиатуры. Проблема в том, что это затрачивает колоссальное количество времени, а нам это не надо.
Чтобы отключить эту фишку надо запускать Firefox с предварительно настроенным профайлом:
private WebDriver firefox()
Вот так мы получили быстрый драйвер.
Если у Вас есть уже готовый профайл для запуска тестов, то можно создать в нём файл user.js:
user_pref("webdriver_enable_native_events", false);
Либо немного поменять метод создания драйвера
private WebDriver firefox(final String profileName)
Отключаем анимацию
Анимация на сайте — это, конечно, вещь, но во время тестов — это зло. Приходится постоянно ждать, когда эта анимация закончится, вставлять какие-то костыли и т.д. И да, анимация сама по-себе съедает часть времени теста. Вообщем, отключаем анимацию на примере jQuery
private WebDriver initDriver() < final WebDriver wrappedDriver = firefox(); final EventFiringWebDriver driver = new EventFiringWebDriver(wrappedDriver); driver.register(new AbstractWebDriverEventListener() < @Override public void afterNavigateTo(final String url, final WebDriver driver) < ((JavascriptExecutor) driver).executeScript("jQuery.fx.off = true;"); >// скорее всего переопределения одного метода не хватит // так что переопределяем тут всякие beforeClickOn, afterNavigateBack и т.д. > ); return driver; >
Дальше работаем с инстансом класса EventFiringWebDriver. В примере я переопределил только метод afterNavigateTo(), который вызывается автоматически после каждого вызова driver.get(someUrl). В реальной жизни этого явно недостаточно. Например, тест жмёт на кнопку и происходит автоматичекий редирект на другую страницу. В этом случае вызова afterNavigateTo() не произойдёт, поэтому приходится переопределять дополнительно beforeClickOn(), чтобы отключить анимацию при первом клике на какой-нибудь элемент после загрузки новой страницы.
Запускаем тесты в параллельном режиме
Изначально, я посматривал в сторону Selenium Grid, но у меня даже дэмку не получилось запустить, да и вообще grid показался мне не очень удобным: нужно запускать хаб и сервер. Поэтому запускаем тесты в двух и более броузерах средствами Maven и TestNG.
Добавляем в pom.xml
org.apache.maven.plugins maven-surefire-plugin 2.8.1 run-integration-tests integration-test test integration classes 2
Если Вы инжектируете в тесты потоконебезопасные инстансы со scope=«singleton», то необходимо поменять на scope=«prototype». В лучшем случае на данном этапе можно отделаться только конфигурацией конфигов (например Spring’a). Если каждый ваш тест использует уникальное имя пользователя для логина, то всё должно заработать с пол-пинка.
Итоги
Раньше в проекте, над которым я работаю, исполнение Selenium-тестов занимало примерно 5 минут, теперь же — 1,5 минуты.
Для справки
Все вышеописанные действия были проведены на свяке Java+Selenium 2.8.0+Maven+TestNG+Firefox 7.0.1+Spring
Как ускорить парсинг данных с Python/Selenium?
В текущем варианте парсинг осуществляется с chromedriver. Практически имею около 100.000 ссылок, по которым находятся таблицы. У каждой таблицы имеется кнопка «Подробнее», которую сейчас нажимает парсер, копирует содержимое попапа, закрывает его и т.д.
В общем чтобы пропарсить наверное миллион таких строк у меня уйдет месяц непрерывной работы селениума. Ищу способ как-то ускорить это.
Проблема, установил небольшие задержки, которые нужны в аккурат дать подгрузиться попапу и дать ему закрыться, иначе возникают ошибки element is not found.
В общем, спасайте. Подскажите как это реально делается, чтобы ускорить работу хотя бы в 10 раз. (за пол часа он прошел около 400 страниц, спарсив около 2000 строк). Это как пройтись мне самому, нажать на каждую ссылку «Подробнее», но копирование отдать скрипту. Это вряд ли можно назвать полной автоматизацией. тем более с такими объемами (не оцениваю их как большие).
Существуют ли «реальные» бустеры таких операций? Я понимаю, что селениум сделан для тестирования или хотя бы для парсинга страниц, где нет кучи попапов, которые все надо прокликать.
upd: после постинга продолжил гуглить и в одном обсуждении нашел следующее:
javascript tables is exactly why I went with selenium for some sites. However, rather than parsing directly with selenium, I was passing driver.page_source (raw html containing whatever javascript generated) to bs4 and parsing with bs4. I was shocked to find out that this round about method was faster than using selenium.find_element_by_XXXXX methods without ever invoking bs4.
Это действительно так?
- Вопрос задан более трёх лет назад
- 7220 просмотров
Комментировать
Решения вопроса 1
Вёбных дел мастер
На 100к ссылок, особенно если требуется их обходить достаточно часто (или на сервере ресурсов мало), есть уже смысл задумать о более кастомных (читай, напилить руками низкоуровневый механизм), но более быстрых механизмах. Как-то запросы на получение AJAX данных через curl. Или если данные получаться в рамтайме на клиенте через замудренный JS, то применить SpiderMonkey, V8 либо другие серверных движки.
В общем чтобы пропарсить наверное миллион таких строк у меня уйдет месяц непрерывной работы селениума
Делал на кластере из PhantomJS парсер который должен был за 15 минут обходит чуть больше 1к страниц и парсить из них разные хитрые таблички. Требовалось что-то около десяти инстансов PhantomJS, 20 Гб ОЗУ и 16 ядер ЦПУ. На таком кластере 100к за сутки переварит реально.
Когда требование по времени ужесточилось до 5 минут, напилил на SpiderMonkey.
element is not found
Нужно использовать wait(). Тогда дальше код будет выполняться когда на странице появиться нужный элемент.
где нет кучи попапов, которые все надо прокликать
Наличие/отсутствие попапов не играет роли. Все, что появляется в DOM, все можно отработать. Регулярно тягаю данные с яндекс ворстата. Много там разных хитрых обработчиков. Но все силами PhantomJS-а через webdriver решается рано или поздно.
Это действительно так?
Возможно. Но так ли это в вашем контексте ни кто кроме эксперимента не скажет. Т.е. берем данное утверждение и проверяем в своей задаче парсинга.
Ответ написан более трёх лет назад
Нравится 2 3 комментария
Bjornie @Bjornie Автор вопроса
«Требовалось что-то около десяти инстансов PhantomJS»
Можете показать кусок кода (или направить на реализацию подобного решения), о котором вы говорите? Если я запускаю «тупо» 2 одинаковых скрипта (естественно на разный список ссылок), то я вижу, что первый работает нормально, а второй «плетется», иногда подтормаживая, или вообще останавливаясь. Не знаю точно в чем проблема: соединение, настройки удаленного сервера, или какие-то другие факторы.
«Нужно использовать wait().»
Расставил везде где нужно по time.sleep(1) или wait.until. Запинаний не было.
«Наличие/отсутствие попапов не играет роли. Все, что появляется в DOM, все можно отработать.». Это понятно, что все появляется в DOM. Сейчас в моем примере сервер отдает целый шаблон с html-тегами (а не просто массив данных), который при открытии появляется или наоборот удаляется. Все это ведь надо прокликать, так или иначе. Иначе как дать появится данным в дереве?
«Возможно. Но так ли это в вашем контексте ни кто кроме эксперимента не скажет.»
В общем я сделал прокликивание ссылок через селениум, а парсинг данных через bs4. Работает, как и обещали — быстрее. В 2 с небольшим раза (т.е. не 5 часов, а 2.7 где-то). Это уже хорошо.
В общем, думаю, что ничего волшебного не бывает, т.к. все зависит от независящих от меня факторов: как быстро сервер отдает информацию, скорость канала и т.д. Единственное решение: максимально быстроработающий код и многопоточность. Насколько я понял. С первым я более-менее разобрался, а вот как увеличить ресурсы — пока нет. Парсинг происходит уже 2-й день.
Bjornie: >Можете показать кусок кода (или направить на реализацию подобного решения), о котором вы говорите?
У меня PHP. На python-е думаю отличий будет не сильно много. Архитектура такая. Есть класс который запускает заданное количество PhantomJS. Поскольку последний может из коробки работать через webdriver, то интансы запускаются в фоном режиме, при этом каждый из них слушает строго свой заданный локальный порт. Кроме того каждый из них запускается строго через прокси (что бы были заходы с разных IP + на случай бана), у каждого своя прокся. После чего приложение когда нужно соединяется с этим фантомами и отправляет в них требуемые задания. Задачи на загрузку складываются в очередь redis, скрипт который заполняет очередь запускается строго в одном экземпляре (гарантируется через семафоры) и заполняет очередь только если она пустая (тогда задачи не дублируются). Это скрипт запускается кроном. Если другой скрипт (назовем его воркер). Он так же пускается по крону каждую минуту. Он забирает из очереди redis одно задание, отправляет его фантому, парсит страницу, складывает результат в базу, завершает работу. Кусок кода (кластер стартует через startWebDriverCluster):
basePath . '/lib/php-webdriver/lib/__init__.php'); // [1- Инициализация окружения $webdriver_host = '127.0.1.1'; $capabilities = array( WebDriverCapabilityType::BROWSER_NAME => 'phantomjs', 'phantomjs.page.settings.userAgent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0', ); try < $driver = RemoteWebDriver::create(":", $capabilities, 10000); > catch (Exception $e) < return false; >// Задаем размер экрана по умолчанию $window = new WebDriverDimension(1024, 768); $driver->manage()->window()->setSize($window); // -1] return $driver; > /** * По идентификатору драйвера вернет running если связанный с ним браузер запущен, * либо stopped если он остановлен. Данные получаются на основании обращения к прослущиваемому сокету, * поэтому отражают реальную ситуацию. */ public function getWebDriverStatus($id) < if ( !array_key_exists($id, $this->_proxy_list) ) < throw new \Exception("Incorrect webdriver id #$id\n"); >$proxy = $this->_proxy_list[$id]; $webdriver_host = '127.0.1.1'; $errno = 0; $errstr = ''; $socket = @fsockopen($webdriver_host, $proxy['wd_port'], $errno, $errstr, 5); return is_resource($socket) ? 'running' : 'stopped'; > /** * Запускает группу webdriver (phamtomjs). Каждый из них ассоциирован с определенным * прокси. Висят как демоны и слушают каждый свой локальный порт. */ public function startWebDriverCluster($ttl = 3600) < $webdriver_host = '127.0.1.1'; foreach ($this->_proxy_list as $key => $proxy) < // Не запускаем неактивные webdrive if ( !$proxy['is_active'] ) < continue; >// Если порт занят, то не стартуем такой webdrive (т.к. скорее всего он был запущен в прошлый вызов команды) $errno = 0; $errstr = ''; $socket = @fsockopen($webdriver_host, $proxy['wd_port'], $errno, $errstr, 5); if ( is_resource($socket) ) < continue; >// Если все впорядке, стартуем webdrive $cookie_dir = Yii::app()->basePath . '/runtime/cookie/'; if ( !file_exists($cookie_dir) ) < mkdir($cookie_dir); chmod($cookie_dir, 0775); >$cookie_file = $cookie_dir . $key . '.txt'; // Костыль - часть загрузок фейлится, возможно из-за кук, поэтому тупо грохаем файл с куками // хотя это и противоречит первоначальной задумке if ( file_exists($cookie_file) ) < unlink($cookie_file); >$cmd = "phantomjs --load-images=false --proxy=: --proxy-auth=: --ignore-ssl-errors=true --cookies-file= --webdriver=127.0.1.1: > /dev/null 2>&1 &"; exec($cmd); // Даем время phantomjs-у запуститься $is_run = false; for ($i = 0; $i < 20; ++$i) < usleep(500000); $socket = @fsockopen($webdriver_host, $proxy['wd_port'], $errno, $errstr, 1); if ( is_resource($socket) ) < $is_run = true; break; >> // Отмечаем как работающий $cache_key = 'proxy.' . $key; $proxy_info = Yii::app()->redis->get($cache_key); // Если данных по прокси в кэше нет, то считаем прокси доступным if ( empty($proxy_info) ) < $proxy_info = $proxy; $proxy_info['is_active'] = $is_run; $proxy_info['wd_port'] = $key + $this->start_port_num; // не используем системные порты $proxy_info['last_req_time'] = 0; // время последнего запроса $proxy_info['req_count'] = 0; // счетчик удачных попыток $proxy_info['status'] = $is_run ? 'running' : 'stopped'; > else < $proxy_info['status'] = $is_run ? 'running' : 'stopped'; >Yii::app()->redis->set($cache_key, $proxy_info); > $cache_key = 'cluster.status'; Yii::app()->redis->set($cache_key, 'run', $ttl); > /** * + * Останавливает все демонты webdrive кластера если вызван без параметров. * Остановит только конкретный драйвер по его $id. * * @param int $id Идентификатор прокси которую нужно выгрузить. */ public function stopWebDriverCluster($id = null) < $webdriver_host = '127.0.1.1'; foreach ($this->_proxy_list as $key => $proxy) < if (!is_null($id) && $id != $key ) < continue; >$errno = 0; $errstr = ''; $socket = @fsockopen($webdriver_host, $proxy['wd_port'], $errno, $errstr, 5); if ( is_resource($socket) ) < $cmd = 'kill -15 `ps ax -o pid,args | grep -v grep | grep phantom | grep \'' . $key . '.txt\' | awk \'\'`'; exec($cmd); > > if ( is_null($id) ) < $cache_key = 'cluster.status'; Yii::app()->redis->delete($cache_key); > > /** * Вернет webdrive который можно использовать для запросов. При заданном $id прокси * вернет связанный с ней webdrive (проверка на факт активности прокси не выполняется). * В случае ошибок (свободных webdrive нет, заданный webdrive деактивирован/выключен) * вернет null. */ public function getDriver($id = null) < $proxy = is_null($id) ? $this->_getActiveProxyInfo($id) : $this->getProxyInfoById($id); // Убеждаемся, что заданный драйвер запущен и работает, если нужно, пытается его запустить $status = $this->getWebDriverStatus($id); if ('stopped' == $status) < $is_run = $this->activateProxy($id); if ( !$is_run ) < return null; >> if ( !empty($proxy) ) < $driver = $this->_getDriver($proxy); if ( !empty($driver) ) < return array($id =>$driver); > > return null; > . >
Ускорить работу Selenium Web Driver
Имеется сайт, мне необходимо протестировать его на битые ссылки. Т.е. начиная с главной страницы иду по всем ссылкам со страницы и так далее.
Тест выполняется очень долго (На Chrome Driver заняло полчаса).
Что посоветуете?
HTML UNit Driver будет быстрее?
#2 vmaximv
Отправлено 14 августа 2013 — 10:23
1. Отключить картинки/js/activex/flash/video/css и прочую не нужную хрень.
2. Использовать грид.
#3 tetchenko
Отправлено 14 августа 2013 — 10:38
1. Отключить картинки/js/activex/flash/video/css и прочую не нужную хрень.
2. Использовать грид.
Спасибо, а как можно отключить?
#4 Snap
Отправлено 14 августа 2013 — 11:29
А почему используете Selenium Webdriver для этих целей?
P.S. голосовал за Html, еще бы знать как в нем вывод на консоль ошибок отключить.
#5 Nwd
Отправлено 19 августа 2013 — 16:26
Голосую за Xenu или что-то в этом роде. Не вижу смысла делать это вебдрайвером
#6 Freiman
Отправлено 20 августа 2013 — 06:26
Можно Xenu, можно вообще не использовать selenium, а просто написать на любом языке программирования.
я подобную штуку писал на python и php.
#7 PavelLobashov
PavelLobashov
Отправлено 20 августа 2013 — 08:07
Можно Xenu, можно вообще не использовать selenium, а просто написать на любом языке программирования.
я подобную штуку писал на python и php.
Да можно хоть на wget+bash. Это задачу можно решить наверно на любом языке, без привлечения тяжелой артиллерии
Количество пользователей, читающих эту тему: 0
0 пользователей, 0 гостей, 0 анонимных
Ответить цитируемым сообщениям Очистить
- Форум тестировщиков
- → Тестирование
- → Автоматизированное тестирование
- → Selenium — Functional Testing
- Политика Конфиденциальности
- Правила форума ·
- Помощь