Kako strugati Rubyjem i Nokogirijem i mapirati podatke

Ponekad želite prikupiti podatke s web mjesta za vlastiti projekt. Pa što koristiš? Ruby, Nokogiri i JSON u pomoć!

Nedavno sam radio na projektu mapiranja podataka o mostovima. Pomoću Nokogirija uspio sam iz tablice prikupiti podatke o gradskom mostu. Zatim sam koristio veze unutar iste tablice za struganje povezanih stranica. Na kraju sam pretvorio strugane podatke u JSON i upotrijebio ih za popunjavanje Google karte.

Ovaj članak vodi vas kroz alate koje sam koristio i kako funkcionira kôd!

Pogledajte cijeli kôd na mom GitHub repo-u.

Demonstracija karte uživo ovdje.

Projekt

Cilj mi je bio uzeti tablicu s web stranice s podacima o mostovima i pretvoriti je u Google kartu s geolociranim iglama koja će stvoriti informativne skočne prozore za svaki most.

Da bih se to dogodilo, trebao bih:

  1. Izbrišite podatke s izvorne web stranice.
  2. Pretvorite te podatke u JSON objekt.
  3. Primijenite te podatke za izradu nove, interaktivne karte.

Vaš će se projekt sigurno razlikovati - koliko ljudi pokušava mapirati antičke mostove? - ali nadam se da će se ovaj postupak pokazati korisnim za vaš kontekst.

Nokogiri

Ruby ima nevjerojatan dragulj za struganje weba nazvan Nokogiri. Između ostalih značajki, omogućuje vam pretraživanje HTML dokumenata po CSS biračima. To znači da ako znamo ID-ove, klase ili čak vrste elemenata u kojima su podaci pohranjeni u DOM-u, možemo ih iščupati.

Strugač

Ako slijedite repo GibHub-a, moju strugalicu možete pronaći u bridges_scraper.rb

require 'open-uri'require 'nokogiri'require 'json'

Open-uri nam omogućuje otvaranje HTML-a poput datoteke i prosljeđivanje Nokogiriju na teško dizanje.

U donjem kodu prosljeđujem DOM informacije s URL-a s podacima o premošćivanju Nokogiriju. Tada pronalazim element tablice u kojem se nalaze podaci, tražim njegove retke i prelazim kroz njih.

url = '//bridgereports.com/city/wichita-kansas/'html = open(url)
doc = Nokogiri::HTML(html)bridges = []table = doc.at('table')
table.search('tr').each do |tr| bridges.push( carries: cells[1].text, crosses: cells[2].text, location: cells[3].text, design: cells[4].text, status: cells[5].text, year_build: cells[6].text.to_i, year_recon: cells[7].text, span_length: cells[8].text.to_f, total_length: cells[9].text.to_f, condition: cells[10].text, suff_rating: cells[11].text.to_f, id: cells[12].text.to_i )end
json = JSON.pretty_generate(bridges)File.open("data.json", 'w')  file.write(json) 

Nokogiri ima puno metoda (ovdje su varalice i početni vodič!). Koristimo samo nekoliko.

Tablica se nalazi s .at ('tablica') , koja vraća prvo pojavljivanje elementa tablice u DOM-u. Ovo dobro funkcionira za ovu relativno jednostavnu stranicu.

S tablicom u ruci, .search ('tr') pruža niz elemenata redaka koje ponavljamo .each . U svakom retku podaci se čiste i guraju u jedan unos za niz mostova.

Nakon što se prikupe svi retci, podaci se pretvaraju u JSON i spremaju u novu datoteku nazvanu "data.json".

Kombiniranje podataka s više stranica

U ovom su mi slučaju potrebne informacije s drugih povezanih stranica. Točnije, trebala su mi se zemljopisna širina i dužina svakog mosta, koji nije bio istaknut na stolu. Međutim, otkrio sam da je veza u prvoj ćeliji svakog retka dovelo do stranice koja je osigurala te detalje.

Trebao sam napisati kod koji je učinio nekoliko stvari:

  • Prikupljene veze iz prve ćelije u tablici.
  • Napravio je novi objekt Nokogiri iz HTML-a na toj stranici.
  • Izvucite zemljopisnu širinu i dužinu.
  • Isključite program dok se taj postupak ne završi.
cells = tr.search('th, td') links = {} cells[0].css('a').each do |a| links[a.text] = a['href'] end got_coords = false if links['NBI report'] nbi = links['NBI report'] report = "//bridgereports.com" + nbi report_html = open(report) sleep 1 until report_html r = Nokogiri::HTML(report_html) lat = r.css('span.latitude').text.strip.to_f long = r.css('span.longitude').text.strip.to_f
 got_coords = true else got_coords = true end sleep 1 until got_coords == true
 bridges.push( links: links, latitude: lat, longitude: long, carries: cells[1].text, ..., # all other previous key/value pairs )end

Ovdje vrijedi istaknuti nekoliko dodatnih stvari:

  • Koristim "got_coords" kao jednostavnu binarnu datoteku. To je prema zadanim postavkama postavljeno na false i prebacuje se kada se podaci prikupe ILI jednostavno nisu dostupni.
  • Zemljopisna širina i dužina nalaze se u rasponima s odgovarajućim klasama. To osiguravanje podataka čini jednostavnim: .css ('span.latitude') Nakon toga slijede .text, .strip i .to_f koji 1) dobivaju tekst iz raspona, 2) uklanjaju višak razmaka i 3) pretvaraju niz na plutajući broj.

JSON → Google karta

Novoformirani JSON objekt mora se modificirati tako da odgovara API-ju Google Maps. To sam učinio s JavaScriptom unutar map.js

JSON podacima je dostupan u map.js jer su premješteni u JS mapu, dodijeljeni varijabli nazvanoj "bridge_data" i uključeni u oznaku u index.html.

U redu! Sada ćemo pretvoriti JSON datoteku (dodijeljenu varijabli bridge_data) u novi niz koji se može koristiti na Google kartama.

const locations = bridge_data.map(function(b) { var mapEntry = []; var info = "Built In: " + b.year_build + "

" + "Span Length: " + b.span_length + " ft

" + "Total Length: " + b.total_length + " ft

" + "Condition: " + b.condition + "

" + "Design: " + b.design + "

"; mapEntry.push( info, b.latitude, b.longitude, b.id ) return mapEntry;});

Koristim .map za stvaranje novog dimenzionalnog niza nazvanog "lokacije". Svaki unos sadrži informacije koje će se pojaviti u skočnom prozoru Google Maps ako korisnik klikne tu pribadaču na karti. Uključujemo i zemljopisnu širinu, dužinu i jedinstveni ID mosta.

Rezultat je Google karta koja za svaki most iscrtava niz lokacija s skočnim prozorima bogatim informacijama!

Je li vam ovo pomoglo? Dajte mu nekoliko pljeska i slijedite!