Julkaistu
29.6.2021
-
Päivitetty

Datan hakeminen nettisivulta automaattisesti (data scraping)

Helppo ja nopea tapa hakea dataa automaattisesti nettisivuilta Pythonin ja BeautifulSoup4:n avulla

Lähdin tutustumaan "data scraping", eli datan automaattiseen hakemiseen (vai kaivamiseen/louhintaan?), päivittäessäni Suomessa myytävien sähköautojen dataa. Blogin julkaisun jälkeen uusia sähköautoja on tullut sen verran, että niiden manuaalinen päivittäminen alkoi olla niin töylästä, etten päivitellyt hintoja melkein vuoteen (pahoittelut).

Erinomaisen lähteen datan koneelliseen hakemiseen tarjoaa mahtava Ev-database.org, joka listaa sähköautojen teknisiä tietoja ja mm. laskeen niille "real world rangen", eli todellisen toimintamatkan, joka on ilmeisesti paljon lähempänä oikeita toimintamatkoja WLTP:n sijaan. Ev-database on rakennettu sen verran kauniisti ja modernisti, että sen koneellinen lukeminen on todella helppoa. Tämä tarkoittaa, että myös hakukoneet tykkäävät kyseisestä sivusta.

Datan hakemiseen päätin käyttää Pythonia ja BeautifulSoup4-kirjastoa, joka vaikuttaa olevan tällä hetkellä suosituin tapa hakea dataa. Yllätyksekseni koko prosessi oli todella yksinkertainen ja nopea.

Lähdetään tutustumaan miten helposti tämä onnistuu myös sinulta.

Pythonin ja kirjastojen asennus

Pythonin voi asentaa omalle tietokoneelle helpoiten python.org-osoitteesta.

Asennuksen jälkeen kannattaa avata Windowsilla Comman Prompt -pääte (löydät sen helpoiten kirjoittamalla start-valikkoon "command"). Voit tarkastaa, että Python asentui oikein kirjoittamalla:

python -V

Jos sait näkyville version, esim. "Python 3.9.5", on Python asennettu onnistuneesti.

Seuraavaksi on aika asentaa BeautifulSoup4-kirjasto (lyhenne bs4). Tämä onnistuu käyttämällä Pythonin sisäänrakenettua "package installeria", eli pip:iä Command Promptilla:

pip install beautifulsoup4

Nyt sinulla pitäisi olla asennettuna kaikki mitä tarvitset datan hakemiseen ja jäsentämiseen.

Datan hakeminen

Seuraavaksi voit avata haluamasi tekstieditorin. Itse tykkään käyttää Windowsin Visual Studio Codea, joka on ilmainen ja todella monipuolinen editori.

Luo seuraavaksi uusi Python-tiedosto haluamaasi sijaintiin, haluamallasi nimellä ja avaa se editorilla. Oma tiedostoni on scraper-ev-database.py ja se sijaitsee omassa ev-data -kansiossa.

Aloitetaan tuomalla tarvittavat kirjastot tiedostoon. Nämä ovat BeatifulSoup4 (bs4) ja urlopen urllib.request:ista (joka tulee Pythonin mukana).

from urllib.request import urlopen as uReq
from bs4 import BeautifulSoup as soup

Seuraavaksi määritetään muuttujaan my_url nettisivu, josta haluamme hakea dataa. Muokkasin ev-databasessa hakua kattamaan kaikki sähköautot yhdellä sivulla ja kopioin osoitteen.

my_url = "https://ev-database.org/compare/newest-upcoming-electric-vehicle#sort:path~type~order=.id~number~desc|range-slider-range:prev~next=0~1200|range-slider-acceleration:prev~next=2~23|range-slider-topspeed:prev~next=110~450|range-slider-battery:prev~next=10~200|range-slider-eff:prev~next=100~300|range-slider-fastcharge:prev~next=0~1500|paging:currentPage=0|paging:number=all"

Seuraavaksi avataan yhteys halutulle verkkosivulle, luetaan sen sisältö ja suljetaan yhteys.

uClient = uReq(my_url)
page_html = uClient.read()
uClient.close()

Nyt meillä on haluttu sivu page_html -muuttujassa.

Seuraavaksi käytetään bs4:sta html-sivun jäsentämiseen (parsing):

page_soup = soup(page_html, "html.parser")

Nyt meillä on data jäsennellyssä muodossa page_soup -muuttujassa.

Lähdetään hakemaan dataa html-tagien avulla. Haluamme ensin tunnistaa missä haluamamme datat sijaitsevat. Ev-databasessa data on onneksi saatavilla selkeästi ja johdonmukaisesti (vink vink Autovertaamo). Pienen lähdekoodin tutkimisen jälkeen löydämme tiedot, jotka haluamme etsiä. Ne löytyvät seuraavien classien sisältä: "title", "model", "battery", "acceleration", "erange_real", ja "fastcharge_speed_print". Nämä tiedot löytyvät jokaiselle sähköautolle, jotka ovat taas omissa diveissä classeinä "list-item".

Aloitetaan ensin etsimällä kaikki sähköautot, eli divit, joiden class on "list-item". Tallennetaan nämä muuttujaan containers.

containers = page_soup.findAll("div", {"class": "list-item"})

Ennen datan kirjoittamista, luodaan/avataan tiedoston, johon haluamme kirjoittaa tiedot. Käytetään .csv-tiedostoa ja avataan se kirjoitus ("w")-tilassa. Jos tiedostoa ei ole, luodaan se automaattisesti samaan kansioon .py-tiedoston kanssa.

filename = "ev-database"
f = open(filename + ".csv", "w")

Kirjoitetaan tähän tiedostoon haluamamme otsikkorivi:

headers = "brand,model,battery,acceleration,erange,charge\n"
f.write(headers)

Nyt voimme hakea jokaisen sähköauton tiedot omiin muuttujiin ja kirjoittaa ne tiedostoon. Tämä toistetaan for-loopilla jokaiselle sähköautolle (jotka etsimme ja tallensimme containers-muuttujaan aiemmin)

for container in containers:

   container_title = container.findAll("a", {"class": "title"})
   brand = container_title[0].span.text.strip()

   container_model = container.findAll("span", {"class": "model"})
   model = container_model[0].text.strip()

   container_battery = container.findAll("span", {"class": "battery"})
   battery = container_battery[0].text.strip()

   container_acceleration = container.findAll("span", {"class": "acceleration"})
   acceleration = container_acceleration[0].text.strip()

   container_erange_real = container.findAll("span", {"class": "erange_real"})
   erange_real = container_erange_real[0].text.strip()

   container_fastcharge_speed_print = container.findAll("span", {"class": "fastcharge_speed_print"})
   fastcharge_speed_print = container_fastcharge_speed_print[0].text.strip()

   f.write(brand + "," + model + "," + battery + "," + acceleration + "," + erange_real + "," + fastcharge_speed_print + "\n")

Nyt olemme kirjoittaneet .csv-tiedostoon kaiken haluamamme tiedon, joten voimme sulkea tiedoston:

f.close()

Sitten voimme suorittaa Python-scriptimme Comman Promptia käyttäen. Pidä huoli, että olet samassa kansiossa, jossa .py-tiedosto sijaitsee! Pääset liikkumaan ylempään kansioon "cd .." ja haluamaasi kansioon "cd [polku]", esim. "cd ev-data". Itse scriptin suoritus tapahtuu komennolla (jossa scraper-eve-database.py on minun nimeämäni tiedosto):

python scraper-ev-database.py

Nyt kansiossa pitäisi olla ev-database.csv (tai sinun nimeämäsi tiedosto), joka pitäisi näyttää kutakuinkin tältä (Excelissä):

Tässä vielä koodi kokonaisuudessaan kommenttien kera, jos haluat vain kopioida koodin.

from urllib.request import urlopen as uReq
from bs4 import BeautifulSoup as soup

my_url = "https://ev-database.org/compare/newest-upcoming-electric-vehicle#sort:path~type~order=.id~number~desc|range-slider-range:prev~next=0~1200|range-slider-acceleration:prev~next=2~23|range-slider-topspeed:prev~next=110~450|range-slider-battery:prev~next=10~200|range-slider-eff:prev~next=100~300|range-slider-fastcharge:prev~next=0~1500|paging:currentPage=0|paging:number=all"

# avataan yhteys, haetaan data ja suljetaan yhteys

uClient = uReq(my_url)
page_html = uClient.read()
uClient.close()

# html parser
page_soup = soup(page_html, "html.parser")

# .csv -tiedosto
filename = "ev-database"
f = open(filename + ".csv", "w")

# otsikkorivi .csv -tiedostoon
headers = "brand,model,battery,acceleration,erange,charge\n"
f.write(headers)

# haetaan jokaisen sähköauton ympäröivä html-elementti
containers = page_soup.findAll("div", {"class": "list-item"})

# haetaan haluamamme data jokaisesta sähköautosta ja kirjoitetaan se tiedostoon
for container in containers:

   container_title = container.findAll("a", {"class": "title"})
   brand = container_title[0].span.text.strip()

   container_model = container.findAll("span", {"class": "model"})
   model = container_model[0].text.strip()

   container_battery = container.findAll("span", {"class": "battery"})
   battery = container_battery[0].text.strip()

   container_acceleration = container.findAll("span", {"class": "acceleration"})
   acceleration = container_acceleration[0].text.strip()

   container_erange_real = container.findAll("span", {"class": "erange_real"})
   erange_real = container_erange_real[0].text.strip()

   container_fastcharge_speed_print = container.findAll("span", {"class": "fastcharge_speed_print"})
   fastcharge_speed_print = container_fastcharge_speed_print[0].text.strip()

   f.write(brand + "," + model + "," + battery + "," + acceleration + "," + erange_real + "," + fastcharge_speed_print + "\n")

# suljetaan tiedosto
f.close()

Toivottavasti tämä blogi avasi automatisoidun datan hakemisen mahdollisuuksia ja ehkä samalla yllätti, kuinka helppoa ja nopeaa se parhaimmillaan on. Itseni se ainakin yllätti.

<- Blogi

Tilaa tulevat kirjoitukset sähköpostiin

Kiinnostuitko myös tulevista kirjoituksistani? Kirjoittelen sen verran epäsäännööllisesti, että lähetän mielelläni sinulle viestiä uusista blogeistani.

Mahtavaa! Jatketaan pian sähköpostin välityksellä.
Oho, jokin meni pieleen. Kokeile ihmeessä uudelleen.