Erstellen eines einfachen Schabers mit Ruby

Blog

Ich liebe Web-Scraping. Es hat sich in den letzten Jahren nicht nur zu einer großen Branche entwickelt, es macht auch viel Spaß. Es kann Ihnen helfen, auf einfache Weise interessante Einblicke zu finden, indem Sie bereits vorhandene Daten verwenden. Heute werde ich einen sehr einfachen Web-Scraper erstellen, der die DuPont Registry (einen Marktplatz für teure Autos, Häuser und Yachten) durchsuchen kann. Bei diesem Schaber konzentriere ich mich auf den Autobereich.



Ich werde zwei relevante Edelsteine ​​verwenden: Nokogiri und HTTParty . Nokogiri ist ein Juwel, mit dem Sie HTML und XML in Ruby-Objekte parsen können. HTTParty hingegen vereinfacht den Prozess des Ziehens von Roh-HTML in Ihren Ruby-Code. Diese beiden Edelsteine ​​werden in unserem Schaber zusammenarbeiten.

Es ist wichtig zu beachten, dass Nokogiri und HTTParty zwar cool sind, aber eher wie Streusel obendrauf. Die Kernkompetenzen zum Erstellen eines Programms damit sind Dinge wie das Definieren und Initialisieren einer Klasse und ihrer Instanz(en), das Durchlaufen von Hashes und Arrays und das Erstellen von Hilfsmethoden.



Einstieg

Wenn Sie dies noch nicht getan haben, stellen Sie sicher, dass Sie Ihre wesentlichen Gems installiert und in Ihrem Code benötigt haben. Außerdem verwende ich gerne byebug oder pry, damit ich den Code bei Bedarf stoppen und mir ansehen kann, was los ist!



require 'nokogiri' require 'httparty' require 'byebug' require 'pry' class Scraper #we will be adding code here shortly end

Initialisieren einer Instanz

Denken wir nun an unsere Scraper-Klasse. Welche Methoden sollte es haben? Was muss diese Klasse im Auge behalten?

Wir müssen dies entscheiden, bevor wir definieren, wie unser neuer Schaber initialisiert wird. Ich habe mir schon ein wenig Gedanken gemacht und mir folgendes einfallen lassen:

class Scraper attr_reader :url, :make, :model def initialize(make, model) @make = make.capitalize @model = model.capitalize @url = 'https://www.dupontregistry.com/autos/results/#{make}/#{model}/for-sale'.sub(' ', '--') end end

initialize.rb

reagieren-native-audio-recorder-player

Lassen Sie uns das aufschlüsseln. Jede Instanz einer Scraper-Klasse sollte wissen, was machen und Modell es soll sich anschauen und was URL es muss besuchen, um seine Daten zu finden. Es wäre auch schön, wenn diese Instanzvariablen lesbar wären, daher habe ich ihnen jeweils einen attr_reader hinzugefügt.

Die URL-Pfade von DuPont Registry sind ziemlich einfach, sodass wir die Vorlagenliterale von „make“ und „model“ abrufen können, um unsere Ziel-URL zu generieren und diese dann in unserer Instanzvariable von . zu speichern @url . Die .sub( , — ) am Ende des URL-Strings ist nur eine Methode, die Leerzeichen durch zwei Bindestriche ersetzt.

Zeit, ein wenig mehr Funktionalität hinzuzufügen. Wir brauchen unsere Scraper-Instanz, um tatsächlich zu schaben. Beginnen wir mit dem Aufbau einiger Instanzmethoden.

class Scraper attr_reader :url, :make, :model def initialize(make, model) @make = make.capitalize @model = model.capitalize @url = 'https://www.dupontregistry.com/autos/results/#{make}/#{model}/for-sale'.sub(' ', '--') end def parse_url(url) unparsed_page = HTTParty.get(url) Nokogiri::HTML(unparsed_page) end def scrape parsed_page = parse_url(@url) binding.pry end binding.pry 0 end

gistfile1.rb

Ich möchte mit diesen Hilfsmethoden früh vor unserem beginnen #kratzen Methode wird zum Roman. Die #parse_url -Methode nimmt eine URL als Argument auf, ruft auf HTTParty um rohes HTML einzufügen, und dann Nokogiri nimmt diese ungeparste Seite und… ähm… es zu analysieren . Auf diese Weise können wir eine ganze Webseite in ein brauchbares Ruby-Objekt umwandeln! Wir speichern dann das gesamte Objekt in einer lokalen Variablen innerhalb von #scrape namens geparste_seite. Jetzt ist ein ziemlich guter Zeitpunkt, um eine Bindung festzulegen. Pry und einen Blick darauf zu werfen, was wir tatsächlich haben, bevor wir weitermachen.

Lassen Sie uns diesen Code mit dem bash-Befehl in unserem Terminal ausführen Rubin Schaber.rb (Dies setzt voraus, dass Sie in einer Datei mit dem Titel scraper.rb codieren, wenn Ihre Datei einen anderen Namen hat, passen Sie ihn entsprechend an). Stellen Sie sicher, dass Sie sich im richtigen Verzeichnis befinden.

Jetzt befinden Sie sich (oder sollten) in einer Pry-Sitzung, damit wir anfangen können, Ruby-Code in unser Terminal einzugeben. Erstellen wir zunächst eine neue Scraper-Instanz:

bentley = Scraper.new('bentley', 'continental GT')

Cool, wir haben eine neue Instanz eines Scrapers, der nach Bentley Continental GTs in suchen wird https://www.dupontregistry.com/autos/results/bentley/continental–gt/for-sale . Wir können dies bestätigen, indem wir Folgendes in unser Terminal eingeben:

bentley.url #=> 'https://www.dupontregistry.com/autos/results/bentley/continental--gt/for-sale'

Groß! Kommen wir nun zur Sache. Wir müssen unsere #scrape-Methode in unserer Scraper-Instanz von . aufrufen Bentley damit wir unser zweites Binding.pry treffen und uns ansehen können, was wir bekommen haben:

bentley.scrape

Das ist der Bildtitel

Was mich an dieser Stelle interessiert, ist was genau parsed_page ist, weil es eine Menge Ruby-Edelstein-Tricks gibt. Rufen wir an parsed_page lokale Variable.

pry(#)> parsed_page

Whoa! Okay. Das ist viel Zeug. Offensichtlich tut es etwas, ich bin mir nur noch nicht sicher, was ich damit anfangen soll. Meine sah ungefähr so ​​aus:

Wie kaufe ich Casper-Krypto?

Das ist der Bildtitel

Dies kann anders aussehen, wenn Sie eine andere Website durchsuchen.

Bevor wir uns zu sehr damit befassen, herauszufinden, wie wir unsere Datenbits aus diesem riesigen Objekt herauskratzen, schauen wir uns an, was ich erreichen möchte, damit wir es zurückentwickeln können. Ich hätte gerne einen Hash, der ungefähr so ​​aussieht:

{ year: 2007, name: 'Bentley', model: 'Continental GT', price: 150000, link: 'https://www.dupontregistry.com/link_to_my_car' }

hash.rb

Cool, also anstatt zu versuchen, alle Daten herauszuziehen, suchen wir nur nach diesen fünf Daten: Jahr, Marke, Modell, Preis und Link. Aber warte! Wir kennen unsere Marke und unser Modell bereits, als wir unsere Scraper-Instanz instanziiert haben, also suchen wir wirklich nur nach 3 Datenpunkten.

Umgang mit HTML

Nachdem wir nun wissen, wonach wir suchen, schauen wir uns den HTML-Code der Seite an, die wir durchsuchen möchten, um unsere Pfade zu finden. Schauen wir zuerst, ob wir den Container finden können, der alle unsere relevanten Fahrzeuginformationen enthält. Wir müssen diese Auflistungsseite besuchen und Entwicklertools öffnen.

Das ist der Bildtitel

Lassen Sie uns jede Auflistung in einer Variablen speichern, die wir später verwenden können. Wir verwenden die .css-Methode von Nokogiri, um dieses Array zu erstellen.

cars = parsed_page.css('div.searchResults') #creates an array where each element is parsed HTML pertaining to a different listing

Jetzt versuchen wir einfach, den Preis zu finden.

Das ist der Bildtitel

Wir müssen damit herumspielen, um den benötigten Text herauszuholen. Es ist am besten, dies in einer Pry- oder Byebug-Sitzung zu tun. Mit folgendem Code konnte ich den Preis herausziehen:

car.css('.cost').children[1].text.sub(',','').to_i #looks in an instance of car for the cost div #looks in its children at an index of 1 (this is where it lives) #converts it to text #gets rid of the comma #converts the string into an integer

Wir haben den Preis, jetzt lass uns das Jahr bekommen. Auf die gleiche Weise müssen wir mit Entwicklertools herumstöbern, um unsere Zeichenfolge zu finden, die das Jahr des Autos enthält. In meinem Fall war dies eine Zeichenfolge, die wie 2017 Bentley Continental GT V8 S aussah, also beschloss ich, einfach die ersten 4 Zeichen zu stehlen und in eine ganze Zahl umzuwandeln:

car.css('a').children[0].text[0..4].strip.to_i #looks in an instance of car for the tag #looks in its children at an index of 0 (this is where it lives) #converts to text #takes the first 4 characters (ie '2017') #strips any whitespace if it exists #converts to integer

Zu guter Letzt erhalten wir unseren Link zur Seite des Autos.

car.css('a').attr('href').value #looks in an instance of car for the tag #gets the attribute of #pulls out is value (as a string)

Wir haben also alle unsere JQuery-ähnlichen Sachen herausgefunden. Während Sie weiterlesen, werden Sie sehen, wie diese in der Helfermethode implementiert sind create_car_hash .

Wie kaufe ich Elongate auf Binance?

Den Schaber fertigstellen

Von hier aus ist es ziemlich einfach, unseren Schaber fertig zu stellen. Lassen Sie uns unsere Hilfsmethoden so einrichten, dass unsere #scrape-Methode alles hat, was sie braucht.

def create_car_hash(car_obj) #creates a hash with the values we need from our parsed car object car_obj.map { |car| { year: car.css('a').children[0].text[0..4].strip.to_i, name: @make, model: @model, price: car.css('.cost').children[1].text.sub(',','').to_i, link: 'https://www.dupontregistry.com/#{car.css('a').attr('href').value}' } } end def get_all_page_urls(array_of_ints) #gets URLs of all pages, not just the first page array_of_ints.map { |number| @url + '/pagenum=#{number}' } end def get_number_of_pages(listings, cars_per_page) #finds how many pages of listings exist a = listings % cars_per_page if a == 0 listings / cars_per_page else listings / cars_per_page + 1 end end def build_full_cars(number_of_pages) #builds an array of car hashes for each page of listings, starting on page 2 a = [*2..number_of_pages] all_page_urls = get_all_page_urls(a) all_page_urls.map url end

helper_methods.rb

Am Ende habe ich 4 verschiedene Hilfsmethoden entwickelt:

  1. create_car_hash erstellt einen Hash basierend auf den Werten, die wir brauchen
  2. get_all_page_urls tut was es klingt! Sammelt alle URLs in einem Array, das es uns ermöglicht, einzubeziehen Seitennummerierung
  3. get_number_of_pages ist ziemlich selbsterklärend — das hilft uns auch bei der Paginierung
  4. build_full_cars gibt uns eine Reihe von Auto-Hashes ab Seite 2 und darüber hinaus

Lassen Sie uns alles in der #scrape-Methode zusammenfassen, damit wir bekommen, was wir wollen. Am Ende sieht mein Code so aus:

require 'nokogiri' require 'rest-client' require 'httparty' require 'byebug' require 'pry' class Scraper attr_reader :url, :make, :model def initialize (make, model) @make = make.capitalize @model = model.capitalize @url = 'https://www.dupontregistry.com/autos/results/#{make}/#{model}/for-sale'.sub(' ', '--') end def parse_url(url) unparsed_page = HTTParty.get(url) Nokogiri::HTML(unparsed_page) end def scrape parsed_page = parse_url(@url) cars = parsed_page.css('div.searchResults') #Nokogiri object containing all cars on a given page per_page = cars.count #counts the number of cars on each page, should be 10 total_listings = parsed_page.css('#mainContentPlaceholder_vehicleCountWithin').text.to_i total_pages = self.get_number_of_pages(total_listings, per_page) first_page = create_car_hash(cars) all_other = build_full_cars(total_pages) first_page + all_other.flatten end def create_car_hash(car_obj) car_obj.map { |car| { year: car.css('a').children[0].text[0..4].strip.to_i, name: @make, model: @model, price: car.css('.cost').children[1].text.sub(',','').to_i, link: 'https://www.dupontregistry.com/#{car.css('a').attr('href').value}' } } end def get_all_page_urls(array_of_ints) array_of_ints.map { |number| @url + '/pagenum=#{number}' } end def get_number_of_pages(listings, cars_per_page) a = listings % cars_per_page if a == 0 listings / cars_per_page else listings / cars_per_page + 1 end end def build_full_cars(number_of_pages) a = [*2..number_of_pages] all_page_urls = get_all_page_urls(a) all_page_urls.map pu = parse_url(url) cars = pu.css('div.searchResults') create_car_hash(cars) end end

DuP.rb

Es mag etwas seltsam erscheinen, unsere Hashes für die erste Seite und den Rest der Seiten getrennt zu erstellen (siehe Zeile 32), und Sie könnten dies wahrscheinlich konsolidieren, aber die URL der ersten Seite ist etwas anders und ist wichtig, um unsere Initialen zu erhalten Daten eingerichtet.

Lassen Sie uns das sehr schnell ausführen, um sicherzustellen, dass es das tut, was wir wollen.

ruby scraper.rb bentley = Scraper.new('bentley', 'continental gt') bentley.scrape

Unsere Leistung ist riesig! Ich musste es etwas kürzen. Das habe ich bekommen:

[{:year=>2017, :name=>'Bentley', :model=>'Continental gt', :price=>179910, :link=>'https://www.dupontregistry.com//autos/listing/2017/bentley/continental--gt/2080775'}, {:year=>2012, :name=>'Bentley', :model=>'Continental gt', :price=>86200, :link=>'https://www.dupontregistry.com//autos/listing/2012/bentley/continental--gt/2070330'}, {:year=>2016, :name=>'Bentley', :model=>'Continental gt', :price=>135988, :link=>'https://www.dupontregistry.com//autos/listing/2016/bentley/continental--gt/2077824'}, {:year=>2016, :name=>'Bentley', :model=>'Continental gt', :price=>139989, :link=>'https://www.dupontregistry.com//autos/listing/2016/bentley/continental--gt/2086794'}, {:year=>2016, :name=>'Bentley', :model=>'Continental gt', :price=>0, :link=>'https://www.dupontregistry.com//autos/listing/2016/bentley/continental--gt--speed/2086825'}, {:year=>2015, :name=>'Bentley', :model=>'Continental gt', :price=>119888, :link=>'https://www.dupontregistry.com//autos/listing/2015/bentley/continental--gt/2086890'}, ...}]

output_hash.rb

Wenn wir in diesem Array .length aufrufen, erhalten wir tatsächlich 120, was unserer Anzahl von Einträgen entspricht. Perfekt!

Es gibt noch viele Methoden, die wir in diese integrieren möchten, um sie nützlich zu machen. Wir könnten beispielsweise eine Methode erstellen, die den Link des derzeit günstigsten Bentleys zurückgibt oder den durchschnittlichen Angebotspreis berechnet. Sie könnten diesen Code sogar ein wenig verschönern, da er alles andere als perfekt ist. Ich werde jedoch für heute hier aufhören, bevor dieser Artikel wird Krieg und Frieden ! Hier ist ein Link zum GitHub-Repository wenn Sie es als Inspiration für Ihren eigenen Web Scraper verwenden möchten.

#ruby #scraper #programmierung

itnext.io

Erstellen eines einfachen Schabers mit Ruby

Erstellen eines einfachen Schabers mit Ruby. Ich liebe Web-Scraping. Es hat sich in den letzten Jahren nicht nur zu einer großen Branche entwickelt, es macht auch viel Spaß. Es kann Ihnen helfen, auf einfache Weise interessante Einblicke zu finden, indem Sie bereits vorhandene Daten verwenden.