Vi scraper med Jsoup, Chrome og selectors

Det er nemt at hive data og tekst ud af websider. Her bruger vi Java-biblioteket Jsoup sammen med Chrome-browseren.

Screenscraping bliver man aldrig for gammel til. Selv i disse tider, med åbne API’er, offentlige data, Json og alt muligt, kan det stadig være den nemme måde at gå efter data på, om det så er ens egne eller andres. Her er et eksempel fra vores helt egen verden.

Version2’s artikler indeholder en lang række faktabokse, og det giver sjældent mening at skrive dem igen og igen fra bunden af. Over kantinens spegepølsemadder er ideen om at have dem alle sammen i et simpelt regneark i vores Google Docs-system blevet drøftet.

Den mest lige vej til data ville være at spadsere op af trappen til webudviklerne på næste etage og bede om et udtræk af artikeldatabasen. Men de hårdtarbejdende mennesker har jo også andet at se til, og data - de rå artikler - skal parses, uanset om de trækkes fra databasen eller via HTTP.

Så vi scraper i stedet for. I vore dage findes der masser af biblioteker til scraping, som kan gøre processen nemmere end selv at rode med parsere og regular expressions, og her i artiklen benytter vi et Java-bibliotek med navnet Jsoup, som retter sig imod HTML af god eller mindre god beskaffenhed.

Jsoup byder på et API, der er lige ud af landevejen. Hvis jeg vil snuppe titlen på en webside, kan det se sådan ud:

Document doc = Jsoup.connect("http://www.version2.dk/").get();
String title = doc.title();

Som det fremgår, parser Jsoup websiden ind i et såkaldt DOM-objekt, en Document Object Model, der indeholder websidens elementer som noder i en graf.

Næste skridt i processen er at undersøge den del af HTML-koden, som indeholder den ønskede tekst, og det gør jeg ved at opmale noget af teksten i Google Chrome, højreklikke og vælge undersøg i menuen der dukker frem.

Nu åbnes Chromes DevTools-vindue. Øverst i vinduet vises HTML-koden, og når musen føres hen over elementerne, hilites de enkelte dele i selve browservindet.

Når jeg holder musen hen over elementet som er markeret <aside class="plugin right box">, er det netop fakta-boksen, der hilites i browseren.

Ved at højreklikke på elementet, kommer en menu frem, hvor jeg vælger punktet copy og undermenuen copy selector, som vist herunder.

Selector’en er et css-udtryk, som peger på en bestemt formatteret del af kildekoden. Den ser sådan ud, hvis man indsætter den i en editor:

#mini-panel-article_content > section.body > aside

Jsoup kan benytte selector-udtrykket i Document-objektets select-metode:

Document doc = Jsoup.connect("https://www.version2.dk/artikel/derfor-kan-kotlin-blive-java-folkets-darling-1077739").get();
Elements faktaboks = doc.select("#mini-panel-article_content > section.body > aside");
System.out.println(faktaboks);

Og det giver os det ønskede resultat i første hug:
<aside class="plugin right box"> 
 <h3>JVM-platformen</h3> 
 <p>Java er et programmeringsprog med en tilhørende virtuel maskine (VM), som benyttes til at afvikle programmer kompileret til byte-kode, en slags virtuelle cpu-instruktioner.</p> 
 <p>Andre sprog kan også kompileres til byte-kode, og denne verden omtales undertiden som JVM-platformen. Mange af disse sprog kan også benytte biblioteker skrevet i Java.</p> 
 <p>Ud over Java og Kotlin byder platformen på mange andre sprog, såsom Scala, Groovy og Clojure.</p> 
</aside>

Det næste skridt går ud på at kigge alle artiklerne igennem for faktabokse. Version2 har ikke nogen URL, hvor man kan løbe artikler igennem efter et fortløbende id-nummer, og hvis der findes sådan en, er der ikke nogen, der har vist mig den. Men det er ikke så galt, for artiklerne kan listes ved hjælp af en URL der ser ud som herunder:

https://www.version2.dk/it-nyheder?page=1

– hvor sideparameteren inkrementeres løbende. Derefter handler det bare om at skabe en løkke, og stoppe, når vi når langt nok tilbage, f.eks. de sidste fem års artikler.

De enkelte liste-sider skal nu undersøges for URL’er, der peger på artiklerne. Ligesom før bruger vi Chromes DevTools. Der er nogle pile, der skal foldes ud for at finde alle elementerne, men det er nu ikke så svært.

I dette tilfælde finder Chrome en meget lang selector, der ender med '...div > section:nth-child(1) > aside'.

Udtrykket ':nth-child(1)' peger på én bestemt del, men vi vil gerne have alle de dele, som ser ud på samme måde.

Vi sletter derfor den sidste del af selectoren, så udtrykket ender med 'div', og håber på det bedste.

Nu anvender vi select-metoden som før, og gemmer resultatet i en variabel vi kalder elements.

Det giver os nu en klump HTML med alle artiklerne på list-visningen:

<div class="items-pane front-article-list"> 
 <section class="container has-gutter"> 
  <aside class="grid w2"> 
   <a href="/artikel/zuckerberg-holder-oeje-med-nynazister-1079277"><img src="https://www.version2.dk/sites/v2/files/styles/image_list_thumbnail/public/illustration/2017/08/facebook-mark-zuckerberg.facebook-mark-zuckerberg?itok=JLUdL_qY" width="100" height="95" alt="" title=""> </a> 
  </aside> 
  ...

HTML-klumpen kan parses med Jsoup-objektets parseBodyFragment-metode:

Document fragment = Jsoup.parseBodyFragment(elements.html());
Elements links = fragment.getElementsByTag("a");
for (Element link : links) {
     String linkHref = link.attr("href");
     System.out.println(linkHref);
}

Det giver os links til artiklerne, blandet med lidt andre links:

/artikel/zuckerberg-holder-oeje-med-nynazister-1079277
/artikel/zuckerberg-holder-oeje-med-nynazister-1079277
/jura
/privacy
…

Herfra er det blot et spørgsmål om at filtrere for URL’er, som begynder med /artikel, og så er faktaboksene i kassen.

Med ganske få linjers kode fik vi udtrukket faktaboksene fra artiklerne. Næste skridt er at transformere HTML-kode til Drupal-markup og gemme som komma-separerede værdier. Det tager vi i en senere ombæring.

Der er mere information og dokumentation om Jsoup på projektets hjemmeside.

Tips og korrekturforslag til denne historie sendes til tip@version2.dk
Følg forløbet
Kommentarer (8)
Peter Hansen

Fedt indlæg! Dejligt at se jer tage den virkeligt nørdede hat på med inspiration fra "robotics"-bevægelsen, der jo for alvor stiller krav til bredere faggrupper om at tilegne sig/anvende scripting skills. Stor ros herfra for, at I vender blikket indad og delagtiggør læserne i processen.

Fremadrettet ville jeg personligt også være interesseret i, at I måske tog udgangspunkt i Python-platformen og BeautifulSoups muligheder, men det meste kode herunder Javascript kan jo læses og forstås af folk med en almen, digital dannelse.

Simon Mikkelsen

Det er ikke fortløbende numre. Ihvertfald er der ikke en side på https://www.version2.dk/1079278.

Når jeg trykker på linket bliver jeg sendt videre til https://karriere.jobfinder.dk/da/artikel/topchef-ramboell-fire-lektier-u...

Linket redirecter og det kan tilsyneladende redirecte til både v2 og ing, så jeg vil kalde nummeret temmelig fortløbende.

Sune Marcher

JSoup er et rigtigt lækkert library - kombination af at det kan parse HTML (og ikke kun f.eks. super korrekt formateret XHTML) blandet med CSS selectors gør det rigtigt anvendeligt.

Jeg lavede et lille scraping projekt sidste jul, hvor jeg kombinerede JSoup til parsing og OKHttp til effektivt retrieval. Det blev skrevet i Kotlin, for at få lidt erfaring med dét sprog, og håndterer login (baseret på cookies). Hvis der er nogen der er interesseret, kan det ses her - jeg vil ikke påstå at det hverken er perfekt eller idiomatisk Kotlin-kode :-)

Peter Sørensen

Fed artikle men scraping har jo eksisteret i 100 år. For dem som arbejder med PHP så kan det snilt gøres med DomDocument og nogen lunde de samme kommandoer bare i php.

Vi laver en del opgaver i https://indexed.dk som henter indhold til analyse fra andre hjemmesider og her benyttes også dom. Det kan f.eks. værer priser på en bolig eller et produkt fra en webshop

Log ind eller Opret konto for at kommentere
Pressemeddelelser

Welcome to the Cloud Integration Enablement Day (Bring your own laptop)

On this track, we will give you the chance to become a "Cloud First" data integration specialist.
15. nov 2017

Silicom i Søborg har fået stærk vind i sejlene…

Silicom Denmark arbejder med cutting-edge teknologier og er helt fremme hvad angår FPGA teknologien, som har eksisteret i over 20 år.
22. sep 2017

Conference: How AI and Machine Learning can accelerate your business growth

Can Artificial Intelligence (AI) and Machine Learning bring actual value to your business? Will it supercharge growth? How do other businesses leverage AI and Machine Learning?
13. sep 2017