Dieser Crawler crawlt hauptsächlich den Inhalt verschiedener Beiträge in Baidu Tieba und analysiert den Inhalt der Beiträge, um die Mobiltelefonnummern und E-Mail-Adressen zu extrahieren. Der Hauptablauf wird in den Codekommentaren ausführlich erläutert.
Testumgebung:
Der Code wurde in der Umgebung Windows7 64bit, Python 2.7 64bit (MySQLDB-Erweiterung installiert) und Centos 6.5, Python 2.7 (mit MySQLDB-Erweiterung) getestet und bestanden
Umgebungsvorbereitung:
Wenn Sie Ihre Arbeit gut machen wollen, müssen Sie zuerst Ihre Werkzeuge schärfen, wie Sie auf dem Screenshot sehen können dass meine Umgebung Windows 7 + PyCharm ist. Die Python-Umgebung ist Python 2.7 64bit. Dies ist eine Entwicklungsumgebung, die eher für Anfänger geeignet ist. Dann schlage ich vor, dass Sie ein easy_install installieren. Wie Sie am Namen erkennen können, wird es zum Installieren einiger Erweiterungspakete verwendet. Wenn wir beispielsweise die MySQL-Datenbank in Python betreiben möchten, wird dies von Python nicht unterstützt . Wir müssen das mysqldb-Paket installieren, damit Python die mysql-Datenbank betreiben kann. Wenn es easy_install gibt, benötigen wir nur eine Befehlszeile, um das mysqldb-Erweiterungspaket schnell zu installieren apt-get in Ubuntu.
Relevante Tools finden Sie in github: cw1997/python-tools. Um easy_install zu installieren, müssen Sie nur das py-Skript unter der Python-Befehlszeile ausführen und einen Moment warten. Es wird automatisch die Windows-Umgebung hinzugefügt Variablen: Wenn Sie easy_install in der Windows-Befehlszeile eingeben und ein Echo ertönt, ist die Installation erfolgreich.
Details zur Umgebungsauswahl:
Was die Computerhardware betrifft, gilt natürlich: Je schneller, desto besser, beginnend mit mindestens 8 GB Speicher, da der Crawler selbst eine große Menge davon speichern und analysieren muss Zwischendaten, insbesondere Multithread-Crawler, beanspruchen beim Crawlen von Listen und Detailseiten mit großen Datenmengen viel Speicher. Manchmal werden die von uns erfassten Daten auch mit JSON gespeichert, wenn sie in einer Nosql-Datenbank wie Mongodb gespeichert werden.
Es wird empfohlen, für die Netzwerkverbindung ein kabelgebundenes Netzwerk zu verwenden, da es bei einigen minderwertigen WLAN-Routern und gewöhnlichen zivilen WLAN-Netzwerkkarten auf dem Markt zu zeitweiligen Netzwerkunterbrechungen, Datenverlusten, Paketverlusten usw. kommt, wenn die Threads unterbrochen werden relativ groß geöffnet.
Was das Betriebssystem und Python betrifft, wählen Sie natürlich 64-Bit. Wenn Sie ein 32-Bit-Betriebssystem verwenden, können Sie keinen großen Speicher verwenden. Wenn Sie 32-Bit-Python verwenden, bemerken Sie möglicherweise keine Probleme bei der Erfassung von Daten in kleinem Maßstab. Wenn jedoch die Datenmenge groß wird, z. B. wenn eine Liste, eine Warteschlange oder ein Wörterbuch große Datenmengen speichert, treten Probleme auf Ursache Wenn die Speichernutzung von Python 2 g überschreitet, wird ein Speicherüberlauffehler gemeldet.
Wenn Sie MySQL zum Speichern von Daten verwenden möchten, wird empfohlen, MySQL5.5 und spätere Versionen zu verwenden, da die MySQL5.5-Version den JSON-Datentyp unterstützt, sodass Sie auf Mongodb verzichten können.
Was Python betrifft, ist Version 3.x jetzt verfügbar. Warum wird hier immer noch Python2.7 verwendet? Der Grund für die Wahl von Version 2.7 ist, dass das Python-Kernprogrammierbuch, das ich vor langer Zeit gekauft habe, die zweite Ausgabe ist und immer noch 2.7 als Beispielversion verwendet. Und es gibt immer noch viele Tutorials im Internet, die 2.7 verwenden, da sich die Version 2.7 in einigen Aspekten immer noch stark von 3.x unterscheidet. Wenn wir 2.7 nicht studiert haben, verstehen wir möglicherweise einige subtile Syntaxunterschiede, die dazu führen werden Es gibt eine Abweichung in unserem Verständnis oder wir können den Democode nicht verstehen. Und es gibt noch einige abhängige Pakete, die nur mit Version 2.7 kompatibel sind. Mein Vorschlag ist: Wenn Sie Python lernen und dann in einem Unternehmen arbeiten möchten und das Unternehmen keinen alten Code pflegen muss, können Sie in Betracht ziehen, direkt mit 3.x zu beginnen Sehr systematischer Experte. Wenn Sie sich zum Lernen auf verstreute Blog-Artikel im Internet verlassen können, sollten Sie zuerst 2.7 und dann 3.x lernen. Schließlich können Sie nach dem Erlernen von 2.7 schnell mit 3.x beginnen.
Wissenspunkte, die an Multithread-Crawlern beteiligt sind:
Wenn wir für jedes Softwareprojekt wissen möchten, welche Wissenspunkte zum Schreiben dieses Projekts erforderlich sind, können wir die Hauptpunkte beobachten dieses Projekts Welche Pakete in die Eintragsdatei importiert werden.
Lassen Sie uns nun einen Blick auf unser Projekt werfen. Als Neuling in Python gibt es möglicherweise einige Pakete, die fast nie verwendet wurden. Daher werden wir in diesem Abschnitt kurz auf die Funktionen dieser Pakete eingehen um welche Wissenspunkte es sich handelt und was die Schlüsselwörter dieser Wissenspunkte sind. Es wird nicht lange dauern, bis dieser Artikel mit den Grundlagen beginnt. Daher müssen wir lernen, Baidu gut zu nutzen und nach Schlüsselwörtern für diese Wissenspunkte zu suchen, um uns weiterzubilden. Lassen Sie uns diese Wissenspunkte einzeln analysieren.
HTTP-Protokoll:
Im Wesentlichen erfasst unser Crawler Daten, indem er kontinuierlich http-Anfragen initiiert, http-Antworten erhält und diese auf unserem Computer speichert. Das Verständnis des http-Protokolls hilft uns, einige Parameter genau zu steuern, die das Crawlen beim Crawlen von Daten beschleunigen können, wie z. B. Keep-Alive usw.
Threading-Modul (Multithreading):
Die Programme, die wir normalerweise schreiben, sind Single-Threaded-Programme. Die Codes, die wir schreiben, werden alle im Hauptthread ausgeführt, und dieser Hauptthread wird in Python ausgeführt Verfahren. .
Multithreading wird in Python durch ein Modul namens Threading implementiert. Früher gab es ein Thread-Modul, aber Threading hat eine stärkere Kontrolle über Threads, daher sind wir später auf Threading umgestiegen, um Multithread-Programmierung zu implementieren.
Um es einfach auszudrücken: Um das Threading-Modul zum Schreiben eines Multithread-Programms zu verwenden, müssen Sie zunächst selbst eine Klasse definieren, dann muss diese Klasse threading.Thread erben und den Arbeitscode schreiben, der von jedem ausgeführt werden soll Wenn der Thread selbst bei der Erstellung einige Initialisierungsarbeiten ausführen muss, muss der für die Initialisierungsarbeiten auszuführende Code natürlich in seine __init__-Methode geschrieben werden die Konstruktormethode in PHP und Java.
Ein weiterer Punkt, über den hier gesprochen werden sollte, ist das Konzept der Thread-Sicherheit. Normalerweise arbeitet in unserer Single-Thread-Situation jeweils nur ein Thread mit Ressourcen (Dateien, Variablen), sodass es unwahrscheinlich ist, dass Konflikte auftreten. Im Fall von Multithreading können jedoch zwei Threads gleichzeitig dieselbe Ressource betreiben, was zu Ressourcenschäden führt. Daher benötigen wir einen Mechanismus, um den durch diesen Konflikt verursachten Schaden zu beheben, der normalerweise Sperren und andere Vorgänge umfasst. Beispielsweise verfügt die Innodb-Tabellen-Engine der MySQL-Datenbank über Sperren auf Zeilenebene usw. und Dateioperationen über Lesesperren usw. Diese werden für uns alle durch die unterste Ebene ihres Programms vervollständigt. Normalerweise müssen wir also nur die Operationen oder Programme kennen, die sich mit Thread-Sicherheitsproblemen befassen, und können sie dann in der Multithread-Programmierung verwenden. Diese Art von Programm, das Thread-Sicherheitsprobleme berücksichtigt, wird im Allgemeinen als „Thread-Sicherheitsversion“ bezeichnet. PHP hat beispielsweise eine TS-Version, und diese TS bedeutet Thread-Sicherheit. Das Warteschlangenmodul, über das wir im Folgenden sprechen werden, ist eine Thread-sichere Warteschlangendatenstruktur, sodass wir sie bedenkenlos in der Multithread-Programmierung verwenden können.
Abschließend werden wir über das entscheidende Konzept der Thread-Blockierung sprechen. Nachdem wir das Threading-Modul im Detail studiert haben, wissen wir wahrscheinlich, wie man Threads erstellt und startet. Aber wenn wir den Thread erstellen und dann die Startmethode aufrufen, werden wir feststellen, dass das gesamte Programm sofort endet. Was ist los? Dies liegt tatsächlich daran, dass wir nur den Code haben, der für das Starten des Unterthreads im Hauptthread verantwortlich ist, was bedeutet, dass der Hauptthread nur die Funktion hat, den Unterthread zu starten. Wie bei den vom Unterthread ausgeführten Codes , es handelt sich im Wesentlichen nur um eine in der Klasse geschriebene Methode. Sie wird nicht tatsächlich im Hauptthread ausgeführt. Nachdem der Hauptthread den Unterthread gestartet hat, ist seine gesamte Arbeit abgeschlossen und er wurde hervorragend beendet. Nachdem der Hauptthread beendet wurde, ist der Python-Prozess beendet und andere Threads haben keinen Speicherplatz mehr, um die Ausführung fortzusetzen. Wir sollten also den Haupt-Thread-Bruder warten lassen, bis die Ausführung aller Sub-Thread-Brüder abgeschlossen ist, bevor wir ihn ordnungsgemäß beenden. Gibt es also eine Methode im Thread-Objekt, um den Haupt-Thread zu blockieren? thread.sleep? Dies ist zwar eine Lösung, aber wie lange sollte der Hauptthread schlafen? Wir wissen nicht genau, wie lange es dauern wird, eine Aufgabe zu erledigen, daher können wir diese Methode definitiv nicht verwenden. Zu diesem Zeitpunkt sollten wir also online prüfen, ob es eine Möglichkeit gibt, den Unter-Thread im Haupt-Thread „hängen zu lassen“? Das Wort „steckengeblieben“ scheint zu vulgär zu sein. Um professioneller zu sein, sollten wir es „Blockieren“ nennen, sodass wir „Python-Sub-Thread blockiert den Haupt-Thread“ abfragen können Finden Sie eine Methode. Sie heißt „join()“. Ja, diese Methode „join()“ ist die Methode, die der Unterthread zum Blockieren des Hauptthreads verwendet erreicht die Zeile, die die Methode „join()“ enthält. Dort wird der Code, der auf die Methode „join()“ folgt, erst ausgeführt, wenn die Ausführung aller Threads abgeschlossen ist.
Warteschlangenmodul (Warteschlange):
Angenommen, es gibt ein Szenario, in dem wir den Blog einer Person crawlen müssen. Wir wissen, dass der Blog dieser Person zwei Seiten hat, eine list.php-Seite mit allen Artikellinks dieses Blogs werden angezeigt, außerdem gibt es eine view.php-Seite, die den spezifischen Inhalt eines Artikels anzeigt.
Wenn wir den gesamten Artikelinhalt im Blog dieser Person crawlen möchten, besteht die Idee beim Schreiben eines Single-Thread-Crawlers darin: Verwenden Sie zunächst reguläre Ausdrücke, um das href-Attribut des a-Tags aller Links in diesem Blog zu crawlen list.php-Seite. Speichern Sie ein Array mit dem Namen „article_list“ (in Python wird es nicht „Array“ genannt, sondern eine Liste, also eine Liste mit chinesischen Namen), und verwenden Sie dann eine for-Schleife, um das Array „article_list“ zu durchlaufen und verschiedene Funktionen zum Erfassen von Webseiteninhalten zu verwenden , und holen Sie sich dann den Inhalt. Speichern Sie ihn in der Datenbank.
Wenn wir einen Multithread-Crawler schreiben möchten, um diese Aufgabe auszuführen, müssen wir unter der Annahme, dass unser Programm 10 Threads verwendet, einen Weg finden, die zuvor gecrawlte Artikelliste in jeweils 10 Teile zu unterteilen wird einem der untergeordneten Threads zugewiesen.
Aber hier kommt das Problem. Wenn die Länge unseres Arrays „article_list“ kein Vielfaches von 10 ist, d. h. die Anzahl der Artikel kein ganzzahliges Vielfaches von 10 ist, werden dem letzten Thread weniger Aufgaben zugewiesen als andere Threads, dann ist es schneller vorbei.
Wenn wir nur ein paar tausend Wörter dieser Art von Blogartikeln crawlen, scheint das kein Problem zu sein, aber wenn wir eine Aufgabe haben (nicht unbedingt eine Aufgabe zum Crawlen von Webseiten, kann es sich um mathematische Berechnungen handeln). oder Grafik-Rendering. Wenn die Laufzeit zeitaufwändiger Aufgaben (z. B. zeitaufwändiger Aufgaben) sehr lang ist, führt dies zu einer enormen Ressourcen- und Zeitverschwendung. Unser Zweck beim Multithreading besteht darin, alle Rechenressourcen und Rechenzeit so weit wie möglich zu nutzen. Daher müssen wir Wege finden, Aufgaben wissenschaftlicher und rationaler zu verteilen.
Und wir müssen auch eine Situation berücksichtigen, das heißt, wenn die Anzahl der Artikel groß ist, müssen wir in der Lage sein, den Artikelinhalt schnell zu crawlen und den von uns gecrawlten Inhalt so schnell wie möglich zu sehen wird oft auf vielen CMS-Sammlungsseiten widergespiegelt.
Zum Beispiel enthält das Zielblog, das wir jetzt crawlen möchten, Dutzende Millionen Artikel. In diesem Fall wird das Blog normalerweise paginiert. Wenn wir also der oben genannten traditionellen Idee folgen und zuerst list.php crawlen, wird alles angezeigt Wenn der Chef hofft, dass Sie den gecrawlten Inhalt so schnell wie möglich anzeigen und den gecrawlten Inhalt so schnell wie möglich auf unserer CMS-Sammelstation anzeigen können, müssen wir das gleichzeitige Crawlen implementieren .php und werfen Sie die erfassten Daten in ein Article_list-Array. Verwenden Sie gleichzeitig einen anderen Thread, um die URL-Adresse des Artikels zu extrahieren, der aus dem Article_list-Array erfasst wurde. Anschließend verwendet dieser Thread reguläre Ausdrücke in der entsprechenden URL-Adresse. Holen Sie sich den Inhalt des Blogbeitrags. Wie implementiert man diese Funktion?
Wir müssen zwei Arten von Threads gleichzeitig öffnen. Eine Art von Thread ist speziell dafür verantwortlich, die URL in list.php zu erfassen und in das Array „article_list“ zu werfen zum Extrahieren der URL aus „article_list“ und zum anschließenden Extrahieren aus der entsprechenden Seite. Erfassen Sie den entsprechenden Blog-Inhalt von der view.php-Seite.
Aber erinnern wir uns noch an das zuvor erwähnte Konzept der Thread-Sicherheit? Der erste Thread-Typ schreibt Daten in das Array „article_list“, während der andere Thread-Typ Daten aus „article_list“ liest und die gelesenen Daten löscht. Allerdings ist die Liste in Python keine threadsichere Version der Datenstruktur, sodass dieser Vorgang unvorhersehbare Fehler verursacht. Daher können wir versuchen, eine bequemere und threadsicherere Datenstruktur zu verwenden, nämlich die in unserem Untertitel erwähnte Warteschlangendatenstruktur.
In ähnlicher Weise verfügt Queue auch über eine Join()-Methode. Diese Join()-Methode ähnelt tatsächlich der im vorherigen Abschnitt erwähnten Join()-Methode beim Threading, mit der Ausnahme, dass in Queue die Blockierungsbedingung von Join( ) ist nur blockiert, wenn die Warteschlange nicht leer ist, andernfalls wird der Code nach join() weiter ausgeführt. In diesem Crawler habe ich diese Methode verwendet, um den Hauptthread zu blockieren, anstatt den Hauptthread direkt über die Join-Methode des Threads zu blockieren. Der Vorteil davon besteht darin, dass ich keine Endlosschleife schreiben muss, um festzustellen, ob noch welche vorhanden sind Unvollendete Aufgaben werden in die aktuelle Aufgabenwarteschlange verschoben, wodurch das Programm effizienter und der Code eleganter wird.
Ein weiteres Detail ist, dass der Name des Warteschlangenmoduls in Python2.7 Queue ist, in Python3 jedoch in Queue umbenannt wurde.
getopt-Modul:
Wenn Sie die Sprache C gelernt haben, sollten Sie mit diesem Modul vertraut sein. Es handelt sich um ein Modul, das für das Extrahieren der angehängten Parameter aus dem Befehl in der Befehlszeile verantwortlich ist. Wenn wir beispielsweise die MySQL-Datenbank normalerweise über die Befehlszeile bedienen, geben wir mysql -h127.0.0.1 -uroot -p ein, wobei „-h127.0.0.1 -uroot -p“ nach MySQL der Parameterteil ist erhalten werden kann.
Wenn wir normalerweise Crawler schreiben, müssen Benutzer einige Parameter manuell eingeben, z. B. MySQL-Host-IP, Benutzername und Passwort usw. Um unser Programm benutzerfreundlicher und vielseitiger zu gestalten, müssen einige Konfigurationselemente nicht im Code fest codiert werden, sondern wir können sie bei der Ausführung dynamisch übergeben. In Kombination mit dem getopt-Modul können wir diese Funktion realisieren.
hashlib (Hash):
Hash ist im Wesentlichen eine Sammlung mathematischer Algorithmen. Wenn Sie einen Parameter angeben, kann dieses Ergebnis zwar sehr kurz sein, es kann jedoch als eindeutig angesehen werden. Beispielsweise hören wir normalerweise MD5, SHA-1 usw., sie gehören alle zu Hash-Algorithmen. Sie können einige Dokumente und Texte nach einer Reihe mathematischer Operationen in eine Folge von Zahlen und Englisch mit weniger als hundert Ziffern umwandeln.
Das Hashlib-Modul in Python kapselt diese mathematischen Operationsfunktionen für uns. Wir müssen es nur aufrufen, um die Hash-Operation abzuschließen.
Warum verwende ich dieses Paket in meinem Crawler? Da der Server bei einigen Schnittstellenanforderungen einige Bestätigungscodes mitbringen muss, um sicherzustellen, dass die von der Schnittstelle angeforderten Daten nicht manipuliert wurden oder verloren gingen, handelt es sich bei diesen Bestätigungscodes im Allgemeinen um Hash-Algorithmen. Daher müssen wir dieses Modul verwenden, um diesen Vorgang abzuschließen .
json:
Oft sind die von uns erfassten Daten kein HTML, sondern einige JSON-Daten sind im Wesentlichen nur eine Zeichenfolge, die Schlüssel-Wert-Paare enthält. Eine bestimmte Zeichenfolge , dann benötigen wir das JSON-Modul, um diesen JSON-String in einen Dikttyp für unsere Operation umzuwandeln.
re (regulärer Ausdruck):
Manchmal erfassen wir einige Webinhalte, müssen jedoch einige Inhalte in einem bestimmten Format aus der Webseite extrahieren, z. B. E-Mail. Das Format ist im Allgemeinen das erste Einige englische Buchstaben plus ein @-Symbol plus den Domänennamen http://xxx.xxx können wir einen Ausdruck namens „regulären Ausdruck“ verwenden, um diesen auszudrücken entspricht diesem speziellen Format aus einer großen Zeichenfolge.
sys:
Dieses Modul wird hauptsächlich zur Behandlung einiger Systemangelegenheiten verwendet. In diesem Crawler verwende ich es, um das Ausgabecodierungsproblem zu lösen.
Zeit:
Jeder, der ein bisschen Englisch gelernt hat, kann erraten, dass dieses Modul zum Verarbeiten von Zeit verwendet wird. In diesem Crawler verwende ich es, um den aktuellen Zeitstempel abzurufen und ihn dann zu übergeben it on the main thread Subtrahieren Sie am Ende den Zeitstempel, zu dem das Programm ausgeführt wurde, vom aktuellen Zeitstempel, um die Laufzeit des Programms zu erhalten.
Wie im Bild gezeigt, öffnen Sie 50 Threads, um 100 Seiten zu crawlen (30 Beiträge pro Seite, entspricht dem Crawlen von 3000 Beiträgen) und extrahieren Sie den Inhalt von Tieba-Beiträgen. Dieser Schritt von Der Zugriff auf das mobile Postfach dauerte insgesamt 330 Sekunden.
urllib und urllib2:
Diese beiden Module werden verwendet, um einige http-Anfragen und URL-Formatierung zu verarbeiten. Der Kerncode des http-Anforderungsteils meines Crawlers wird mit diesem Modul vervollständigt.
MySQLdb:
Dies ist ein Drittanbietermodul für den Betrieb einer MySQL-Datenbank in Python.
Hier müssen wir auf ein Detail achten: Das mysqldb-Modul ist keine Thread-sichere Version, was bedeutet, dass wir nicht das gleiche MySQL-Verbindungshandle in mehreren Threads teilen können. Sie können also in meinem Code sehen, dass ich im Konstruktor jedes Threads ein neues MySQL-Verbindungshandle übergebe. Daher verwendet jeder untergeordnete Thread nur sein eigenes unabhängiges MySQL-Verbindungshandle.
cmd_color_printers:
Dies ist auch ein Modul eines Drittanbieters, und der entsprechende Code kann online gefunden werden. Dieses Modul wird hauptsächlich zur Ausgabe von Farbzeichenfolgen an die Befehlszeile verwendet. Wenn wir beispielsweise normalerweise einen Fehler im Crawler haben und rote Schriftarten ausgeben möchten, die auffälliger sind, müssen wir dieses Modul verwenden.
Automatisierte Crawler-Fehlerbehandlung:
Wenn Sie diesen Crawler in einer Umgebung verwenden, in der die Netzwerkqualität nicht sehr gut ist, werden Sie feststellen, dass dies manchmal der Fall ist Melden Sie Folgendes: Für die in der Abbildung gezeigte Ausnahme wird hier keine Ausnahmebehandlung geschrieben.
Wenn wir einen hochautomatisierten Crawler schreiben möchten, müssen wir normalerweise alle ungewöhnlichen Situationen, denen unser Crawler begegnen kann, vorhersehen und diese abnormalen Situationen bewältigen.
Wenn beispielsweise ein Fehler auftritt, wie im Bild gezeigt, sollten wir die zu diesem Zeitpunkt verarbeitete Aufgabe erneut in die Aufgabenwarteschlange einfügen, da wir sonst Informationen verpassen. Dies ist auch ein komplizierter Punkt beim Crawler-Schreiben.