sperea.es
Published on

Open Source IP-based localisation

Authors

IP Geolocation plays a pivotal role in a broad range of applications. From tracking user locations in a mobile app to bolstering online security, geolocation accuracy is essential.

In this article, I introduce you to GeoIP.Network, an open-source IP geolocation solution that can assist us in these tasks.

GeoIP.Network: Getting Started

One of the standout features of GeoIP.Network is its accessibility. You can utilize its API for free for up to 1,000 requests per day, which is more than enough for development purposes. Should you need to make more requests, you can obtain an API key by sponsoring the project.

To begin, you'll need some libraries. First, set up a virtual environment. If you use PyCharm, this process is streamlined, as creating a new project automatically sets up a virtual environment and lets you install libraries via its package manager. To create a new virtual environment, follow these steps:

Create a directory called "geoip_demo". Execute the following commands in your terminal:

mkdir geoip_demo
python3 -m venv ./geoip_demo
cd geoip_demo
source bin/activate

Now that you have a clean virtual environment, it's time to install the essential library: "requests". This library is vital for constructing API clients and is popular due to its ease of use. Run the following command:

pip install requests

Once "requests" is installed, you can start interacting with GeoIP.Network. Open a Python console and make the following requests:

import requests

public_ip = requests.get("https://ifconfig.co/ip").text.strip("\n")
geoip = requests.get(f"https://api.geoip.network/v1.0/cidr/{public_ip}").json()
print(geoip)

These requests will provide you with information about your public IP address and approximate location. The response includes details like the country, network name, ASN number, CIDR representation, and more.

Stochastic Algorithms: Beyond Geolocation

Stochastic algorithms are a fascinating subject in the computing world. Often associated with machine learning, they're loosely defined as "guess and check". Typically, they're paired with some form of incremental algorithm that influences the starting conditions of the next iteration by slightly varying the initial conditions of the prior iteration. This is done to approximate the desired goal.

A primary advantage of stochastic algorithms is that they offer quick approximations to results that are "good enough". However, this speed often comes at the expense of accuracy. Stochastic algorithms are frequently employed in machine learning and digital graphics representation, where finding a perfect answer is nearly impossible.

IP Geolocation: Beyond Individual Addresses

When working with IP geolocation, understanding the concept of CIDR (Classless Inter-Domain Routing) is crucial. GeoIP.Network often returns a CIDR representation rather than a specific IP address. This is because the exact IP address might not be pinpointed accurately, and GeoIP.Network relies on ISPs typically allocating IP address blocks to specific geographical areas.

Thus, if you see two IP addresses, like 198.51.100.215 and 198.51.100.114, when visiting a website, you can safely assume they are both in the same country and possibly even in the same city or town.

Creating Maps with GeoJSON and Folium

GeoJSON is a standardized format for representing geospatial data in JSON format. This makes transitioning between mapping libraries like geoplot or folium seamless without reformatting your data.

To begin working with Folium, first install it. Execute the following command in the terminal within your virtual environment:

pip install folium

Once you have Folium installed, you can begin crafting interactive maps with GeoIP.Network data. Open the Python console again and execute the following code:

import folium

folium_map = folium.Map(
    location=geoip["geo"]["geometry"]["coordinates"][::-1],
    tiles="OpenStreetMap",
    zoom_start=7
)

folium.GeoJson(
    geoip["geo"],
    name="geojson"
).add_to(folium_map)

folium.vector_layers.Circle(
    geoip["geo"]["geometry"]["coordinates"][::-1],
    geoip["geo"]["properties"]["radius"]
).add_to(folium_map)

folium_map.save("example.html")

This code constructs a map centered on the coordinates provided by GeoIP.Network. Note that we've reversed the coordinates' order since GeoJSON uses the longitude, latitude format, while Folium employs latitude, longitude. We then add a GeoJSON layer to the map and represent the location info as a circle with a confidence radius. Finally, we save the interactive map as an HTML file you can launch in your browser.

Building a Website with Python

Now that we've delved into how to use GeoIP.Network and Folium to craft interactive maps, it's time to integrate this functionality into a website. To do this, we'll employ Flask, a popular Python web development framework.

First, ensure you have Flask installed in your virtual environment. Execute the following command:

pip install flask

Once Flask is installed, create a file named "app.py" and insert the following code:

from flask import Flask, request
from functools import wraps

app = Flask(__name__)

# Decorador para rastrear direcciones IP
tracked_ips = set()
def tracking(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if request.headers.getlist("X-Forwarded-For"):
            ip = request.headers.getlist("X-Forwarded-For")[0]
        else:
            ip = request.remote_addr
        global tracked_ips
        tracked_ips.add(ip)
        return func(*args, **kwargs)
    return wrapper

@app.route('/', methods=['GET'])
@tracking
def index():
    return "¡Hola, mundo!"

@app.route('/visitors', methods=['GET'])
def visitors():
    return ", ".join(tracked_ips)

if __name__ == "__main__":
    app.run()

This code sets up a Flask app with two main routes: one for the home page that says "Hello, world!" and another for the visitors' page displaying the IP addresses of visitors who accessed the home page.

I've implemented a decorator called "tracking" that logs the IP addresses of visitors. Now, upon visiting the homepage and then the visitors' page, you'll see the logged IP addresses.

Integrating GeoIP.Network and Folium into the Website

To elevate the website further, we can incorporate the previously created interactive maps using Folium. First, we devise a function to fetch GeoIP.Network information based on the logged IP addresses.

Then, we use Folium to represent these locations on an interactive map. The following code showcases how to do this:

def lookup_geoip(public_ip):
    return requests.get(f"https://api.geoip.network/v1.0/cidr/{public_ip}").json().get("geo")

@app.route('/visitors_map', methods=['GET'])
def visitors_map():
    geoip_records = [lookup_geoip(public_ip) for public_ip in tracked_ips]
    folium_map = folium.Map(location=(0, 0), tiles="OpenStreetMap", zoom_start=1)

    for record in geoip_records:
        folium.GeoJson(record, name="geojson").add_to(folium_map)

    return folium_map._repr_html_()

The above code provides a route "visitors_map" that returns an interactive map with the geolocations of all tracked IP addresses. This allows you to visualize where your website visitors are coming from.

Optimization: Caching GeoIP.Network Responses

To prevent making repeated requests to the GeoIP.Network API every time the visitors' page is loaded, we can implement a caching system using Python's "@lru_cache" decorator.

This decorator caches the responses from the "lookup_geoip" function for the most commonly used IP addresses, enhancing efficiency. Below is how to apply the caching:

from functools import lru_cache

@lru_cache(maxsize=1000)
def lookup_geoip(public_ip):
    return requests.get(f"https://api.geoip.network/v1.0/cidr/{public_ip}").json().get("geo")

In this example, I've set up the cache to store responses from the last 1,000 IP addresses used. If you have more than 1,000 visitors, you might experience some cases where there are "cache misses" when loading the page. At that point, consider sponsoring GeoIP.Network and use the GeoIPNetwork library to circumvent the need for implementing login logic and token renewal.