Web::Scraper - Daten aus Webseiten extrahieren

Max Maischein

Frankfurt.pm

Übersicht

  • Was macht Web::Scraper?

  • Wie können wir Web::Scraper steuern?

  • Anwendungen von Web::Scraper

Motivation

  • Information im Web

  • Traditionell: Automation, Automation, Automation

  • Fahrplan

  • Kinoprogramm

  • neue Dateien zum Download

Motivation (2)

  • HTML im Browser

  • Daten für Perl freilegen

  • Navigation (WWW::Mechanize)

  • Extraktion (Web::Scraper)

Web::Scraper

Web::Scraper

  • Geschrieben von Tatsuhiko Miyagawa

  • Plagger (RSS-Anwendung)

  • Angelehnt an Ruby's scrapi-Modul

Das Vorgehen

  • Die Webseite

  • Die Beschreibung in Web::Scraper

  • Abfragen in Web::Scraper mit CSS-Selektoren

  • Abfragen in Web::Scraper mit XPath

Die Webseite

  • Beispiel:

  • Extraktion aus Kinoprogramm

Die Webseite

Echte Webseite

Die Webseite

Vereinfachte Webseite

Die Webseite

Vereinfachte Webseite

Zielelemente

Die Webseite (HTML)

Die Zielelemente (HTML)

Web::Scraper API

Ein erstes Beispiel:

 1:  use LWP::Simple 'get';
 2:  use Web::Scraper;
 3:  my $s = scraper {
 4:      process 'title' => 'titel' => 'TEXT';
 5:  };

Web::Scraper API

Ein erstes Beispiel:

 1:  use LWP::Simple 'get';
 2:  use Web::Scraper;
 3:  my $s = scraper {
 4:      process 'title' => 'titel' => 'TEXT';
 5:  };
 6:  my $r = $s->scrape(get 'beispiele/kino.html');
 7:  print Dumper $r;

Web::Scraper API

Ein erstes Beispiel:

 1:  use LWP::Simple 'get';
 2:  use Web::Scraper;
 3:  my $s = scraper {
 4:      process 'title' => 'titel' => 'TEXT';
 5:  };
 6:  my $r = $s->scrape(get 'beispiele/kino.html');
 7:  print Dumper $r;
 1:  $VAR1 = {
 2:          'titel' => 'Programm vom 19.12.2007 bis zum 26.12.2007',
 3:  }

Web::Scraper

  • API ist wenig dokumentiert

  • eigene "Sprache" mit Perl als Grundlage

  • Gut: Beschreibung als Code, nicht als Daten

  • Schlecht: Beschreibung als Code, nicht als Daten

Web::Scraper API

 1:  my $s = scraper {
 2:      process 'title' => 'titel' => 'TEXT';
 3:      result 'title';
 4:  };
  • scraper { ... } - erzeugt ein neues Web::Scraper Objekt

  • process REGEL, ZIELNAME, ELEMENTE - definiert eine neue Regel zur Extraktion von Daten

Web::Scraper API

 1:  my $s = scraper {
 2:      process 'title' => 'titel' => 'TEXT';
 3:      
 4:      # Ab 20 Uhr die Filme für morgen anzeigen:
 5:      if (strftime('%H%M',localtime) > '2000') {
 6:          process 'p.morgen' => 'uhrzeit' => 'TEXT'
 7:      } else { ... }
 8:      
 9:      result 'title';
10:  };
  • Perl Code ist erlaubt, zum Beispiel für if-Bedingungen

  • result LISTE - zur genauen Angabe der Ergebnisse

CSS Selektoren und HTML Tagnamen

CSS Selektoren und HTML Tagnamen

CSS Selektoren und HTML Tagnamen

CSS Selektoren und HTML Tagnamen

Beispiel f. mehrere Elemente

[] sammelt Werte

  my $s = scraper {
    process 'title' => 'titel' => 'TEXT';
    process 'p' => 'texte[]' => 'TEXT';
    }

Beispiel f. mehrere Elemente

[] sammelt Werte

  my $s = scraper {
    process 'title' => 'titel' => 'TEXT';
    process 'p' => 'texte[]' => 'TEXT';
    }

Beispiel f. mehrere Elemente

Beispiel f. mehrere Elemente

Beispiel f. mehrere Elemente

Beispiel f. mehrere Elemente

[] sammelt Werte

 1:  my $s = scraper {
 2:      process 'title' => 'titel' => 'TEXT';
 3:      process 'p' => 'texte[]' => 'TEXT';
 4:  }
 1:  $VAR1 = {
 2:          'titel' => 'Kinoprogramm vom 19.12.2007 bis zum 26.12.2007',
 3:          'texte' => [
 4:                       'Der Goldene Kompass',
 5:                       '3.12.07',
 6:                       'Elisabeth - Das Goldene Zeitalter',
 7:                       '3.12.07'
 8:                     ],
 9:  }

CSS Klassenattribut

Mit

 1:  p.f

matchen wir CSS Klassen

 1:  <p class="f">Der Goldene Kompass</p>

Beispiel Klassenattribut

Beispiel Klassenattribut

Beispiel Klassenattribut

Beispiel Klassenattribut

Beispiel Klassenattribut

Beispiel Klassenattribut

 1:  my $s = scraper {
 2:      process 'title' => 'titel' => 'TEXT';
 3:      process 'p.f' => 'filme[]' => 'TEXT';
 4:      process 'p.d' => 'tage[]' => 'TEXT';
 5:  };
 1:  $VAR1 = {
 2:          'titel' => 'Kinoprogramm vom 19.12.2007 bis zum 26.12.2007',
 3:          'filme' => [
 4:                       'Der Goldene Kompass',
 5:                       'Elisabeth - Das Goldene Zeitalter'
 6:                     ],
 7:          'tage' => [
 8:                      '3.12.07',
 9:                      '3.12.07'
10:                    ]
11:  }

Andere Beispiele

Andere Beispiele

Andere Beispiele

Andere Beispiele

Übersicht über CSS

 1:  p                        <p ...
 2:  
 3:  p.d                      <p class="d" ...

Übersicht über CSS

 1:  p                        <p ...
 2:  
 3:  p.d                      <p class="d" ...
 4:  
 5:  #elizabeth               <* id="elizabeth" ...
 6:  
 7:  p#elizabeth              <p id="elizabeth" ...

Übersicht über CSS

 1:  p                        <p ...
 2:  
 3:  p.d                      <p class="d" ...
 4:  
 5:  #elizabeth               <* id="elizabeth" ...
 6:  
 7:  p#elizabeth              <p id="elizabeth" ...
 8:  
 9:  div p                    <div ...><p ...>...</div>
10:  
11:  div,p                    <div>... oder <p>...

Zusammenfassung CSS Selektoren

CSS Selektoren

  • ... finden HTML Elemente

  • ... in einfacher Hierarchie

  • Wenige Attribute (class, id)

XPath

Falls CSS nicht genug ist

XPath

  • XPath beschreibt den Pfad innerhalb des Dokuments

     1:  # Erster Paragraph
     2:  /html/body/div/p[0]
  • Wildcards sind erlaubt

     1:  # In DIV enthaltene Tags
     2:  //div/*

XPath Klassenattribute

  • Klassenattribute

     1:  # Filmelement
     2:  //*@class="f"
     1:      ...
     2:      <div>
     3:        <p class="f">Der Goldene Kompass</p>
     4:        <p>3.12.07</p>
     5:      </div>
     6:      ...
  • Index

     1:  //div/p[0] -> Filmtitel
     2:  //div/p[1] -> Filmdatum

XPath als Baumstruktur

XPath kann wie ein Verzeichnisbaum navigieren:

 1:      ...
 2:      <div>
 3:        <p>Der Goldene Kompass</p>
 4:        <p>3.12.07</p>
 5:      </div>
 6:      ...

XPath als Baumstruktur

XPath kann wie ein Verzeichnisbaum navigieren:

 1:      ...
 2:      <div>
 3:        <p>Der Goldene Kompass</p>
 4:        <p>3.12.07</p>
 5:      </div>
 6:      ...
 1:  //div/p[0]/.      -> Filmtitel

XPath als Baumstruktur

XPath kann wie ein Verzeichnisbaum navigieren:

 1:      ...
 2:      <div>
 3:        <p>Der Goldene Kompass</p>
 4:        <p>3.12.07</p>
 5:      </div>
 6:      ...
 1:  //div/p[0]/.      -> Filmtitel
 2:  //div/p[1]/../    -> div

XPath als Baumstruktur

XPath kann wie ein Verzeichnisbaum navigieren:

 1:      ...
 2:      <div>
 3:        <p>Der Goldene Kompass</p>
 4:        <p>3.12.07</p>
 5:      </div>
 6:      ...
 1:  //div/p[0]/.      -> Filmtitel
 2:  //div/p[1]/../    -> div
 3:  //div/*           -> Alle Kinder des div

XPath mit Prädikaten

 1:      ...
 2:      <div>
 3:        <p class="f">Der Goldene Kompass</p>
 4:        <p>3.12.07</p>
 5:      </div>
 6:      ...

XPath mit Prädikaten

 1:      ...
 2:      <div>
 3:        <p class="f">Der Goldene Kompass</p>
 4:        <p>3.12.07</p>
 5:      </div>
 6:      ...
 1:  //div/p[@f]             -> Filmtitel

XPath mit Prädikaten

 1:      ...
 2:      <div>
 3:        <p class="f">Der Goldene Kompass</p>
 4:        <p>3.12.07</p>
 5:      </div>
 6:      ...
 1:  //div/p[@f]             -> Filmtitel
 2:  //div/p[not(@f)]/../    -> Filmdatum

Zusammenfassung XPath Selektoren

XPath Selektoren

  • ... finden HTML Elemente

  • ... in komplexer Hierarchie

  • Alle Attribute (@class)

  • Komplexe Prädikate (div/[p@f])

  • Komplexe Abfragesprache

Hierarchie in HTML

 1:  $VAR1 = {
 2:          'filme' => [
 3:                       'Der Goldene Kompass',
 4:                       'Elisabeth - Das Goldene Zeitalter'
 5:                     ],
 6:          'tage' => [
 7:                      '3.12.07',
 8:                      '3.12.07'
 9:                    ]
10:  }
  • Hierarchie im HTML

  • Hierarchie im Code

  • Schleife mit Web::Scraper statt Schleife in Perl

Schleifen mit Web::Scraper

Schleifen mit Web::Scraper

Schleifen mit Web::Scraper

Schleifen mit Web::Scraper

Schleifen mit Web::Scraper

Schleifen mit Web::Scraper

 1:    process 'div' => 'filme[]' => scraper {
 2:        process( 'p.f', 'filmtitel' => 'TEXT');
 3:        process( 'p.d', 'datum' => 'TEXT');
 4:    };
 1:  =>
 1:  $VAR1 = {
 2:    'titel' => 'Programm vom 19.12.2007 bis zum 26.12.2007',
 3:    'filme' => [
 4:      {
 5:        'filmtitel' => 'Der Goldene Kompass',
 6:        'datum' => '3.12.07'
 7:      },
 8:      {
 9:        'filmtitel' => 'Elisabeth - Das Goldene Zeitalter',
10:        'datum' => '3.12.07'
11:      }
12:    ]
13:  };

Zusammenfassung API Web::Scraper

Web::Scraper API

  • process Regel, Name, Werte

  • Regel (CSS oder XPath)

  • Name (einfacher Name oder mit [])

  • Werte TEXT, scraper { ... }, Perl Code

Andere Module und Hilfsmittel

Perl Module

  • HTML::TableExtract

  • Template::Extract

  • Web::Scraper shell

Andere Anwendungen

  • FireBug (FireFox)

  • DOM Inspector (FireFox)

Informationen zu den Selektorsprachen

CSS Selektoren

HTML::Selector::XPath

XPath Queries (W3C)

HTML::Selector::XPath

Danke

Fragen?

Bonusprogramm

Extraktion aus RMV.de

Abfahrtszeiten und Fahrtstrecken

Abfrage-URL

 1:  http://www.rmv.de/auskunft/bin/jp/query.exe/dn?
 2:    L=vs_rmv&REQ0JourneyStopsN=-1&REQ0JourneyStopsS0A
 3:    =255&REQ0JourneyStopsS0G=f+roederbergweg&
 4:    REQ0JourneyStopsZ0A=255&REQ0JourneyStopsZ0G=
 5:    perlworkshop&start%26date%3D%2B0
 6:    %26times%3D%2B0%26dummy=jetzt
 7:    &querypagedisplayed=yes

Ergebnis-Seite

Ergebnis-HTML

 1:  ...
 2:  <td headers="hafasOVStop" ...>Frankfurt (Main) Röderbergweg
 3:  <td headers="hafasOVDate" ...>06.02.08
 4:  <td headers="hafasOVTime" ...>20:36
 5:  <td headers="hafasOVDuration" ...>7 Min.
 6:  ...

CSS Query

 1:    my $item = scraper {
 2:        process '//td[@headers = "hafasOVStop"]',
 3:           haltestelle => 'TEXT';

CSS Query

 1:    my $item = scraper {
 2:        process '//td[@headers = "hafasOVStop"]',
 3:           haltestelle => 'TEXT';
 4:        process '//td[@headers = "hafasOVDate"]',
 5:           datum => 'TEXT';
 6:        process '//td[@headers = "hafasOVTime"]/following-sibling::td',
 7:           uhrzeit => 'TEXT';
 8:        process '//td[@headers = "hafasOVDuration"]',
 9:           zeit => 'TEXT';
10:    };

CSS Query

 1:    my $item = scraper {
 2:        process '//td[@headers = "hafasOVStop"]',
 3:           haltestelle => 'TEXT';
 4:        process '//td[@headers = "hafasOVDate"]',
 5:           datum => 'TEXT';
 6:        process '//td[@headers = "hafasOVTime"]/following-sibling::td',
 7:           uhrzeit => 'TEXT';
 8:        process '//td[@headers = "hafasOVDuration"]',
 9:           zeit => 'TEXT';
10:    };
11:    
12:    my $page = scraper {
13:        process q{//table[@class = "hafasResult"]/tr[td/input]},
14:            'items[]' => $item;
15:    };

Ergebnis

 1:  $VAR1 = {
 2:    'items' => [
 3:      {
 4:        'uhrzeit' => '21:2721:34',
 5:        'zeit' => ' 0:07 ',
 6:        'datum' => '12.02.08',
 7:        'haltestelle' => "..."
 8:      },
 9:    ...

Danke

Fragen?