Web Scraping in R, Teil 2. Beschleunigen Sie den Prozess durch paralleles Rechnen und Verwenden des Rcrawler-Pakets


In einem früheren Artikel habe ich mithilfe von Scraping-Parsing Filmbewertungen von IMDB- und Kinopoisk-Websites gesammelt und verglichen. Repository auf Github .


Der Code hat seine Aufgabe gut erledigt, aber das Scraping wird oft verwendet, um nicht ein paar Seiten, sondern ein paar dreitausend zu "kratzen", und der Code aus dem vorherigen Artikel ist nicht für ein so "großes" Scraping geeignet. Genauer gesagt wird es nicht optimal sein. Im Prinzip hindert Sie praktisch nichts daran, damit Tausende von Seiten zu crawlen. Praktisch, weil Sie einfach nicht so viel Zeit haben



Als ich mich entschied, Scraping_imdb.R zu verwenden, um 1000 Seiten zu crawlen


Codeoptimierung. Eine einmalige Verwendung der Funktion read_html

In diesem Artikel werden 100 Links zu den Seiten des Labyrinth- Buchladens verwendet, um den Betrieb und die Geschwindigkeit des Codes zu überprüfen.


Eine explizite Änderung, die den Prozess beschleunigen kann, ist die einmalige Verwendung der "langsamsten" read_html - read_html . Ich möchte Sie daran erinnern, dass sie die HTML-Seite "liest". In der ersten Version des Codes für read_html habe ich jedes Mal read_html wenn ich einen Wert erhalten wollte ( read_html , Jahr, Genre, Bewertung). Jetzt wurden die Spuren dieser „Schande“ von GitHuba gelöscht, aber es ist so. Dies macht keinen Sinn, da die mit read_html erstellte read_html Informationen über die gesamte Seite enthält und es ausreicht, diese Variable der Funktion html_nodes und nicht jedes Mal mit dem Lesen von HTML zu beginnen, um unterschiedliche Daten daraus zu erhalten. So können Sie Zeit proportional zur Anzahl der Werte sparen, die Sie erhalten möchten. Aus dem Labyrinth erhalte ich jeweils sieben Werte. Code, der nur einen einzigen Lesevorgang einer HTML-Seite verwendet, funktioniert etwa siebenmal schneller. Nicht schlecht! Aber bevor ich wieder "beschleunige", werde ich abschweifen und über interessante Punkte sprechen, die sich beim Schaben von der Labyrinth-Website ergeben.


Funktionen des Seitenkratzens im Labyrinth

In diesem Teil werde ich nicht auf das Verfahren zum Abrufen und Löschen der im vorherigen Artikel erwähnten Daten eingehen. Ich werde nur die Momente erwähnen, die ich zum ersten Mal erlebt habe, als ich Code für das Scrapbooking eines Buchladens geschrieben habe.


Zunächst ist die Struktur zu erwähnen. Sie fühlt sich nicht sehr wohl. Im Gegensatz dazu geben beispielsweise auf der Read-Cities-Website in Abschnitten des Genres mit "leeren Filtern" nur 17 Seiten aus. Natürlich passen nicht alle 8011 Bücher des Genres "Contemporary Foreign Prose" darauf.


Daher habe ich mir nichts Besseres ausgedacht, als die https://www.labirint.ru/books/ **** Links mit einer einfachen Büste zu umgehen. Ehrlich gesagt ist die Methode nicht die beste (schon allein deshalb, weil die meisten "alten" Bücher außer dem Namen keine Informationen enthalten und daher praktisch nutzlos sind). Wenn also jemand eine elegantere Lösung anbietet, bin ich froh. Aber ich fand heraus, dass es unter der stolzen ersten Nummer auf der Website des Labyrinths ein Buch "Wie man Mondschein macht" gibt. Leider ist es bereits unmöglich, dieses Wissensspeicher zu kaufen.


Alle Adressen während der Aufzählung können in zwei Typen unterteilt werden:


  • Seiten, die existieren
  • Seiten, die nicht existieren

Bestehende Seiten können wiederum in zwei Teile unterteilt werden:


  • Seiten, die alle notwendigen Informationen enthalten
  • Seiten, die nicht alle erforderlichen Informationen enthalten

Am Ende habe ich eine Datentabelle mit sieben Spalten:


  1. ISBN - ISBN-Buchnummer
  2. PREIS - Buchpreis
  3. NAME - Buchtitel
  4. AUTOR - Autor des Buches
  5. VERLAG - Verlag
  6. JAHR - Erscheinungsjahr
  7. SEITE - Anzahl der Seiten

Mit den Seiten mit vollständigen Informationen ist alles klar, sie erfordern keine Änderungen im Vergleich zum Code für Filmseiten.


Seiten, auf denen einige Daten nicht verfügbar sind, sind mit ihnen nicht so einfach. Eine Suche auf der Seite gibt nur die gefundenen Werte zurück und die Ausgabelänge verringert sich um die Anzahl der Elemente, die nicht gefunden werden. Dies wird die gesamte Struktur brechen. Um dies zu vermeiden, wurde jedem Argument ein if ... else-Konstrukt hinzugefügt, das die Länge des nach Verwendung der Funktion html_nodes erhaltenen Vektors html_nodes Wenn es Null ist, wird NA , um html_nodes zu vermeiden.


  PUBLISHER <- unlist(lapply(list_html, function(n){ publishing <- if(n != "NA") { publishing_html <- html_nodes(n, ".publisher a") publishing <- if(length(publishing_html) == 0){ NA } else { publishing <- html_text(publishing_html) } } else { NA } })) 

Aber wie Sie hier sehen können, sind es bis zu zwei Wenns und bis zu zwei andere. Nur die "internen" if..esle sind für die Lösung des oben beschriebenen Problems relevant. Exterieur löst das Problem mit nicht vorhandenen Seiten.


Seiten, die einfach nicht die meisten Probleme haben. Wenn Werte auf Seiten mit fehlenden Daten verschoben werden und die Eingabe read_html nicht vorhandene Seite read_html , gibt die Funktion read_html Fehler aus und der Code wird nicht mehr ausgeführt. Weil Irgendwie ist es nicht möglich, solche Seiten im Voraus zu erkennen. Es muss sichergestellt werden, dass der Fehler nicht den gesamten Prozess stoppt.


Die possibly des possibly Pakets hilft uns dabei. Die Bedeutung von possibly (außer possibly quietly und safely ) besteht darin, die gedruckte Ausgabe von Nebenwirkungen (z. B. Fehler) durch einen Wert zu ersetzen, der zu uns passt. possibly eine possibly(.f, otherwise) Struktur possibly(.f, otherwise) und wenn ein Fehler im Code auftritt, verwendet er den Standardwert (ansonsten), anstatt die Ausführung zu stoppen. In unserem Fall sieht es so aus:


 book_html <- possibly(read_html, "NA")(n) 

n ist eine Liste der Adressen der Seiten der Site, die wir abgekratzt haben. Bei der Ausgabe erhalten wir eine Liste der Länge n, in der Elemente von vorhandenen Seiten in der "normalen" Form vorliegen, um die Funktion read_html , und Elemente von nicht vorhandenen Seiten aus dem Zeichenvektor "NA" bestehen. Bitte beachten Sie, dass der Standardwert ein Zeichenvektor sein muss, da wir in Zukunft darauf verweisen werden. Wenn wir nur NA schreiben, wie im PUBLISHER-Codeteil, ist dies nicht möglich. Um Verwirrung zu vermeiden, können Sie den ansonsten angegebenen Wert von NA in einen anderen ändern.


Und jetzt zurück zum Code, um den Namen des Herausgebers zu erhalten. Extern, wenn ... sonst für die gleichen Zwecke wie intern benötigt wird, jedoch in Bezug auf nicht vorhandene Seiten. Wenn die Variable book_html "NA" ist, ist jeder der "abgekratzten" Werte auch gleich NA (hier können Sie bereits die "echte" NA anstelle eines symbolischen Betrügers verwenden). Am Ende erhalten wir also eine Tabelle mit der folgenden Form:


ISBNPREISNAMEAUTORVERLAGJahrSeite
46653057703221488Set String Art "Netter Welpe" (30 * 30 cm) (DH6021)NAIngwerkatze2019NA
NANANANANANANA
9785171160814273Arkady Averchenko: Lustige Geschichten für KinderAutor: Averchenko Arkady Timofeevich, Künstler: Vlasova Anna YulievnaKind2019288

Nun zurück mit der Beschleunigung des Kratzprozesses.


Paralleles Rechnen in R. Geschwindigkeitsvergleich und Fallstricke bei Verwendung der Funktion read_html

Standardmäßig werden alle Berechnungen in R auf demselben Prozessorkern ausgeführt. Und während dieser unglückliche Kern im Gesicht arbeitet und Daten von Tausenden von Seiten für uns „kratzt“, „kühlt“ sich der Rest unserer Kameraden ab und führt einige andere Aufgaben aus. Die Verwendung von Parallel Computing hilft dabei, alle Prozessorkerne für die Verarbeitung / den Empfang von Daten zu gewinnen, was den Prozess beschleunigt.


Ich werde nicht weiter auf das Design des parallelen Rechnens auf R eingehen. Sie können hier zum Beispiel mehr darüber lesen. Ich habe die Parallelität von R so verstanden, dass Kopien von R in separaten Clustern entsprechend der Anzahl der angegebenen Kernel erstellt werden, die über Sockets miteinander interagieren.


Ich erzähle Ihnen von dem Fehler, den ich bei der Verwendung von Parallel Computing gemacht habe. Ursprünglich war mein Plan folgender: Mit Parallel Computing erhalte ich eine Liste mit 100 "gelesenen" read_html Seiten und read_html dann im normalen Modus nur die Daten, die ich benötige. Zuerst lief alles gut: Ich bekam eine Liste und verbrachte viel weniger Zeit damit als im normalen Modus R. Aber erst als ich versuchte, mit dieser Liste zu interagieren, erhielt ich eine Fehlermeldung:


 Error: external pointer is not valid 

Infolgedessen erkannte ich das Problem, indem ich mir Beispiele im Internet ansah, und fand danach nach dem Gesetz der Gemeinheit Henrik Bengtssons Erklärung in der Vignette für das zukünftige Paket. Tatsache ist, dass die XML-Funktionen des xml2 Pakets nicht exportierbare Objekte sind.
) Diese Objekte sind an diese R-Sitzung „gebunden“ und können nicht auf einen anderen Prozess übertragen werden, was ich versucht habe. Daher sollte die beim parallelen Rechnen gestartete Funktion einen „vollständigen Zyklus“ von Vorgängen enthalten: Lesen einer HTML-Seite, Empfangen und Bereinigen der erforderlichen Daten.


Das Erstellen von Parallel Computing selbst erfordert nicht viel Zeit und Codezeilen. Als erstes müssen Sie die Bibliotheken herunterladen. Das Github-Repository gibt an, welche Pakete für welche Methoden benötigt werden. Hier zeige ich paralleles Rechnen mit der parLapply Funktion des parallel Pakets. Führen doParallel einfach doParallel ( parallel wird in diesem Fall automatisch gestartet). Wenn Sie die Anzahl der Kerne Ihres Prozessors plötzlich nicht mehr kennen oder vergessen haben, ermitteln Sie, wie viele von ihnen detectCores werden detectCores


 # detectCores - ,     number_cl <- detectCores() 

Erstellen Sie als Nächstes parallele Kopien von R:


  # makePSOCKcluster -    R,    cluster <- makePSOCKcluster(number_cl) registerDoParallel(cluster) 

Jetzt schreiben wir eine Funktion, die alle erforderlichen Prozeduren ausführt. Ich stelle das seitdem fest Es werden neue Sitzungen erstellt. R-Pakete, deren Funktionen in unserer eigenen Funktion verwendet werden, sollten in den Hauptteil der Funktion geschrieben werden. In spider_parallel.R wird das stringr Paket dadurch zweimal ausgeführt: zuerst zum stringr der stringr und dann zum Löschen der Daten.


Und dann unterscheidet sich das Verfahren fast nicht von der üblichen lapply Funktion. In parLapply stellen wir eine Liste von Adressen, unsere eigene Funktion und als einzige Ergänzung eine Variable mit den von uns erstellten Clustern parLapply .


 # parLapply -  lapply     big_list <- parLapply(cluster, list_url, scraping_parellel_func) #    stopCluster(cluster) 

Das ist alles, jetzt bleibt es Zeit, die verbrachte Zeit zu vergleichen.


Vergleich der seriellen und parallelen Rechengeschwindigkeit

Dies wird der kürzeste Punkt sein. Paralleles Rechnen war fünfmal schneller als gewöhnlich:


Scraping-Geschwindigkeit ohne Parallel-Computing


der Benutzerdas Systembestanden
13.570,40112,84

Scraping-Geschwindigkeit mit Parallel Computing


der Benutzerdas Systembestanden
0,140,0521.12

Was soll ich sagen? Paralleles Rechnen kann viel Zeit sparen, ohne dass Schwierigkeiten beim Erstellen des Codes entstehen. Mit zunehmender Anzahl von Kernen steigt die Geschwindigkeit fast proportional zu ihrer Anzahl. Mit einigen Änderungen haben wir den Code zuerst siebenmal beschleunigt (die Berechnung von read_html bei jedem Schritt read_html ) und dann weitere fünfmal mithilfe paralleler Berechnungen. Spider-Skripte ohne paralleles Rechnen mit den Paketen parallel und foreach befinden sich im Repository von Github.


Eine kleine Übersicht über das Rcrawler Paket. Geschwindigkeitsvergleich.

Es gibt verschiedene andere Möglichkeiten, HTML-Seiten in R zu verschrotten , aber ich werde mich auf das Rcrawler- Paket konzentrieren. Das Unterscheidungsmerkmal von anderen Tools in der R-Sprache ist die Möglichkeit, Websites zu crawlen. Sie können die gleichnamige Rcrawler Funktion auf die Rcrawler Adresse setzen und methodisch Seite für Seite die gesamte Site umgehen. Rcrawler verfügt über viele Argumente zum Einrichten der Suche (z. B. können Sie nach Schlüsselwörtern, Sektoren der Site suchen (nützlich, wenn die Site aus einer großen Anzahl von Seiten besteht), Suchtiefe, Ignorieren von URL-Parametern, die doppelte Seiten erstellen, und vieles mehr Die Funktionen wurden bereits für parallele Berechnungen festgelegt, die durch die Argumente no_cores (Anzahl der beteiligten Prozessorkerne) und no_conn (Anzahl der parallelen Anforderungen) angegeben werden.


In unserem Fall gibt es beim Scraping von den angegebenen Adressen eine ContentScraper Funktion. Standardmäßig wird kein paralleles Rechnen verwendet, daher müssen Sie alle oben beschriebenen Manipulationen wiederholen. Ich mochte die Funktion selbst - sie bietet viele Optionen zum Einrichten des Scrapings und ist auf einer intuitiven Ebene gut verstanden. Auch hier können Sie if..else nicht für fehlende Seiten oder fehlende Werte verwenden, as Die Funktionsausführung wird nicht gestoppt.


 #   ContentScraper: # CssPatterns -    CSS    . # ExcludeCSSPat -    CSS ,    . # ,   CSS     CSS ,    . # ManyPerPattern -  FALSE,       , #  .  TRUE,     ,   . # PatternsName -      .   #   c  ,      t_func <- function(n){ library(Rcrawler) t <- ContentScraper(n, CssPatterns = c("#product-title", ".authors", ".buying-price-val-number", ".buying-pricenew-val-number", ".publisher", ".isbn", ".pages2"), ExcludeCSSPat = c(".prodtitle-availibility", ".js-open-block-page_count"), ManyPerPattern = FALSE, PatternsName = c("title", "author", "price1", "price2", "publisher", "isbn", "page")) return(t) } 

Bei all den positiven Eigenschaften hat die ContentScraper Funktion ein sehr schwerwiegendes Minus - die Arbeitsgeschwindigkeit.


Rcrawler ContentScraper ContentScraper Rcrawler ohne paralleles Computing


der Benutzerdas Systembestanden
47,470,29212,24

Rcrawler ContentScraper- ContentScraper Rcrawler mithilfe von Parallel Computing


der Benutzerdas Systembestanden
0,010,0067,97

Daher sollte Rcrawler verwendet werden, wenn Sie die Site umgehen müssen, ohne zuvor URL-Adressen anzugeben, sowie mit einer kleinen Anzahl von Seiten. In anderen Fällen überwiegt die langsame Geschwindigkeit alle möglichen Vorteile der Verwendung dieses Pakets.


Für Kommentare, Vorschläge und Beschwerden wäre ich dankbar
Github- Repository-Link
Mein Kreisprofil

Source: https://habr.com/ru/post/de464399/


All Articles