Wyzwanie Python #7: Rozwiązanie

Maciej Bartoszuk 7 stycznia 2022
 

Poniżej rozwiązanie do naszego siódmego wyzwania

Linki na stronie

To bardzo proste, ale pouczające zadanie. Zwróćmy uwagę na to, że przekazaliśmy argument href=True do find_all(). Oznacza to, że szukamy tylko takich znaczników, w których występuje atrybut href, niezależnie od jego wartości. Poza tym, gdy przyjrzymy się wyświetlanym wynikom, zobaczymy zarówno linki bezwzględne, jak i względne oraz odnoszące się do tej samej strony. Jeśli chodzi o wnętrze znacznika, czyli zazwyczaj tekst opisujący link, to okazuje się, że może to być nie tylko tekst, ale też inny znacznik, np. opisujący obrazek. Aby go wyświetlić, musielibyśmy użyć pola contents, a nie string.

import requests
from bs4 import BeautifulSoup

odpowiedz = requests.get("https://pl.wikipedia.org/wiki/Zygmunt_III_Waza")
soup = BeautifulSoup(odpowiedz.text, 'html.parser')

for link in soup.find_all('a', href=True):
    print(f"{link.get('href')} - {link.string}")

Giełda

Głównym problemem tego zadania jest kwestia jak “namierzać” poszczególne miejsca na stronie, np. komórkę tabeli, w której jest kurs. Okazuje się, że każda z poszukiwanych liczb jest zamknięta w znaczniku span, który ma unikatowy atrybut id. Dlatego możemy po prostu szukać po nim (nazwijmy takie podejście podejściem 1). Co ciekawe, to id zależy od trzyliterowego kodu spółki. Co jednak, gdybyśmy nie mieli tak wygodnie nazwanych znaczników? Wtedy możemy szukać np. po tekście jaki występuje nieopodal, np. Kurs. Nazwijmy takie poszukiwanie podejściem 2. Co, gdy już znajdziemy taki tekst? Możemy się odwołać albo do rodzica, a następnie w ramach tylko tego znacznika (rodzica) poszukiwać odpowiedniego znacznika span (podejście 2a), albo użyć next_element, aby przemieścić się dalej w kodzie źródłowym (podejście 2b).

Zwróćmy uwagę na jeszcze jedną istotną kwestię, tzw. czyszczenie danych. Samo odnalezienie napisu na stronie, np. (-1.53%) czy 1 491 to jedno. Jednak jak na takim tekście pracować? Trzeba go przekonwertować do zdatnego dla nas formatu danych, w tym wypadku liczbowego. Przyjmijmy, że chcemy uzyskać -0.0153 oraz 1491. Dlatego po pierwsze musimy usunąć zbędne znaki, jak nawiasy, spacje i znak procenta, a po drugie dokonać konwersji poprzez int() lub float().

Parametry do strony, czyli część adresu ?s=cdr możemy przekazać poprzez słownik i tak też robimy w rozwiązaniu (params w funkcji get()). Jest to szczególnie przydatne, gdy parametrów jest więcej, jak w adresie https://allegro.pl/kategoria/tv-i-video-telewizory-257732?order=m&bmatch=product-base-tel-1-3-0107&price_from=1000&price_to=3000&typ-telewizora=LCD.

import requests
from bs4 import BeautifulSoup

spolka = "kgh"
p = {"s": spolka}
odpowiedz = requests.get("http://stooq.pl/q/", params=p)
soup = BeautifulSoup(odpowiedz.text, 'html.parser')

# kurs
# podejście 1: po id
znacznik_kurs = soup.find("span", id=f"aq_{spolka}_c2")
kurs = float(znacznik_kurs.text)
print(f"Podejście 1, kurs = {kurs}")

# podejście 2a: po napisie, a potem find

znacznik_kurs = soup.find(text="Kurs").parent.find("span")
kurs = float(znacznik_kurs.text)
print(f"Podejście 2a, kurs = {kurs}")

#zmiana kursu

znacznik_zmiana_bezwzgledna = soup.find("span", id=f"aq_{spolka}_m2")
znacznik_zmiana_wzgledna = soup.find("span", id=f"aq_{spolka}_m3")

zmiana_bezwzgledna = float(znacznik_zmiana_bezwzgledna.text)
zmiana_wzgledna = float(znacznik_zmiana_wzgledna.text[1:-2])/100

print(f"Podejście 1, zmiana_bezwzgledna = {zmiana_bezwzgledna}")
print(f"Podejście 1, zmiana_wzgledna = {zmiana_wzgledna}")

# transakcje

# podejście 2b: po napisie, a potem next_element
znacznik_transakcje = soup.find(text="Transakcje").next_element.next_element
transakcje = int(znacznik_transakcje.text.replace(" ", ""))
print(f"Podejście 2b, transakcje = {transakcje}")

Filmweb

Zacznijmy od przypadku, gdy mamy podany dokładny link do informacji o filmie. Tutaj wszystko przebiega bardzo podobnie do poprzedniego zadania. Szukamy po tekście, następnie przemieszczamy się w kodzie w przód, a następnie czyściemy dane, np. usuwamy znak dolara w boxoffice.

Pewnym problemem może być znalezienie oceny filmu. Okazuje się, że nie jest ona od razu wpisana tam, gdzie ją widzimy, czyli w prawym górnym rogu i inspekcja tego miejsca w kodzie nie da nam pożądanego rezultatu. Powodem jest fakt, że ocena jest wpisywana w to miejsce już po załadowaniu strony, z poziomu JavaScript. Wynika to z faktu, że ocena nie jest wpisywana w niektórych przypadkach, np. gdy w danym kraju nie było jeszcze premiery danego filmu. Dlatego należy otworzyć kod źródłowy strony i poszukać (ctrl + F) oceny danego filmu, np. 7,8. Okazuje się, że znajdziemy ją w pewnym znaczniku span, który nie jest wyświetlany na stronie.

import requests
from bs4 import BeautifulSoup

odpowiedz = requests.get("https://www.filmweb.pl/film/Narodziny+gwiazdy-2018-542576")
soup = BeautifulSoup(odpowiedz.text, 'html.parser')
ocena = float(soup.find("span", itemprop="ratingValue").text.replace(",", "."))
rezyseria = soup.find(text="reżyseria:").next_element.find("a").text
scenariusz = soup.find(text="scenariusz:").next_element.find("a").text
gatunek = soup.find(text="gatunek:").next_element.find("a").text
oryginalny_tytul = soup.find("h2", class_="cap s-16 top-5").text
boxoffice = int(soup.find(text="boxoffice:").next_element.text[1:].replace(" ", ""))

print(f"ocena: {ocena}, rezyseria:{rezyseria}, scenariusz: {scenariusz}, gatunek: {gatunek}, oryginalny_tytul: {oryginalny_tytul}, boxoffice:{boxoffice}")

Przejdźmy teraz do szukania odpowiedniego linka na podstawie tytułu filmu. Zrobimy to dokładnie w ten sposób, w jaki robimy to jako ludzie. Wejdziemy na stronę wyników poszukiwań na Filmwebie (https://www.filmweb.pl/search, tytuł filmu to parametr q), a następnie wybierzemy pierwszy link, który kieruje do filmu o dokładnie takim tytule, o jaki prosiliśmy.

tytul = "Avengers"
url = f"https://www.filmweb.pl/search?q={tytul}",
p = {
        "q": tytul
    }
tytul = tytul.lower()
f = requests.get("http://www.filmweb.pl/films/search", params=p)
soup = BeautifulSoup(f.text, 'html.parser')
for znacznik in soup.find_all(class_="filmPreview__title"):
    tytul_znacznika = znacznik.text.lower()
    if tytul == tytul_znacznika:
        print(tytul_znacznika)
        print(znacznik.parent["href"])
        print(f"https://www.filmweb.pl{znacznik.parent['href']}")	

Maciej Bartoszuk

Ukończył z wyróżnieniem informatykę na wydziale Matematyki i Nauk Informacyjnych Politechniki Warszawskiej, gdzie aktualnie pracuje w zakładzie Sztucznej Inteligencji i Metod Obliczeniowych. Tam też od 2013 roku prowadzi zajęcia dydaktyczne z programowania w R, Pythonie, C/C++, C#. Uczestnik studiów doktoranckich w Instytucie Podstaw Informatyki Polskiej Akademii Nauk w latach 2013-2015. W 2018 roku obronił doktorat z wyróżnieniem na swoim rodzimym wydziale: System do oceny podobieństwa kodów źródłowych w językach funkcyjnych oparty na metodach uczenia maszynowego i agregacji danych, który obejmuje zarówno algorytmy przetwarzania kodów źródłowych programów, jak i data science. Współautor książki Przetwarzanie i analiza danych w języku Python wydanej przez PWN. Ponadto trener na bootcampach Data Science, gdzie uczy programować w języku Python pod kątem analizy danych.
Komentarze
Ostatnie posty
Data Science News #202
Data Science News #201
Data Science News #200