Blog der Heimetli Software AG

Mit Python Werte aus einer HTML-Seite auslesen

Dieser Post beschreibt nicht einen allgemeinen Screen-Scraper, sondern ein Script das Variablen aus einer HTML-Seite mit vorgegebenem Format extrahieren kann.

Wenn Sie also den Aufbau der HTML-Seite nicht ändern können, bleibt nichts anderes übrig als das Script anzupassen.

Die Struktur der HTML-Seite

Die HTML-Seite für dieses Script hat folgenden Aufbau:

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>Eine Tabelle mit Variablen</title>
 </head>
 <body>
  <table>
   <tr>
    <td class="name">Spannung</td><td class="value">13</td>
   </tr>
   <tr>
    <td class="name">Strom</td><td class="value">31</td>
   </tr>
  </table>
 </body>
</html>

Die Inhalte der td-Elemente werden extrahiert, und das class-Attribut gibt an, was der String sein soll. class="name" gibt an, dass die Zelle einen Variablennamen enthält, während class="value" den Wert der Variablen bezeichnet.

Um das Script relativ einfach zu halten, kann es nur eine Variable pro Zeile der Tabelle geben. Weitere Zellen mit beliebigem Inhalt sind dagegen erlaubt, so lange sie keinen der beiden Attributwerte haben.

Entities wie &#x2103; (℃) werden nicht expandiert.

Was macht das Script ?

Das Script erwartet ein oder mehrere URLs auf der Kommandozeile. Die entsprechenden Seiten werden geladen und die Variablen gesammelt:

pi@raspberrypi ~ $ python fetch.py http://192.168.1.222/test/variables.html
Spannung = 13
Strom = 31
pi@raspberrypi ~ $ 

Die Ausgabe des Scripts ist nur für den Funktionstest gedacht, denn der User will ja schliesslich mit diesen Variablen etwas anfangen.

Das Script

Das Script wurde mit Python 2.7.3 auf einem Raspberry PI geschrieben und dort auch getestet.

Es benutzt httplib zum Laden der HTML-Seite und die HTMLParser-Klasse um sie zu zerlegen.

"""This script reads a HTML page and extracts variable names and values.

   The names and values are encoded as table cells with class-attributes
   like this:

   <tr>
    <td class="name">Voltage</td><td class="value">13</td>
   </tr>

   The number of variables in the file is not limited, but only one
   variable definition per table row is allowed.

   Limits:
     Entities in names or values will be ignored.

   V0.01  16-FEB-2014 Te
"""

import sys
import httplib
from HTMLParser import HTMLParser
from urlparse import urlparse

class HTMLHandler( HTMLParser ):
    """Parses the HTML and collects the variables."""

    def __ignore( self, text ):
        """Ignores the text."""
        pass

    def __addtoname( self, text ):
        """Adds the test to the name."""
        self.__name += text

    def __addtovalue( self, text ):
        """Adds the test to the value."""
        self.__value += text

    def handle_starttag( self, tag, attrs ):
        """Handles the start tag of td."""
        if tag == "td":
            for attr in attrs:
                if attr[0] == "class":
                    if attr[1] == "name":
                        self.__name = ""
                        self.__texthandler = self.__addtoname

                    elif attr[1] == "value":
                        self.__value = ""
                        self.__texthandler = self.__addtovalue

    def handle_endtag( self, tag ):
        """Handles the end tag of td and tr."""
        if tag == "td":
            self.__texthandler = self.__ignore

        elif (tag == "tr") and (self.__name != ""):
            self.variables.append( (self.__name, self.__value) )

    def handle_data( self, data ):
        """Forwards the text to the current texthandler."""
        self.__texthandler( data )

    def __init__( self ):
        """Initializes the class members."""
        HTMLParser.__init__( self )

        self.variables     = []
        self.__texthandler = self.__ignore
        self.__name        = ""
        self.__value       = ""

def processpage( url, parser ):
    """Reads and parses one URL from the command line."""
    try:
        components = urlparse( url )

        connection = httplib.HTTPConnection( components.netloc )
        connection.putrequest( "GET", components.path )
        connection.putheader( "Accept", "text/html" )
        connection.endheaders()

        reply = connection.getresponse()
        if reply.status != 200:
            print "Error: {0} {1} {2}".format( url, reply.status, reply.reason )
        else:
            body = reply.read()
            reply.close()

            parser.feed( body )
    except Exception as exception:
        print "Error: {0} {1}".format( url, exception )
   
def main( argv ):
    """The main function of the script."""
    parser = HTMLHandler()

    for url in argv[1:]:
        processpage( url, parser )

    for var in parser.variables:
        print "{0} = {1}".format( var[0], var[1] )

if __name__ == "__main__":
    main( sys.argv )

Ueber diesen Link können Sie das Script herunterladen.