Monday 24 April 2017

Web scraping eli tietojen "kaavinta"

Tänä keväänä tori.fi:llä järjesti toista kertaa Autokuume-nimisen kilpailuarvonnan. Kilpailussa Tori.fi piilottaa automyynti-ilmoitusten joukkoon voittoina olevat autot, jotka tulee löytää normaalien ilmoitusten joukosta jotta pääsee osallistumaan arvontaan. Palkintoautojen ilmoitukset ovat muuten sisällöltään normaalit, mutta ne tunnistaa siitä että yhdessä myyntikuvista auto on peitetty punaisella peitteellä. Ilmoitukset on piilotettu kolmena peräkkäisenä lauantaina, eli jaossa on yhteensä kolme autoa.

Autokuume 2017 palkinnot.
Autoista julkistetaan etukäteen kuva joissa autot on peitetty. Tosi autotietäjät tietysti tunnistavat automallit jo tästä kuvasta, mutta me muut voimme arvailla tai ainakin rajoittaa valikoimaa hieman ulkomuodon perusteella.

Tori.fi:ssä on nyt noin 70000 automyynti-ilmoitusta ja haluaisi löytää tuon palkintona olevan auton niin kaikkien ilmoitusten läpikäyntiin menisi noin 100h klikkailua koneen ääressä. Aikaa voi tietysi säästää tekemällä rajoituksia hakuun kuvan perusteella ja muuten olettaen palkintona oleva auton muita hakuominaisuuksia (hinta, vuosimalli, km). Siltikin jos jäljelle jäisi esim. 1500 hakutulosta niin niiden läpikäyntiin menisi noin 2h.

Miten tämä sitten liittyy tiedon kaavintaan eli web scraping:iin? No tämä hakutulosten läpikäynti voidaan helposti automatisoida. Aikasemmin kirjoitin auton ostosta Excelin avulla ja silloin jo mainitsin että kopioin autojen myyntidataa Nettiautosta iMacros-selainlaajennuksen avulla. Samaa työkalua voidaan käyttää myös tässä yhteydessä.

Homma lähtee siitä että tehdään tori.fi:ssä haku myytävistä autoista ja avataan ensimmäinen hakutulos.
Hakutulos.
Nyt kun lähdemme automatisoimaan tulosten läpikäyntiä niin meitä kiinnostaa autojen kuvat. Ison kuvan alla on pikkukuvat joista oletettavasti löydämme etsittävän auton joten tavoitteenamme on tallettaa ne ja siirtyä seuraavaan hakutulokseen klikkaamalla oikeassa yläkulmassa olevaa "Seuraava>>" -linkkiä ja toistaa kuvien tallettaminen uudelleen.

Kaiken tämän voimme tehdä iMacros-selainlaajennuksella. iMacros käyttää omaa, hyvin rajoittunutta, koodikieltä makroissa, mutta makroja on myös mahdollista höystää muilla ohjelmointikielillä kuten JavaScriptillä. Tässä tapauksessa iMacrosin omat ominaisuudet eivät riittäneet koska tarvitsimme kaksi erillistä silmukkaa. iMacros JavaScript-koodi, jota tässä tapauksessa käytettiin on seuraavanlainen:

//imacros-js:showsteps no
// Make a search
// Set number of hits and number of images to be downloaded
// Open first hit and run the script

var macro1 = "CODE:";
macro1 += "SET !TIMEOUT_STEP 0" + "\n";
macro1 += "TAG POS=1 TYPE=H1 ATTR=TXT:* EXTRACT=TXT" + "\n";
macro1 += "ONDOWNLOAD FOLDER=* FILE={{!NOW:yyyymmdd_hhnnss}}_{{hit}}{{image}}_{{!EXTRACT}}.jpg WAIT=YES" + "\n";
macro1 += "TAG POS={{image}} TYPE=IMG ATTR=SRC:https://d38a5rkle4vfil.cloudfront.net/image/thumbs* CONTENT=EVENT:SAVEPICTUREAS" + "\n";

var macro2 = "CODE:";
macro2 += "TAG POS=1 TYPE=A ATTR=ID:next_url" + "\n";

for (var hit = 1; hit <= 1500; hit++) 
{
   iimSet("hit", hit);
   iimPlay(macro2);
   
   for (var image = 1; image <= 6; image++) 
   {
      iimSet("image", image);
      iimSet("hit", hit);
      var ret = iimPlay(macro1);
      if (ret != 1)
         break;
   }
}

Koodissa macro1 hoitaa kuvien talletuksen. Ensin asetetaan aikakatkaisu nollaksi joka ehkä nopeuttaa makron suorittamista tilanteissa joissa kuvat loppuvat kesken. TAG komennolla etsimme ensimmäisen (POS=1) H1-tyyppiä olevan elementin sivulta joka on sivun otsikko joka näkyy selaimen yläpalkissa. Tätä käytetään seuraavalla rivillä osana tiedostonimeä. Tiedostonimi asetetaan siis FILE= parametrilla ja olen käyttänyt tässä tapauksessa ensin päiväys+kellonaika ja sen jälkeen monesko hakuosuma {{hit}} on kyseessä ja sen hakuosuman kuvan numero {{image}}. Sen jälkeen {{!EXTRACT}} lisää vielä edellisellä rivillä talletetun sivun otsikon tiedostonimeen, jotta löydetty auto on helpompi löytää uudelleen. Tiedosonimi on siis muotoa: 20170330_190349_31_Skoda Fabia Farmari Tsi.jpg. Viimeinen rivi macro1:ssä tekee varsinaisen kuvan talletuksen. Siinä siis käydään läpi kaikki kuvat joiden osoite alkaa https://d38a5rkle4vfil.cloudfront.net/image/thumbs. {{image}} on kuvan järjestysnumero jota kasvatetaan alempana olevassa for-silmukassa.

macro2 taasen hoitaa seuraavaan hakutulokseen siirtymisen. Siinä aktivoidaan ensimmäinen linkki jonka ID on next_url. Linkkien ID:t ja järjestysnumerot saa helpoiten selvitettyä nauhoittamalla iMacros-työkalulla makron jossa painetaan linkkiä.

Alimmaisena on sitten varsinainen JavaScript osuus joka generoi iMacros makrot. Siinä ulommainen for-silmukka määrittelee kuinka monta kertaa siirrytään seuraavaan hakutulokseen (hit). Tämän numeron tulee olla sama kuin hakutulosten määrä. Makro siis aloittaa suoraan siirtymällä seuraavaan hakutulokseen kun oletettavasti avoinna olevan sivu on jo tarkistettu manuaalisesti.

Sisempi silmukka taas määrittelee kuinka monta kuvaa ilmoituksesta talletetaan. Makrot palauttavat aina jonkun paluuarvon (ret). Tässä tapauksessa paluuarvo on 1 jos kuva löytyi ja jos kuvaa taas ei löydy niin palautusarvona tulee joku virhekoodi. Jos palautusarvo on jotain muuta kuin 1 niin sisemmän silmukan suorittaminen keskeytetään ja siirrytään seuraavaan hakutukseen. Näin emme turhaan käytä aikaa ylimääräisiin silmukoihin ilmoituksissa joissa on vain yksi kuva.

Näin saamme talletettua kaikkien hakutulosten pikkukuvat yhteen hakemistoon ilman että käyttäjän tarvitsee tehdä muuta kuin käynnistää makro. Kun makro on mennyt läpi hakutulokset muutamassa tunnissa, niin käyn vain hakemiston kuvat läpi ja etsin punaiseen kankaaseen käärityn auton ja pääsen osallistumaan arvontaan.

----------------------------------------------------------
Ja miten tämä sitten toimi käytännössä... no aika huonosti.

1.4. Oletin että palkintoauto olisi vuosimallia 2010 tai uudempi. Palkintoauto oli 2009 vuosimallia ja jäi haustani pois.
8.4. Jätin haustani pois mm. viistoperäiset kun ei kuvassa selaiselta näyttänyt, auto oli viistoperäinen.
15.4. En ollut koneen ääressä ko. päivänä.

Palkintoautot paljastettuna.
Seuraava vuotta varten muistiin seuraavaa:
  • Palkintoautot ovat yksityisen henkilön ilmoituksia
  • Palkinnon ilmaisevat kuvat ovat toinen ja kolmas kuva, eli riittää että ottaa kuvan 2 tai 3 talteen
  • Kuvien kuvasuhde on kamerakuvista poikkeva; 670x475