de Andrew Bales

Cum să răzuiești cu Ruby și Nokogiri și să mapezi datele

Cum sa razuiesti cu Ruby si Nokogiri si sa mapezi
Ruby, JSON și Nokogiri

Uneori doriți să obțineți date de pe un site web pentru propriul dvs. proiect. Deci, ce folosești? Ruby, Nokogiri și JSON la salvare!

Recent, lucram la un proiect de cartografiere date despre poduri. Folosind Nokogiri, am reușit să captez datele podului unui oraș dintr-un tabel. Am folosit apoi linkuri în același tabel pentru a răzuia paginile asociate. În cele din urmă, am convertit datele răzuite în JSON și le-am folosit pentru a popula o hartă Google.

Acest articol vă prezintă instrumentele pe care le-am folosit și cum funcționează codul!

Vedeți codul complet pe GitHub repo.

Demo de hartă live Aici.

Proiectul

Scopul meu a fost să iau o masă dintr-o punte de date site-ul web și transformați-o într-o hartă Google cu pini geolocați care ar produce ferestre pop-up informaționale pentru fiecare pod.

1612137007 485 Cum sa razuiesti cu Ruby si Nokogiri si sa mapezi
Ideea: Tabel HTML pentru hartă

Pentru ca acest lucru să se întâmple, ar trebui să:

  1. Scrape date de pe site-ul original.
  2. Convertiți aceste date într-un fișier Obiect JSON.
  3. Aplicați aceste date pentru a crea o hartă nouă, interactivă.

Proiectul dvs. va varia, cu siguranță – câți oameni încearcă să cartografieze poduri antice? – dar sper că acest proces se va dovedi util pentru contextul dvs.

Nokogiri

Ruby are o bijuterie uimitoare de răzuire web Nokogiri. Printre alte caracteristici, vă permite să căutați documente HTML prin selectoare CSS. Asta înseamnă că, dacă cunoaștem ID-urile, clasele sau chiar tipurile de elemente în care datele sunt stocate în DOM, putem să le scoatem.

Răzuitorul

Dacă urmăriți împreună cu GibHub repo, puteți găsi răzuitorul meu în bridges_scraper.rb

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

Open-uri ne permite să deschidem HTML-ul ca un fișier și să-l transmitem lui Nokogiri pentru ridicări grele.

În codul de mai jos, transmit informațiile DOM de pe URL-ul cu datele podului către Nokogiri. Apoi găsesc elementul de tabel care conține datele, căutăm rândurile sale și iterez prin ele.

url="https://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| file.write(json) }

Nokogiri are o mulțime de metode (aici este un foaie de înșelăciune și un starter ghid!). Folosim doar câteva.

Tabelul se găsește cu .at („masă”), care returnează prima apariție a unui element de tabel în DOM. Acest lucru funcționează foarte bine pentru această pagină relativ simplă.

Cu masa în mână, .search (‘tr’) oferă o serie de elemente de rând pe care le repetăm .fiecare. În fiecare rând, datele sunt curățate și împinse într-o singură intrare pentru matricea de poduri.

După colectarea tuturor rândurilor, datele sunt convertite în JSON și salvate într-un fișier nou numit „data.json”.

Combinarea datelor din mai multe pagini

În acest caz, aveam nevoie de informații din alte pagini asociate. Mai exact, aveam nevoie de latitudinea și longitudinea fiecărui pod, care nu era prezentată pe masă. Cu toate acestea, am constatat că link-ul din prima celulă a fiecărui rând a dus la o pagină care făcut furnizați aceste detalii.

Aveam nevoie să scriu cod care să facă câteva lucruri:

  • Linkuri adunate din prima celulă din tabel.
  • Am creat un nou obiect Nokogiri din codul HTML din pagina respectivă.
  • Scoateți latitudinea și longitudinea.
  • Adormiți programul până când procesul se finalizează.
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 = "https://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

Câteva lucruri suplimentare merită subliniate aici:

  • Folosesc „got_coords” ca un simplu binar. Aceasta este setată la fals implicit și este comutat când datele sunt capturate SAU pur și simplu nu sunt disponibile.
  • Latitudinea și longitudinea sunt situate în intervale cu clase corespunzătoare. Acest lucru face ca securizarea datelor să fie simplă: .css („span.latitude”) Aceasta este urmată de .text, .strip și .to_f care 1) primește textul din interval, 2) elimină orice spațiu alb în exces și 3) convertește șirul într-un număr flotant.

JSON → Google Map

Obiectul JSON nou format trebuie modificat o atingere pentru a se potrivi cu API-ul Google Maps. Am făcut asta cu JavaScript în interior map.js

Datele JSON sunt accesibile în interior map.js deoarece a fost mutat în folderul JS, atribuit unei variabile numite „bridge_data” și inclus într-o etichetă