Geocoding Bagaimana cara mengikat 250 ribu alamat ke koordinat dalam 10 menit?



Halo, Habr!

Dalam artikel ini, saya ingin berbagi pengalaman saya dalam memecahkan masalah kecil dengan sejumlah besar alamat. Jika Anda pernah bekerja dengan geocoding API atau alat online yang digunakan, maka saya pikir Anda berbagi rasa sakit saya menunggu hasilnya selama beberapa jam, atau bahkan lebih.

Ini bukan tentang algoritma optimasi yang kompleks, tetapi tentang menggunakan layanan geocoding paket yang mengambil daftar alamat sebagai input dan mengembalikan file dengan hasilnya. Ini dapat mengurangi waktu pemrosesan dari beberapa jam menjadi beberapa menit.

Hal pertama yang pertama:


Latar Belakang


Tugas tiba - "Mengikat koordinat 24 ribu alamat." Hanya dua solusi untuk masalah terjadi:

  1. Aplikasi web untuk geocoding, yang digunakan di universitas;
  2. Tulis skrip berdasarkan REST API geocoder.

Dalam kasus pertama, ternyata aplikasi web mogok setelah memproses ribuan alamat. Mendistribusikan data antar kolega adalah ide yang segera ditinggalkan.

Oleh karena itu, Anda perlu menggunakan API REST dari geocoder untuk menulis skrip Anda sendiri, dengan hasil disimpan (ini bukan cara yang sepenuhnya legal dan Anda perlu membaca ketentuan penggunaan layanan). Masalah baru muncul - itu adalah satu hal ketika kami menggunakan pencarian alamat dalam aplikasi dan segera mendapatkan hasilnya, tetapi ketika tugasnya adalah memproses lebih dari sepuluh ribu alamat dengan pengawetan, pekerjaan skrip sangat tertunda. Anda dapat menunggu satu atau dua jam, tetapi sejuta alamat harus memiliki geocode "waktu yang sangat banyak", jadi Anda perlu mencari solusi lain dan itu!

Penyedia besar layanan geolokasi, selain layanan geocoding biasa, menawarkan paket geocoder (Batch Geocoder), hanya untuk memproses sejumlah besar alamat dalam satu permintaan.

Batch Geocoding


Nama layanan berbicara untuk dirinya sendiri - kami memiliki paket (misalnya, file csv dengan daftar alamat dalam bentuk tabel), yang kami unggah ke server, dan itu semua berfungsi untuk kami.

Prosesnya terlihat seperti ini:

  1. Mempersiapkan dataset sehingga layanan dapat menerimanya tanpa kesalahan;
  2. Mengatur parameter hasil pekerjaan (pemilihan kolom, pemisah ...);
  3. Unggah file ke cloud;
  4. Menunggu pemrosesan selesai;
  5. Unduh file yang sudah selesai.

Berkat kekuatan komputasi awan, apa yang dilakukan dengan skrip yang ditulis sendiri dalam 1 jam selesai dalam 1 menit.

Langkah selanjutnya adalah memilih perusahaan dengan ketentuan penggunaan paket geocoder yang paling loyal. Pertama, tidak semua orang memiliki layanan seperti itu, yang lain memungkinkan Anda untuk menguji layanan dengan batasan yang serius. Juga, jika Anda memiliki volume yang sangat besar, Anda perlu memperhatikan biaya transaksi tambahan, jika batas paket gratis terlampaui.

Memilih Penyedia Layanan Geocoding Batch


Di pasar global untuk layanan geolokasi, posisi utama ditempati oleh:

  • Google Maps
  • SINI Teknologi;
  • MapBox
  • TomTom
  • ESRI.

Tentu saja, jangan lupa tentang Yandex Technologies, yang memiliki posisi cukup kuat di Rusia.

Saya mengambil parameter berikut sebagai dasar untuk memilih penyedia:

  • Jumlah permintaan ke layanan geocoding per bulan adalah gratis;
  • Batasi jumlah transaksi per hari;
  • Ketersediaan layanan geocoding batch;
  • Kemampuan untuk menggunakan geocoder paket dalam paket gratis.

Setiap perusahaan memiliki model monetisasi sendiri. Tergantung pada proyeknya, satu atau model lain dapat bermain ke tangan atau sebaliknya menjadi batasan yang signifikan.

Google maps


Untuk memulai dengan layanan geo Google, hal pertama yang perlu Anda lakukan adalah menambahkan informasi kartu kredit ke akun Anda. Batas bulanan 200 dolar virtual, kemudian muncul pembayaran transaksi tambahan dari kartu yang ditautkan. Dalam batas ini, Anda dapat menggunakan berbagai layanan, tetapi setiap transaksi dianggap berbeda. Misalnya, seribu permintaan geocoding akan menelan biaya $ 5, tetapi layanan pembangunan rute dua kali lebih mahal. Rincian lebih lanjut dapat ditemukan di situs, kami hanya tertarik pada layanan geocoding.

Jika $ 200 per bulan, maka mudah untuk menghitung jumlah transaksi gratis - 40.000 (layanan geocoding). Tidak ada paket geocoder di antara layanan. Ini berarti Anda harus menulis skrip sendiri dan hasilnya adalah sekitar 1 alamat per detik, yaitu enam jam untuk 24 ribu alamat. Untuk mempercepat prosesnya, Anda dapat mencoba menjalankan skrip pada platform Google Cloud API, tetapi saya memutuskan untuk mencari solusi alternatif. Tidak ada batasan jumlah transaksi per hari, jadi semua empat puluh ribu dapat dihabiskan sekaligus.

SINI Teknologi


Di masa lalu, Nokia Maps, dan di masa lalu yang lebih dalam, Navteq, menyediakan 250.000 transaksi setiap bulan secara gratis. Sama halnya dengan Google Maps, nomor ini berlaku untuk semua layanan dan masing-masing dianggap berbeda. Saat menggunakan paket gratis, Anda tidak perlu melampirkan kartu bank. Jika Anda melebihi batas, maka untuk setiap seribu transaksi tambahan Anda harus membayar $ 1.

Penting untuk memiliki geocoder paket sebagai layanan terpisah, yang termasuk dalam paket gratis. Transaksi di dalamnya diperhitungkan sesuai dengan model yang sama seperti yang biasa, yaitu, setiap alamat dalam file, geocoder paket akan melihat dalam satu transaksi.
Dengan judul artikel, jelas bahwa saya menggunakan geocoder batch HERE, karena Anda dapat menghabiskan semua transaksi pada geocoder dan melakukan 250.000 operasi geocoding per bulan. Tetapi ini bukan satu-satunya pilihan, jadi kami melihat apa yang dimiliki perusahaan lain.

Kotak Map


Saat menggunakan geocoder MapBox, tersedia 100 ribu transaksi per bulan. Perusahaan menganut model monetisasi yang sama dengan pembayaran untuk transaksi tambahan. Hanya ada opsi yang menarik untuk "grosir" - semakin banyak transaksi yang Anda miliki, semakin sedikit biayanya (tentu saja ada batas pengurangan harga). Misalnya, dari 100 ribu hingga 500 ribu, seribu permintaan tambahan akan dikenakan biaya $ 0,75, dari 500 ribu hingga 1 juta - $ 0,60, dll., Untuk lebih jelasnya, lihat situs web. Sayangnya, batch geocoder hanya tersedia di akun berbayar.

Tomtom


Platform ini memungkinkan untuk melakukan 2.500 transaksi per hari, sekitar 75.000 per bulan. Selama pengujian dan pengembangan, batas harian tidak terlihat sangat menarik dibandingkan dengan pesaing, tetapi pembayaran untuk transaksi tambahan adalah yang paling fleksibel. Ada 8 opsi pembayaran untuk seribu permintaan tambahan dan harganya dikurangi dari $ 0,5 menjadi $ 0,42.

Di antara layanan ada geocoder batch dengan kemampuan untuk memproses hingga 10 ribu alamat per permintaan (namun, batas harian harus diperhitungkan).

Teknologi Yandex


Sebuah model dengan batas transaksi harian untuk Yandex, tetapi lebih setia 25 ribu permintaan. Jika Anda mengalikan angka ini dengan jumlah hari dalam sebulan, Anda mendapatkan angka yang mengesankan 750 ribu. Situs ini menyajikan harga untuk seribu transaksi tambahan dalam rubel mulai dari 120 rubel. hingga 11 rubel

Paket geocoder sebagai layanan tidak disajikan, sehingga untuk mencapai beberapa jenis optimasi akan gagal.

ESRI


Paket gratis yang sangat menggoda dengan 1 juta transaksi per bulan. Perusahaan juga membebankan 50 kredit untuk setiap akun (kurang lebih setara dengan $ 5). Perlu dicatat bahwa ini adalah rencana paling setia untuk penggunaan layanan geolokasi. Ada juga layanan geocoding batch, tetapi Anda dapat menggunakannya hanya jika Anda memiliki akun perusahaan di platform ArcGIS Online.

Apa yang harus dipilih pada akhirnya?


Cara termudah adalah membuat pilihan dengan menyusun tabel kecil:



Akibatnya, pilihan saya jatuh di SINI karena itu adalah pilihan terbaik untuk menyelesaikan masalah saya. Tentu saja, saya melakukan jauh dari analisis lengkap, idealnya Anda perlu menjalankan dataset Anda melalui semua geocoder untuk menilai kualitas. Plus, jika Anda memiliki beberapa juta alamat, Anda harus memikirkan paket berbayar dan kemudian Anda perlu memperhitungkan biaya penambahan. transaksi.

Tujuan artikel ini bukan untuk membandingkan perusahaan, tetapi untuk memecahkan masalah mengoptimalkan geocoding dari sejumlah besar alamat. Saya baru saja menunjukkan pikiran saya ketika memilih penyedia layanan.

Panduan Layanan Python


Pertama, Anda perlu membuat akun di portal untuk pengembang dan menghasilkan REST API KEY di bagian proyek.

Sekarang Anda dapat bekerja dengan platform. Saya hanya akan menjelaskan sebagian dari fungsi yang dimiliki geocoder paket HERE: memuat data, memeriksa status, menyimpan hasil.

Jadi, mari kita mulai dengan mengimpor perpustakaan yang diperlukan:

import requests
import json
import time
import zipfile
import io
from bs4 import BeautifulSoup

Selanjutnya, jika tidak ada kesalahan terjadi, buat kelas:

class Batch:

    SERVICE_URL = "https://batch.geocoder.ls.hereapi.com/6.2/jobs"
    jobId = None

    def __init__(self, apikey="your_api_key"):
        self.apikey = apikey

Artinya, selama inisialisasi, kelas harus melewati kuncinya sendiri untuk REST API.
Variabel SERVICE_URL adalah URL dasar untuk bekerja dengan layanan geocoding batch.
Dan dalam jobId pengidentifikasi pekerjaan geocoder saat ini akan disimpan.

Poin penting adalah struktur data yang benar berdasarkan permintaan. File harus berisi dua kolom yang diperlukan: recId dan searchText. Jika tidak, layanan akan mengembalikan respons dengan informasi tentang kesalahan unduhan.

Berikut ini contoh dataset:

   recId; searchText
   1; -, . , 6
   2; ,  1,  -., 72
   3; 425 W Randolph St Chicago IL 60606
   4; , DJ106 20-30, Sibiu 557260
   5; 200 S Mathilda Ave Sunnyvale CA 94086
  

Fungsi untuk mengunggah file ke cloud:

def start(self, filename, indelim=";", outdelim=";"):
        
        file = open(filename, 'rb')

        params = {
            "action": "run",
            "apiKey": self.apikey,
            "politicalview":"RUS",
            "gen": 9,
            "maxresults": "1",
            "header": "true",
            "indelim": indelim,
            "outdelim": outdelim,
            "outcols": "displayLatitude,displayLongitude,locationLabel,houseNumber,street,district,city,postalCode,county,state,country",
            "outputcombined": "true",
        }

        response = requests.post(self.SERVICE_URL, params=params, data=file)
        self.__stats (response)
        file.close()


Semuanya cukup sederhana, buka file dengan daftar alamat untuk dibaca, bentuk kamus dari parameter permintaan GET. Beberapa parameter layak dijelaskan:

  • "Aksi": "jalankan" - mulai pemrosesan alamat;
  • “politicalView”: “RUS” – . ( );
  • “gen”: 9 – ( );
  • “maxresults”: 1 – ;
  • “header”: true – ;
  • “indelim”: “;” – , ;
  • “outdelim”: “;” – ;
  • “outcols”: “” – , ;
  • “outcombined”: true – .

Selanjutnya, cukup kirim permintaan menggunakan perpustakaan permintaan dan tampilkan statistik. Tentu saja, Anda harus menutup file di akhir fungsi. Fungsi __stats mem-parsing respons server, yang berisi Id dari pekerjaan yang sedang berjalan, dan juga menampilkan informasi umum tentang operasi.

Langkah selanjutnya adalah memeriksa status pekerjaan. Permintaan dibentuk dengan cara yang sama, hanya perlu untuk mentransfer ID operasi. Parameter tindakan harus mengandung nilai "status". Fungsi __stats menampilkan statistik lengkap ke konsol untuk memperkirakan waktu shutdown geocoder.

    def status (self, jobId = None):

        if jobId is not None:
            self.jobId = jobId
        
        statusUrl = self.SERVICE_URL + "/" + self.jobId
        
        params = {
            "action": "status",
            "apiKey": self.apikey,
        }
        
        response = requests.get(statusUrl, params=params)
        self.__stats (response)

Salah satu fungsi terpenting adalah menyimpan hasilnya. Untuk kenyamanan, lebih baik segera unzip file yang berasal dari server. Permintaan untuk menyimpan file identik dengan memeriksa status, cukup tambahkan / hasil di akhir.

    def result (self, jobId = None):

        if jobId is not None:
            self.jobId = jobId
        
        print("Requesting result data ...")
        
        resultUrl = self.SERVICE_URL + "/" + self.jobId + "/result"
        
        params = {
            "apiKey": self.apikey
        }
        
        response = requests.get(resultUrl, params=params, stream=True)
        
        if (response.ok):    
            zipResult = zipfile.ZipFile(io.BytesIO(response.content))
            zipResult.extractall()
            print("File saved successfully")
        
        else:
            print("Error")
            print(response.text)

Fungsi terakhir untuk mem-parsing respons layanan. Juga tugasnya adalah untuk menyimpan pengidentifikasi tugas geocoding saat ini:

    def __stats (self, response):
        if (response.ok):
            parsedXMLResponse = BeautifulSoup(response.text, "lxml")

            self.jobId = parsedXMLResponse.find('requestid').get_text()
            
            for stat in parsedXMLResponse.find('response').findChildren():
                if(len(stat.findChildren()) == 0):
                    print("{name}: {data}".format(name=stat.name, data=stat.get_text()))

        else:
            print(response.text)

Untuk mengujinya, jalankan interpreter Python di folder skrip. Kelas Batch ada di file geocoder.py :

>>> from geocoder import Batch
>>> service = Batch(apikey="   REST API")
>>> service.start("big_data_addresses.csv", indelim=";", outdelim=";")

requestid: "  Id "
status: accepted
totalcount: 0
validcount: 0
invalidcount: 0
processedcount: 0
pendingcount: 0
successcount: 0
errorcount: 0


Pekerjaan bagus dimulai. Periksa status:

>>> service.status()

requestid: "  Id "
status: completed
jobstarted: 2020-04-27T10:09:58.000Z
jobfinished: 2020-04-27T10:17:18.000Z
totalcount: 249999
validcount: 249999
invalidcount: 0
processedcount: 249999
pendingcount: 0
successcount: 249978
errorcount: 21

Kami melihat bahwa pemrosesan dataset selesai. Hanya dalam tujuh menit, dimungkinkan untuk memprogram 250 ribu alamat (tidak termasuk kesalahan - kesalahan). Tetap menyimpan hasilnya:

>>> service.result()
Requesting result data ...
File saved successfully

Deskripsi Kelas Penuh


Saya pikir tidak ada salahnya menambahkan skrip sepenuhnya:

import requests
import json
import time
import zipfile
import io
from bs4 import BeautifulSoup

class Batch:

    SERVICE_URL = "https://batch.geocoder.ls.hereapi.com/6.2/jobs"
    jobId = None

    def __init__(self, apikey="   REST API "):
        self.apikey = apikey
        
            
    def start(self, filename, indelim=";", outdelim=";"):
        
        file = open(filename, 'rb')

        params = {
            "action": "run",
            "apiKey": self.apikey,
            "politicalview":"RUS",
            "gen": 9,
            "maxresults": "1",
            "header": "true",
            "indelim": indelim,
            "outdelim": outdelim,
            "outcols": "displayLatitude,displayLongitude,locationLabel,houseNumber,street,district,city,postalCode,county,state,country",
            "outputcombined": "true",
        }

        response = requests.post(self.SERVICE_URL, params=params, data=file)
        self.__stats (response)
        file.close()
    

    def status (self, jobId = None):

        if jobId is not None:
            self.jobId = jobId
        
        statusUrl = self.SERVICE_URL + "/" + self.jobId
        
        params = {
            "action": "status",
            "apiKey": self.apikey,
        }
        
        response = requests.get(statusUrl, params=params)
        self.__stats (response)
        

    def result (self, jobId = None):

        if jobId is not None:
            self.jobId = jobId
        
        print("Requesting result data ...")
        
        resultUrl = self.SERVICE_URL + "/" + self.jobId + "/result"
        
        params = {
            "apiKey": self.apikey
        }
        
        response = requests.get(resultUrl, params=params, stream=True)
        
        if (response.ok):    
            zipResult = zipfile.ZipFile(io.BytesIO(response.content))
            zipResult.extractall()
            print("File saved successfully")
        
        else:
            print("Error")
            print(response.text)
    

    
    def __stats (self, response):
        if (response.ok):
            parsedXMLResponse = BeautifulSoup(response.text, "lxml")

            self.jobId = parsedXMLResponse.find('requestid').get_text()
            
            for stat in parsedXMLResponse.find('response').findChildren():
                if(len(stat.findChildren()) == 0):
                    print("{name}: {data}".format(name=stat.name, data=stat.get_text()))

        else:
            print(response.text)

Analisis Hasil


Sebagai hasilnya, saya beralih dari aplikasi online yang bekerja lambat ke layanan geocoding batch. Pilihan penyedia layanan geografis bergantung sepenuhnya pada tugas yang Anda hadapi. Saya secara teratur menerima permintaan untuk memproses sejumlah besar alamat dan pendekatan yang dijelaskan dalam artikel membantu mengurangi waktu secara signifikan.

Saya harap artikel ini bermanfaat dan tentu saja saya terbuka untuk komentar dan tambahan!

All Articles