Parse nginx access log top IPs in Python

Reads an nginx access log line by line, extracts the client IP, and returns the most frequent IPs using a regex and Counter.

Easy Python 3.9+ Aug 9, 2026 Automation & scripting 14 views 0 copies

Python code

29 lines
Python 3.9+
import re
from collections import Counter

def top_ips(log_file, n=10):
    ip_pattern = re.compile(r'^(\S+)')
    ip_counts = Counter()

    with open(log_file, 'r') as f:
        for line in f:
            match = ip_pattern.match(line)
            if match:
                ip_counts[match.group(1)] += 1

    return ip_counts.most_common(n)

if __name__ == "__main__":
    # Sample nginx access log data
    sample_log = """192.168.1.1 - - [10/Oct/2023:13:55:36 +0000] "GET /index.html HTTP/1.1" 200 2326
10.0.0.2 - - [10/Oct/2023:13:55:37 +0000] "GET /api/data HTTP/1.1" 200 153
192.168.1.1 - - [10/Oct/2023:13:55:38 +0000] "GET /about HTTP/1.1" 404 12
203.0.113.5 - - [10/Oct/2023:13:55:39 +0000] "POST /login HTTP/1.1" 200 89
10.0.0.2 - - [10/Oct/2023:13:55:40 +0000] "GET /api/data HTTP/1.1" 200 153
192.168.1.1 - - [10/Oct/2023:13:55:41 +0000] "GET /contact HTTP/1.1" 200 45"""

    with open('access.log', 'w') as f:
        f.write(sample_log)

    for ip, count in top_ips('access.log', 3):
        print(f"{ip}: {count}")

Output

stdout
192.168.1.1: 3
10.0.0.2: 2
203.0.113.5: 1

How it works

This script uses a regex r'^(\S+)' to capture the first whitespace-delimited token on each line, which is the client IP in nginx's default combined log format. The Counter from collections tallies each IP as lines are read, and most_common(n) returns the top n IPs sorted by descending count. Reading the file line by line keeps memory usage low even for large logs. The if __name__ == "__main__" block creates a sample log file for demonstration, then prints the top IPs and their counts.

Common mistakes

  • Using `re.search` without anchoring and matching the IP in the middle of the line instead of the start.
  • Forgetting that the regex must start with `^` to avoid matching part of the timestamp or other fields.
  • Not handling lines that don't match the pattern (e.g., blank lines) which can cause AttributeError if you assume a match.

Variations

  1. Use `split()[0]` instead of regex for simpler extraction when the log format is consistent.
  2. Read the entire file into memory with `readlines()` and use a generator expression for counting, but this is less memory-efficient for large logs.

Real-world use cases

  • Identifying heavy clients that are hammering your web server to adjust rate limits or evict abusive traffic.
  • Spotting scraping bots by comparing IP frequency against known bot signatures in an analytics pipeline.
  • Auditing access logs before a security review to detect suspicious IPs that correlate with failed login attempts.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Automation & scripting

Related tutorials and quizzes for this topic.