Python Scripting to Fetch and Parse Domain TXT Records
You can perform a python dns txt lookup by utilizing robust third-party libraries like dnspython to query domain name servers, retrieve text payloads, and parse complex security policies. TXT records store human- and machine-readable text data, making them the backbone for email authentication frameworks like SPF, DKIM, and DMARC, as well as domain ownership verification methods. Whether you are automating security audits, building monitoring dashboards, or verifying your DNS configurations programmatically, writing a dedicated Python script saves countless hours of manual terminal work.
Before writing code, it is often helpful to quickly inspect your target records via a web interface to understand what data to expect. You can use the TXT Lookup tool to instantly inspect raw text entries for any domain, helping you validate your queries before parsing them in your scripts.
Setting Up Your Python DNS Environment
Python's built-in libraries do not include native DNS resolution capabilities that easily handle advanced record types like TXT. Therefore, you need to install the industry-standard dnspython library. Open your terminal or command prompt and run the following command to install it in your active Python environment:
pip install dnspython
Once installed, you can import the resolver module inside your scripts to query nameservers directly. This library handles everything from standard lookups to custom timeout configurations and specific DNS server targeting.
Basic Python Script for DNS TXT Lookup
Writing a basic script to query TXT records requires initializing a resolver object and calling its resolve() method. Let's write a script that targets example.com and prints all raw TXT strings returned by the authoritative nameservers.
import dns.resolver
def fetch_txt_records(domain):
try:
# Query TXT records for the specified domain
answers = dns.resolver.resolve(domain, 'TXT')
print(f"Found {len(answers)} TXT record(s) for {domain}:\n")
for rdata in answers:
# rdata.strings returns a tuple of bytes objects
# We decode each byte string to UTF-8
full_text = ''.join([b.decode('utf-8') for b in rdata.strings])
print(f"- {full_text}")
except dns.resolver.NXDOMAIN:
print(f"Error: The domain {domain} does not exist.")
except dns.resolver.NoAnswer:
print(f"Error: No TXT records found for {domain}.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
target_domain = "example.com"
fetch_txt_records(target_domain)
When you run this script, the output will display the exact strings configured in the DNS zone file. For example:
Found 2 TXT record(s) for example.com:
- v=spf1 include:_spf.example.com ~all
- Ed25519=123456789abcdef
Parsing Security Policies (SPF, DKIM, DMARC)
Retrieving text records is only the first step. Because TXT records contain vital email security data, your scripts will often need to parse them to check configurations. For instance, parsing an SPF record involves identifying mechanisms like include, ip4, ip6, and the final qualifier (~all or -all).
Here is an intermediate script that filters and parses specific security policies from the returned TXT record set:
import dns.resolver
def analyze_security_txt(domain):
resolver = dns.resolver.Resolver()
# Optional: Use public resolvers like Cloudflare or Google
resolver.nameservers = ['1.1.1.1', '8.8.8.8']
try:
answers = resolver.resolve(domain, 'TXT')
for rdata in answers:
txt_content = ''.join([b.decode('utf-8') for b in rdata.strings])
if txt_content.startswith('v=spf1'):
print(f"[SPF Policy Found]")
print(f" Raw: {txt_content}")
mechanisms = txt_content.split()
print(f" Mechanisms count: {len(mechanisms) - 1}\n")
elif 'dmarc' in txt_content.lower() or txt_content.startswith('v=DMARC1'):
print(f"[DMARC Policy Found]")
print(f" Raw: {txt_content}\n")
elif 'dkim' in txt_content.lower() or 'p=' in txt_content:
print(f"[Possible DKIM/Public Key Found]")
print(f" Raw: {txt_content}\n")
except Exception as e:
print(f"Error processing domain {domain}: {e}")
if __name__ == "__main__":
analyze_security_txt("example.com")
Handling Split Strings (Chunking)
DNS limits a single character string inside a TXT record to 255 characters. For long keys—such as 2048-bit DKIM public keys—DNS providers automatically split the string into multiple chunks within the same record object. If you do not join these chunks correctly using b.decode('utf-8') over rdata.strings, your script will output truncated data or syntax errors when parsing cryptographic keys.
Comparison of DNS Query Methods in Python
| Method / Library | Pros | Cons | Best Use Case |
|---|---|---|---|
dnspython |
Feature-rich, supports custom nameservers, robust error handling | Requires external installation (pip install) |
Production scripts, security auditing, complex automation |
socket module |
Built into Python standard library, zero dependencies | Very limited DNS parsing, lacks low-level control for TXT chunks | Basic connectivity checks only |
subprocess (dig/nslookup) |
Leverages native OS tools | Platform-dependent, requires parsing raw CLI output strings | Quick local debugging scripts |
Advanced Querying: Custom Nameservers and Timeouts
In enterprise environments or when debugging propagation issues, querying your local Internet Service Provider (ISP) nameserver is insufficient. You need to query authoritative nameservers directly. dnspython makes this straightforward by allowing you to instantiate a custom resolver instance.
import dns.resolver
def query_authoritative_server(domain, nameserver_ip):
resolver = dns.resolver.Resolver()
resolver.nameservers = [nameserver_ip]
resolver.timeout = 5.0
resolver.lifetime = 5.0
try:
answers = resolver.resolve(domain, 'TXT')
for rdata in answers:
print(''.join([b.decode('utf-8') for b in rdata.strings]))
except dns.resolver.Timeout:
print("The DNS query timed out.")
except Exception as e:
print(f"Query failed: {e}")
if __name__ == "__main__":
# Querying using a documentation IP representing a nameserver
query_authoritative_server("example.com", "192.0.2.1")
Common Mistakes and How to Fix Them
When writing automation scripts for DNS lookups, developers frequently encounter a few specific pitfalls:
- Ignoring Chunked Strings: Treating
rdataas a single string instead of iterating throughrdata.stringsresults in broken DKIM keys. Always join the decoded byte segments. - Not Catching Specific Exceptions: Failing to handle
NXDOMAINorNoAnswerexceptions will cause your script to crash when encountering unregistered domains or missing records. Always wrap your resolve calls in try-except blocks. - Hitting Rate Limits: Querying public resolvers too rapidly in a loop can trigger rate limits or temporary IP bans. Implement
time.sleep()delays between requests when scanning multiple domains. - Name Resolution Caching:
dnspythoncaches results by default in certain contexts. If you are tracking real-time DNS propagation changes, ensure you disable or bypass the cache in your resolver configuration.
Quick Checklist for Python DNS TXT Scripts
- Installed
dnspythonviapip install dnspythonin your virtual environment. - Implemented error handling for
NXDOMAIN,NoAnswer, andTimeoutexceptions. - Joined string chunks correctly using
rdata.stringsto support long DKIM records. - Configured custom timeout and lifetime values to prevent hanging threads.
- Verified record outputs against an independent validation tool.