Skip to content

Instantly share code, notes, and snippets.

@willb
Created July 18, 2009 04:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save willb/149413 to your computer and use it in GitHub Desktop.
Save willb/149413 to your computer and use it in GitHub Desktop.
This little script uses the geobytes.com API to find the probable geographic location for a particular IP address
#!/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