News Notifier auf Crawlerbasis

Nexlamar

Erfahrenes Mitglied
Hallo!

Ich suche ein paar Tipps zu folgendem Problem.
Ich publiziere News in einem Blog und würde gerne den Zeitraum zwischen Erscheinungdatum der Quellnews und dem Post in meinem Blog verkürzen.

Jetzt möchte ich sozusagen per Skript oder mit einer Software (sofern es sowas gibt) diese Seiten crawlen, um festzustellen ob es etwas neues gibt, da nicht alle Quellseiten einen RSS-Feed anbieten. Es handelt sich um eine überschaubare Zielgruppe daher kann ich die in Frage kommenden Quellsites in einer Liste sammeln und den Crawl quasi begrenzen. Dabei möchte ich keinen Content klauen, sondern nur benachrichtigt werden wenn es eine Änderung gibt damit ich dann zeitnah einen Newsbericht verfassen kann.
Das Script soll also mehrmals am Tag die Quellseiten besuchen und mir mitteilen wenn es etwas neues gibt.

Da ich mich bisher nicht mit dem Thema Crawling befasst habe, suche ich ein paar Ideen, eventuell Scripte oder Software auf die ich aufbauen kann etc.

Danke, N.
 
Ein erster Grundgedanke ist, die letzte Änderung einer Seite nachzuvollziehen. Das wird etwas problematisch bei einigen dynamischen Seiten, aber nun gut...
Im eigenen Filesystem eignet sich für diese zwecke [phpf]filemtime[/phpf]; Für Remotezugriffe auf fremde Seiten muss man mit den empfangenen HTTP-Headers rumspielen und so eventuell an eine Datums- und Zeitangabe gelangen. Dazu steht unter angegebener Funktion ein Usercomment:
solarijj at gmail dot com hat gesagt.:
PHP:
// get remote file last modification date (returns unix timestamp)
function GetRemoteLastModified( $uri )
{
    // default
    $unixtime = 0;
   
    $fp = fopen( $uri, "r" );
    if( !$fp ) {return;}
   
    $MetaData = stream_get_meta_data( $fp );
       
    foreach( $MetaData['wrapper_data'] as $response )
    {
        // case: redirection
        if( substr( strtolower($response), 0, 10 ) == 'location: ' )
        {
            $newUri = substr( $response, 10 );
            fclose( $fp );
            return GetRemoteLastModified( $newUri );
        }
        // case: last-modified
        elseif( substr( strtolower($response), 0, 15 ) == 'last-modified: ' )
        {
            $unixtime = strtotime( substr($response, 15) );
            break;
        }
    }
    fclose( $fp );
    return $unixtime;
}

Alternativ eignet sich eventuell das fetchen eines Hashwerts des momentanen Zustands, wie zB über [phpf]sha1file[/phpf]. Diesen Wert kannst du in eine Datenbank speichern und später abgleichen. Wenn sich der Wert geändert hat, kann man weiterschauen, was sich geändert hat.
Auch hier wieder alternativ: Die komplette Inhaltsstruktur zerlegen (zB via [phpf]preg_match[/phpf] oder [phpf]preg_match_all[/phpf]), Headlines in eine Datenbank speichern und abgleichen. Headlines sind zumeist durch <h1> oder <h2>, stellenweise aber auch durch andere HTML-Elemente und deren Klassen- oder Styledefinitionen zu identifizieren.
Solltest du bei einer dieser Sachen Probleme haben, kann da sicherlich spezifiziert geholfen werden, aber unnötigerweise möchte ich erstmal nicht zu viel schreiben ;)

Grundsätzliche Ansätze also:
  • regelmäßig (oder beim Aufruf einer bestimmten Datei) Seiteninhalt auf Neuerungen überprüfen (Vergleich mit gespeicherten Daten zur Quelle)
  • Neuerungen identifizieren und verwerten
  • neuen Zustand speichern für spätere Vergleiche

Das wäre es erstmal.
 
Hallo!

Danke, perfekt - Deine Grundgedanken fassen exakt das zusammen was ich machen möchte.
Ich habe jetzt auf Basis des Usercomments ein Script zusammengebaut das mir die Headerinformationen einer beliebigen Website einliest und in der Form
Code:
HTTP/1.1 200 OK
Date: Fri, 09 Nov 2007 09:56:50 GMT
Server: Apache/1.3.34 (Debian) PHP/5.2.4 mod_ssl/2.8.25 OpenSSL/0.9.8c
X-Powered-By: PHP/4.4.7
Connection: close
Content-Type: text/html

ausgibt. Dazu wird der Content der betreffenden Seite in einer Variable gespeichert.
Interessant wäre aus dem Header das last-modified herauszulesen, aber es scheint so zu sein dass so wie Du gesagt hast besonders bei dynamischen Seiten eben diese Info sehr häufig nicht mitgeliefert wird. Daher dürfte die zweite Variante, nämlich einen Hashwert aus dem Content zu erzeugen besser sein. Wie kann ich denn so einen Hashwert erzeugen?
Mit sha1file() habe ich es nicht geschafft - dies scheint nur bei files zu funktionieren. Besser wäre es aber wenn ich auf Basis der Content-Variable so einen Wert erzeugen kann und diesen dann in der DB speichere ?

Danke, N.
 
In der Doku steht zu [phpf]sha1_file[/phpf], dass in PHP >= 5.1.0 ein Stream-Wrapper verwendet wird, somit kann auch das HTTP-Protokoll verwendet werden, um an die Datei zu kommen. Leider ist es so, dass die "normalen" Hash-Funktionen maximal 256 Zeichen berücksichtigen, weshalb ein Einlesen des HTML-Quelltextes und ein anschließendes Verschlüsseln ein wenig unnütz sein dürfte (sofern die ersten 256 Zeichen statisch sind).
Entweder entschließt du dich, auf PHP >= 5.1.0 umzusteigen oder du schreibst deinen eigenen Hash-Algorithmus.

Oder du suchst nach signifikanten Merkmalen im HTML-Quelltext, liest diesen ein, und extrahierst dann via reguläre Ausdrücke entscheidende Komponenten (wie zB ein Tag mit der Klasse "news-headline"):
PHP:
preg_match("%<h2 class=\"news-headline\">(.*)</h2>%Uis", $html, $matches)
Das lässt sich dann noch beliebig ausbauen ;)
 
Leider ist es so, dass die "normalen" Hash-Funktionen maximal 256 Zeichen berücksichtigen, weshalb ein Einlesen des HTML-Quelltextes und ein anschließendes Verschlüsseln ein wenig unnütz sein dürfte (sofern die ersten 256 Zeichen statisch sind).
Exakt, und daher ist MD5 mein Freund. Hier scheint es kein Limit zu geben, jedenfalls bleibt auch nach mehrmaligem crawlen einer beliebig großen statischen Site der Hash-Wert konstant. Lediglich bei Blogs/dynamischen Sites habe ich das Problem, dass es bei erneutem Crawlen zu verändertem Content kommt (z.B. durch Plugins mit dynamisch generiertem Content), sodaß hier der Hashwert immer variiert. Aber das könnte ich dann mit Deinem Vorschlag zu preg_match lösen, in dem ich z.B. nur Blogbereiche auslese die entsprechend neue News enthalten.
Ein letzter Punkt wäre jetzt noch das automatische Auslösen des Scripts. Ich möchte per Formular ein crawlintervall festlegen und einen flag der das script startet/stoppt. Wie könnte ich sowas mit PHP lösen (vermutlich irgendwie durch ein regelmäßiges Abfragen der Serverzeit?)

Danke!
N.
 
Die Scriptausführung könnte über einen Cronjob unter Linux oder geplanten Ereignissen unter Windows (mir fällt gerade nicht die korrekte Bezeichnung ein, lässt sich sicher aber ganz schnell finden via "Cronjob+Windows") ausgeführt werden.
Alternativ ginge das auch ohne durch eine Emulierung eines solchen Events.
Einen Ansatz haben wir mal zB hier diskutiert. Etwas modifiziert könnte dann zB das hier draus werden:
PHP:
<?php
$timeout = 10 * 60; // <-- 10 Minuten

while ( true ) {
    // setze Timeout-Interval neu ( zur Sicherheit x2 ;) )
    set_time_limit( 2 * $timeout );

    /**
     * tue was
     * - Fremdcontent fetchen
     * - auswerten
     * - verarbeiten
     */
    
    // Für den Intervall warten
    sleep( $timeout );
}

Oder anders:
PHP:
<?php
$timeout = 10 * 60; // <-- 10 Minuten

/**
 * tue was
 * - Fremdcontent fetchen
 * - auswerten
 * - verarbeiten
 */

sleep( $timeout );
system( "php myEvalScript.php" );

Gibt dazu einige Ideen, die teilweise mehr oder weniger belastend sind (zB ist ersteres auf lange Sicht sehr speicherbelastend).
 
Zurück