Wyzwanie Python #7: Rozwiązanie
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']}")