Главная страница
Навигация по странице:

  • Контрольный список: как выдать программу за человека

  • Глава 15. Тестирование сайтов с помощью веб- скраперов

  • Выполнение unittest в Jupyter Notebook

  • Enter .Тестирование «Википедии».

  • Современный_скрапинг_веб_сайтов_с_помощью_Python_2021_Райан_М. Руководство по решению его наиболее распространенных задач


    Скачать 3.96 Mb.
    НазваниеРуководство по решению его наиболее распространенных задач
    Дата01.03.2023
    Размер3.96 Mb.
    Формат файлаpdf
    Имя файлаСовременный_скрапинг_веб_сайтов_с_помощью_Python_2021_Райан_М.pdf
    ТипРуководство
    #961920
    страница2 из 6
    1   2   3   4   5   6

    An Interesting Title


    Обратите внимание: этот код возвращает только первый попавшийся ему на странице экземпляр тега h1. По существующему соглашению на странице может быть только один тег h1, однако принятые в Интернете соглашения часто нарушаются. Поэтому следует помнить, что таким образом будет получен только первый экземпляр тега и не обязательно тот, который вы ищете.
    Как и в предыдущих примерах веб-скрапинга, мы импортируем функцию urlopen и вызываем html.read(),
    чтобы получить контент страницы в формате HTML. Помимо текстовой строки, BeautifulSoup также может принимать файловый объект, непосредственно возвращаемый функцией urlopen
    . Чтобы получить этот объект, не нужно вызывать функцию .read():
    bs = BeautifulSoup(html, 'html.parser')
    Здесь контент HTML-файла преобразуется в объект
    BeautifulSoup
    , имеющий следующую структуру:
    html
    ...

    head


    A bot-proof form


    "http://pythonscraping.com/dontgohere"
    >Go here!
    Click me!

    value="valueShouldNotBeModified"/>

    value="intentionallyBlank"/>





    Например, следующий код извлекает описанную ранее страницу, находит скрытые ссылки и поля ввода форм:
    from selenium import webdriver from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.chrome.options import
    Options driver = webdriver.Chrome(
    executable_path='drivers/chromedriver',
    options=chrome_options)
    driver.get('http://pythonscraping.com/pages/its atrap.html')
    links = driver.find_elements_by_tag_name('a')
    for link in links:
    if not link.is_displayed():
    print('The link {} is a trap'.format(link.get_attribute('href')))
    fields
    =
    driver.find_elements_by_tag_name('input')
    for field in fields:
    if not field.is_displayed():
    print('Do not change value of
    {}'.format(field.get_attribute('name')))
    Selenium находит все скрытые поля и выводит следующие данные:
    The link http://pythonscraping.com/dontgohere is a trap
    Do not change value of phone

    Do not change value of email
    Скорее всего, вы не захотите переходить по обнаруженным скрытым ссылкам, однако, отправляя форму, стоит убедиться в правильности заполнения скрытых полей (или в их заполнении
    Selenium). Резюмируем: просто игнорировать скрытые поля опасно, хотя следует быть осторожными при взаимодействии с ними.
    Контрольный список: как выдать программу за человека
    В данной главе, да и вообще во всей книге, много говорится о способах разработки веб-скрапера, который будет как можно меньше похож на него и как можно больше — на человека.
    Если, несмотря на это, вас все равно блокируют сайты и вы не знаете почему, то вот контрольный список, который можно использовать для устранения проблемы.
    • Прежде всего, если страница, которую вы получаете с веб- сервера, пуста, на ней отсутствует информация или она каким-либо иным образом не соответствует вашим ожиданиям (или увиденному вами в браузере), — это,
    вероятно, вызвано тем, что для создания данной страницы выполняется скрипт JavaScript. Обратитесь к главе 11.
    • Отправляя на сайт форму или POST-запрос, просмотрите страницу и убедитесь в отправке всего, что сайт ожидает от вас, причем в правильном формате. Чтобы увидеть, какой
    POST-запрос в действительности отправляется на сайт, и убедиться, что в нем есть все необходимое и «естественный»
    запрос выглядит идентично тому, который отправляет ваш бот, используйте соответствующий инструмент, такой как панель Chrome Inspector.

    • Если при попытке аутентифицироваться на сайте вам не удается там «закрепиться» или сайт себя ведет как-то странно, то проверьте данные cookie. Убедитесь, что они правильно сохраняются перед каждой загрузкой страницы и ваши данные cookie передаются на сайт при каждом запросе.
    • Если вы получаете от клиента ошибки HTTP, особенно ошибку 403 Forbidden (доступ запрещен), то это может означать, что сайт идентифицировал ваш IP-адрес как адрес бота и не желает принимать какие-либо дополнительные запросы. Нужно либо подождать, пока ваш IP-адрес будет удален из списка, либо получить новый IP-адрес (например,
    сходить в ближайшее кафе или заглянуть в главу 17). Чтобы убедиться в отсутствии блокировок, попробуйте сделать следующее.
    • Убедитесь, что веб-скраперы не перемещаются по сайту слишком быстро. Быстрый веб-скрапинг — порочная практика. Она ложится тяжелым бременем на серверы веб-администратора, может привести к юридическим проблемам и является главной причиной попадания веб- скраперов в черный список. Добавьте в ваши веб- скраперы задержки и оставьте их работать на ночь.
    Помните: писать программы или собирать данные в спешке — признак плохого управления проектами;
    стройте планы заранее, в первую очередь чтобы избежать подобной суматохи.
    • Самое очевидное: смените заголовки! Некоторые сайты блокируют все, что объявляет себя веб-скрапером. Если вы не знаете точно, какие значения заголовков стоит использовать, то скопируйте заголовки из браузера.

    • Убедитесь, что не нажимаете что-то и не получаете доступ к чему-либо, обычно недоступному человеку (подробнее об этом см. в подразделе «Как избежать ловушек» раздела
    «Основные средства защиты форм» на с. 265).
    • Если обнаружится, что для получения доступа вам требуется слишком много «танцев с бубном», то посмотрите, нельзя ли связаться с администратором сайта, объяснить ему ваши действия и получить разрешение на использование веб-скраперов.
    Попробуйте отправить письмо по адресу webmaster@<имядомена>
    или admin@<имядомена>.
    Администраторы тоже люди, и вы будете удивлены тем,
    насколько они готовы делиться данными.

    Глава 15. Тестирование сайтов с помощью веб-
    скраперов
    При работе с веб-проектами, в которых используется большой стек технологий, как правило, регулярно тестируется только серверная часть стека. У большинства современных языков программирования (включая Python) есть тот или иной фреймворк для тестирования, но фронтенд сайтов часто остается за пределами этих автоматических тестов, хотя именно он обычно является единственной частью проекта,
    которую видит клиент.
    Проблема отчасти состоит в том, что сайты, как правило,
    представляют собой смесь многих языков разметки и программирования. Можно написать юнит-тесты для разделов
    JavaScript, но эти тесты будут бесполезны, если HTML-код, с которым взаимодействует JavaScript, изменится таким образом, что JavaScript не будет выполнять предполагаемое действие на странице даже при правильной работе самого скрипта.
    Задачу тестирования клиентской части сайтов часто оставляют напоследок или делегируют программистам более низкого уровня, вооруженным в лучшем случае контрольным списком того, что нужно проверить, и средством для отслеживания ошибок. Но если заблаговременно приложить немного больше усилий, то можно заменить этот контрольный список серией юнит-тестов, а человеческий глаз — веб- скрапером.
    Вы только представьте: веб-разработка через тестирование!
    Ежедневные тесты, позволяющие убедиться, что все части веб- интерфейса работают должным образом. Каждый раз, когда кто-то добавляет на сайт новую функцию или меняет
    положение элемента, запускается набор тестов. В этой главе мы рассмотрим основы тестирования и узнаем, как тестировать всевозможные виды сайтов, от простых до сложных, с помощью веб-скраперов на основе Python.
    Основы тестирования
    Если вам прежде не приходилось писать тесты для кода, то самое время начинать. Набор тестов, который можно запустить, чтобы убедиться в должной работе кода (по крайней мере, настолько, насколько вы написали тесты), экономит время и нервы, а также упрощает выпуск обновлений.
    Что такое юнит-тесты
    Термины «тесты» и «юнит-тесты» (иногда называют
    модульными) нередко считают взаимозаменяемыми. Часто,
    когда программисты говорят о «написании тестов», они действительно имеют в виду написание юнит-тестов. Но есть и такие, которые, говоря о написании юнит-тестов, на самом деле пишут какие-то другие.
    Определения и методы юнит-тестирования часто меняются от компании к компании, однако такое тестирование обычно характеризуется следу ющими общими свойствами.
    • Каждый юнит-тест проверяет один аспект функциональности компонента. Например, он может проверять, выдается ли соответствующее сообщение об ошибке при попытке снять с банковского счета отрицательное количество долларов.
    Юнит-тесты часто группируются в один класс для того компонента, который тестируют. Например, после теста на
    отрицательное значение суммы в долларах, снятых с банковского счета, может идти юнит-тест поведения банковского счета при попытке снять сумму, превышающую остаток.
    • Каждый юнит-тест может проводиться совершенно независимо от других. Все настройки или отмены настройки, требуемые для юнит-теста, должен совершать он сам. Аналогично юнит-тесты не должны влиять на успешное или неудачное прохождение других тестов и должны иметь возможность успешно выполняться в любой последовательности.
    • Каждый юнит-тест обычно содержит хотя бы одно
    утверждение. Например, юнит-тест может утверждать, что 2
    + 2 равно 4. Иногда такой тест содержит только состояние неудачи. Например, может завершиться неудачно при выдаче исключения, но выполниться по умолчанию, если все идет хорошо.
    • Юнит-тесты отделены от основной части кода. Они обязательно должны импортировать и использовать тестируемый код, однако обычно тесты хранятся в отдельных классах и каталогах.
    Есть много других типов тестов — например, комплексные и контрольные, — однако в этой главе основное внимание уделяется юнит-тестированию. Такие тесты не просто стали чрезвычайно популярными благодаря последним тенденциям разработки на основе тестирования; длина кода и гибкость этих тестов облегчают взаимодействие с ними в качестве примеров, а у Python есть ряд встроенных возможностей для
    юнит-тестирования, о которых вы узнаете в следующем разделе.
    Python-модуль unittest
    Библиотека юнит-тестирования Python под названием unittest входит в комплект всех стандартных инсталляционных пакетов Python. Достаточно импортировать и расширить класс unittest.TestCase, и у вас появятся следующие возможности:
    • функции setUp и tearDown, которые выполняются до и после каждого юнит-теста;
    несколько типов операторов утверждений, описывающих условия прохо ждения или непрохождения тестов;
    • возможность выполнять любые функции, имена которых начинаются с test_, как у юнит-тестов, и игнорировать функции, не объявленные тестами.
    Вот пример простого юнит-теста, позволяющего проверить,
    что в Python 2 + 2 = 4:
    import unittest class TestAddition(unittest.TestCase):
    def setUp(self):
    print('Setting up the test')
    def tearDown(self):
    print('Tearing down the test')
    def test_twoPlusTwo(self):
    total = 2+2
    self.assertEqual(4, total);
    if __name__ == '__main__':
    unittest.main()
    Несмотря на то что функции setUp и tearDown здесь не совершают никаких полезных действий, они все же включены в код в иллюстративных целях. Обратите внимание: эти функции выполняются до и после каждого теста, а не до и после всех тестов класса.
    Результат выполнения тестовой функции при запуске из командной строки должен выглядеть так:
    Setting up the test
    Tearing down the test
    -----------------------------------------------
    -----------------------
    Ran 1 test in 0.000s
    OK
    Это указывает на то, что тест пройден успешно и 2 + 2
    действительно равно 4.
    Выполнение unittest в Jupyter Notebook
    Все сценарии юнит-тестов в этой главе запускаются одинаково:
    if __name__ == '__main__':
    unittest.main()
    Условие if __name__ == '__main__' истинно только в том случае, если данная строка выполняется непосредственно в
    Python, а не с помощью оператора import. Это позволяет запускать юнит-тест, используя расширение класса unittest.TestCase
    , непосредственно из командной строки.
    В Jupyter Notebook все немного иначе. Параметры argv,
    создаваемые Jupyter, могут вызывать ошибки в юнит-тестах, и,
    поскольку фреймворк unittest по умолчанию завершает работу Python после выполнения теста (что вызывает проблемы в ядре Notebook), это необходимо предотвратить.
    В Jupyter Notebook мы будем запускать юнит-тесты так:
    if __name__ == '__main__':
    unittest.main(argv=[''], exit=False)
    %reset
    Во второй строке всем переменным argv (аргументам командной строки) присваиваются значения в виде пустых строк, которые unnittest.main игнорирует. Таким образом предотвращается еще и завершение работы unittest после выполнения теста.
    Строка %reset нужна для того, чтобы освободить память и уничтожить все переменные, созданные пользователем в
    Jupyter Notebook. Без нее каждый юнит-тест, написанный в
    Notebook, будет содержать все методы всех ранее
    выполненных тестов, которые тоже являются наследниками unittest.TestCase
    , включая методы setUp и tearDown
    Это также означает, что каждый следующий юнит-тест будет запускать все методы из предыдущих аналогичных тестов!
    Однако использование %reset говорит о том, что пользователь при выполнении тестов должен будет совершить еще одну операцию. При запуске теста Notebook выведет подсказку и спросит у пользователя, уверен ли он в своем желании освободить память. Чтобы это сделать, нужно просто ввести y и нажать Enter.
    Тестирование «Википедии». Чтобы протестировать клиентский интерфейс вашего сайта (за исключением скриптов JavaScript, о которых я расскажу далее), надо всего лишь объединить Python-библиотеку unittest с веб- скрапером:
    from urllib.request import urlopen from bs4 import BeautifulSoup import unittest class TestWikipedia(unittest.TestCase):
    bs = None def setUpClass():
    url
    =
    'http://en.wikipedia.org/wiki/Monty_Python'
    TestWikipedia.bs
    =
    BeautifulSoup(urlopen(url), 'html.parser')
    def test_titleText(self):
    pageTitle
    =
    TestWikipedia.bs.find('h1').get_text()
    self.assertEqual('Monty Python',
    pageTitle);
    def test_contentExists(self):
    content = TestWikipedia.bs.find('div',
    {'id':'mw-content-text'})
    self.assertIsNotNone(content)
    if __name__ == '__main__':
    unittest.main()
    На этот раз у нас два теста: первый проверяет, соответствует ли заголовок страницы ожидаемому Monty Python, а второй —
    есть ли на странице элемент div с контентом.
    Обратите внимание: контент страницы загружается только один раз, и глобальный объект bs используется тестами совместно. Это возможно благодаря тому, что в unittest определена функция setUpClass, которая выполняется только один раз в начале класса (в отличие от setUp, запускаемой перед каждым тестом в отдельности). Применяя setUpClass вместо setUp, мы исключаем лишние загрузки страницы;
    можно получить контент один раз и провести для него несколько тестов.
    Помимо того, когда и как часто выполняются эти функции,
    между setUpClass и setUp есть еще одно фундаментальное архитектурное различие: setUpClass — статический метод,
    который «принадлежит» самому классу и использует глобальные переменные класса, а setUp — функция экземпляра класса, и она принадлежит конкретному экземпляру класса. Именно поэтому setUp может
    устанавливать атрибуты для self — конкретного экземпляра класса, тогда как setUpClass может обращаться только к статическим атрибутам класса TestWikipedia.
    Тестирование каждой страницы в отдельности может показаться не таким уж мощным или интересным, однако, как вы, вероятно, помните из главы 3, сравнительно легко построить веб-краулер, который бы итеративно перемещался по всем страницам сайта. Что произойдет, если объединить веб-краулер с юнит-тестом, проверяющим одну страницу?
    Есть много способов запускать тесты многократно, но это следует делать осторожно, чтобы загружать каждую страницу только один раз для каждого набора тестов, которые вы хотите выполнить на этой странице. Кроме того, следует по возможности не хранить в памяти слишком много информации одновременно. Именно так работает следующая функция:
    from urllib.request import urlopen from bs4 import BeautifulSoup import unittest import re import random from urllib.parse import unquote class TestWikipedia(unittest.TestCase):
    def test_PageProperties(self):
    self.url
    =
    'http://en.wikipedia.org/wiki/Monty_Python'
    # протестировать первые 10 попавшихся страниц for i in range(1, 10):
    self.bs
    =
    BeautifulSoup(urlopen(self.url), 'html.parser')
    titles = self.titleMatchesURL()
    self.assertEquals(titles[0],
    titles[1])
    self.assertTrue(self.contentExists(
    ))
    self.url = self.getNextLink()
    print('Done!')
    def titleMatchesURL(self):
    pageTitle
    =
    self.bs.find('h1').get_text()
    urlTitle
    =
    self.url[(self.url.index('/wiki/')+6):]
    urlTitle = urlTitle.replace('_', ' ')
    urlTitle = unquote(urlTitle)
    return [pageTitle.lower(),
    urlTitle.lower()]
    def contentExists(self):
    content = self.bs.find('div',{'id':'mw- content-text'})
    if content is not None:
    return True return False def getNextLink(self):
    # возвращает случайную ссылку,
    найденную на странице,
    # используя методику из главы 3
    links = self.bs.find('div',
    {'id':'bodyContent'}).find_all(
    'a', href=re.compile('^(/wiki/)
    ((?!:).)*$'))
    randomLink
    =
    random.SystemRandom().choice(links)
    return
    'https://wikipedia.org{}'.format(randomLink.att rs['href'])
    if __name__ == '__main__':
    unittest.main()
    Есть несколько моментов, на которые стоит обратить внимание. Во-первых, в этом классе содержится только один тест. Остальные функции с технической точки зрения являются лишь вспомогательными, даже если проделывают основную часть вычислительной работы с целью определить, был ли тест пройден без ошибок. Поскольку тестовая функция выполняет операторы утверждений, результаты теста возвращаются в тестовую функцию, в которой выполняются утверждения.
    Кроме того, хоть contentExists и возвращает логическое значение, функция titleMatchesURL возвращает сами значения для оценки. Чтобы понять, зачем возвращать сами значения, а не только логику, сравним результаты следующего логической проверки:
    ===============================================
    =======================
    FAIL: test_PageProperties
    (__main__.TestWikipedia)
    -----------------------------------------------
    -----------------------

    Traceback (most recent call last):
    File
    "15-3.py", line
    22, in test_PageProperties self.assertTrue(self.titleMatchesURL())
    AssertionError: False is not true с результатами выполнения оператора assertEquals:
    ===============================================
    =======================
    FAIL: test_PageProperties
    (__main__.TestWikipedia)
    -----------------------------------------------
    -----------------------
    Traceback (most recent call last):
    File
    "15-3.py", line
    23, in test_PageProperties self.assertEquals(titles[0], titles[1])
    AssertionError: 'lockheed u-2' != 'u-2 spy plane'
    Какой из них легче отлаживать? (В данном случае ошибка возникает из-за перенаправления, когда публикация
    http://wikipedia.org/wiki/u-2%20spy%20plane
    перенаправляет на статью под названием Lockheed U-2.)
    Тестирование с помощью Selenium
    JavaScript создает определенные проблемы не только при веб- скрапинге скриптов Ajax, описанном в главе 11, но и при тестировании сайтов. К счастью, у Selenium есть отличный фреймворк для работы с особенно сложными сайтами;
    собственно говоря, эта библиотека изначально была создана именно для тестирования сайтов!
    Несмотря на то что юнит-тесты на Python и на Selenium,
    безусловно, написаны на одном языке, в их синтаксисе на удивление мало общего. Selenium не требует представления юнит-тестов в виде функций внутри классов; здесь операторы assert не требуют круглых скобок, а тесты выполняются без каких-либо сообщений, за исключением сообщений о сбое:
    driver = webdriver.Chrome()
    driver.get('http://en.wikipedia.org/wiki/Monty_
    Python')
    assert 'Monty Python' in driver.title driver.close()
    При выполнении этого теста результаты выводиться не должны.
    Таким образом, тесты на Selenium можно писать более небрежно, чем юнит-тесты на Python, и операторы assert можно интегрировать в обычный код, если вы хотите, чтобы действие кода прекращалось, когда не выполняется какое-либо условие.
    Взаимодействие с сайтом
    Недавно я захотела связаться с небольшой местной компанией через контактную форму на их сайте, но обнаружила повреждение HTML-формы; когда я нажимала кнопку отправки, ничего не происходило. Проведя небольшое расследование, я обнаружила, что на сайте применялась простая форма с отправкой данных по электронной почте —
    каждый раз при заполнении формы отправлялось электронное письмо с контентом формы. К счастью, зная это, я сумела
    отправить представителям компании электронное письмо, в котором объяснила суть проблемы с их формой, и все же воспользовалась их услугами, несмотря на техническую проблему.
    Если бы я захотела написать традиционный веб-скрапер,
    который бы использовал или тестировал эту форму, то он,
    скорее всего, просто скопировал бы ее разметку и отправил электронное письмо напрямую, вообще минуя данную форму.
    Как же проверить функциональность формы и убедиться, что она правильно работает через браузер?
    В предыдущих главах мы уже обсуждали навигацию по ссылкам, отправку форм и другие виды интерактивных действий, однако все наши действия в своей основе были предназначены для того, чтобы обойти интерфейс браузера, а не использовать его. Но ведь Selenium позволяет именно вводить текст, нажимать кнопки и делать все остальное в браузере (в данном случае в Chrome в режиме консоли) и обнаруживать такие вещи, как неправильно написанные формы, плохой код JavaScript, опечатки в HTML-коде и другие проблемы, которые бы поставили в тупик обычных посетителей сайта.
    Главное условие в тестах подобного рода — концепция elements в Selenium. Я уже упоминала этот объект в главе 11,
    он возвращается при вызовах функций такого типа:
    usernameField
    =
    driver.find_element_by_name('username')
    Подобно тому как в браузере можно совершать всевозможные действия с различными элементами сайта,
    Selenium точно так же позволяет выполнять множество действий с любым элементом, в том числе следующие:
    myElement.click()
    myElement.click_and_hold()
    myElement.release()
    myElement.double_click()
    myElement.send_keys_to_element('content to enter')
    Элементы позволяют не только совершать одиночные действия — строки действий можно объединять в цепи
    действий, сохранять их и выполнять в программе один или несколько раз. Эти цепи полезны тем, что позволяют удобно объединять действия в длинные наборы, но при этом функционально идентичны явному вызову действия для одного элемента, как в предыдущих примерах.
    Чтобы увидеть эту разницу, рассмотрим страницу формы,
    размещенную по адресу
    http://pythonscraping.com/pages/files/form.html
    (которая уже использовалась в качестве примера в главе 10). Мы можем заполнить и отправить эту форму следующим образом:
    from selenium import webdriver from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.common.keys import Keys from selenium.webdriver import ActionChains from selenium.webdriver.chrome.options import
    Options chrome_options = Options()
    chrome_options.add_argument('--headless')
    driver = webdriver.Chrome(
    executable_path='drivers/chromedriver',
    options=chrome_options)
    driver.get('http://pythonscraping.com/pages/fil es/form.html')
    firstnameField
    =
    driver.find_element_by_name('firstname')
    lastnameField
    =
    driver.find_element_by_name('lastname')
    submitButton
    =
    driver.find_element_by_id('submit')
    ### МЕТОД 1 ###
    #firstnameField.send_keys('Ryan')
    lastnameField.send_keys('Mitchell')
    submitButton.click()
    ################
    ### МЕТОД 2 ###
    actions
    =
    ActionChains(driver).click(firstnameField)
    .send_keys('Ryan')
    .click(lastnameField)
    .send_keys('Mitchell')
    .send_keys(Keys.RETURN)
    actions.perform()
    ################
    print(driver.find_element_by_tag_name('body').t ext)
    driver.close()

    Метод 1 состоит в вызове функции send_keys для двух полей, после чего нажимается кнопка отправки формы. В
    методе 2 используется общая цепь действий, в которой после вызова метода perform последовательно производится нажатие каждого поля формы, после чего туда вводится текст.
    Результат выполнения скрипта в обоих случаях один и тот же.
    Независимо от того, какой из двух методов был применен,
    выводится следующая строка:
    Hello there, Ryan Mitchell!
    Помимо этих двух методов, есть еще один вариант, в дополнение к объектам, которые используются для обработки команд: обратите внимание, что в первом методе для отправки формы мы нажимаем кнопку отправки, а во втором —
    используем клавишу Enter (Ввод) после заполнения текстового поля. Поскольку существует множество последовательностей событий для выполнения одного и того же действия, есть большое количество способов совершить его с помощью
    Selenium.
    Метод drag-and-drop
    Нажатие кнопок и ввод текста — само по себе уже неплохо, но в чем Selenium действительно незаменим, так это в работе с относительно новыми способами взаимодействия в Интернете.
    Selenium легко управляет интерфейсами, построенными на основе метода перетаскивания
    (drag-and-drop).
    Для использования функции перетаскивания необходимо указать
    исходный элемент (подлежащий перетаскиванию) и задать либо величину смещения, на которую он будет сдвинут, либо
    конечный элемент, на который нужно перетащить исходный элемент.

    Пример интерфейса такого типа представлен на демонстрационной странице, расположенной по адресу
    http://pythonscraping.com/pages/javascript/draggableDemo.html
    :
    from selenium import webdriver from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver import ActionChains from selenium.webdriver.chrome.options import
    Options import unittest class TestAddition(unittest.TestCase):
    driver = None def setUp(self):
    chrome_options = Options()
    chrome_options.add_argument('-- headless')
    self.driver = webdriver.Chrome(
    executable_path='drivers/chromedriv er', options=chrome_options)
    url
    =
    'http://pythonscraping.com/pages/javascript/dra ggableDemo.html'
    self.driver.get(url)
    def tearDown(self):
    driver.close()
    def test_drag(self):
    element
    =
    self.driver.find_element_by_id('draggable')
    target
    =
    self.driver.find_element_by_id('div2')
    actions = ActionChains(self.driver)
    actions.drag_and_drop(element,
    target).perform()
    self.assertEqual('You are definitely not a bot!',
    self.driver.find_element_by_id('mes sage').text)
    На демонстрационной странице в теге div с идентификатором message выводятся два сообщения. Первое гласит:
    Prove you are not a bot, by dragging the square from the blue area to the red area!
    (Докажите, что вы не бот, перетащив квадрат из синей области в красную!) Затем, сразу же после выполнения этой задачи, выводится другое сообщение:
    You are definitely not a bot!
    (Вы точно не бот!) Как следует из примера, показанного на демонстрационной странице, перетаскивание элементов с целью доказать, что вы не бот, является обычной практикой во многих тестах капчи. Боты уже давно успешно перетаскивают объекты (ведь это всего лишь последовательность действий:
    нажать, удерживать и перемещать), однако идея предложить
    посетителю сайта перетащить тот или иной элемент с целью подтвердить, что он человек, еще долго не умрет.
    Кроме того, библиотеки капч с перетаскиваниями редко используют какие-либо трудные для ботов задания, такие как
    «перетащить изображение котенка на изображение коровы»
    (что потребовало бы от программы синтаксического анализа умения различать изображения котенка и коровы); вместо этого они часто задействуют упорядочение чисел или какую- либо другую довольно тривиальную задачу, подобную приведенной в предыдущем примере.
    Конечно, эффективность этих библиотек заключается в том,
    что вариантов слишком много, а такие тесты используются слишком редко; скорее всего, никто не озаботится созданием бота, способного проходить любые подобные тесты. В любом случае данного примера должно хватить для демонстрации того, почему никогда не следует применять описанный метод для крупных сайтов.
    Создание снимков экрана
    Помимо обычных возможностей тестирования, у Selenium есть интересная хитрость, которая позволит вам значительно упростить тестирование (или произвести впечатление на шефа): снимки экрана. Да, вы действительно можете получать фотоподтверждения прямо из юнит-тестов и вам не придется самолично нажимать PrtScn:
    driver = webdriver.Chrome()
    driver.get('http://www.pythonscraping.com/')
    driver.get_screenshot_as_file('tmp/pythonscrapi ng.png')

    Этот скрипт переходит на страницу
    http://pythonscraping.com
    , делает снимок экрана начальной страницы и сохраняет его в локальной папке tmp (для правильного сохранения файла она уже должна существовать).
    Снимки можно сохранять в различных графических форматах.
    unittest или Selenium
    Синтаксическая строгость и многословность Python- библиотеки unittest, возможно, желательна для большинства крупных наборов тестов. Но если нужно протестировать лишь несколько функций сайта, то единственным вариантом может оказаться гибкий и мощный Selenium. Что же выбрать?
    Раскрою секрет: вам не нужно выбирать. Selenium вполне пригоден для получения информации о сайте, а unittest позволит оценить, соответствует ли эта информация критериям прохождения теста. Нет никаких причин, по которым вы не могли бы импортировать инструменты
    Selenium в Python unittest, объединив лучшие свойства обоих.
    Например, в следующем скрипте создается юнит-тест для сайта с интерфейсом drag-and-drop с предположением, что при правильной работе после перетаскивания одного элемента на другой выводится сообщение You are not a bot! (Вы не бот!):
    from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.chrome.options import
    Options import unittest class TestDragAndDrop(unittest.TestCase):
    driver = None def setUp(self):
    chrome_options = Options()
    chrome_options.add_argument('-- headless')
    self.driver = webdriver.Chrome(
    executable_path='drivers/chromedriv er', options=chrome_options)
    url
    =
    'http://pythonscraping.com/pages/javascript/dra ggableDemo.html'
    self.driver.get(url)
    def tearDown(self):
    self.driver.close()
    def test_drag(self):
    element
    =
    self.driver.find_element_by_id('draggable')
    target
    =
    self.driver.find_element_by_id('div2')
    actions = ActionChains(self.driver)
    actions.drag_and_drop(element,
    target).perform()
    self.assertEqual('You are definitely not a bot!',
    self.driver.find_element_by_id('mes sage').text)
    Комбинируя Python unittest и Selenium, можно протестировать практически все, что есть на сайте. В сущности,
    присоединив некоторые библиотеки обработки изображений,
    описанные в главе 13, можно даже сделать снимок экрана веб- страницы и попиксельно проверить, что должно находиться на ней.

    1   2   3   4   5   6


    написать администратору сайта