back to blog
Python 1 min read

Automating Repetitive Recon Tasks With Python

March 5, 2026

Reconnaissance involves a lot of repetitive checking: is this host alive, what does it respond with, what's actually on the page. Doing that by hand for more than a handful of targets gets old fast, so I wrote a small script to handle the first pass.

The goal

Take a list of subdomains and produce a clean table showing, for each one:

  • Whether it responded
  • The HTTP status code
  • The page <title>, if any

It is only a first pass, but it quickly shows which hosts are worth checking manually.

The script

import requests
from bs4 import BeautifulSoup

def check_host(host: str) -> dict:
    url = f"https://{host}"
    try:
        response = requests.get(url, timeout=5)
        title = BeautifulSoup(response.text, "html.parser").title
        return {
            "host": host,
            "status": response.status_code,
            "title": title.string.strip() if title else "—",
        }
    except requests.RequestException:
        return {"host": host, "status": "unreachable", "title": "—"}

def main(subdomain_file: str):
    with open(subdomain_file) as f:
        hosts = [line.strip() for line in f if line.strip()]

    results = [check_host(h) for h in hosts]

    print(f"{'HOST':30} {'STATUS':10} {'TITLE'}")
    for r in results:
        print(f"{r['host']:30} {str(r['status']):10} {r['title']}")

if __name__ == "__main__":
    main("subdomains.txt")

Why I wrote it

  • It is a filter, not a replacement for thinking. The script removes dead or unreachable hosts from the first pass; deciding what matters still requires manual work.
  • The exception handling keeps one bad host from stopping the run. Timeouts, DNS failures, and connection errors are handled by the same exception type.
  • The five-second timeout is a trade-off. A shorter timeout can skip slow hosts; a longer one makes unreachable hosts take more time.

What I would change next

The script currently checks hosts one at a time. For larger lists, I would use concurrent.futures.ThreadPoolExecutor to make the requests concurrently.

Share this post: