Tutorial: Website parsen mit dem ESP8266 – Teil 2

Einleitung

Im ersten Teil dieses Tutorials habe ich gezeigt, wie man Informationen nach einem Suchschema aus einer Website extrahiert. In diesem Teil des Tutorial möchte ich zunächst auch darauf eingehen, was man beachten sollte, wenn man eine Website auswählt, aus der Daten extrahiert werden sollen. Später werden wir dann ein zweites Beispiel genauer betrachten.

Der Inhalt einer Website

Die heutigen Websites werden immer umfangreicher und bieten eine Fülle von Informationen, was leider auch eine Menge von Daten zur Folge hat. Zusätzlich werden Animationen, Grafiken oder Werbung mit übertragen, was die Datenmenge zusätzlich erhöht. Je mehr Daten empfangen werden, umso mehr muss der ESP analysieren, um die gesuchte Information zu finden.

Nehmen wir als Beipiel an, wir wollen die aktuelle Temperatur an unserem Wohnort aus einer Website auslesen. Es gibt eine große Zahl von Websites, die diese Information bereit stellen. Um zu bewerten, ob eine Website geeignet ist, diese Information für den ESP verträglich zu liefern, sollte man sich den Quelltext der Website mal ansehen. Ich habe als Beispiel mal eine Website ausgewählt, die die aktuelle Temperatur für sehr viele Orte liefern kann. Auf der Seite http://www.wunderground.com/ kann man sich eine Messstation in der Nähe seines Wohnortes suchen. Die url für die Messstation am Kölner Flughafen wäre dann http://www.wunderground.com/global/stations/10513.html.

Eine durchaus auch sinnvolle Alternative wäre es, eine Website auszuwählen, die weniger Daten transferiert. Eine gute Adresse sind in einem solchen Fall z.B. private Websites, die Informationen ihrer Wetterstationen online stellen.

Auf jeden Fall sollte man sich jetzt mal den Quelltext dieser Website ansehen, am einfachsten geht das z.B. im Firefox über den Menuepunkt Entwicklerwerkzeuge->Seitenquelltext anzeigen oder die Kurztaste Strg+u. Man muss kein HTML verstehen, um zu erkennen, dass hier Massen an Daten übertragen werden.

OpenWeather1

In diesem Fall von wunderground.com hat man, was die aktuelle Temperatur angeht Glück, die steht nämlich im Quelltext ganz oben als Meta-Information.

    <meta property="og:title" content="Koeln, Germany | 3.6&deg; | Mostly Cloudy" /> 

Um jetzt einen eindeutigen Suchbegriff in der Zeile zu finden, testen wir, ob bestimmte Buchstabenketten nur in dieser Zeile des Quelltextes vorkommen. Ich entscheide mich für den Suchstring „og:title„. Das hat den Vorteil, dass ich ohne den Suchstring zu ändern auch andere Städte suchen könnte.

Für einen ersten Test sollte es reichen den folgenden Codeblock im Code aus dem ersten Teil des Tutorials einzusetzen

  
while (client.available()) {
  String line = client.readStringUntil('\n');
  if (line.indexOf("og:title") >= 0) { // Searchstring exists?
    Serial.println();
    Serial.print(line);
  }
}

Natürlich muss der Server und die URL ebenfalls eingetragen werden

 
... 
const char* host = "www.wunderground.com"; 
... 
String url = "/global/stations/10513.html?MR=1";
... 

Die Ausgabe sieht jetzt so aus:


Connecting to TP-LINK_360E60
............
WiFi connected
IP address:
192.168.0.100

connecting to www.wunderground.com
Requesting URL: /global/stations/10513.html?MR=1

         <meta property="og:title" content="Koeln, Germany | 3.5&deg; | Scattered Clouds" />
closing connection

Die richtige Zeile habe ich also schon gefunden, jetzt gehts darum, den Temperaturwert aus der Zeile zu extrahieren. Der Wert steht zwischen den Zeichen „| “ und „&“. Wir suchen also das erste Vorkommen des ersten Suchstrings und extrahieren von da bis zum zweiten Suchstring. Der Code wird also folgendermaßen ergänzt. Als kleines Extra habe ich auch noch die Umwandlung der Temperatur in float eingebaut.


while (client.available()) {
  String line = client.readStringUntil('\n');
  if (line.indexOf("og:title") >= 0) { // Searchstring exists?
    Serial.println();
    Serial.println(line);
    int vonPos = line.indexOf("| "); 
    int bisPos = line.indexOf("&");
    Serial.print("die aktuelle Temperatur ist ");
    Serial.print(line.substring(vonPos+2, bisPos));
    Serial.println(" Grad C");
   // Umandlung in float
   String temp=line.substring(vonPos+2, bisPos);
   char char1[8];
   temp.toCharArray(char1, temp.length()+1);
   float tempWert=atof(char1);
   Serial.println(tempWert);
  }
}

Die Ausgabe sieht jetzt so aus


Connecting to TP-LINK_360E60
........
WiFi connected
IP address:
192.168.0.100

connecting to www.wunderground.com
Requesting URL: /global/stations/10513.html?MR=1

<meta property="og:title" content="Koeln, Germany | 3.6&deg; | Mostly Cloudy" />
die aktuelle Temperatur ist 3.6 Grad C
3.60

closing connection

Als zweiten Parameter hätte ich gerne den Luftdruck, hier wird es schon schwieriger, den im Quelltext zu finden. Ich suche im Quelltext nach „Druck“, das sollte ja in der Nähe des eigentlichen Wertes zu finden sein, wie mir die Website selbst verrät. Der Suchstring „Druck“ kommt 3x vor und ich sehe, dass der gesuchte Messwert des Luftdrucks 2x auf der Website ausgegeben wird. In beiden Fällen steht der Messwert aber nicht in der selben Zeile. Jetzt heißt es also zusätzlich Zeilen abzählen.

Ich suche also zunächst nach „<td><dfn>Druck“ und dann nach „wx-value“. In dieser Zeile findet sich dann mein Luftdruckwert. So dachte ich! Es passiert hier etwas seltsames.

Nachdem ich den Suchstring „Druck“ nicht finde konnte, obwohl er ja im Firefox im Quelltext war, habe ich in der Linux Command Line mit

wget http://www.wunderground.com/global/stations/10513.html?MR=1

den Quelltext in einen File geschrieben. Bei der Untersuchung dieses Files fiel mir dann auf, dass hier „Pressure“ statt „Druck“ verwendet wird. Die Website scheint zu erkennen, dass mein Firefox deutsch spricht, was mein ESP sicher nicht zu erkennen gibt.

Der Code wird jetzt schon etwas umfangreicher, ich habe zusätzliche Flags eingefügt, die mir zeigen, ob ich den ersten Suchstring gefunden habe, dann , ob ich den zweiten Suchstring gefunden habe. Zusätzlich ein Flag, damit ich den selben Suchstring nicht noch ein zweites mal auswerte, da er ja auf der Seite 2x vorkommt.


  // Read all the lines of the reply from server and print them to Serial
  boolean markF = false;
  boolean foundF = false;
  while (client.available()) {
    String line = client.readStringUntil('\n');
    if (line.indexOf("og:title") >= 0) { // Searchstring exists?
      Serial.println();
      Serial.println(line);
      int vonPos = line.indexOf("| ");
      int bisPos = line.indexOf("&");
      Serial.print("die aktuelle Temperatur ist ");
      Serial.print(line.substring(vonPos + 2, bisPos));
      Serial.println(" Grad C");
      // Umandlung in float
      String temp = line.substring(vonPos + 2, bisPos);
      char char1[8];
      temp.toCharArray(char1, temp.length() + 1);
      float tempWert = atof(char1);
      Serial.println(tempWert);
    }
    if (line.indexOf(">Pressure") >= 0 && !foundF) {  
      Serial.println();
      Serial.println(line);
      markF = true; // first time found
    } 
    if (line.indexOf("wx-value") >=0 && markF && !foundF) {   // search only the first 
      Serial.println();
      Serial.println(line);
      int vonPos = line.indexOf(">");
      int bisPos = line.indexOf("<",vonPos+1);
      Serial.print("der aktuelle Luftdruck ist ");
      Serial.print(line.substring(vonPos + 1, bisPos));
      Serial.println(" hPa");
      // Umandlung in float
      String druck = line.substring(vonPos + 1, bisPos);
      char char1[8];
      druck.toCharArray(char1, druck.length() + 1);
      float druckWert = atof(char1);
      Serial.println(druckWert);
      markF=false;
      foundF=true;      // don't search the second 
    }
  }

[/code]

Und zum Schluß noch die Ausgabe

[code]
Connecting to TP-LINK_360E60 
........... 
WiFi connected 
IP address:  
192.168.0.100 
 
connecting to www.wunderground.com 
Requesting URL: /global/stations/10513.html?MR=1 
 
    <meta property="og:title" content="Koeln, Germany | 3.9&deg; | Mostly Cloudy" /> 
die aktuelle Temperatur ist 3.9 Grad C 
3.90 
 
        <td><dfn>Pressure</dfn></td> 
 
        <span class="wx-value">1025</span> 
der aktuelle Luftdruck ist 1025 hPa 
1025.00 
 
closing connection 

Ich hoffe, dass mein Beispiel zeigt, wie einfach es ist, Informationen aus Websites zu parsen. Die Weiterverwendung dieser Information ist eurer Fantasie überlassen. Im nächsten und letzten Teil dieses Tutorials werde ich ein Beispiel für eine Steuerung vorstellen.

Aufgabe ist: Die Klappe eines Hühnerstalls soll bei Sonnenaufgang geöffnet werden und bei Sonnenuntergang wieder geschlossen werden.Die Informationen dazu – ihr könnt es euch denken – hole ich mir von einer Website.

Advertisements

Veröffentlicht am 5. Februar 2016, in Uncategorized. Setze ein Lesezeichen auf den Permalink. 2 Kommentare.

  1. Grundsätzlich sollte man sich fragen, ob man die Daten nicht anderweitig bekommen kann. Gerade für das Wetterbeispiel gibt es deutlich einfachere Wege um an die entsprechenden Daten zu kommen. Beispiele: Wetter.com oder auch Openweathermap stellen eine Web-API zur Verfügung, die auf ein HTTP-Request die Daten entweder in XML oder besser sogar in JSON liefern. Ich bin gerade nicht sicher, ob es für Arduino eine entsprechende Lib zum Deserialisieren von JSON gibt, aber zur Not parst man den JSON-String. Da es sich hierbei aber nicht um einen View handelt ist die Gefahr gering, dass sich die Struktur ändert. Die Webseite muss nur mal ihr Layout ändern und schon kann es zu Problemen beim Parsen geben.

    Um Grundsätzlich das Parsen zu erklären, ist das Tutorial okay. Um Wetter-Daten zu bekommen finde ich verfolgt es den falschen Ansatz.

  2. Vollkommen richtig. Mir ging es um das Prinzip des Parsens, nicht um den Inhalt als solchen. Ich habe lange überlegt, welchen Inhalt ich für das Beispiel nehme. Alternativ hatte ich noch die Messwerte der Luftqualitätsmessung in Betracht gezogen (z.B. http://www.lanuv.nrw.de/fileadmin/lanuv/luft/temes/heut/VKCL ).
    Vielleicht werde ich aber in einem späteren Tutorial den Zugriff auf openweathermap per API vorstellen. Arduino stellt in der Tat ein json Library zur Verfügung.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: