Geokodierung Wie binde ich in 10 Minuten 250.000 Adressen an Koordinaten?



Hallo Habr!

In diesem Artikel möchte ich meine Erfahrungen bei der Lösung eines kleinen Problems mit einer großen Anzahl von Adressen teilen. Wenn Sie jemals mit der Geokodierungs-API gearbeitet oder Online-Tools verwendet haben, dann teilen Sie wahrscheinlich meinen Schmerz, mehrere Stunden oder sogar länger auf das Ergebnis zu warten.

Hier geht es nicht um komplexe Optimierungsalgorithmen, sondern um die Verwendung eines Paket-Geokodierungsdienstes, der eine Liste von Adressen als Eingabe verwendet und eine Datei mit den Ergebnissen zurückgibt. Dies kann die Verarbeitungszeit von einigen Stunden auf Minuten verkürzen.

Das wichtigste zuerst:


Hintergrund


Die Aufgabe kam - "Binden Sie an die Koordinaten von 24 Tausend Adressen." Es gab nur zwei Lösungen für das Problem:

  1. Webanwendung für Geokodierung, die an der Universität verwendet wurde;
  2. Schreiben Sie ein Skript, das auf der REST-API des Geocoders basiert.

Im ersten Fall stellte sich heraus, dass die Webanwendung nach der Verarbeitung von Tausenden von Adressen abstürzt. Das Verteilen eines Datensatzes zwischen Kollegen ist eine Idee, die sofort aufgegeben wurde.

Daher müssen Sie die REST-API des Geocoders verwenden, um Ihr eigenes Skript zu schreiben, wobei die Ergebnisse gespeichert werden (dies ist kein völlig legaler Weg und Sie müssen die Nutzungsbedingungen des Dienstes lesen). Ein neues Problem tritt auf - es ist eine Sache, wenn wir die Adressensuche in der Anwendung verwenden und sofort das Ergebnis erhalten, aber wenn die Aufgabe darin besteht, mehr als zehntausend Adressen unter Beibehaltung zu verarbeiten, verzögert sich die Arbeit des Skripts erheblich. Sie können ein oder zwei Stunden warten, aber eine Million Adressen müssen "immense Zeit" geocodieren, also müssen Sie nach einer anderen Lösung suchen und das ist es!

Große Anbieter von Geolokalisierungsdiensten bieten zusätzlich zum üblichen Geokodierungsdienst einen Paket-Geocoder (Batch-Geocoder) an, um eine große Anzahl von Adressen in einer Anfrage zu verarbeiten.

Batch-Geokodierung


Der Name des Dienstes spricht für sich - wir haben ein Paket (zum Beispiel eine CSV-Datei mit einer Liste von Adressen in Form einer Tabelle), das wir auf den Server hochladen und das die ganze Arbeit für uns erledigt.

Der Prozess sieht folgendermaßen aus:

  1. Vorbereiten des Datensatzes, damit der Dienst ihn fehlerfrei akzeptiert;
  2. Einstellen der Parameter des Arbeitsergebnisses (Auswahl der Spalten, Trennzeichen ...);
  3. Laden Sie eine Datei in die Cloud hoch.
  4. Warten auf den Abschluss der Verarbeitung;
  5. Laden Sie die fertige Datei herunter.

Dank der Cloud-Computing-Leistung ist das, was mit einem selbst geschriebenen Skript in 1 Stunde erledigt wird, in 1 Minute erledigt.

Der nächste Schritt besteht darin, das Unternehmen mit den loyalsten Nutzungsbedingungen für den Paket-Geocoder auszuwählen. Erstens hat nicht jeder einen solchen Dienst, andere erlauben es Ihnen, den Dienst mit einer ernsthaften Einschränkung zu testen. Wenn Sie sehr große Mengen haben, müssen Sie auch die Kosten für zusätzliche Transaktionen berücksichtigen, falls das Limit des kostenlosen Pakets überschritten wird.

Auswählen eines Batch-Geokodierungsdienstanbieters


Auf dem globalen Markt für Geolokalisierungsdienste sind die führenden Positionen besetzt mit:

  • Google Maps
  • HIER Technologien;
  • MapBox
  • TomTom
  • ESRI.

Natürlich sollten Sie Yandex Technologies nicht vergessen, das in Russland eine ziemlich starke Position einnimmt.

Ich habe die folgenden Parameter als Grundlage für die Auswahl eines Anbieters herangezogen:

  • Die Anzahl der Anfragen an den Geokodierungsdienst pro Monat ist kostenlos.
  • Begrenzung der Anzahl der Transaktionen pro Tag;
  • Verfügbarkeit des Batch-Geokodierungsdienstes;
  • Möglichkeit, einen Paket-Geocoder in einem kostenlosen Plan zu verwenden.

Jedes Unternehmen hat sein eigenes Monetarisierungsmodell. Je nach Projekt kann das eine oder andere Modell in die Hände spielen oder umgekehrt eine erhebliche Einschränkung darstellen.

Google Maps


Um mit den Google Geo-Diensten zu beginnen, müssen Sie zunächst Ihrem Konto Kreditkarteninformationen hinzufügen. Ein monatliches Limit von 200 virtuellen Dollar, dann kommt die Zahlung zusätzlicher Transaktionen von der verknüpften Karte. Innerhalb dieser Grenze können Sie verschiedene Dienste nutzen, aber jede Transaktion wird anders betrachtet. Zum Beispiel kosten eintausend Geokodierungsanfragen 5 US-Dollar, aber der Routenaufbau ist doppelt so teuer. Weitere Details finden Sie auf der Website, wir interessieren uns nur für den Geokodierungsdienst.

Bei 200 USD pro Monat ist es einfach, die kostenlose Anzahl von Transaktionen zu berechnen - 40.000 (Geokodierungsdienst). Es gibt keinen Paket-Geocoder unter den Diensten. Dies bedeutet, dass Sie Ihr eigenes Skript schreiben müssen. Das Ergebnis ist ungefähr 1 Adresse pro Sekunde, was sechs Stunden für 24.000 Adressen entspricht. Um den Prozess zu beschleunigen, können Sie versuchen, das Skript auf der Google Cloud APIs-Plattform auszuführen. Ich habe mich jedoch entschlossen, nach alternativen Lösungen zu suchen. Die Anzahl der Transaktionen pro Tag unterliegt keinen Einschränkungen, sodass alle vierzigtausend gleichzeitig ausgegeben werden können.

HIER Technologien


In der Vergangenheit bietet Nokia Maps und in der noch tieferen Vergangenheit Navteq jeden Monat 250.000 Transaktionen kostenlos an. Ähnlich wie bei Google Maps gilt diese Nummer für alle Dienste und wird jeweils unterschiedlich betrachtet. Wenn Sie das kostenlose Paket verwenden, müssen Sie keine Bankkarte anhängen. Wenn Sie das Limit überschreiten, müssen Sie für jede weiteren tausend Transaktionen 1 US-Dollar bezahlen.

Es ist wichtig, einen Paket-Geocoder als separaten Dienst zu haben, der im kostenlosen Plan enthalten ist. Die darin enthaltenen Transaktionen werden nach demselben Modell wie im üblichen Modell berücksichtigt, dh nach jeder Adresse in der Datei, die der Paket-Geocoder in einer Transaktion wahrnimmt.
Aus dem Titel des Artikels geht hervor, dass ich den HERE-Batch-Geocoder verwendet habe, da Sie alle Transaktionen für den Geocoder ausgeben und 250.000 Geokodierungsvorgänge pro Monat ausführen können. Dies ist jedoch nicht die einzige Option, daher schauen wir uns an, was andere Unternehmen haben.

Mapbox


Bei Verwendung des MapBox-Geocoders stehen 100.000 Transaktionen pro Monat zur Verfügung. Das Unternehmen hält an demselben Monetarisierungsmodell fest und zahlt für zusätzliche Transaktionen. Nur für „Großhändler“ gibt es eine interessante Option - je mehr Transaktionen Sie haben, desto weniger kosten sie (natürlich gibt es eine Preissenkungsgrenze). Beispielsweise kosten von 100.000 bis 500.000 weitere tausend Anfragen 0,75 USD, von 500.000 bis 1 Million USD - 0,60 USD. Weitere Informationen finden Sie auf der Website. Batch-Geocoder ist leider nur auf einem kostenpflichtigen Konto verfügbar.

Tomtom


Die Plattform ermöglicht die Durchführung von 2500 Transaktionen pro Tag, ungefähr 75.000 pro Monat. Während des Testens und der Entwicklung sieht das Tageslimit im Vergleich zu Wettbewerbern nicht sehr attraktiv aus, aber die Zahlung für zusätzliche Transaktionen ist am flexibelsten. Es gibt 8 Zahlungsoptionen für zusätzliche tausend Anfragen und der Preis wird von 0,5 USD auf 0,42 USD reduziert.

Unter den Diensten gibt es einen Batch-Geocoder mit der Fähigkeit, bis zu 10.000 Adressen pro Anfrage zu verarbeiten (das Tageslimit muss jedoch berücksichtigt werden).

Yandex Technologies


Ein Modell mit einem täglichen Transaktionslimit für Yandex, aber loyaleren 25.000 Anfragen. Wenn Sie diese Zahl mit der Anzahl der Tage in einem Monat multiplizieren, erhalten Sie eine beeindruckende Zahl von 750.000. Die Website bietet Preise für weitere tausend Transaktionen in Rubel im Bereich von 120 Rubel. bis zu 11 Rubel

Packet Geocoder als Service wird nicht vorgestellt, daher schlägt eine Optimierung fehl.

ESRI


Ein sehr verlockender kostenloser Plan mit 1 Million Transaktionen pro Monat. Das Unternehmen belastet außerdem jedes Konto mit 50 Gutschriften (ungefähr 5 US-Dollar). Es ist erwähnenswert, dass dies der loyalste Plan für die Nutzung von Geolokalisierungsdiensten ist. Es gibt auch einen Batch-Geokodierungsdienst, den Sie jedoch nur verwenden können, wenn Sie über ein Unternehmenskonto auf der ArcGIS Online-Plattform verfügen.

Was soll man am Ende wählen?


Am einfachsten ist es, eine Auswahl zu treffen, indem Sie eine kleine Tabelle zusammenstellen:



Daher fiel meine Auswahl auf HIER, da dies die beste Option zur Lösung meines Problems ist. Natürlich habe ich keine vollständige Analyse durchgeführt. Idealerweise müssen Sie Ihren Datensatz durch alle Geocodierer laufen lassen, um die Qualität zu bewerten. Wenn Sie mehrere Millionen Adressen haben, sollten Sie über ein kostenpflichtiges Paket nachdenken und dann die Kosten für das Hinzufügen berücksichtigen. Transaktionen.

Der Artikel dient nicht dazu, Unternehmen zu vergleichen, sondern das Problem der Optimierung der Geokodierung eines großen Adressvolumens zu lösen. Ich habe nur meine Gedanken bei der Auswahl eines Dienstleisters gezeigt.

Python-Servicehandbuch


Zuerst müssen Sie im Portal ein Konto für Entwickler erstellen und im Projektabschnitt einen REST-API-SCHLÜSSEL generieren.

Jetzt können Sie mit der Plattform arbeiten. Ich werde nur einen Teil der Funktionalität beschreiben, die der HERE-Paket-Geocoder bietet: Laden von Daten, Statusprüfung, Speichern von Ergebnissen.

Beginnen wir also mit dem Importieren der erforderlichen Bibliotheken:

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

Wenn keine Fehler aufgetreten sind, erstellen Sie eine Klasse:

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

Das heißt, während der Initialisierung muss die Klasse ihren eigenen Schlüssel für die REST-API übergeben.
Die Variable SERVICE_URL ist die Basis-URL für die Arbeit mit dem Batch-Geokodierungsdienst.
Und in jobId wird die Kennung der aktuellen Arbeit des Geocoders gespeichert.

Ein wichtiger Punkt ist die korrekte Datenstruktur auf Anfrage. Die Datei muss zwei erforderliche Spalten enthalten: recId und searchText. Andernfalls gibt der Dienst eine Antwort mit Informationen zum Downloadfehler zurück.

Hier ist ein Beispieldatensatz:

   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
  

Funktion zum Hochladen einer Datei in die 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()


Alles ist ganz einfach, öffnen Sie eine Datei mit einer Liste von Adressen zum Lesen, bilden Sie ein Wörterbuch mit GET-Anforderungsparametern. Einige Parameter sind erklärenswert:

  • "Aktion": "Ausführen" - Start der Adressverarbeitung;
  • “politicalView”: “RUS” – . ( );
  • “gen”: 9 – ( );
  • “maxresults”: 1 – ;
  • “header”: true – ;
  • “indelim”: “;” – , ;
  • “outdelim”: “;” – ;
  • “outcols”: “” – , ;
  • “outcombined”: true – .

Senden Sie als Nächstes einfach eine Anfrage über die Anforderungsbibliothek und zeigen Sie Statistiken an. Natürlich müssen Sie die Datei am Ende der Funktion schließen. Die Funktion __stats analysiert die Antwort des Servers, die die ID der laufenden Arbeit enthält, und zeigt auch allgemeine Informationen zum Vorgang an.

Der nächste Schritt besteht darin, den Status der Arbeit zu überprüfen. Die Anforderung wird auf ähnliche Weise gebildet, nur ist es erforderlich, die Operations-ID zu übertragen. Der Aktionsparameter muss den Wert „Status“ enthalten. Die Funktion __stats zeigt der Konsole vollständige Statistiken an, um die Abschaltzeit des Geocoders abzuschätzen.

    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)

Eine der wichtigsten Funktionen ist das Speichern des Ergebnisses. Der Einfachheit halber ist es besser, die vom Server stammende Datei sofort zu entpacken. Die Anforderung zum Speichern der Datei ist identisch mit der Überprüfung des Status. Fügen Sie am Ende einfach / result hinzu.

    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)

Die letzte Funktion zum Parsen der Antwort des Dienstes. Ihre Aufgabe ist es auch, die Kennung der aktuellen Geokodierungsaufgabe zu speichern:

    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)

Führen Sie zum Testen einfach den Python-Interpreter im Skriptordner aus. Die Batch-Klasse befindet sich in der Datei 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


Großartige Arbeit hat begonnen. Überprüfen Sie den 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

Wir sehen, dass die Verarbeitung des Datensatzes abgeschlossen ist. In nur sieben Minuten konnten die 250.000 Adressen proklamiert werden (ohne Fehler - Fehleranzahl). Es bleibt, um die Ergebnisse zu speichern:

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

Beschreibung der vollständigen Chargenklasse


Ich denke, es tut nicht weh, das Skript vollständig hinzuzufügen:

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)

Ergebnisanalyse


Infolgedessen wechselte ich von langsam arbeitenden Online-Anwendungen zu Batch-Geokodierungsdiensten. Die Wahl eines Geodienstleisters hängt ganz von den Aufgaben ab, mit denen Sie konfrontiert sind. Ich erhalte regelmäßig Anfragen zur Verarbeitung einer großen Anzahl von Adressen, und der im Artikel beschriebene Ansatz hat dazu beigetragen, die Zeit erheblich zu verkürzen.

Ich hoffe, dass dieser Artikel nützlich sein wird und bin natürlich offen für Kommentare und Ergänzungen!

All Articles