Jesper Petersen
Jesper Petersen

Reputation: 77

Returning the country code of an IP address

I have a CSV file, where IP ranges are stored along with their landcode:

"1.0.0.0","1.0.0.255","16777216","16777471","AU","Australia"
"1.0.1.0","1.0.3.255","16777472","16778239","CN","China"
"1.0.4.0","1.0.7.255","16778240","16779263","AU","Australia"
"1.0.8.0","1.0.15.255","16779264","16781311","CN","China"

This can be read like this:

range_start, range_stop, ignored, ignored, country_code, country_name

When the user asks for a particular IP address, I want to return him the country code corresponding to this IP. Here for example, 1.0.9.10 would return CN for China, because it is between 1.0.8.0 and 1.0.15.255.

I don't know how to deal with that. Here is what I've done so far, but I doubt I'm in the right direction:

import csv

with open("GeoIPCountryWhois.csv") as csvfile:

readCSV = csv.reader(csvfile, delimiter = ",")

IP_LOWs = []
IP_HIGHs = []
Land_CODEs = []
Lands = []

for row in readCSV:
    IP_LOW = row[0]
    IP_HIGH = row[1]
    Land_CODE = row[4]
    Land = row[5]

    IP_LOWs.append(IP_LOW)
    IP_HIGHs.append(IP_HIGH)
    Land_CODEs.append(Land_CODE)
    Lands.append(Land)

whatIP = input("What IP should be looked up? ")

codedex = IP_LOWs.index(whatIP)
landdex = Lands.index(whatIP)
IP_to_Code = Land_CODE[codedex]
IP_to_land = Land[landdex]

print(IP_to_Code)
print(IP_to_land)

Upvotes: 0

Views: 3086

Answers (5)

JordanC
JordanC

Reputation: 4369

You can use IP Find for this.

import urllib, json
url = 'https://ipfind.co/?auth=ip=' + ip_address;
response = urllib.urlopen(url)
data = json.loads(response.read())

This will return a JSON object with ISO country code as a property.

Upvotes: 0

Ujjwal
Ujjwal

Reputation: 3158

You can use the library netaddr while dealing with IP Addresses.

from netaddr import *
IPAddress("1.0.3.255")>IPAddress("1.1.2.1")
#False

Then you can use something like:

Note: The below code is untested, since I don't have your data. You might have to make minor changes to actually run the code. If you get any error while running this, post it here, I will try to rectify.

import csv
from netaddr import *
with open("GeoIPCountryWhois.csv") as csvfile:

    readCSV = csv.reader(csvfile, delimiter = ",")

    whatIP = input("What IP should be looked up? ")

    for row in readCSV:
        IP_LOW = IPAddress(row[0])    
        IP_HIGH = IPAddress(row[1])
        Land = row[4]

        if IPAddress(whatIP)>=IP_LOW and IPAddress(whatIP)<=IP_HIGH:
            print Land

Upvotes: 0

mhawke
mhawke

Reputation: 87074

Assuming that you are working only with IPV4 addresses, convert from dotted-decimal notation into its 32-bit equivalent. Now it is a matter of looking up the location using the 32-bit IP addresses provided in the source file in columns 3 and 4.

If you will be performing multiple lookups, a good data structure to use is a sorted list. Lookup can be performed using the bisection algorithm provided in the bisect module.

Note that there are gaps in the source data GeoIPCountryWhois.csv for unallocated IP addresses, or those IPs in a private address range, so a little upfront data massaging is required. Here is a class that loads the data from the file, fills in any gaps, and uses bisect_left() to perform lookups:

import csv
import socket
import struct

from bisect import bisect_left

def ip2long(ip):
    """
    Convert an IP string to long (see http://stackoverflow.com/a/9591005/21945)
    """
    packedIP = socket.inet_aton(ip)
    return struct.unpack("!L", packedIP)[0]


class GeoIPCountry(object):
    def __init__(self, geoips_file):
        """
        Load IP range location map from CSV file filling in any empty ranges as
        we go. Assumes that the data in geoips_file is sorted by IP address.
        """
        r = csv.reader(geoips_file)
        self.geoips = []
        last_hi = 0
        for row in r:
            if int(row[2]) != last_hi+1:
                self.geoips.append((last_hi+1, int(row[2])-1, None, None))
            self.geoips.append((int(row[2]), int(row[3]), row[4], row[5]))
            last_hi = int(row[3])
        if last_hi < ip2long('255.255.255.255'):
            self.geoips.append((last_hi+1, ip2long('255.255.255.255'), None, None))
        self.keys = [geoip[1] for geoip in self.geoips]
#        assert sorted(self.keys) == self.keys

    def lookup_country(self, ip_address):
        """
        Return tuple of country code and country name for an IP address.
        """
        return self.geoips[bisect_left(self.keys, ip2long(ip_address))][-2:]


if __name__ == '__main__':
    with open('GeoIPCountryWhois.csv') as geoips_file:
        geoip = GeoIPCountry(geoips_file)

    for ip_address in ('0.1.2.3', '1.2.3.4', '192.168.1.1', '203.123.4.23',
                       '123.132.123.123', '223.255.255.255', '255.255.255.255'):
        country = geoip.lookup_country(ip_address)
        if country[0] is not None:
            print "{:<15} -> {} ({})".format(ip_address, country[1], country[0])
        else:
            print "{:<15} -> unknown".format(ip_address)

Output

0.1.2.3         -> unknown
1.2.3.4         -> United States (US)
192.168.1.1     -> unknown
203.123.4.23    -> Singapore (SG)
123.132.123.123 -> China (CN)
223.255.255.255 -> Australia (AU)
255.255.255.255 -> unknown

Upvotes: 0

StefanNch
StefanNch

Reputation: 2609

A bit unrelated to the question, but reinventing the wheel made me throw my two cents. If it's not homework or a learning project, you can use GeoIP + free MaxMind database (or the paid version). The code is stable, tested and ready to use in production.

Example:

#
from pygeoip import GeoIP, MEMORY_CACHE
#
try:
    import win_inet_pton#patches socket library for Windows use
except ImportError:
    pass
import socket

###############################################################################

def is_valid_ipv4(ip_str):
    """
    Check the validity of an IPv4 address
    """
    try:
        socket.inet_pton(socket.AF_INET, ip_str)#@UndefinedVariable
    except AttributeError:
        try:
            socket.inet_aton(ip_str)
        except socket.error:
            return False
        return ip_str.count('.') == 3
    except socket.error:
        return False
    return True

def is_valid_ipv6(ip_str):
    """
    Check the validity of an IPv6 address
    """
    try:
        socket.inet_pton(socket.AF_INET6, ip_str)#@UndefinedVariable
    except socket.error:
        return False
    return True

###############################################################################

def get_country_from_ip(ip):

    geo_record = None

    if is_valid_ipv4(ip):
        geo_country_ipv4_db = GeoIP('[path/to/dat/file]GeoLiteCity.dat', MEMORY_CACHE)
        geo_record = geo_country_ipv4_db.record_by_addr(ip)

    if is_valid_ipv6(ip):
        geo_country_ipv6_db = GeoIP('[path/to/dat/file]GeoLiteCityv6.dat', MEMORY_CACHE)
        geo_record = geo_country_ipv6_db.record_by_addr(ip)

    if geo_record:
        return geo_record.get('country_code', '').lower()

    return None

###############################################################################

print get_country_from_ip('1.0.9.10')

Requirements:

Related:

Regexp to check if an IP is valid

Install the required extensions, replace the "[path/to/dat/file]" and you're done.

Upvotes: 0

Barmar
Barmar

Reputation: 780974

Using the code from Convert an IP string to a number and vice versa, you can convert the input IP to a number. Then you can compare it to the numeric version of the start/end IP, which are in columns 3 and 4 of the CSV.

import csv

with open("GeoIPCountryWhois.csv") as csvfile:

    readCSV = csv.reader(csvfile, delimiter = ",")

    GeoIPs = []

    for row in readCSV:
        GeoIPs.append({ low: row[2], high: row[3], land_code: row[4], land: row[5] })

    whatIP = input("What IP should be looked up? ")
    whatIP_long = ip2long(whatIP)        
    found_range = next((GeoIP for GeoIP in GeoIPs if GeoIP.low <= whatIP_long <= GeoIP.high), None)
    if found_range:
        IP_to_Code = found_range.land_code
        IP_to_land = found_range.land

    print(IP_to_Code)
    print(IP_to_land)

Upvotes: 1

Related Questions