Internet är en näst intill outtömlig källa till information och data av varierande kvalitet. Också myndigheter och organisationer gör numera ofta aktuell information tillgänglig för medborgarna. Som exempel ska vi i denna laboration betrakta Nordpools data om elförbrukningen. Då man besöker deras webbplats http://wwwdynamic.nordpoolspot.com/marketinfo/consumption/sweden/consumption.cgi?interval=last8 får man se en tabell över den totala elförbrukningen i Sverige i MWh timme för timme under de senaste åtta dygnen. Data är mycket aktuella; senaste värdet i tabellen är mindre än en timme gammalt.
En nackdel med att se dessa data i en webbläsare är att man inte kan fortsätta bearbetningen själv i ett program. Tabellen visar medelvärden samt max- och minvärden per dygn och för hela perioden och man kan också välja att få se data i grafisk form, men det finns många andra sätt att bearbeta data som skulle kunna vara intressant. Tekniken att hämta data över nätet och sedan förädla dessa för vidare bearbetning kallas på engelska för web scraping.
I denna uppgift ska ni först skriva ett program som läser elförbrukningsdata från nätet och lagrar dessa i en heltalsmatris av lämplig storlek, dvs int[24][8]. När programmet körs onsdagen 20 januari 2010 klockan 10.40 ger det följande utskrift:
19643 19780 18990 17975 17464 17639 17925 17825
19283 19450 18507 17684 17236 17547 17626 17523
19245 19202 18374 17457 16994 17325 17366 17521
19340 19206 18303 17414 16961 17561 17455 17389
19671 19417 18665 17473 17011 17763 17780 17683
20344 20196 19536 17781 17128 18662 18767 18755
22756 22127 21556 18228 17592 20762 20738 20954
24925 24305 23649 18846 18041 22902 22926 23239
25272 24424 24145 19481 18538 23322 23078 23386
24737 24349 23976 20029 19049 23185 22939 23175
24920 24415 23993 20396 19608 23387 23162 0
24768 24330 23828 20512 20134 23437 23304 0
24616 23829 23626 20426 20410 23076 22967 0
24334 23772 23220 20360 20433 23002 22763 0
24153 23636 23081 20186 20517 22831 22715 0
24805 23958 23033 20582 20843 23253 23096 0
25413 24382 23409 21383 21546 23831 23499 0
25523 24530 23593 21763 21714 24132 23732 0
25036 24286 23099 21296 21679 23637 23368 0
24368 24001 22261 20869 21439 22962 22742 0
23677 22896 21247 20093 20697 21972 21688 0
22760 21975 20332 19460 19901 21128 20735 0
21754 20897 19636 18718 19018 20111 19682 0
20303 19618 18813 18050 18143 18723 18545 0
Den sista kolumnen (representerande "idag"), innehåller då bara data fram till klockan 10; resterande värden anges som 0.
För att illustrera hur data som dessa kan användas tillhandahåller vi en klass ECIcon som kan skapa ikoner, som visar den senaste veckans elkonsumtion. En ikon är en grafisk representation av data som kan användas i klickbara komponenter. Objekt av vår klass ECIcon kan skapas i olika storlekar; här visas de i storlekarna 1, 3 och 6:
Storleken anger antalet timmar som slås samman till en pixel på bredden; i storlek 3 har alltså ikonen storleken 8*24/3 = 64 pixels på både bredden och höjden. Det röda strecket anger konsumtionsnivån 15000 MWh/h.
Er uppgift är att definiera en subrutin i klassen URLReader
som läser data från websidan över nätet och fyller en matris av heltal som den som skrivits ut ovan; matrisen ges som parameter. Rutinen har därför följande signatur:
public static void getEightDays(int[][] data) throws Exception
Rutinen förväntar sig att data är en 24x8-matris; denna ska fyllas med den senaste veckans konsumtionsdata. Vi gör det genom att begära data från Nordpool på precis samma sätt som en webbläsare gör. Vi är alltså beroende av att hämta data över nätet från en webbplats; detta kan förstås misslyckas, i vilket fall rutinen kastar ett undantag.
Att hämta data över nätet från ett Java-program är mycket enkelt med hjälp av de bibliotek Java tillhandahåller. Problemet är i stället att det vi får är HTML-data som beskriver hela webbsidan; data består av många hundra rader. De data vi söker finns här, men är inte så lätta att hitta. Huvudsvårigheten är därför att finna dessa värden och lagra dem i matrisen data.
Börja med att ladda ner URLReader.java till er katalog (förslagsvis katalogen lab6). Kompilera och kör programmet. Om allt fungerar så ska ni på terminalen få en listning av hela HTML-koden för webbsidan från Nordpool. Kör ni programmet på egen dator måste den förstås vara ansluten till Internet för att detta ska fungera. Läs nu igenom programmet. Som synes behövs det bara två rader i början på main för att få tillgång till ett objekt in av standardklassen Scanner, som innehåller data från Nordpool:
URL url = new URL(nordpoolURL);
Scanner in = new Scanner(new InputStreamReader(url.openStream()));
nordpoolURL
är här en sträng innehållande adressen till sidan vi vill läsa, som definierats tidigare i samma fil. Vad klasserna URL
och InputStreamReader
exakt gör behöver vi inte veta för denna labb. Men klassen Scanner
behöver vi veta hur man använder på ett mer sofistikerat sätt än tidigare. Det är med hjälp av dess metoder vi ska söka de data vi behöver. Den givna main använder in på enklaste sätt; så länge det finns fler rader hämtas en rad och skrivs ut på terminalen.
Härnäst föreslår vi att ni tittar på HTML-koden för sidan med data. Klicka på länken ovan och välj Page source i View-menyn i Firefox. Förslagsvis söker ni efter det första talet i matrisen; i vårt fall är det 19643, men för er kommer det förstås att vara ett annat tal, elkonsumtionen i Sverige mellan midnatt och 01 för åtta dagar sedan. Ni kommer att finna att det återfinns i en del av HTML-filen med följande principiella utseende:
<tr bgcolor="#e1e1e1">
<td>00-01</td>
<td align="right"> 19643</td>
<td align="right"> 19780</td>
<td align="right"> 18990</td>
<td align="right"> 17975</td>
<td align="right"> 17464</td>
<td align="right"> 17639</td>
<td align="right"> 17925</td>
<td align="right"> <b>17825</b></td>
</tr>
Det är inte viktigt att förstå HTML-koden, men här beskrivs en table row (HTML-tagg tr) som består av ett antal celler (HTML-tagg td). Vi känner också igen talen som de som återfinns på första raden. Dessutom kommer de andra raderna med samma principiella utseende i turordning därefter; så vad det gäller är att hoppa förbi början av filen fram till ovanstående och sedan komma åt själva talen och rensa bort resten. Här kommer klassen Scanner till hjälp: den erbjuder en metod
String findInLine(String pattern)
med vars hjälp man kan söka efter en sträng som matchar argumentet pattern i den aktuella raden i input. Matchningen sker med hjälp av reguljära uttryck. Reguljära uttryck som hjälp för att genomföra sökning förekommer i många olika sammanhang och det är därför väl värt att lära sig något om sådana. Man kan söka på nätet, men vi erbjuder också en kort introduktion.
Vi utgår nu från att ni läst detta och är beredda att gå vidare. Vi observerar att strängen "00-01" på raden före det första talet i tabellen kan användas för att identifiera denna rad i filen; denna sträng förekommer inte någon annanstans. Vi kan alltså göra in.findInLine("00-01") för att se om nästa rad i in innehåller strängen "00-01". Metoden findInLine fungerar så att om strängen återfinns i raden läses input fram till och med denna sträng (och strängen returneras); annars läses ingen input och null returneras. Så vi får helt enkelt läsa nya rader så länge findInLine returnerar null. När resultatet blir något annat än null så vet vi i själva verket att resultatet blir "00-01"; viktigare är att aktuell position nu är direkt efter denna sträng i input; dvs före </td>
på den andra raden i utdraget ovan. Vi kan läsa förbi resten av raden och är därefter redo att läsa raden som innehåller det första talet. Följande kodavsnitt åstadkommer det som beskrivits i detta stycke:
while (in.findInLine("00-01") == null) in.nextLine();
in.nextLine(); // skip rest of the line containing "00-01"
Det återstår nu att fortsätta att läsa filen för att fylla matrisen. Detta gör vi genom att 24 gånger (en för varje timme på dygnet)
läsa åtta rader från filen med data för denna timme under de åtta dagarna; för varje rad ger vi ett reguljärt uttryck till findInLine
som matchar ett heltal (en följd siffror). Vi vet att ett sådant tal finns på raden; för första raden ovan kommer findInLine
att returnera "19643", osv.
läsa förbi tre ointressanta rader innan nästa timme börjar.
Observera att sista kolumnen ("idag") ska innehålla 0 för de timmar där data ännu inte finns och där Nordpools tabell anger -
. Observera också att metoden ska bara ha parametern data. Det betyder att skapandet av in som i den givna filen görs i main måste flyttas till getEightDays
. Metoden får heller inte fånga de eventuella undantag som kastas vid skapandet av in; klienter får avgöra vad som ska göras i dessa fall. Slutligen ska första index i resultatet ange timme (mellan 0 och 23) och andra index dag (mellan 0 och 7).
Klassen ska också, som test, innehålla en main-rutin som deklarerar och skapar en 24x8-matris, anropar getEightDays
för att fylla den med senaste veckans elförbrukning samt skriver ut matrisen på terminalen som ovan.
Vi tillhandahåller klasserna ECIcon och ECPanel som ni ska ladda ner till er katalog. Ni kan provköra ECPanel i jGrasp.
Klassen är i första hand till för att tillhandahålla en grafisk komponent, men innehåller också en main för teständamål. Observera att i metoden update i ECIcon anropas er metod URLReader.getEightDays()
. Det är därför viktigt att klass och metod har precis dessa namn. Prova att klicka på ikonen och se en webbläsare med tabellen öppnas. (Hur man öppnar en webbläsare varierar mellan plattformar. Klassen ECPanel är skriven för att fungera i våra labbsalar; på en egen dator måste argumentet till Runtime.exec kanske ändras. På en Mac ersätts t ex strängen "firefox " i argumentet till exec med "open ".)
Ni behöver inte gör någonting med den grafiska komponenten. Den är bara till för att visualisera den data ni hämtar in.
Observera till sist att ert program har kontakt med Nordpool på två sätt:
Varje gång ni startar programmet kontaktar det Nordpool, de senaste åtta dagarnas konsumtion hämtas och ikonen ritas upp. Ikonen ser alltså olika ut varje gång programmet startas (utom, förstås, om ni startar det två gånger under samma timme). Däremot ändras inte ikonen under programmets exekvering, även om ni skulle spela flera dygn i sträck...
Om ikonen ser intressant ut, kan man klicka på den; då öppnas en webbläsare med den fullständiga webbsidan (och då är det webbläsaren som kontaktar Nordpool, inte ert program).
Vi är beroende av att Nordpools webb tillhandahåller data på den URL som ges i början på denna labbinstruktion. Tyvärr är det fullt möjligt att Nordpool när som helst organiserar om sin webb och gör ovanstående URL felaktig; detta har skett två gånger sedan denna labbinstruktion skrevs. Om URL-en ovan inte fungerar kan ni i stället testa ert program med http://www.cse.chalmers.se/edu/course/TDA540HT/Laborations/lab6/nordpool.html
Detta är en fil med rätt struktur, men förstås inte med aktuella data.
Som vanligt redovisar ni labben genom att skicka in er lösning i fire. Glöm inte trycka på Sumbit
-knappen.