Every scanner built so far in this unit checks for known vulnerabilities. This article's tool asks a more basic question first: what is actually out there to check? Nmap has answered that question, reliably, for over two decades.
Analogy: The translator at the counter
Nmap is the industry's own port scanner, far more capable than the one built in Unit 2, with operating-system fingerprinting and thousands of service-detection rules refined over two decades. Rather than reimplement that, the python-nmap package sits beside Nmap like a translator at an immigration counter: it hands your request to Nmap in the language Nmap expects, and hands Nmap's answer back to you as Python objects.
nmap_integration.py
# nmap_integration.py
import nmap
scanner = nmap.PortScanner()
scanner.scan("10.0.0.0/24", arguments="-sV -T4") # version detection, faster timing
for host in scanner.all_hosts():
if scanner[host].state() != "up":
continue
print(f"Host: {host}")
for proto in scanner[host].all_protocols():
for port, info in scanner[host][proto].items():
print(f" {port}/{proto}: {info['name']} {info['product']} {info['version']}")
Caution: This one needs the real Nmap binary installed this course cannot ship
python-nmap is a thin wrapper: it builds the command line, runs the real nmap executable as a subprocess, and parses its XML output into the nested dictionary-like object scanner[host][proto][port] shown above. Without Nmap itself installed on the machine running the script, there is nothing for the wrapper to call, so this script has not been run for real here. The shape of the result, host, then protocol, then port, with name, product and version fields, is Nmap's own well-documented output structure, unchanged by the wrapper.
Sample output, as Nmap's documentation and the eBook both show it, against a lab host
Host: 10.0.0.5
22/tcp: ssh OpenSSH 7.4
80/tcp: http Apache httpd 2.4.6
443/tcp: https Apache httpd 2.4.6
scanner.all_hosts() lists every host Nmap found, and scanner[host].state() reports whether that host answered at all, "up" or not; the continue skips straight past anything that never replied. The nested loop then walks every protocol Nmap checked on that host, TCP and UDP are both possible, and every port found within it.
The flag that does the real work
-sV asks Nmap for version detection, which it performs through a mix of banner grabbing and protocol-specific probes, the same idea as Unit 1's grab_banner() but with a far larger library of known responses behind it. -T4 is a timing template, trading a little stealth for a faster scan, reasonable against a lab host you control.
Python concept: Reading an Nmap result as nested data
scanner[host][proto].items() is exactly the shape of a nested dictionary: for each port number, a dictionary of fields describing what was found there. Once you know that shape, pulling out just the open web ports, or just the hosts running a particular product, is ordinary Python: a comprehension filtering on info['name'] == 'http' or 'Apache' in info['product'], nothing Nmap-specific about the filtering itself.
Where this fits in the pipeline
Wrapping this inside the run_assessment() pipeline from the last article adds an inexpensive, fast first pass before the slower, deeper Nessus scan ever starts. A thousand hosts can be swept for what is even listening in a fraction of the time a full vulnerability scan would take, and only the hosts that actually answer need to go on to the heavier check.
Check your understanding
Question 1: What does python-nmap actually do, and what does it rely on being installed separately?
It builds the command line for a real Nmap scan, runs the actual nmap executable as a subprocess, and parses Nmap's own XML output into Python objects. It relies on the Nmap program itself being installed on the machine running the script; the package is a wrapper, not a reimplementation.
Question 2: What does the -sV flag add to a scan, and how does it relate to banner grabbing from Unit 1?
It asks Nmap to perform version detection on each open port, identifying the specific software and version running there. It is the same underlying idea as Unit 1's grab_banner(), reading what a service announces about itself, but backed by Nmap's much larger library of known responses and protocol-specific probes.
Question 3: Why would a fast Nmap sweep run before a slower Nessus scan in a combined pipeline, rather than after it or instead of it?
A broad Nmap sweep quickly narrows a large target range down to the hosts that are actually up and worth examining further, at a fraction of the time a full vulnerability scan takes. Running it first means the slower, deeper Nessus scan only has to spend its time on hosts already confirmed to be reachable, rather than the whole original range.
Key takeaway
python-nmap hands your scan request to the real, battle-tested Nmap binary and hands its answer back as ordinary nested Python data. As a fast first pass ahead of a deeper scanner, it is the cheapest way to narrow a large range down to what is actually worth a closer look.

