Was macht Web::Scraper?
Wie können wir Web::Scraper steuern?
Anwendungen von Web::Scraper
Information im Web
Traditionell: Automation, Automation, Automation
Fahrplan
Kinoprogramm
neue Dateien zum Download
HTML im Browser
Daten für Perl freilegen
Navigation (WWW::Mechanize)
Extraktion (Web::Scraper)
Web::Scraper
Geschrieben von Tatsuhiko Miyagawa
Plagger (RSS-Anwendung)
Angelehnt an Ruby's scrapi-Modul
Die Webseite
Die Beschreibung in Web::Scraper
Abfragen in Web::Scraper mit CSS-Selektoren
Abfragen in Web::Scraper mit XPath
Beispiel:
Extraktion aus Kinoprogramm
Echte Webseite
Vereinfachte Webseite
Vereinfachte Webseite
Zielelemente
Ein erstes Beispiel:
1: use LWP::Simple 'get';
2: use Web::Scraper;
3: my $s = scraper {
4: process 'title' => 'titel' => 'TEXT';
5: };
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;
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: }
API ist wenig dokumentiert
eigene "Sprache" mit Perl als Grundlage
Gut: Beschreibung als Code, nicht als Daten
Schlecht: Beschreibung als Code, nicht als Daten
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
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
[] sammelt Werte
my $s = scraper {
process 'title' => 'titel' => 'TEXT';
process 'p' => 'texte[]' => 'TEXT';
}
[] sammelt Werte
my $s = scraper {
process 'title' => 'titel' => 'TEXT';
process 'p' => 'texte[]' => 'TEXT';
}
[] 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: }
Mit
1: p.f
matchen wir CSS Klassen
1: <p class="f">Der Goldene Kompass</p>
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: }
1: p <p ... 2: 3: p.d <p class="d" ...
1: p <p ... 2: 3: p.d <p class="d" ... 4: 5: #elizabeth <* id="elizabeth" ... 6: 7: p#elizabeth <p id="elizabeth" ...
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>...
CSS Selektoren
... finden HTML Elemente
... in einfacher Hierarchie
Wenige Attribute (class, id)
Falls CSS nicht genug ist
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/*
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 kann wie ein Verzeichnisbaum navigieren:
1: ... 2: <div> 3: <p>Der Goldene Kompass</p> 4: <p>3.12.07</p> 5: </div> 6: ...
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 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 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
1: ... 2: <div> 3: <p class="f">Der Goldene Kompass</p> 4: <p>3.12.07</p> 5: </div> 6: ...
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
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
XPath Selektoren
... finden HTML Elemente
... in komplexer Hierarchie
Alle Attribute (@class)
Komplexe Prädikate (div/[p@f])
Komplexe Abfragesprache
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
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: };
Web::Scraper API
process Regel, Name, Werte
Regel (CSS oder XPath)
Name (einfacher Name oder mit [])
Werte TEXT, scraper { ... }, Perl Code
Perl Module
HTML::TableExtract
Template::Extract
Web::Scraper shell
Andere Anwendungen
FireBug (FireFox)
DOM Inspector (FireFox)
CSS Selektoren
HTML::Selector::XPath
XPath Queries (W3C)
HTML::Selector::XPath
Fragen?
Abfahrtszeiten und Fahrtstrecken
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
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: ...
1: my $item = scraper {
2: process '//td[@headers = "hafasOVStop"]',
3: haltestelle => 'TEXT';
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: };
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: };
1: $VAR1 = {
2: 'items' => [
3: {
4: 'uhrzeit' => '21:2721:34',
5: 'zeit' => ' 0:07 ',
6: 'datum' => '12.02.08',
7: 'haltestelle' => "..."
8: },
9: ...
Fragen?