Build a Network Health Checker
Ping multiple hosts and parse real network data using Python.
Introduction
30 Second Summary
Ever wondered whether your internet connection is actually working, or just slow? Network engineers check this by "pinging" servers and measuring how quickly they respond.
In this project, you will build a command-line tool with Python that pings multiple websites, measures their response times, and displays a clean health report in your terminal.
What You'll Build
A Python script that runs ICMP ping commands against a list of hosts and parses the results into a formatted status report showing availability, latency, and packet loss.
By the end of this project, you'll have:
- A working Python script that pings multiple hosts and reports their network status.
- Parsed real network data using regular expressions and string manipulation.
- A reusable network health checker tool that runs from the command line.
- Secret Mission: Add continuous monitoring mode that checks hosts every 30 seconds and logs results to a CSV file.
Are there any prerequisites?
None. This project includes full setup instructions for Python on Windows. You do not need any prior programming experience to follow along.
Before We Start
Your network health checker will ping real servers and report whether they're reachable. Before you can build it, you need Python installed on your computer and a project file ready to go.
In this step, you'll install Python, verify it works in your terminal, and create the project folder where your script will live.
In this step, get ready to:
- Install Python on your computer.
- Verify Python works in your terminal.
- Create your project folder and script file.
Open your terminal and check for Python
Python is the programming language you'll use to automate network checks. Let's see if it's already installed on your computer.
- Press the Windows key to open your search bar.
- Type PowerShell and press Enter to open it.
What is PowerShell?
PowerShell is a terminal app built into Windows. It lets you run commands to interact with your computer without clicking through menus.
- Check if Python is already installed by running this command:
python --version
Look at the output and pick the tab below that matches what you see.
✔️ I see version 3.13 or higher
Python is ready to go. Skip ahead to the next substep below.
ⓧ I see an older version
You have Python installed, but this project needs version 3.13 or higher. Let's install a newer version.
- Open your browser and go to python.org/downloads.
- Click the large yellow Download Python button at the top of the page.
- Run the downloaded installer file.
- Check the box that says Add python.exe to PATH at the bottom of the installer window.
- Click Install Now and wait for the installation to finish.
- Close your PowerShell window completely.
- Reopen PowerShell using the same steps as before (press the Windows key, type PowerShell, press Enter).
- Run python --version again to confirm you now see 3.13 or higher.
Why close and reopen PowerShell?
Your terminal loads its PATH settings when it first opens. Installing Python updates the PATH, but your current terminal session still has the old settings. Reopening it picks up the new Python path.
ⓧ Command not found
Python isn't installed yet. Let's install it now.
- Open your browser and go to python.org/downloads.
- Click the large yellow Download Python button at the top of the page.
- Run the downloaded installer file.
- Check the box that says Add python.exe to PATH at the bottom of the installer window. This is critical for running Python from your terminal.
- Click Install Now and wait for the installation to finish.
- Close your PowerShell window completely.
- Reopen PowerShell using the same steps as before (press the Windows key, type PowerShell, press Enter).
- Run python --version again to confirm you now see a version number like 3.13 or higher.
Still seeing an error after installing?
Make sure you checked Add python.exe to PATH during installation. If you missed it, run the installer again, select Modify, and ensure the PATH option is checked.
Also confirm you fully closed and reopened PowerShell after installing.
Help me fix my Python installation on Windows.
Create your project folder and file
Now you'll create a dedicated folder on your Desktop to hold your health checker script. Keeping project files in one place makes them easy to find.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir network-health-checker
cd network-health-checker
- Create an empty Python file for your health checker script by running:
New-Item health_checker.py -ItemType File
- Confirm the file exists by running:
ls
You should see health_checker.py listed in the output.
Don't see health_checker.py?
Make sure you ran cd network-health-checker before creating the file. If you're in the wrong folder, the file will end up somewhere else.
Try running pwd to see your current location. It should end with network-health-checker.
Help me find where my file was created in PowerShell.
Your environment is ready. Next up, you'll write your first function to ping a server and capture its response.
Ping a Single Host
Your Python environment is ready and your project file is waiting. Now it's time to make your script do something useful: reach out across the network and check if a host is responding.
Python's subprocess module lets you run system commands (like ping) directly from a script and capture their output. You'll use this to ping a host and grab the raw response text.
In this step, get ready to:
- Write a function that pings a host using Python's subprocess module.
- Print raw ping output to understand its format.
- Run the script against google.com to confirm it works.
Import subprocess and create the ping function
The Windows ping command sends ICMP packets to a host and reports whether it responded. Your function will run this command behind the scenes and capture whatever text it prints.
- Open health_checker.py in your text editor and add the following code:
import subprocess
import re
import time
def ping_host(host, count=4):
"""Ping a host and return the raw command output."""
try:
result = subprocess.run(
["ping", "-n", str(count), host],
capture_output=True,
text=True,
timeout=30
)
return result.stdout
except subprocess.TimeoutExpired:
return ""
What does this code do?
The three imports bring in modules you'll use throughout this project. subprocess runs system commands. re handles regular expressions (used in the next step). time provides timestamps (used later for the report).
The ping_host() function runs the Windows ping -n 4 command against whatever host you pass in. capture_output=True grabs the text that ping would normally print to the terminal. text=True returns it as a readable string instead of raw bytes.
If the command takes longer than 30 seconds, the except subprocess.TimeoutExpired block catches the timeout and returns an empty string so your script doesn't crash.
Why subprocess instead of a ping library?
Python has third-party ping libraries like icmplib and pythonping, but they require administrator privileges on Windows to create raw sockets. The built-in ping command works without admin access.
Using subprocess also teaches a transferable skill. Many automation scripts wrap system commands this way to capture and process their output.
- Save health_checker.py.
- You won't see any output yet. The next chunk adds the code that actually calls this function and prints the result.
Add a main block to capture and print output
Your function exists, but nothing calls it yet. You need an entry point that runs the function and prints whatever it returns.
- At the bottom of health_checker.py, add the following code below the ping_host() function:
if __name__ == "__main__":
output = ping_host("google.com")
print(output)
What does this code do?
The if __name__ == "__main__": guard ensures this code only runs when you execute the file directly. It won't run if you import the file as a module later.
Inside it, ping_host("google.com") calls your function with google.com as the target. The raw output gets stored in output and printed to the terminal so you can see exactly what Windows ping returns.
- Save health_checker.py.
- You won't see output until you run the script in the next substep.
Run the script and observe the output
Time to see your function in action. Running the script sends four ping packets to google.com and prints the raw response.
- Run your script by entering this command in your terminal:
python health_checker.py
The command will take a few seconds to complete as it waits for four ping responses.
What should I see?
You should see output similar to this:
Pinging google.com [142.250.69.110] with 32 bytes of data:
Reply from 142.250.69.110: bytes=32 time=15ms TTL=117
Reply from 142.250.69.110: bytes=32 time=16ms TTL=117
Reply from 142.250.69.110: bytes=32 time=15ms TTL=117
Reply from 142.250.69.110: bytes=32 time=15ms TTL=117
Ping statistics for 142.250.69.110:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 15ms, Maximum = 16ms, Average = 15ms
Your IP address and latency numbers will differ. The key parts to notice are the (0% loss) pattern and the Average = 15ms line. You'll write code to extract these values in the next step.
Seeing an error or no output?
- If you see 'python' is not recognized, Python isn't on your PATH. Reinstall Python and check the "Add python.exe to PATH" box.
- If you see SyntaxError, double-check that your code matches exactly. A missing comma or quotation mark inside subprocess.run() is the most common cause.
- If the command hangs for more than 30 seconds, the timeout will kick in and return empty output. Check your internet connection.
Help me debug my ping script
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
Here's what your complete health_checker.py should look like at this point:
import subprocess
import re
import time
def ping_host(host, count=4):
"""Ping a host and return the raw command output."""
try:
result = subprocess.run(
["ping", "-n", str(count), host],
capture_output=True,
text=True,
timeout=30
)
return result.stdout
except subprocess.TimeoutExpired:
return ""
if __name__ == "__main__":
output = ping_host("google.com")
print(output)
You've got a working function that pings any host and captures the response. Next up, you'll write code to extract the important numbers from that raw text and turn them into structured data.
Parse Ping Results
In the previous step, you pinged google.com and saw the raw output from the Windows ping command. That output contains useful data, but it is buried in unformatted text that a human has to read line by line.
In this step, you will write a function that uses regular expressions to pull out two key metrics from that text: average latency and packet loss percentage. The result is a clean Python dictionary you can use anywhere in your code.
In this step, get ready to:
- Write regex patterns that match the latency and loss values in Windows ping output.
- Build a parse_ping_output() function that returns structured data as a dictionary.
- Test the parser against both a reachable and an unreachable host.
Write the parse_ping_output() function
The re module is already imported at the top of your file. You will use re.search() to find two specific text patterns inside the ping output string.
- In health_checker.py, find the end of the ping_host() function (the line return "" inside the except block).
- Add the following function below ping_host(), leaving one blank line between the two functions:
def parse_ping_output(host, output):
"""Parse Windows ping output into a structured dictionary."""
result = {
"host": host,
"is_alive": False,
"avg_latency_ms": None,
"packet_loss_pct": 100.0
}
# If there's no output, the host is unreachable
if not output:
return result
# Extract packet loss percentage from the stats line
loss_match = re.search(r"\((\d+)% loss\)", output)
if loss_match:
result["packet_loss_pct"] = float(loss_match.group(1))
# Extract average latency from the round-trip summary
avg_match = re.search(r"Average = (\d+)ms", output)
if avg_match:
result["avg_latency_ms"] = int(avg_match.group(1))
result["is_alive"] = True
return result
What does this code do?
The function starts by assuming the worst case: the host is down, latency is unknown, and packet loss is 100%. It then tries to find evidence that the host is actually alive.
- r"\((\d+)% loss\)" matches the text (0% loss) or (100% loss) in Windows ping output. The parentheses around \d+ capture just the number.
- r"Average = (\d+)ms" matches the average round-trip time line. If this line exists, the host responded to at least one ping, so is_alive is set to True.
- If the output string is empty (meaning ping_host() hit a timeout), the function returns the default "dead" dictionary immediately.
You will not see a visible change yet because this function is not called in your __main__ block. The next substep connects it.
Test the parser against reachable and unreachable hosts
To confirm the parser handles both success and failure, you will test it against google.com (a host that replies) and 192.0.2.1 (an address reserved for testing that never replies).
Why 192.0.2.1?
The address 192.0.2.1 belongs to a range (TEST-NET-1) that is officially reserved and guaranteed to be unreachable. It is a safe way to simulate a failed ping without accidentally contacting a real server.
- In health_checker.py, scroll to the bottom of the file and find the existing if __name__ == "__main__": block.
- Find this code:
if __name__ == "__main__":
output = ping_host("google.com")
print(output)
- Replace the entire if __name__ block with this updated version that loops through both hosts:
if __name__ == "__main__":
for test_host in ["google.com", "192.0.2.1"]:
output = ping_host(test_host)
parsed = parse_ping_output(test_host, output)
print(parsed)
What does this test code do?
The loop pings two hosts one at a time. For each host, it captures the raw output and feeds it into your new parse_ping_output() function. The parsed dictionary gets printed so you can verify the values.
- Save health_checker.py.
- Run the script by entering this command in your terminal:
python health_checker.py
This will take about 20 seconds
The first host (google.com) replies quickly. The second host (192.0.2.1) waits 4 seconds per packet with no reply, so 4 packets takes about 16 seconds. Be patient while it finishes.
You should see two dictionaries printed. The first one shows google.com as alive with a latency value and 0% loss. The second shows 192.0.2.1 as not alive with None latency and 100% loss.
Not seeing the expected output?
- If both hosts show is_alive: False, check that you typed the regex patterns exactly as shown. A missing backslash or wrong parenthesis will cause the match to fail silently.
- If you see a NameError for parse_ping_output, make sure the function definition appears above the if __name__ block. Python reads top to bottom.
- If google.com shows 100% loss, your network might be blocking ICMP packets. Try a different host like 1.1.1.1.
help me troubleshoot my parse_ping_output function
✔️ Awesome, I've got everything!
Great. Double check you have saved health_checker.py and both dictionaries printed correctly.
ⓧ I'd like to double check the full code
Here is the complete health_checker.py file at this point:
import subprocess
import re
import time
def ping_host(host, count=4):
"""Ping a host and return the raw command output."""
try:
result = subprocess.run(
["ping", "-n", str(count), host],
capture_output=True,
text=True,
timeout=30
)
return result.stdout
except subprocess.TimeoutExpired:
return ""
def parse_ping_output(host, output):
"""Parse Windows ping output into a structured dictionary."""
result = {
"host": host,
"is_alive": False,
"avg_latency_ms": None,
"packet_loss_pct": 100.0
}
if not output:
return result
loss_match = re.search(r"\((\d+)% loss\)", output)
if loss_match:
result["packet_loss_pct"] = float(loss_match.group(1))
avg_match = re.search(r"Average = (\d+)ms", output)
if avg_match:
result["avg_latency_ms"] = int(avg_match.group(1))
result["is_alive"] = True
return result
if __name__ == "__main__":
for test_host in ["google.com", "192.0.2.1"]:
output = ping_host(test_host)
parsed = parse_ping_output(test_host, output)
print(parsed)
Your script can now turn messy ping output into clean, structured data. Next up, you will scale this to check multiple hosts and display the results in a formatted report.
Check Multiple Hosts and Display a Report
Your parser can now extract latency and packet loss from raw ping output. The next step is scaling up from one host to many.
In this step, you'll build the functions that tie everything together. A convenience wrapper combines pinging and parsing into one call. A report function formats the results into a readable table. A main function orchestrates the whole health check across multiple hosts.
In this step, get ready to:
- Write a check_host() wrapper that combines pinging and parsing in one call.
- Create a display_report() function that prints a formatted status table with timestamps.
- Build a main() function that checks multiple hosts and displays the full report.
Create a convenience wrapper for checking hosts
Right now, checking a host requires two separate function calls. A small wrapper function makes the rest of your code cleaner by combining both steps into one.
- In health_checker.py, find the end of the parse_ping_output() function.
- Add the following check_host() function below it:
def check_host(host, count=4):
"""Ping a host and return parsed results."""
output = ping_host(host, count)
return parse_ping_output(host, output)
What does this code do?
This is a convenience function that calls ping_host() to get the raw output, then immediately passes it to parse_ping_output() to get a structured dictionary back.
Wrapping two calls into one keeps your main loop clean. Instead of managing raw output yourself, you just call check_host("google.com") and get a result dictionary.
You won't see a change yet. The next function will use this wrapper to build the full report.
Write the display_report() function
The results from each host check are just dictionaries in memory. You need a function that formats them into a human-readable table with clear columns and a timestamp.
- Below your new check_host() function, add the display_report() function:
def display_report(results):
"""Display a formatted health check report."""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
print(f"\n{'='*60}")
print(f" Network Health Report - {timestamp}")
print(f"{'='*60}")
print(f" {'Host':<25} {'Status':<10} {'Latency':<12} {'Loss':<10}")
print(f" {'-'*25} {'-'*10} {'-'*12} {'-'*10}")
for r in results:
status = "UP" if r["is_alive"] else "DOWN"
latency = f"{r['avg_latency_ms']}ms" if r["avg_latency_ms"] else "N/A"
loss = f"{r['packet_loss_pct']:.0f}%"
print(f" {r['host']:<25} {status:<10} {latency:<12} {loss:<10}")
print(f"{'='*60}\n")
What does this code do?
- time.strftime("%Y-%m-%d %H:%M:%S") captures the current date and time for the report header.
- The :<25, :<10, and :<12 inside the f-strings are alignment specifiers. They left-align each value within a fixed-width column so the output forms a neat table.
- The loop converts each result dictionary into a row showing UP/DOWN status, latency in milliseconds (or N/A if unreachable), and packet loss as a percentage.
You won't see output yet. The next chunk adds the main() function that calls display_report().
Build main() and run the full health check
The main() function ties everything together. It defines which hosts to check, loops through them, and hands the collected results to the report.
- Below display_report(), add the main() function:
def main():
hosts = ["google.com", "cloudflare.com", "github.com"]
print("Running network health check...")
results = []
for host in hosts:
print(f" Pinging {host}...")
result = check_host(host)
results.append(result)
display_report(results)
What does this code do?
- hosts defines the three targets your health checker will ping: google.com, cloudflare.com, and github.com.
- The loop pings each host one at a time and prints a progress message so you know which host is being checked.
- After all hosts are checked, display_report(results) prints the formatted table.
- Now replace the old test code at the bottom of the file. Find this block:
if __name__ == "__main__":
for test_host in ["google.com", "192.0.2.1"]:
output = ping_host(test_host)
parsed = parse_ping_output(test_host, output)
print(parsed)
- Replace it with this:
if __name__ == "__main__":
main()
- Save health_checker.py.
- Run the complete health check by running this command:
python health_checker.py
You should see progress messages as each host is pinged, followed by a formatted table showing each host's status, latency, and packet loss. The report header includes the current date and time.
What should I see?
Your output will look something like this (latency values will vary based on your network):
Running network health check...
Pinging google.com...
Pinging cloudflare.com...
Pinging github.com...
============================================================
Network Health Report - 2026-06-04 14:30:22
============================================================
Host Status Latency Loss
------------------------- ---------- ------------ ----------
google.com UP 14ms 0%
cloudflare.com UP 11ms 0%
github.com UP 18ms 0%
============================================================
All three hosts should show UP status with low latency values and 0% packet loss.
Not seeing the report?
- If you see NameError: name 'check_host' is not defined, make sure check_host() is defined above main() in the file. Python reads top-to-bottom, so functions must be defined before they are called.
- If the script hangs for a long time, one of the hosts might be slow to respond. Wait up to 30 seconds per host (the timeout value in ping_host()).
- If you see NameError: name 'display_report' is not defined, check that display_report() is defined above main().
Still stuck? Why isn't my health_checker.py running correctly?
✔️ Awesome, I've got everything!
Great. Double check you have saved health_checker.py and your report printed successfully.
ⓧ I'd like to double check the full code
Here is the complete health_checker.py at this point:
import subprocess
import re
import time
def ping_host(host, count=4):
"""Ping a host and return the raw command output."""
try:
result = subprocess.run(
["ping", "-n", str(count), host],
capture_output=True,
text=True,
timeout=30
)
return result.stdout
except subprocess.TimeoutExpired:
return ""
def parse_ping_output(host, output):
"""Parse Windows ping output into a structured dictionary."""
result = {
"host": host,
"is_alive": False,
"avg_latency_ms": None,
"packet_loss_pct": 100.0
}
if not output:
return result
loss_match = re.search(r"\((\d+)% loss\)", output)
if loss_match:
result["packet_loss_pct"] = float(loss_match.group(1))
avg_match = re.search(r"Average = (\d+)ms", output)
if avg_match:
result["avg_latency_ms"] = int(avg_match.group(1))
result["is_alive"] = True
return result
def check_host(host, count=4):
"""Ping a host and return parsed results."""
output = ping_host(host, count)
return parse_ping_output(host, output)
def display_report(results):
"""Display a formatted health check report."""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
print(f"\n{'='*60}")
print(f" Network Health Report - {timestamp}")
print(f"{'='*60}")
print(f" {'Host':<25} {'Status':<10} {'Latency':<12} {'Loss':<10}")
print(f" {'-'*25} {'-'*10} {'-'*12} {'-'*10}")
for r in results:
status = "UP" if r["is_alive"] else "DOWN"
latency = f"{r['avg_latency_ms']}ms" if r["avg_latency_ms"] else "N/A"
loss = f"{r['packet_loss_pct']:.0f}%"
print(f" {r['host']:<25} {status:<10} {latency:<12} {loss:<10}")
print(f"{'='*60}\n")
def main():
hosts = ["google.com", "cloudflare.com", "github.com"]
print("Running network health check...")
results = []
for host in hosts:
print(f" Pinging {host}...")
result = check_host(host)
results.append(result)
display_report(results)
if __name__ == "__main__":
main()
Your network health checker is complete. You now have a tool that pings real hosts, parses their responses, and displays everything in a clean, timestamped report. Next, you can take this further with continuous monitoring in the Secret Mission.
Secret mission
Add Continuous Monitoring with CSV Logging
Your health checker runs once and exits. Real network monitoring tools run continuously, alerting you when something changes. Can you add a monitoring mode that checks your hosts every 30 seconds and logs the results to a file?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- health_checker.py and health_log.csv on your Desktop.
- Python 3.13+ installation.
Keep everything running
No action needed. Choose this if you plan to keep using your network health checker or want to modify the script further.
- Your health_checker.py script and health_log.csv log file remain on your Desktop.
- Python stays installed and available for other projects.
Pause - I'll come back to this later
No processes to stop. The script only runs when you invoke it manually, so nothing is using resources in the background.
- Your files stay on your Desktop and are ready to use whenever you return.
- If the monitoring mode is still running in your terminal, press Ctrl+C to stop it.
Delete - I don't want to use this again
Remove all project files and optionally uninstall Python from your machine.
- Delete health_checker.py and health_log.csv by running this command in your terminal:
cd ~/Desktop
del health_checker.py
del health_log.csv
If you created a project folder for these files, delete the entire folder instead:
cd ~/Desktop
rmdir /s /q health-checker
- To uninstall Python, open Settings on your computer.
- Select Apps, then Installed apps.
- Find the Python entry, click the three-dot menu next to it, and select Uninstall.
- Confirm the uninstall when prompted.
Nice Work!
Nice Work!
Congratulations! You just built a working network diagnostics tool from scratch using nothing but Python's standard library.
What you learned:
- Used Python's subprocess module to run system commands and capture their output.
- Wrote regular expressions to parse unstructured text into structured data.
- Built a multi-host network health checker with formatted terminal output showing status, latency, and packet loss.
- Secret Mission: Added continuous monitoring with CSV logging for historical tracking.
Ready to quiz yourself?