Reputation: 77
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
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
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
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
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
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