Cara mengikis dengan Ruby dan Nokogiri dan memetakan data

Terkadang Anda ingin mengambil data dari situs web untuk proyek Anda sendiri. Jadi, apa yang Anda gunakan? Ruby, Nokogiri, dan JSON untuk menyelamatkan!

Baru-baru ini, saya sedang mengerjakan proyek untuk memetakan data tentang jembatan. Dengan menggunakan Nokogiri, saya dapat menangkap data jembatan kota dari tabel. Saya kemudian menggunakan tautan dalam tabel yang sama untuk mengikis halaman terkait. Akhirnya, saya mengonversi data bekas ke JSON dan menggunakannya untuk mengisi Google Map.

Artikel ini memandu Anda melalui alat yang saya gunakan dan cara kerja kode!

Lihat kode lengkap di repo GitHub saya.

Demo peta langsung di sini.

Proyek

Tujuan saya adalah mengambil tabel dari situs web data jembatan dan mengubahnya menjadi peta Google dengan pin geolokasi yang akan menghasilkan sembulan informasi untuk setiap jembatan.

Untuk mewujudkannya, saya perlu:

  1. Kikis data dari situs asli.
  2. Ubah data itu menjadi objek JSON.
  3. Terapkan data tersebut untuk membuat peta baru yang interaktif.

Proyek Anda akan bervariasi, tentunya - berapa banyak orang yang mencoba memetakan jembatan antik? - tapi saya harap proses ini berguna untuk konteks Anda.

Nokogiri

Ruby memiliki permata pengikis web yang luar biasa yang disebut Nokogiri. Di antara fitur lainnya, ini memungkinkan Anda untuk mencari dokumen HTML oleh pemilih CSS. Itu berarti jika kita mengetahui id, kelas, atau bahkan jenis elemen tempat data disimpan di DOM, kita dapat mengambilnya.

Pengikis

Jika Anda mengikuti repo GibHub, Anda dapat menemukan scraper saya di bridges_scraper.rb

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

Open-uri memungkinkan kita membuka HTML seperti file dan meneruskannya ke Nokogiri untuk pekerjaan berat.

Pada kode di bawah ini, saya meneruskan informasi DOM dari URL dengan data jembatan ke Nokogiri. Saya kemudian menemukan elemen tabel yang menyimpan data, mencari barisnya, dan mengulanginya.

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 memiliki banyak metode (berikut adalah contekan dan panduan awal!). Kami hanya menggunakan beberapa.

Tabel ditemukan dengan .at ('table') , yang mengembalikan kemunculan pertama elemen tabel di DOM. Ini berfungsi dengan baik untuk halaman yang relatif sederhana ini.

Dengan tabel di tangan, .search ('tr') menyediakan larik elemen baris yang kita iterasi dengan .each . Di setiap baris, data dibersihkan dan didorong menjadi satu entri untuk larik jembatan.

Setelah semua baris terkumpul, data diubah menjadi JSON dan disimpan dalam file baru bernama “data.json”.

Menggabungkan data dari banyak halaman

Dalam kasus ini, saya membutuhkan informasi dari halaman terkait lainnya. Secara khusus, saya membutuhkan lintang dan bujur dari setiap jembatan, yang tidak ditampilkan di tabel. Namun, saya menemukan bahwa link dalam sel pertama dari setiap baris menyebabkan halaman yang tidak memberikan rincian mereka.

Saya perlu menulis kode yang melakukan beberapa hal:

  • Mengumpulkan link dari sel pertama di tabel.
  • Membuat objek Nokogiri baru dari HTML di halaman itu.
  • Cabut garis lintang dan bujur.
  • Tidur program sampai proses itu selesai.
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

Beberapa hal tambahan yang perlu ditunjukkan di sini:

  • Saya menggunakan "got_coords" sebagai biner sederhana. Ini disetel ke false secara default dan diubah saat data diambil ATAU tidak tersedia.
  • Lintang dan bujur terletak dalam bentang dengan kelas yang sesuai. Itu membuat pengamanan data sederhana: .css ('span.latitude') Ini diikuti oleh .text, .strip dan .to_f yang 1) mendapatkan teks dari span, 2) menghapus spasi kosong berlebih, dan 3) mengonversi string ke nomor float.

JSON → Google Map

Objek JSON yang baru dibentuk harus dimodifikasi satu sentuhan agar sesuai dengan Google Maps API. Saya melakukan ini dengan JavaScript di dalam map.js

Data JSON dapat diakses dalam map.js karena telah dipindahkan ke folder JS, ditetapkan ke variabel yang disebut "bridge_data", dan disertakan dalam tag di index.html.

Baiklah! Kami sekarang akan mengonversi file JSON (ditugaskan ke variabel bridge_data) ke array baru yang dapat digunakan oleh Google Maps.

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;});

Saya menggunakan .map untuk membuat array dimensi baru yang disebut "lokasi". Setiap entri memiliki info, yang akan muncul di popup Google Maps kami jika pengguna mengklik pin itu di peta. Kami juga menyertakan lintang, bujur, dan ID jembatan unik.

Hasilnya adalah Google Map yang memplot berbagai lokasi dengan popup kaya info untuk setiap jembatan!

Apakah ini membantu Anda? Beri beberapa tepukan dan ikuti!