This little script uses the geobytes.com API to find the probable geographic location for a particular IP address
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# geo-ip.py uses the geobytes.com API to find the probable | |
# geographic location for a particular IP address | |
# requires python 2.6 or json library; note that the free- | |
# of-charge geobytes API only allows 20 requests per hour. | |
# original author: Will Benton, 2009 | |
# this is public domain, there is no warranty, share and enjoy | |
import urllib2 | |
import optparse | |
import sys | |
unsafe = False | |
nojson = True | |
try: | |
from json import loads as jsonparse | |
nojson = False | |
except ImportError: | |
try: | |
from simplejson import loads as jsonparse | |
nojson = False | |
except ImportError: | |
def jsonparse(js): | |
if unsafe: | |
return eval(js) | |
raise RuntimeError("Neither the json or simplejson module is available.\nUse the --unsafe option to use python eval() as an ad-hoc json decoder") | |
def geolookup(ip, format): | |
geo_url = "http://www.geobytes.com/IpLocator.htm?GetLocation&template=json.txt&IpAddress=%s" % ip | |
json_stream = urllib2.urlopen(geo_url) | |
json_dict = jsonparse(json_stream.read().replace("\r\n",""))['geobytes'] | |
if json_dict['ipaddress'] != ip: | |
raise RuntimeError("GeoBytes error: Results don't match request -- got results for %s" % json_dict['ipaddress']) | |
return format % json_dict | |
def formathelp(opt, ostr, v, p, *a, **kwa): | |
print """Format strings may include variables of the form %(varname)c, | |
where c is a conversion type (a la python or printf) and | |
varname is one of the following: | |
certainty city cityid code country countryid | |
fips internet ipaddress iso2 iso3 ison latitude | |
locationcode longitude region regionid timezone | |
As an example, --format="%%(latitude)f, %%(longitude)f" is equivalent to --icbm""" | |
sys.exit(1) | |
def process_args(argv=None): | |
global unsafe | |
if argv is None: | |
argv = sys.argv[1:] | |
parser = optparse.OptionParser(usage="""usage: %prog [options] IP [...] | |
Options are mutually exclusive; only the last one specified will take effect. | |
For help, use --help. | |
If no format is specified, print latitude and longitude. | |
If no IP addresses are specified, read IP addresses, one per line, from standard input.""") | |
basic_group = optparse.OptionGroup(parser, "Basic output options") | |
custom_group = optparse.OptionGroup(parser, "Custom output options") | |
parser.set_defaults(format="""%(latitude)f, %(longitude)f""", unsafe=False) | |
parser.add_option('--help-format', help='describe valid format strings', action='callback', callback=formathelp) | |
if nojson: | |
parser.add_option('--unsafe', help='use eval() as an ad-hoc json decoder (use at your own risk)', action='store_true', dest='unsafe') | |
basic_group.add_option('--certainty', help='print geobytes.com\'s certainty about the location', action='store_const', const='%(certainty)d', dest='format') | |
basic_group.add_option('--city', help='print the city name', action='store_const', const='%(city)s', dest='format') | |
basic_group.add_option('--country', help='print the country', action='store_const', const='%(country)s', dest='format') | |
basic_group.add_option('--icbm', help='print the latitude and longitude; equivalent to --format="%(latitude)f, %(longitude)f"', action='store_const', const="%(latitude), %(longitude)", dest='format') | |
basic_group.add_option('--internet', help='print the internet country code', action='store_const', const='%(internet)s', dest='format') | |
basic_group.add_option('--iso2', help='print the 2-letter ISO country code', action='store_const', const='%(iso2)s', dest='format') | |
basic_group.add_option('--iso3', help='print the 3-letter ISO country code', action='store_const', const='%(iso3)s', dest='format') | |
basic_group.add_option('--latitude', help='print the latitude', action='store_const', const='%(latitude)f', dest='format') | |
basic_group.add_option('--longitude', help='print the longitude', action='store_const', const='%(longitude)f', dest='format') | |
basic_group.add_option('--region', help='print the region', action='store_const', const='%(region)s', dest='format') | |
basic_group.add_option('--timezone', help='print the timezone', action='store_const', const='%(timezone)d', dest='format') | |
basic_group.add_option('--verbose-city', help='print the city name, region name, and country name', action='store_const', const='%(city)s, %(region)s, %(country)s', dest='format') | |
custom_group.add_option('--format', help='print output formatted as FORMAT; see --help-format', metavar="FORMAT", action='store', dest='format') | |
parser.add_option_group(basic_group) | |
parser.add_option_group(custom_group) | |
(options, args) = parser.parse_args() | |
unsafe = options.unsafe | |
return (options, args) | |
def main(args=None): | |
options, ips = process_args(args) | |
if len(ips) == 0: | |
ips = sys.stdin.readlines() | |
for ip in ips: | |
print ip | |
try: | |
print geolookup(ip, options.format) | |
except Exception, err: | |
print "Failed to look up %s: %s" % (ip, err) | |
sys.exit(1) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment