When it comes to brute-forcing, wordlists make the world go ’round. Some people spend days, weeks, months, and even years refining their lists: most common names, company-specific guesses, service names, and all sorts of community-built maybes. Crafting an efficient wordlist is a loveless labour, but handling one well is a very learnable Python task.
In this lesson, you’ll teach the bruteforcer how to load candidate subdomain labels from disk. The code stays small, but it introduces an important tool-building habit: treat input as something that can be missing, blank, or messy.
A scanner is only as helpful as the data you feed it. Wordlist handling is where beginner tools often fall over, so this lesson makes that path predictable before the project adds network behaviour.
Objective
Load a wordlist from disk and return a clean list of candidate labels.
Path Building in Python
pathlib.Path lets Python work with file paths without hand-building strings for each operating system. The course fixture gives us the repository root, and Path joins the pieces from there.
This is the modern version of “where did I put that file?” Instead of gluing strings together and hoping the slashes are right, we let Path do the operating-system-aware work for us.
from pathlib import Pathfrom fixtures.local_targets import course_rootwordlist_path = course_root() /"data"/"wordlist.txt"print(wordlist_path.relative_to(course_root()))print(wordlist_path.is_file())
1
Join path pieces with / instead of manually typing path separators.
2
Print the path relative to the course root so the output is stable across machines.
3
Check that the path points to a real file.
data/wordlist.txt
True
Here’s what’s happening: wordlist_path is a Path object, not just text. That means it can answer useful questions like “are you a file?” before the rest of the tool tries to read from it.
Reading and Cleaning Lines
When handling files it’s good practice to use a context manager. The with keyword ensures the file is closed properly even if an exception is raised, which is one less small cleanup problem for your application.
readlines() and normal file iteration keep newline characters. For a wordlist, those newlines are noise, so the loader should strip each line before returning it. strip() returns a copy of the string with whitespace removed from the beginning and end; it doesn’t modify the original string in place.
Python’s motto is often described as “ask for forgiveness, not permission”. Here, we open the file and let FileNotFoundError happen naturally for now. In the next pieces of the project, we’ll decide which failures should be handled and which should stop the tool.
def load_wordlist(path: Path) ->list[str]:with path.open(encoding="utf-8") asfile:return [line.strip() for line infileif line.strip()]candidates = load_wordlist(wordlist_path)print(candidates[:4])print(f"{len(candidates)} candidates loaded")
1
Use a context manager so Python closes the file automatically.
2
Strip whitespace and skip blank lines before the rest of the tool sees the input.
Here’s what’s happening: by the time the wordlist leaves this function, the rest of the program receives useful labels, not a jumble of trailing \n characters and empty lines.
Checkpoint
Add load_wordlist(path) to your project script. It should:
accept a Path
open the file with UTF-8 encoding
return stripped, non-empty lines
let FileNotFoundError happen naturally for now
When you load data/wordlist.txt, the first four entries should be:
['www', 'mail', 'ftp', 'localhost']
If your output includes extra blank strings or \n characters, don’t despair; Python is showing you exactly where the cleaning step still needs attention.
from pathlib import Pathdef load_wordlist(path: Path) ->list[str]:# Open the file and return cleaned lines.pass
Extension Activity
Update load_wordlist() so it also skips lines that begin with #. Keep the original order of the remaining entries.
For more advanced users: try writing the same function once with a normal for loop and once with a list comprehension. List comprehensions are more concise, but sometimes being more verbose is easier on the programmer, and that’s a perfectly respectable choice.