import argparse import csv import gzip import sys def domains(path): with gzip.open(path, "rt", encoding="utf-8") as source: for line in source: domain = line.strip().lower().rstrip(".") if domain: yield domain import hashlib import json import sqlite3 import tempfile from pathlib import Path parser = argparse.ArgumentParser(description="Compare two domain snapshots") parser.add_argument("before") parser.add_argument("after") args = parser.parse_args() def checksum(path): digest = hashlib.sha256() with open(path, "rb") as source: for chunk in iter(lambda: source.read(1024 * 1024), b""): digest.update(chunk) return digest.hexdigest() with tempfile.TemporaryDirectory() as temp: connection = sqlite3.connect(str(Path(temp) / "comparison.sqlite")) try: with connection: for table, path in [("before_names", args.before), ("after_names", args.after)]: connection.execute(f"CREATE TABLE {table} (domain TEXT PRIMARY KEY)") connection.executemany(f"INSERT OR IGNORE INTO {table} VALUES (?)", ((name,) for name in domains(path))) added = connection.execute("SELECT COUNT(*) FROM (SELECT domain FROM after_names " "EXCEPT SELECT domain FROM before_names)").fetchone()[0] removed = connection.execute("SELECT COUNT(*) FROM (SELECT domain FROM before_names " "EXCEPT SELECT domain FROM after_names)").fetchone()[0] finally: connection.close() print(json.dumps({"before_file": args.before, "after_file": args.after, "before_sha256": checksum(args.before), "after_sha256": checksum(args.after), "observed_additions": added, "observed_removals": removed}, indent=2))