Open Source im professionellen Einsatz
Linux-Magazin 07/2016
1285

Rohrpost

Die Item-Pipeline verarbeitet die »Item« -Objekte dann weiter, die Variable »ITEM_PIPELINES« konfiguriert die Pipeline in den Zeilen 4 bis 8 der Beispielanwendung von Listing 3. Die übergibt die »Item« -Objekte der Reihe nach jeweils an ein Objekt der Pipeline-Klassen »Words« (300), »Injections« (400) und »Attributes« (500), modifiziert sie oder speichert sie und schiebt sie weiter.

Listing 6 zeigt den Code zur Klasse »Words« , die Wörter für die folgende Auswertung prüft und vereinheitlicht. Scrapy erzeugt und bindet hier die Pipeline-Objekte ein, indem es für jedes »Item« -Objekt die Methode »process_item()« aufruft (Zeile 2). Sie übernimmt im zweiten Argument das »Item« -Objekt, im dritten (»spider« ) eine Referenz auf das aufrufende Spider-Objekt. Die zwei Folgezeilen überschreiben die Werte zu den Attributen »keywords« und »words« . In Zeile 3 fischt die Funktion »filter()« alle Elemente aus der Liste »item[key]« , für die die Lambda-Funktion (»lambda wd: wd.isalnum()« ) einen wahren Wert zurückgibt. Das betrifft Zeichenketten aus rein alphanumerischen Zeichen.

Listing 6

mirror/pipelines/normalize.py

01 class Words(object):
02   def process_item(self, item, spider):
03     for key in ['words', 'keywords']:
04       item[key] = map(lambda wd: wd.lower(), filter(lambda wd: wd.isalnum(), item[key]))
05     return item

Die zweite Lambda-Funktion – »map()« – formatiert die Wörter der Ergebnismenge anschließend in Kleinbuchstaben. Die »return« -Anweisung der letzten Zeile überreicht das »Item« -Objekt dann an das nächste Pipeline-Objekt (Listing 7). Dies betrachtet den möglichen Einfluss fremden Contents auf die aktuelle Browser-Session.

Listing 7

mirror/pipelines/filter.py

01 from mirror.utils import is_absurl, domain
02
03 class Injections(object):
04   def process_item(self, item, spider):
05     item['injections'] = [other
06        for own in item['url']
07        for other in item['injections']
08        if domain(other) != domain(own) and is_absurl(other)
09     ]
10     return item

Die Pipeline-Klasse »Injections« aus Listing 7 reduziert mit den Hilfsfunktionen »is_absurl()« und »domain()« , die wiederum aus »mirror/utils.py« stammen, die Liste der eingebunden Ressourcen auf fremden Content.

Dazu überschreibt die Methode »process_item()« das Attribut mit Hilfe der gefilterten Liste, die der Listenausdruck ab Zeile 5 erzeugt. Die erste »for« -Schleife liest lediglich die URL der Seite aus, die zweite iteriert über die zu injizierenden Tags. Stellt das Attribut gemäß der »if« -Fallunterscheidung eine absolute URL dar, die von der aktuellen Domain abweicht, übernimmt die Schleife die Ressource aus der Variablen »other« . Die »return« -Anweisung reicht das so veränderte Item erneut an das letzte Glied der Pipeline weiter (Listing 8). Das reduziert die Resultate zwecks späterer Auswertung und legt sie in einer Datenbank ab.

Listing 8

mirror/pipelines/store.py

01 import sqlite3
02 from os.path import join
03 from time import gmtime, strftime
04 from mirror.utils import relevance, optvalue
05
06 class Attributes(object):
07   def __init__(self, path):
08     self.db = path
09
10   @classmethod
11   def from_crawler(cls, crawler):
12     return cls(join(crawler.settings.get('RESULTS'),  "%s.sqlite3" %(strftime('%Y%m%d%H%M%s', gmtime()))))
13
14   def open_spider(self, spider):
15     self.conn = sqlite3.connect(self.db, isolation_level = None)
16     self.cur = self.conn.cursor()
17     self.cur.execute("CREATE TABLE Attributes (url text PRIMARY KEY, keywords int, words int, relevancy int, tags int, semantics int, medias int, links int, injections int)")
18
19   def close_spider(self, spider):
20     self.conn.close()
21
22   def process_item(self, item, spider):
23     self.cur.execute('INSERT INTO Attributes VALUES (?,?,?,?,?,?,?,?,?  )', (item['url'][0], len(item['keywords']), len(optvalue(item, 'words')), relevance(optvalue(item, 'keywords'), optvalue(item, 'words')), len(optvalue(item, 'tags')), len(optvalue(item, 'semantics')), len(optvalue(item, 'medias')), len(optvalue(item, 'links')), len(optvalue(item, 'injections'))))
24     return item

Die Pipeline-Klasse »Attributes« (Zeile 6 von Listing 8) wertet das Item-Objekt aus und speichert das Ergebnis in einer SQlite-Datenbankdatei [8] ab. Das SQL-kompatible freie Datenbank-Framework kommt ohne Serverprozesse aus und alle gängigen Programmiersprachen unterstützen es.

Listing 8 schreibt alle Daten direkt und synchron über den Treiber in die Datenbankdatei. Zeile 1 bindet den passenden Treiber aus Python ein, die nächsten drei Zeilen importieren benötigte Funktionen aus den Standardpaketen »os.path« , »time« sowie »mirror/utils.py« .

Die Konstruktorfunktion »__init__()« übernimmt im zweiten Aufrufparameter »path« den Pfad, über den Python die SQlite-Datenbank öffnet. Scrapy verwendet die Klassenmethode »from_crawler()« (Zeilen 10 bis 12), um ein Objekt zu instanzieren. Ein Blick in die Methode zeigt, dass »from_crawler()« in seiner Parameterliste mit dem Crawler-Objekt eine Referenz auf die Einstellungen aus Listing 3 übernimmt. Erst liest sie den Wert der Variablen »RESULTS« aus Zeile 9 in Listing 3 aus. Dann bezieht sie ihn im Konstruktor-Aufruf in den Zeilen 7 und 8 von Listing 8 mit ein. Am Ende erzeugen »gmtime()« und »strftime()« im Zusammenspiel mit einer Variante der Zeichenketten-Formatierung noch einen Zeitstempel für den Dateinamen.

Die Methode »open_spider()« (Zeilen 14 bis 17) ruft die Scrapy-Engine beim Erstellen des Spiders im Stile einer Callback-Funktion einmalig auf. Sie erzeugt und speichert in Zeile 15 eine Datenbankverbindung im Attribut »conn« . Die Angabe »isolation_level=None« veranlasst den Treiber dazu, jede SQL-Anweisung sofort persistent in der Datenbankdatei anzulegen. Zeile 16 erzeugt und speichert das Datenbank-Cursor-Objekt, das Datenbankoperationen ausführt. Zu diesen gehört auch der SQL-Befehl, der die Ergebnistabelle anlegt:

CREATE TABLE Attributes (url text PRIMARY  KEY, keywords int, words int, relevancy int, tags int, semantics int, medias int,  links int, injections int)

Die Methode »process_item()« ab Zeile 22 fügt die Werte aus dem Item-Objekt gemäß Tabelle 1 mit Hilfe des SQL-Befehls »INSERT« in die Tabelle »Attributes« ein. An der Stelle der Fragezeichen landen die Werte des nachfolgenden, von den runden Klammern umfassten Tupels. Die Funktion »len()« zählt dabei mehrfach die Länge der ausgelesenen Listen. Die Hilfsfunktion »optvalue()« ersetzt None-Werte gegen leere Listen, »relevance()« ermittelt die Häufigkeit aller Keywords im restlichen Text der Webseite.

Auswertung

Der User startet den Crawler wie erwähnt über die Kommandozeile und aus dem Projektverzeichnis »mirror« heraus:

scrapy crawl attr

Listing 9 zeigt die SQL-Abfrage, die den Report aus Abbildung 1 gemäß den Tabellen 1 und 2 erzeugt. Die Stärke von SQL offenbart sich in der kompakten und satzähnlichen Ausdrucksweise. Nur das Umwandeln der Typen und das Formatieren erfordern lästige Tipparbeit.

Tabelle 2

Interpretation der erhobenen Daten

Größe

Berechnung

Interpretation

Relevancy

-

Maß für die Glaubwürdigkeit des Titels

Entropy

(Words+Semantics)/(Words+Tags)

Nicht-semantische-Tags wie »div« oder »span« reduzieren den Informationsgehalt

Expressivity

Semantics/Tags

Semantische Tags verbessern die funktionale Einordnung der Dokument-Bestandteile

Richness

Medias

Medien bereichern den Inhalt

Reliability

Links/Words

Verlinkungen bürgen für die Glaubwürdigkeit

Mutability

Injections

Externe Ressourcen entfremden die Seite

Die endogenen Seitenfaktoren beurteilen eine Webseite aus verschiedenen Perspektiven. Die abgeleitete Größe der Entropie ist ein Maß für den mittleren Informationsgehalt der Seite. Die Größen sind wortgleich, aber nicht identisch mit den korrespondierenden Terminologien aus der Informationstheorie [9]. In der Beispielanwendung beschreiben sie nur, wie nicht semantische Tags wie »div« und »span« den Inhalt verwässern. Wäre der Wert der Entropie 1, würden generische Spider bessere Ergebnisse erzielen. Der Durchschnitt 0,837 aus Abbildung 1 lässt noch Luft nach oben.

Listing 9

SQL-Abfrage für den Report

01 SELECT url, printf('%.3f', relevancy), printf('%.3f', cast((words + semantics) as float)/(words + tags)) as entropy, printf('%.3f', cast(semantics as float)/tags) as expressivity, printf('%.3f', cast(medias as float)) as richness, printf('%.3f', cast(links as float)/words) as reliablity, printf('%.3f', cast(injections as float)) as mutability FROM Attributes

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 6 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Portia

    Die Börsenkurse in maschinenlesbarer Form mal eben aus dem Internet holen? Kein Problem: Nach ein paar Mausklicks auf eine Börsen-Webseite webt Portia einen geeigneten Kommandozeilenbefehl und wickelt die gewünschten Daten in das passende Json-Format ein.

  • Muschelzucht im Ausland

    Microsoft veröffentlicht demnächst eine Admin-Shell für Windows, die diesen Namen auch verdient. Beim Pipelining verhält sich die Powershell unter Umständen sogar robuster als die Unix-Pendants, weil sie typisierte Objekte durchreicht.

  • E-Commerce-APIs

    Gutgehende Onlineshops sind selten und das Ergebnis langer Arbeit. Wer gerade erst beginnt Waren zu verticken, tut gut daran, bei einem großen Shoppingportal unterzuschlüpfen. Über APIs lassen sich eigene Warenwirtschafts- oder Buchhaltungsprogramme anbinden.

  • Appetithäppchen

    Newsticker, Web-Blogs und andere Informationssysteme verwenden gerne das RSS-Format, um kurze Nachrichten in standardisierter Form an die Interessenten zu verteilen. Diese News-Schnipsel lassen sich bestens mit Tcl und der objektorientierten Erweiterung CZRSS verarbeiten.

  • Tragendes Rahmenwerk

    Wer Web- oder SaaS-Anwendungen anbieten will, bekommt es mit Kodierungsrelikten der HTML- und Skriptsprachen-Frühzeit zu tun. Abhilfe schaffen moderne Frameworks wie Prado, die viele Elemente und Templates bereits als fertige Bausteine bereitstellen. Doch deren Struktur will zuerst verstanden sein.

comments powered by Disqus

Ausgabe 08/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.