Selenium jest modułem dostępnym, między innymi dla języka programowania Python. Selenium daje nam możliwość sterowania przeglądarką internetową za pośrednictwem kodu. W głównej mierze jest wykorzystywany do testowania aplikacji webowych, pisania skryptów automatyzujących/scrapujących, czy botów. W tym wpisie, chciałbym wprowadzić Ciebie do pisania programów opartych o selenium, a także pokazać kilka ciekawych jego zastosowań.
Webdriver
Webdrivery, to po prostu sterowniki przeglądarek. Selenium oferuje drivery do większości z popularnych przeglądarek (Firefox, Chrome / Chromium, Opera itd…). Jeżeli już pobierzemy plik webdriver’a, możemy przystąpić do pisania kodu.
from selenium import webdriver path = '/home/.../geckodriver' driver = webdriver.Firefox(executable_path=path)
Z modułu selenium, zaimportowałem obiekt webdriver, a za jego pomocą utworzyłem sobie uchwyt obsługujący przeglądarkę Firefox. Uchwyt przypisany został do zmiennej „driver”. Zmienna path powinna zawierać ścieżkę do pliku webdrivera, dla konkretnej przeglądarki (dla firefoxa, nazywa się on geckodriver). Po wykonaniu tego kodu powinna uruchomić się przeglądarka internetowa.
Teraz każdą operację na przeglądarce będziemy inicjować za pomocą utworzonego uchwytu (zmiennej driver).
Nawigacja i lokalizacja elementów
Przejście do wskazanego adresu URL wyegzekwujemy za pomocą polecenia „get”, w sposób następujący.
driver.get('https://duckduckgo.com')
Na tym etapie, warto znać jeszcze dwa polecenia: forward(), oraz back(), które służą do nawigowania po historii przeglądarki.
Po wykonaniu naszego kodu wzbogaconego o powyższe polecenie, przeglądarka po otwarciu przejdzie do witryny Duckduckgo.com.
Skoro mamy już otwartą jakąś witrynę, możemy wykonać na niej pewną, określoną akcję. Niech więc program kliknie logo Duckduckgo.
Lokalizować elementy można za pośrednictwem:
- id – atrybut id w elemencie html
- name – atrybut name w elemencie html
- xpath – ścieżka xpath
- link text
- tag name – nazwa tagu html
- class name – wartość atrybutu „class”
- css selector
Logo w taki sposób prezentuje się w kodzie strony, skorzystam więc z lokalizowania za pomocą atrybutu id.
duck = driver.find_element_by_id('logo_homepage_link')
Przypisałem ten obiekt do zmiennej by można było się do niego odwoływać. Na ten przykład, teraz możemy wykonać na nim polecenie „click”, lub wyświetlić jego zawartość html.
duck.click() print(duck.get_attribute('outerHTML'))
Problem pojawi się jednak, gdy nie będzie elementu, który próbujemy wyszukać. Program się wtedy zwyczajnie „wywali”, dlatego mamy coś takiego jak wyjątki. O wyjątkach będzie trochę niżej, jednak ten jeden przykład podam już tutaj.
Musimy zaimportować następującą zależność z modułu selenium.
from selenium.common.exceptions import NoSuchElementException
I możemy już obsługiwać wyjątek, gdy wyszukiwany przez nas element, nie występuje na stronie.
try: duck = driver.find_element_by_id('logo_homepage_link') except NoSuchElementException: print('Nie ma takiego elementu')
Możesz podmienić argument funkcji „find_element_by_id” by zauważyć, że obsługiwanie wyjątku działa poprawnie.
Wysyłanie tekstu i klawiszy
Także bardzo istotna kwestia, na przykład przy wypełnianiu formularzy. Pokaże to na przykładzie wyszukiwarki duckduckgo.
Na samym początku zlokalizować trzeba pole do wprowadzania tekstu, a kolejno wysłać do niego tekst.
duck = driver.find_element_by_xpath('//*[@id="search_form_input_homepage"]') duck.send_keys('mcinm')
Obiekt wyszukałem za pomocą xpath, jest to ścieżka do elementu w kodzie html. By wprowadzić tekst wystarczy wywołać na uchwycie obiektu polecenie „send_keys”. Po wykonaniu tego kodu, w formularzu wyszukiwania widnieje tekst „mcinm”, by wywołać takie zapytanie, mógłbym wyszukać button lupy i go kliknąć. Selenium oferuje nam jednak możliwość wysyłania pojedynczych klawiszy, lub ich kombinacji. Wystarczy więc kliknąć enter, ale musimy zaimportować kolejną, odpowiadającą za klawisze zależność.
from selenium.webdriver.common.keys import Keys duck = driver.find_element_by_xpath('//*[@id="search_form_input_homepage"]') duck.send_keys('mcinm', Keys.RETURN)
Po wpisaniu w pole tekstowe wartości „mcinm”, program wyśle sygnał klawisza return (enter).
Polecenie clear(), wyczyści wartość formularza.
Nawigacja po kartach przeglądarki
Nie ma żadnego problemu by selenium operował na kilku kartach przeglądarki. Co prawda, nie możemy równocześnie dokonywać operacji na kilku kartach, bez przełączania się między nimi. Wtedy musielibyśmy operować na dwóch instancjach webdriver’a.
driver = webdriver.Firefox(executable_path=path) driver.get('https://duckduckgo.com') link = driver.find_element_by_partial_link_text('iOS & Android').click()
W tym przykładzie posłużyłem się znowu stroną duckduckgo. Dodałem polecenie wyszukujące element po tekście zawartym w tagu <a>. Po kliknięciu w ten link, strona odsyła nas do innej podstrony, uruchamiając ją w nowym oknie.
Jeżeli teraz bym próbował dokonywać jakichś operacji za pomocą mojego uchwytu „driver”, nadal operowałbym na pierwszej karcie. Można łatwo sprawdzić na jakiej karcie operuje driver, wyświetlając zmienną, która przechowuje adres url, na której znajduje się webdriver.
print(driver.current_url)
Teraz zmiana karty:
driver.switch_to.window(driver.window_handles[1])
Python selenium wyjątki
Python selenium jest modułem, który raczej będziemy zaprzęgali do pracy bardziej autonomicznej. Nie możemy sobie pozwolić by cały czas sprawdzać czy nasz program działa, czy się gdzieś nie wykrzaczył. A to jest bardzo prawdopodobne, bo albo jakiś element się nie załaduje, albo będzie chwilowy problem z łączem etc… Dlatego w przypadku pisania kompleksowych skryptów istotna jest odpowiednia obsługa wyjątków.
Już wyżej pokazałem prosty przykład implementacji wyjątku braku szukanego elementu. To jednak jeden z wielu zdefiniowanych w selenium wyjątków.
Pełną listę można znaleźć oczywiście w dokumentacji modułu: selenium wyjątki
W poniższym przykładzie próbuje przełączyć driver na sterowanie obiektem typu iframe. Taki oczywiście nie widnieje w kodzie witryny więc program przerwie pracę i zwróci mi błąd typu „selenium.common.exceptions.NoSuchFrameException”.
driver = webdriver.Firefox(executable_path=path) driver.get('https://duckduckgo.com') link = driver.find_element_by_partial_link_text('iOS & Android').click() driver.switch_to.frame('ramka') sleep(1) print(driver.current_url)
By temu zaradzić wystarczy zaimportować zależność obsługującą ten wyjątek.
from selenium.common.exceptions import NoSuchFrameException try: driver.switch_to.frame('ramka') except NoSuchFrameException: print('nie ma takiego elementu') sleep(1) print(driver.current_url)
Dzięki zmyślnemu pisaniu funkcji obsługujących wyjątki jesteśmy w stanie napisać w pełni autonomiczny i kompleksowy program, który nie będzie wymagał naszej ingerencji, aż do momentu rozbudowy jego funkcjonalności.
Zrzuty ekranu / screenshots
Funkcjonalność wykonywania zrzutów widoku naszej przeglądarki może być przydatna choćby do archiwizacji pewnych odczytów/danych. Selenium daje nam możliwość wykonywania zrzutów okna przeglądarki, a także zrzutów widoku konkretnych elementów.
Zrzut widoku całego okna przeglądarki wykonamy następującym poleceniem.
driver.save_screenshot('scr.png')
Ciekawsze możliwości daje funkcjonalność robienia zrzutów poszczególnych elementów/obiektów. Wystarczy stworzyć zmienną będącą uchwytem obiektu, a następnie wykonać na niej polecenie „screenshot()”.
driver = webdriver.Firefox(executable_path=path) driver.get('https://mcinm.pl/') element = driver.find_element_by_id('logo') element.screenshot('scr.png')
Wynikiem tych poleceń będzie wykonanie następującego zrzutu:
Opcje webdrivera
Webdriver może pracować według wielu przez nas zdefiniowanych wytycznych. Przykładowo, możemy uruchomić przeglądarkę, tak by pracowała w trybie incognito, była zmaksymalizowana, czy działała w trybie headless.
Do tego potrzebujemy kolejnej zależności modułu python selenium:
from selenium.webdriver.firefox.options import Options // firefox from selenium.webdriver.chrome.options import Options // chrome
Tryb incognito / prywatny
Poniżej przedstawiam uruchomienie przeglądarki w trybie prywatnym/incognito dla firefox’a i chrome’a.
from selenium import webdriver from selenium.webdriver.firefox.options import Options opts = Options() opts.add_argument('--private') driver = webdriver.Firefox(options=opts, executable_path=path) ------------------ Chrome ------------------ from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--incognito') driver = webdriver.Chrome(chrome_options=chrome_options)
Headless driver
Tryb headless polega na wyłączeniu graficznego interfejsu użytkownika. Jest to bardzo przydatne i często używane przy pisaniu różnych skryptów.
Jeżeli chcemy napisać skrypt pobierający jakieś dane z sieci, który sami egzekwujemy gdy tego potrzebujemy, bez sensu jest obserwowanie okna przeglądarki i czekanie, aż program zakończy pracę. To wszystko może dziać się przecież w tle, a my nie musimy tego widzieć.
Headless daje także lepsze rezultaty jeżeli chodzi o wydajność, dzięki czemu możemy uruchamiać więcej skryptów naraz. Dodatkowo headless driver można uruchomić na serwerze z systemem nie posiadającym GUI.
Tak wygląda uruchomienie przeglądarki w trybie headless.
from selenium import webdriver from selenium.webdriver.firefox.options import Options opts = Options() opts.add_argument('--headless') driver = webdriver.Firefox(options=opts, executable_path=path) driver.get('https://duckduckgo.com') driver.close() ------------------ Chrome ------------------ from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--headless') driver = webdriver.Chrome(chrome_options=chrome_options) driver.get('https://duckduckgo.com') driver.close()
W ten sposób skrypt wejdzie na stronę duckduckgo.com bez wyświetlania okna przeglądarki. Pamiętać jednak należy by zawsze po zakończeniu skryptu zamknąć driver’a. W przeciwnym wypadku w procesach może pozostać uruchomiony driver pobierający nasze zasoby. Dlatego pisząc bardziej skomplikowane skrypty należy pamiętać o poprawnej obsłudze wyjątków.
Chciałem przedstawić w tym tekście najbardziej podstawowe aspekty modułu python selenium. Z pomocą nabytej tutaj wiedzy, już powinieneś/aś móc stworzyć proste programy automatyzujące, czy scrapujące.
Nawet jeśli niektóre, funkcje zostały zaprezentowane tylko pobieżnie to przynajmniej wiesz o ich istnieniu, a więc przy rozwiązywaniu konkretnego problemu – będziesz wiedzieć czego szukać.
I to właśnie miał sprawiać powyższy tekst…
Można się z Tobą jakoś skontaktować? Będziesz kontynuował serię ESP?
Witaj, możesz napisać na mail m@mcinm.pl.
Kurs ESP będzie kontynuowany, właśnie zamieściłem część odnośnie modułu Wi-Fi (póki co tylko dla esp32). Wiem, że trwa to długo ale miałem ostatnio gorszy okres, postaram się poważniej tym zająć.