How to Make a Phone Number Tracker in Python

Learn how to build a phone number tracker in Python using phonenumbers, OpenCage and folium libraries.
  · 11 min read · Updated oct 2023 · Ethical Hacking · Application Programming Interfaces

Confused by complex code? Let our AI-powered Code Explainer demystify it for you. Try it out!

We are going to be performing one of those cool tricks you see in movies - How to track phone numbers. In this article, I am going to be showing you how to get the precise or approximate location of a person via their phone numbers using Python.

Before we go on, It's important to note that tracking someone's phone without their explicit consent is illegal and unethical. This explanation is provided for educational purposes only, and any use of phone tracking should always adhere to legal and ethical guidelines and respect an individual's privacy.

There is a phonenumbers library that provides modules for working with phone numbers. It is used for parsing, formatting, and validating phone numbers based on the rules and conventions of different countries and regions. The library also has a geocoder module that can provide information about the geographic location of a phone number based on its country. Let’s get into it!

Before getting started, let's install the required libraries:

$ pip install phonenumbers folium colorama opencage

First off, we import the necessary libraries:

import phonenumbers, folium, sys, argparse, os
from phonenumbers import geocoder, timezone, carrier from colorama import init, Fore

init()

We already talked about the phonenumbers library. The folium library is what we are going to use to generate a map after getting the person’s location. Somewhat an Aerial view of the person’s location.

The sys module in Python is used for interacting with the Python interpreter and the system environment.

The argparse module is used to accept input from users when running scripts from the terminal.

The os module is used to interact with the operating system, obviously :)

From phonenumbers, we imported:

  • geocoder - This module is used for geocoding phone numbers, providing information about the geographic location of the said number.
  • timezone - This module is used for determining the time zone associated with a phone number, helping you understand the time zone in which a phone number is located.
  • carrier - This module is used for identifying the carrier (mobile network provider) associated with a phone number, helping you determine the mobile service provider of a given number.
  • Feel free to check out the documentation to see how other modules work.
  • colorama is basically used to add colors to our output.

From colorama we imported:

  • init - This function initializes the Colorama library.
  • Fore - This module is basically used to specify our desired colors.
  • init() - Initializes colorama like we just said.

Now that we’ve imported the necessary libraries and modules, the party can begin. We’ll kick the party off by creating a function that processes the phone number passed as input. i.e. the phone number to track:

def process_number(number):
    try:
        global location
        # Parse the phone number. See this as extracting relevant information from the Phone number.
        parsed_number = phonenumbers.parse(number)
        '''Display a message indicating the tracking attempt. We'll also format the parsed number to the 
        international format.'''
        print(f"{Fore.GREEN}[+] Attempting to track location of "
              f"{phonenumbers.format_number(parsed_number, phonenumbers.PhoneNumberFormat.INTERNATIONAL)}..")
        # Get and display the time zone ID
        print(f"{Fore.GREEN}[+] Time Zone ID: {timezone.time_zones_for_number(parsed_number)}")
        # Get the geographic location of the Phone number and display it.
        location = geocoder.description_for_number(parsed_number, "en")
        if location:
            print(f"{Fore.GREEN}[+] Region: {location}")
        else:
            print(f"{Fore.RED}[-] Region: Unknown")
        '''Get the service provider (carrier) and display it if available. Some businesses and 
        organizations do not use public service providers. So you may not see the carrier in that case.'''
        if carrier.name_for_number(parsed_number, 'en'):
            print(f"{Fore.GREEN}[+] Service Provider:  {carrier.name_for_number(parsed_number, 'en')}")
        else:
            pass
    # Handle exceptions, such as invalid phone numbers or connectivity issues.
    except Exception:
        print(f"{Fore.RED}[-] Please specify a valid phone number (with country code)"
              " or check your internet connection.")
        sys.exit()

We basically processed the user’s phone number. We started off by parsing the number. Just see parsing as extracting useful information such as the country code. We also formatted the number to the internationally accepted standard. Then we got useful information such as the carrier (service provider), timezone, and location of the number. We still need to do a bit of processing on the location. We’ll see that next.

We have been able to get a bit of information by processing the user’s number. Next, let’s try to get the coordinates of the user’s location. But before we do that, I must tell you that for the geolocation, we’re making use of an API. We’re using OpenCage’s API for this. OpenCage is a geocoding service that provides an API for converting between geographic coordinates (latitude and longitude) and human-readable addresses. Geocoding is the process of translating addresses into geographic coordinates, and reverse geocoding is the process of translating coordinates into addresses.

So go to OpenCage, create an account (for free), go to your Dashboard > Geocoding API, and have your API key ready!

Moving on:

def get_approx_coordinates():
    # Import the OpenCageGeocode class from the opencage.geocoder module
    from opencage.geocoder import OpenCageGeocode
    global coder, latitude, longitude
    # Try to execute the following block, and handle exceptions if they occur.
    try:
        # Create an instance of the OpenCageGeocode class with your API key.
        coder = OpenCageGeocode("42c84373c47e490ba410d4132ae64fc4")
        query = location
        # Perform a geocoding query to obtain results.
        results = coder.geocode(query)
        # Extract latitude and longitude from the geocoding results. These are the coordinates of the number's location.
        latitude = results[0]['geometry']['lat']
        longitude = results[0]['geometry']['lng']
        # Print the obtained latitude and longitude.
        print(f"[+] Latitude: {latitude}, Longitude: {longitude}")
        # Perform a reverse geocoding query to obtain an address based on coordinates.
        address = coder.reverse_geocode(latitude, longitude)
        # Check if an address was found.
        if address:
            address = address[0]['formatted']
            print(f"{Fore.LIGHTRED_EX}[+] Approximate Location is {address}")
        else:
            # If no address was found, print an error message.
            print(f"{Fore.RED}[-] No address found for the given coordinates.")
    except Exception:
        '''Handle exceptions by printing an error message and exiting the script. This would prevent the program from 
        crashing'''
        print(f"{Fore.RED}[-] Could not get the location of this number. Please specify a valid phone number or "
              "check your internet connection.")
        sys.exit()

Basically, in this function, we’re trying to geocode our location parameter (From the process_number() function). Afterward, we extract the longitude and latitude coordinates. Then we reverse geocoding the coordinates to get a physical address (Human-readable address).

The next function we are going to see is one that removes unwanted characters from the user’s phone number. This is so that when we enter a number with spaces in between, we can remove those spaces and the number would still be valid. The primary aim of doing this is because as you’ll see later on, we are saving the generated maps of the user's location with their phone numbers. And doing that, we don’t want any spaces in the file name:

# This function basically removes unwanted characters from the Phone number such as white spaces.
def clean_phone_number(phone_number):
    cleaned = ''.join(char for part in phone_number for char in part if char.isdigit() or char == '+')
    return cleaned or "unknown"

Perfect. Now, we can create a function to generate or draw a map for us:

# Function to see Aerial view of the person's location.
def draw_map():
    try:
        # Create a Folium map centered around the latitude and longitude of the number's coordinates.
        my_map = folium.Map(location=[latitude, longitude], zoom_start=9)
        # Add a marker to the map at the specified latitude and longitude with a popup displaying the 'location' variable.
        folium.Marker([latitude, longitude], popup=location).add_to(my_map)
        ''' Clean the phone number and use it to generate a file name with an '.html' extension
        we'll basically save each map with the number of the owner for easy identification.'''
        cleaned_phone_number = clean_phone_number(args.phone_number) # We'll see 'args' soon.
        file_name = f"{cleaned_phone_number}.html"
        # Save the map as an HTML file with the generated file name.
        my_map.save(file_name)
        # Print a message indicating where the saved HTML file can be found.
        print(f"[+] See Aerial Coverage at: {os.path.abspath(file_name)}")
    # Handle the 'NameError' exception, which can occur if the 'latitude' or 'longitude' variables are not defined.
    except NameError:
        print(f"{Fore.RED}[-] Could not get Aerial coverage for this number. Please check the number again.")

In this function, we are trying to generate a map based on our obtained coordinates (Longitude and Latitude). We are passing our Longitude and Latitude variables to folium to be able to locate and mark the location, if found. Afterward, we are cleaning the phone number in preparation for saving the map. Finally, we save the map (with the number of the user) and tell the operator where the file is saved.

Next up, we’ll create a function to collect arguments from the command line. In our code, we are accepting the target phone number either as -p or --phone.

# Function to handle command-line arguments.
def cli_argument():
    # Create an ArgumentParser object and specify a description.
    parser = argparse.ArgumentParser(description="Get approximate location of a Phone number.")
    # Define a command-line argument: -p or --phone. This is to receive the user's number from terminal.
    parser.add_argument("-p", "--phone", dest="phone_number", type=str,
                        help="Phone number to track. Please include the country code when specifying the number.",
                        required=True, nargs="+")
    # Parse the command-line arguments.
    argument = parser.parse_args()
    # Check if the 'phone_number' argument is not provided.
    if not argument.phone_number:
        # Print an error message indicating that the phone number is required.
        print(f"{Fore.RED}[-] Please specify the phone number to track (including country code)."
              " Use --help to see usage.")
        # Exit the script.
        sys.exit()
    # Return the parsed command-line arguments.
    return argument

So in this function, we’re saying hey, allow the user to enter a phone number if they specify the -p or --phone argument. And if the operator does not specify a phone number we throw them an error. Also, please bear in mind that when entering the phone number, you need to do so with the correct code.

And finally, we call all the necessary functions:

# Parse command-line arguments using the 'cli_argument' function.
args = cli_argument()
# Call the process_number function and pass the phone number as a single string.
process_number("".join(args.phone_number))
get_approx_coordinates()
draw_map()

To run the command, you can open up your terminal, navigate to the directory where your code is saved, and type:

python [file_name.py] -p phone_number

Also, if Python is not added to the PATH of your PC, you may need to add Python’s full path. It is very unlikely that Linux users face this problem.

Your output should be similar to:

Copy the path the Aerial coverage is saved in and paste it into your browser.

You can zoom in to see the Location (in yours).

Now, I’m obviously not showing the number and address due to privacy reasons but that is the actual address of that number. Also, throughout this article, I made use of the word approximate. This is because the accuracy of the program depends on the quality of the geodata of the user’s location. Some areas will have more quality geodata than others. It’s just the way it is.

As programmers, there’s not much we can do about that. Even security agencies that track the phone numbers of individuals often have to get in contact with the service providers and perform triangulation on the number to get the location. Service providers have access to real-time data about the location of their subscribers based on the cell towers or base stations they connect to. Triangulation is a technique that involves calculating the location of a mobile device by measuring its signal strength from multiple cell towers. When a phone connects to a cell tower, it establishes a connection, and the network can determine the phone's approximate location based on the time it takes for signals to travel between the phone and the towers. So if you know a service provider that has an API for this purpose, feel free to try it out (If it’s legal).

Learn also: How to Get Geolocation in Python.

Happy coding ♥

Why juggle between languages when you can convert? Check out our Code Converter. Try it out today!

View Full Code Transform My Code
Sharing is caring!



Read Also



Comment panel

    Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!