Web crawler w Pythonie – podstawowa metoda

14-01-2024

Python nadaje się świetnie po pisania web crawlerów. Jest to nie tylko bajecznie proste, ale także niezwykle użyteczne.

Co to jest web crawler?

Web crawler to program, który przegląda Internet w poszukiwaniu określonych przez jego twórcę treści. Wachlarz zastosowań tego typu aplikacji jest bardzo szeroki. Od automatyzacji, po analizę i prowadzenie statystyk. Wyobraź sobie, ze chcesz napisać program, który będzie przeszukiwał różne portale z ogłoszeniami o pracę, a następnie będzie przedstawiał Ci wyłącznie przefiltrowane propozycje. Albo będzie obliczał będzie prowadził statystyki SEO dla wybranych słów kluczowych. Możesz zaprogramować go tak, że będzie wchodził na różne strony Internetowe, a następnie będzie pobierał informacje o długości tekstów, czy nasyceniu strony słowami kluczowymi. A może chcesz go wykorzystywać researchu? Zastosowań jest na prawdę wiele.

Niezbędne biblioteki

Do napisania prostego web crawlera nie potrzeba nam wiele. Request jest dostępny od razu po instalacji Pythona. Konieczne będzie wyłącznie zainstalowanie Beatifullsoup4.

pip3 install bs4

Zasada działania będzie dosyć prosta. Za pomocą request będziemy wykonywać zapytania do serwera i pobierać dane, a za pomocą BS4 będziemy nawigować po drzewie DOM. W poniższym przykładzie pomijam na razie wykonywanie requesta. Po prostu pokazuję Ci jak działa BS4.

from bs4 import BeautifulSoup

soup = BeautifulSoup("<p>I am learning <span>BeautifulSoup</span></p>", features="lxml")
found = soup.find('span')
print(found)

Powyższy przykład zwróci nam <span>. Użyłem także flagi features, ponieważ w przeciwnym wypadku Python zwróciłby ostrzeżenie, o użyciu domyślnego analizatora – lxml. Teraz możemy przystąpić do tworzenia naszego crawlera.

Omówienie naszego projektu

Nasz prosty web crawler będzie pobierał oferty pracy z pracuj.pl, a dokładniej oferty pracy z Javą w Lublinie. Zobaczmy jak wygląda struktura naszego adresu url:

https://it.pracuj.pl/praca/java;kw/lublin;wp?rd=30

Od tego adresu rozpoczniemy nasz request. Następnie nasz crawler przejdzie przez wszystkie podstrony, a zebrane dane będziemy mogli zapisać, albo wyświetlić. Myślę, że będzie to całkiem niezłe wprowadzenie do pisania web crawlerów w Python.

Web crawler w praktyce

Pobieramy oferty pracy z pierwszej strony

Pisanie naszego web crawlera zaczniemy od zaimportowania niezbędnych bibliotek i wykonania requesta. Zauważ, że w naszym przykładzie wyświetlam dane z requesta dwukrotnie. Samo page wyświetli nam jedynie status strony. W naszym przypadku będzie to status 200. Nam jednam zależy na tym, żeby dostać się do kodu html strony. Musimy więc użyć page.text.

import requests
from bs4 import BeautifulSoup

url = 'https://it.pracuj.pl/praca/java;kw/lublin;wp?rd=30'
page = requests.get(url)
print(page) #zwraca status - w naszym przypadku 200
print(page.text) #zwraca kod html

Nie jesteśmy w stanie jednak w ten sposób poruszać się po drzewie DOM, ani wydobywać ważnych dla nas treści w wygodny sposób. Do tego użyjemy biblioteki BeautifulSoup. Na początku spróbujmy zwrócić treść wszystkich nagłówków h2 na tej stronie

soup = BeautifulSoup(page.text, 'html.parser')
titles = soup.find_all('h2', {'data-test': 'offer-title'})
for title in titles:
    print(title.getText())

Wydaje mi się, że wskazany kod jest dosyć prosty. Najpierw używamy parsera html na naszym kodzie html, a następny używamy metody find_all, żeby pobrać wszystkie nagłówki h2, które posiadają data-test równe offer-title, które przez tę metodę są zwracane w formie tablicy. Za pomocą metody getText(), wyświetlamy treść naszych nagłówków.

Rozwiązanie problemu paginacji

Pojawia się jednak inny problem – strona wykorzystuje paginację, a my chcemy pobrać dane o wszystkich ofertach. Zastanówmy się jakie mamy opcje.

  1. Możemy wyświetlić wszystkie oferty na jednej stronie. Zwróć uwagę na adres url. Domyślnie zmienna rd jest równa 30. Zwiększając tę liczbę, np. do 100 jesteśmy w stanie wyświetlić wszystkie oferty na jednej stronie.
  2. Po dojściu do ostatnie strony znika button następna. Możemy więc po wyświetleniu wszystkich ofert z danej strony sprawdzać, czy ten button znajduje się na danej stronie. Jeśli tak, przechodzimy na kolejną stronę.
  3. Możemy już po uruchomieniu pierwszej strony sprawdzić ile mamy stron, a następnie wykonań taką ilość iteracji.

Pierwszą opcję sobie odpuścimy, ponieważ niczego się dzięki niej nie nauczycie. Zanijmy od opcji ostatniej. Najpierw pokażę Ci kod, a później wytłumaczę jak działa.

def is_pagination_button(tag):
    return tag.has_attr('data-test') and tag['data-test'].startswith('bottom-pagination-button-page-')

pagination_buttons = soup.find_all(is_pagination_button)
print(len(pagination_buttons))

W tym przypadku skorzystałem z tego, że buttony paginacji zawierają data-test = bottom-pagination-button-page-x, gdzie x, to oczywiście numer strony. Szukam więc tagów, które zawierają data-test, i jest wartość zaczyna się od bottom-pagination-button-page-.

Web crawler w Pythonie – cały kod

import requests
from bs4 import BeautifulSoup

def run(page):
    url = 'https://it.pracuj.pl/praca/java;kw/lublin;wp?rd=30&pn=' + str(page)
    page = requests.get(url)
    soup = BeautifulSoup(page.text, 'html.parser')
    return soup

def is_pagination_button(tag):
    return tag.has_attr('data-test') and tag['data-test'].startswith('bottom-pagination-button-page-')

pagination_buttons = run(1).find_all(is_pagination_button)
pages = len(pagination_buttons)

for i in range(1, pages):
    titles = run(i).find_all('h2', {'data-test': 'offer-title'})

    for title in titles:
        print(title.getText())

Zauważ w jak niewielu liniach udało nam się napisać działający webrawler, który na prawdę może być użyteczny! Sam skrypt oczywiście nie jest idealny, ale idealnie pokazuje jak można napisać crawler, który będzie pobierał te dane na których Ci zależy.

BeatifulSoup – garść dodatkowych informacji

Wyszukiwanie elementów

Oczywiście powyższy przykład w żaden sposób nie wyczerpał tematu BS4, ani metod wyszukiwania elementów na stronie. Często będziemy wyszukiwać elementy po ichj klasie, alb identyfikatorze. Dlatego postaram się w tym miejscu nieco poszerzyć ten temat. Załóżmy, że chcemy wyszukać element po jego id. Nic prostszego. Wystarczy użyć:

soup.find_all(id='my-testing-id')

Oczywiście uzyskamy w ten sposób listę elementów. Możemy więc ją wyświetlić w pętli, jak to robiliśmy w poprzednim przykładzie, albo wyświetlić konkretny odwołując się do jego klucza.

soup.find_all(id='my-testing-id')[0].getText()

W podobny sposób możemy odwołać się do elementu o określonym id

soup.find_all(class_='my-testing-class')[0].getText()

Należy tylko zwrócić uwagę na podkreślnik po class. Bardzo często jednak pisząc web crawler będziesz musiał poruszać się bardziej swobodnie po drzewie DOM. W końcu często zdarzają się sytuacje, że nie chcemy pobrać wszystkich elementów danej klasy, ale tylko te, które znajdują się, np. w określonym kontenerze. BeatifulSoup udostępnia metodę select, która właśnie do tego służy.

soup.select('body .hello-world')

Po prostu zawsze musisz myśleć o tym, czy nasz crawler nie pobierze danych na których nam w danej chwili nie zależy.

Pobieranie atrybutów

Do tej porty pobieraliśmy wyłącznie tekst z danego elementu, ale czasami potrzebujemy pobrać inne informacje, jak adres linku, bądź inne atrybuty. Możemy to zrobić w banalnie prosty sposób:

a['href']
input['name']

Przydatna strategia

Tworząc własne web crawlery pewnie może się okazać, że potrzebujesz mieć większą swobodę w przeskakiwaniu pomiędzy stronami. Nasz przykładowy crawler był dosyć prosty, ale co w sytuacji, kiedy chcielibyśmy wejść we wszystkie ogłoszenia i pobrać z nich odpowiednie dane?

Strategia jest dosyć prosta. W pierwszym kroku przechodzimy kolejno po wszystkich stronach, tak jak robiliśmy to w głównym przykładzie, ale nasz web crawler zamiast wyświetlać text, będzie zapisywał w tablicy adresy url wszystkich ogłoszeń. Mając już w tablicy wszystkie potrzebne nam adresy url, możemy kolejno po nich przejść i wyciągnąć te dane o które nam chodzi. Jest to bardzo popularna metoda, która na pewno Ci się przyda.

Ograniczenia tej metody

Podstawowym ograniczeniem, z którym pewnie często się spotkasz są strony renderowane przez JavaScript. We wspomnianej metodzie pobieramy kod html, a następnie parsujemy go za pomocą BeatifulSoup. Jeżeli strona została stworzona, np. w React, albo Angularze – ta metoda nie zadziała. W takich sytuacjach możemy skorzystać chociażby z Selenium. Istnieją też metody na blokowanie crawlownia stron. Wtedy pojawia się prośba o potwierdzenie, że jesteś człowiekiem. Na to też są sposoby, ale raczej wtedy nie korzysta się z tej metody. Jest ona jednak dosyć prosta, szybka i sprawdza się przy większości stron.

Dalsza rozbudowa skryptu

Jak wspomniałem wcześniej – skrypt jest raczej szkicem, który pokazuje jak może tworzyć web crawlery za pomocą request i BeatifulSoup. na pewno wypadałoby dodać obsługę wyjątków, pomyśleć nad zapisem naszych wyników do bazy danych – możesz skorzystać chociażby z sqlite, albo możesz wyświetlić wyniki w nieco bardziej atrakcyjnej formie. Może warto też przefiltrować wyniki? Często dostaniesz ogłoszenia pracy na stanowiska inne niż Java Developer (niestety pracuj.pl tak działa). Możesz usunąć wyniki, które zawierają w tytule inne języki programowania. A co w sytuacji, kiedy strona w ogóle nie posiada paginacji? Wtedy nie wykona się pętla, a związku z tym, żadne dane nie zostaną pobrane. Koniecznie trzeba o to zadbać! Jak widzisz, masz sporo możliwości dalszej rozbudowy tego skryptu.

Podsumowanie

Przedstawiona metoda jest jedną z kilku, które są używane do tworzenia web crawlerów. Do bardziej zaawansowanych rzeczy świetnie sprawdza się Scrapy, który jest frameworkiem i posiada wiele zaawansowanych funkcji. Mamy też oczywiście Selenium, które emuluje działanie przeglądarki internetowej. Jest to świetna biblioteka do automatyzowania pewnych czynności na stronach www, albo do pobierania określonych treści też się nadaje. Niewątpliwą zaletą jest to, że może bez problemu działać na stronach, które zostały stworzone, np., w oparciu o React, Vue, czy innego Angulara. Ale o tych metodach może powiemy sobie innym razem.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Archiwum

Ostatnie wpisy

Potrzebujesz strony? Dobrze trafiłeś!

  • W pierwszym kroku staram się rozpoznać potrzeby klienta i jego cele biznesowe - budowanie społeczności, rozpoznawalności marki, a może zwiększenie konwersji?
  • Przygotowanie projektu graficznego strony WWW. Strony powinny nie tylko być eleganckie i odzwierciedlać charakter Twojej działalności, ale przede wszystkim powinny spełniać założenia biznesowe.
  • Po akceptacji projektu graficznego mogę przystąpić do stworzenia strony. Tworzę je głównie w WordPressie, ale możliwe jest tworzenie dedykowanych rozwiązań.
  • Zoptymalizuję Twoją stronę pod kątem SEO.
  • Podłączę narzędzia do śledzenia konwersji i zainstaluję niezbędne wtyczki. Piszę także autorskie rozwiązania na potrzeby moich klientów.

Napisz do mnie

Aby wypełnić ten formularz, włącz obsługę JavaScript w przeglądarce.