GeoIP-Abfrage

Ermittelt die Ortsangabe zu einer IP-Adresse aus einer portablen, kompakten Datenbank. Verwendet Daten von MaxMind GeoIP oder GeoLite.

Um den ungefähren Standort eines Webseitenbesuchers zu ermitteln, ohne das Lokalisierungs-API des Browsers zu verwenden oder den Benutzer zu fragen, kann man auch dessen IP-Adresse verwenden. Die Firma MaxMind unterhält genau so eine Datenbank, die IP-Adressen auf Länder und Orte abbildet. Damit ist es je nach verwendeter Auflösung der Datenbank möglich, eine Lokalisierung mit recht hoher Wahrscheinlichkeit auf einige zig Kilometer durchzuführen. Unter dem Namen GeoLite bietet MaxMind diese Datenbank kostenlos an. Die kommerzielle Version heißt GeoIP und bietet in manchen Ländern mehr Adressdetails und höhere Genauigkeit.

Je nach Auflösung ist die Datenmenge recht groß. Die CSV-Rohdaten umfassen für die Land-Auflösung (country) derzeit knapp 10 MB, die Stadt-Auflösung (city) belegt schon 90 MB. MaxMind bietet die Datenbanken auch in einem eigenen optimierten Binärformat an, sie sind dann noch 1,2 MB und 18 MB groß. Das Dateiformat ist aber undurchsichtig und die bereitgestellte Zugriffs-Bibliothek für PHP unhandlich und ebenfalls undurchsichtig. Deshalb habe ich mich, wie manch anderer wohl auch, dazu entschieden, ein eigenes optimiertes Binärformat zu verwenden.

Die Daten in diesem Format werden in verschiedenen Tabellen in Datensätzen fester Länge sortiert abgelegt. Dadurch ist eine effiziente binäre Suche nach der gewünschten IP-Adresse möglich. Genauso wie es große Datenbankserver tun. Zusätzliche Textdaten wie der Name der Stadt oder des Lands werden direkt hintereinander abgelegt und jeder Adressbereichseintrag enthält einen direkten Zeiger auf die Zeichenkette. So ist in etwa die vereinfachte Beschreibung meines Dateiformats. Eine Abfrage dauert auf durchschnittlich langsamer Hardware 1-2 Millisekunden. IPv4- und IPv6-Adressen sowie alle verwendeten Bezeichnungen sind in einer Datei zusammengefasst, die dafür in mehrere Abschnitte aufgeteilt ist. Es gibt eine kleine Datei mit Länderdaten (800 kB) und eine große mit Stadtdaten (20 MB). Mein Format für Länderdaten ist also deutlich speicherplatzeffizienter als das von Max­Mind, das für Stadtdaten nur geringfügig größer.

Die Klasse zur Datenbankabfrage ist nur für PHP verfügbar. Portierungen in andere Programmiersprachen sollten aufgrund des übersichtlichen Codes aber leicht möglich sein. Während ich in einer früheren Version auch ein PHP-Skript zur Erstellung der Datenbanken verwendet habe, gibt es jetzt nur noch eine C#-Anwendung dafür. Die ist einfach deutlich schneller. Auf meinem PC dauert die Konvertierung aller Dateien ca. 20 Sekunden (statt 5 Minuten). Dieses Konvertierungsprogramm ist ebenfalls verfügbar, damit jeder selbst Updates der Datenbank erstellen kann.

Kompatibilität: PHP Ab Version 5.0

Beispiel

Der folgende Beispielcode zeigt, wie der Standort des Besuchers abgefragt werden kann:

require_once 'lib/geoip.class.php';

// Wenn die Datenbankdateien nicht im gleichen Verzeichnis wie die GeoIP-Klasse liegen,
// muss der Pfad dorthin angegeben werden. Die Dateien müssen geolitecountry.bin und
// geolitecity.bin heißen.
GeoIP::SetDBFileName('/path/to/database-files');

// IP-Adresse des Web-Besuchers verwenden
$ipAddress = $_SERVER['REMOTE_ADDR'];

// Der Abruf erfolgt mit einem einzigen statischen Funktionsaufruf. Es wird nur die
// gesuchte IP-Adresse übergeben. IPv4 und IPv6 werden gleichermaßen akzeptiert. Die
// Rückgabe ist ein Array, das die gefundenen Ortsangaben enthält. Die Verfügbarkeit
// der einzelnen Felder ist von der verwendeten Datenbankedition abhängig. Zuerst wird
// versucht, die city-Datenbank zu verwenden. Wenn die nicht existiert, wird die
// country-Datenbank verwendet.
// ----------------
// Hier ist der wesentliche Aufruf:
$data = GeoIP::LookupIP($ipAddress);
// ----------------

// Der Ländercode ist praktisch immer verfügbar, wenn die Adresse in der Datenbank
// erfasst ist. Beispiel: DE, GB, US, JP
$countryCode = $data['cc'];
// Der Name der Region, Provinz, des Bundeslands oder Bundesstaats, in Landessprache
// und lateinischer Schrift (7-Bit-ASCII). Beispiel: Bayern, Baden-Wurttemberg, Kent,
// Catalonia, Mississippi
$region = $data['region'];
// Der Name der Stadt oder des Orts. Das kann eine Weltstadt oder ein kleines
// Provinznest sein. Oft in Landessprache, in lateinischer Schrift.
$city = strlen($data['city']) ? utf8_encode($data['city']) : null;
// Breiten- und Längengrad des Orts. Bei Städten ist das recht genau, bei Regionen und
// Ländern irgendwo in der Mitte an halbwegs geraden Gradzahlen.
$lat = $data['lat'];
$lng = $data['lng'];

Download

Der erste Download enthält die Zugriffsklasse, die zum Auflösen von IP-Adressen benötigt wird:

geoip.class.php12,9 KiBQuelltext der GeoIP-Klasse für Ortsabfragen in PHP

Zur Verwendung der Klasse wird eine konvertierte Datenbank benötigt. Die hier bereitgestellten Dateien sind möglicherweise nicht immer aktuell. Es wird nur eine der beiden Versionen benötigt, die größere enthält die kleinere.

geolitecountry-201310.7z205 KiBGeoLite-Datenbank in Länder-Auflösung, Stand 2013-10

geolitecity-201310.7z8,2 MiBGeoLite-Datenbank in Stadt-Auflösung, Stand 2013-10

Die nachfolgenden Dateien sind für Entwickler an dieser Klasse bzw. zum Konvertieren neuerer Datenbanken gedacht:

geoip_test.7z1,0 KiBPHP-Testskripte zum Testen der Zugriffsklasse (Stand Ende 2012)

GeoIPConverter.7z22,2 KiBDatenbank-Konverter (C#-Projekt und kompilierte Anwendung)

TODO: Verwendung des C#-Konverters beschreiben

Lizenz und Nutzungsbedingungen

Diese Software wird unter den Bedingungen der GNU-GPL-Lizenz Version 3 veröffentlicht. Die genauen Lizenzbedingungen befinden sich im Download oder auf der GNU-Website.

Für die GeoLite-Datenbanken gelten gesonderte Lizenzbedingungen, die hier einsehbar sind. This product includes GeoLite data created by MaxMind, available from http://maxmind.com/.

Statistische Daten

  • Erstellt am 2012-11-16, aktualisiert am 2012-11-17.
  • Erstmals in tòmò web analytics verwendet.
  • Ca. 1 120 Codezeilen, geschätzte Ent­wick­lungs­kos­ten: 1 100 - 4 500 €