Alle Artikel

Ungültige und doppelte E-Mail-Adressen aus einer CSV-Datei entfernen

··7 Min. Lesezeit

Um ungültige und doppelte E-Mail-Adressen aus einer CSV-Datei zu entfernen, schreiben Sie die E-Mail-Spalte klein und entfernen umgebende Leerzeichen, deduplizieren anhand dieses bereinigten Werts, entfernen Adressen, die eine Syntaxprüfung nicht bestehen, und entfernen dann Domains ohne Mailserver. Excel und Google Sheets schaffen die ersten drei Schritte; ein kurzes Python-Skript erledigt alle vier und protokolliert, warum jede Zeile entfernt wurde.

Syntax- und MX-Prüfungen finden Formatierungsfehler und tote Domains. Ob ein Postfach existiert, können sie nicht sagen, dafür ist eine SMTP-Unterhaltung mit dem Server des Empfängers nötig. Betrachten Sie das hier als kostenlosen ersten Durchgang vor der eigentlichen Verifizierung.

Was jede Methode kann

Aufgabe Excel Google Sheets Python-Skript unten
Kleinschreiben und Leerzeichen entfernen Ja Ja Ja
Geschützte Leerzeichen Braucht SUBSTITUTE(…, CHAR(160), " ") Braucht ebenfalls SUBSTITUTE Wird behandelt
Duplikate entfernen Ja Ja Ja, behält das erste
Syntaxprüfung Regex nur in Microsoft 365 Ja, REGEXMATCH Ja
Grund für jede entfernte Zeile protokollieren Manuell Manuell Automatisch
MX-Prüfung Nein Nein Ja, mit dnspython
Postfach existiert Nein Nein Nein

Für Dateien mit ein paar Tausend Zeilen, die Sie einmal bereinigen, reicht eine Tabellenkalkulation. Für wiederholte Bereinigungen, sehr große Dateien oder alles, wofür Sie einen Prüfpfad brauchen, nehmen Sie das Skript.

In Excel oder Google Sheets

Normalisieren Sie in einer Hilfsspalte, prüfen Sie die Syntax dieser Spalte und deduplizieren Sie dann anhand davon. Für Excel und Google Sheets haben wir vollständige, getestete Anleitungen, deshalb hier die Kurzfassung.

  1. Hilfsspalte: =LOWER(TRIM(SUBSTITUTE(A2, CHAR(160), " "))) (in der deutschen Excel-Version =KLEIN(GLÄTTEN(WECHSELN(A2; ZEICHEN(160); " ")))). Das SUBSTITUTE steht dort, weil sowohl Excels GLÄTTEN (TRIM) als auch die Funktion Leerzeichen entfernen in Sheets geschützte Leerzeichen stehen lassen.
  2. Syntax: REGEXMATCH in Sheets, REGEXTEST in Excel für Microsoft 365, jeweils mit dem Muster aus dem Skript unten. Ältere Excel-Versionen brauchen eine längere Ersatzformel, die in unserem Test vier fehlerhafte Adressen durchgelassen hat.
  3. Deduplizieren Sie anhand der Hilfsspalte mit Duplikate entfernen (Microsoft dokumentiert, dass Excel das erste Vorkommen behält) oder mit UNIQUE in ein neues Tabellenblatt.
  4. Filtern Sie die Zeilen heraus, die durchgefallen sind, und speichern Sie als CSV.

In Python: ein getestetes 20-Zeilen-Skript

Dieses Skript bereinigt, prüft und dedupliziert in einem Durchgang und schreibt die abgelehnten Zeilen mit Begründung in eine separate Datei. Es nutzt nur die Standardbibliothek von Python.

import csv, re, sys

PATTERN = re.compile(r"^[a-z0-9_%+'-]+(\.[a-z0-9_%+'-]+)*@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*\.[a-z]{2,}$")

src, column = sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else "email"
seen = set()
with open(src, newline="", encoding="utf-8-sig") as f, \
     open("clean.csv", "w", newline="", encoding="utf-8") as ok, \
     open("rejected.csv", "w", newline="", encoding="utf-8") as bad:
    reader = csv.DictReader(f)
    good = csv.DictWriter(ok, fieldnames=reader.fieldnames)
    rejected = csv.DictWriter(bad, fieldnames=reader.fieldnames + ["reason"])
    good.writeheader(); rejected.writeheader()
    for row in reader:
        email = (row.get(column) or "").replace(" ", " ").strip().lower()
        reason = "empty" if not email else "bad syntax" if not PATTERN.match(email) else "duplicate" if email in seen else ""
        if reason:
            rejected.writerow({**row, "reason": reason})
            continue
        seen.add(email)
        good.writerow({**row, column: email})
print(f"kept {len(seen)} unique addresses")

Starten Sie es mit dem Dateinamen und, falls die Spalte nicht email heißt, mit dem Spaltennamen:

python3 clean_emails.py leads.csv
python3 clean_emails.py leads.csv "Email Address"

Es schreibt clean.csv (alle ursprünglichen Spalten, die E-Mail-Adresse normalisiert) und rejected.csv (die ursprünglichen Zeilen plus eine Spalte reason: empty für leer, bad syntax für fehlerhafte Syntax oder duplicate für Duplikat).

Was passiert ist, als wir es ausgeführt haben

Wir haben das Skript am 1. Oktober 2026 (Python 3.14, macOS) auf eine Testdatei mit 19 Zeilen angewendet, die aus den Fehlern besteht, die in echten Exporten auftauchen. Es gab kept 4 unique addresses aus.

Eingabe Ergebnis
ana.silva@example.com Behalten (erstes Vorkommen)
Ana.Silva@Example.com Abgelehnt: duplicate
ana.silva@example.com + Leerzeichen am Ende Abgelehnt: duplicate
ana.silva@example.com + geschütztes Leerzeichen Abgelehnt: duplicate
o'brien@example.co.uk Behalten
jo+news@example.com Behalten
kai@sub.example.io Behalten
ben@example Abgelehnt: bad syntax
cara@@example.com Abgelehnt: bad syntax
dev@example..com Abgelehnt: bad syntax
eve@example,com Abgelehnt: bad syntax
gus.@example.com Abgelehnt: bad syntax
max@-example.com Abgelehnt: bad syntax
ned@example.c Abgelehnt: bad syntax
mailto:finn@example.com Abgelehnt: bad syntax
Pia <pia@example.com> Abgelehnt: bad syntax
ola@example.com;ola2@example.com Abgelehnt: bad syntax
hana o@example.com Abgelehnt: bad syntax
(leer) Abgelehnt: empty

Die letzten vier abgelehnten Zeilen enthalten echte Adressen, die in Datenmüll verpackt sind. Genau deshalb schreibt das Skript abgelehnte Zeilen in eine Datei, statt sie zu verwerfen: Öffnen Sie rejected.csv, korrigieren Sie von Hand, was offensichtlich korrigierbar ist, und fügen Sie diese Zeilen wieder hinzu. „Korrigieren“ Sie keine mehrdeutigen Fälle. hana o@example.com könnte hanao@ oder hana.o@ sein, und wer rät, erzeugt eine Adresse, die Ihnen niemand gegeben hat.

Zwei CSV-Fallen, die das Skript vermeidet

Am Komma aufteilen. Die Zeile eve@example,com steht in der Datei als Eve,"eve@example,com", in Anführungszeichen, weil sie ein Komma enthält. Wir haben es geprüft: Ein naives line.split(",") macht daraus drei Felder, ['Eve', '"eve@example', 'com"'], während das csv-Modul von Python korrekt zwei liefert. Verwenden Sie immer einen echten CSV-Parser.

Die Byte-Order-Mark. Manche Programme schreiben drei unsichtbare Bytes, eine Byte-Order-Mark, an den Anfang von UTF-8-Dateien. Öffnet Python die Datei als einfaches utf-8, liest es die erste Spaltenüberschrift als 'email', eine Suche nach email findet also nichts, und jede Zeile wirkt leer. Wir haben das mit einer zweizeiligen Testdatei nachgestellt. encoding="utf-8-sig" entfernt die Markierung, wenn sie vorhanden ist, und tut nichts, wenn nicht.

Eine Anmerkung zum geschützten Leerzeichen: Pythons strip() behandelt es bereits als Leerraum, anders als die Trim-Funktionen der Tabellenkalkulationen. Das explizite replace erwischt zusätzlich eines, das mitten in einer Adresse steht; diese fällt dann korrekterweise bei der Syntaxprüfung durch.

Eine MX-Prüfung hinzufügen

Eine Domain-Prüfung entfernt Adressen bei Domains, die keine E-Mails empfangen können, und sie ist günstig, weil jede Domain nur einmal abgefragt wird. Sie braucht ein Paket: pip install dnspython.

import csv, sys
import dns.resolver  # pip install dnspython

cache = {}

def mail_status(domain):
    if domain in cache:
        return cache[domain]
    try:
        answers = dns.resolver.resolve(domain, "MX", lifetime=5)
        hosts = [str(r.exchange) for r in answers]
        status = "null MX (accepts no mail)" if hosts == ["."] else "has MX"
    except dns.resolver.NXDOMAIN:
        status = "domain does not exist"
    except dns.resolver.NoAnswer:
        try:
            dns.resolver.resolve(domain, "A", lifetime=5)
            status = "no MX, A record fallback"
        except Exception:
            status = "no mail server"
    except Exception:
        status = "lookup failed, retry"
    cache[domain] = status
    return status

with open(sys.argv[1], newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        email = row["email"]
        print(f"{email:45} {mail_status(email.rsplit('@', 1)[1])}")

Auf eine Testdatei mit fünf Zeilen angewendet, mit dnspython 2.8.0 am 1. Oktober 2026, lieferte es:

someone@gmail.com                             has MX
someone@example.com                           null MX (accepts no mail)
someone@nonexistent-domain-zz91x.com          domain does not exist
someone@neverssl.com                          no MX, A record fallback
other@gmail.com                               has MX

Wir haben jedes Ergebnis mit dig gegengeprüft. Zwei dieser Zeilen sind der Grund, warum das Skript länger ist als „Gibt es einen MX-Eintrag?“:

  • Null-MX. example.com veröffentlicht einen MX-Eintrag, aber er lautet 0 ., was RFC 7505 als Signal definiert, dass eine Domain keine E-Mails annimmt. Eine Prüfung, die nur fragt, ob es einen MX-Eintrag gibt, lässt sie durch.
  • A-Record-Fallback. neverssl.com hat keinen MX-Eintrag, aber einen A-Record. Die Zusammenfassung von RFC 7505 beschreibt die Zustellung von E-Mails als „zuerst durch die Suche nach einem MX-Eintrag und dann durch die Suche nach einem A/AAAA-Eintrag als Fallback“ („first by looking for an MX record and then by looking for an A/AAAA record as a fallback“). Ein fehlender MX-Eintrag allein beweist also nicht, dass eine Domain unzustellbar ist. Markieren Sie diese Fälle für die Verifizierung, statt sie zu löschen.

Entfernen Sie domain does not exist, null MX und no mail server. Wiederholen Sie lookup failed, retry später, denn ein DNS-Timeout sagt nichts über die Domain aus. Das MX-Lookup-Tool prüft einzelne Domains im Browser, falls Sie Stichproben machen möchten.

Was das nicht leistet

Weder das Skript noch eine Tabellenkalkulation kann Ihnen sagen, ob ein Postfach existiert. nobody-here-12345@gmail.com besteht die Syntax- und die MX-Prüfung und bounct, wenn es kein solches Postfach gibt. Um ein Postfach zu bestätigen, ist eine Unterhaltung mit dem Mailserver des Empfängers über Port 25 nötig, den die meisten Heim- und Cloud-Netzwerke blockieren; Warum Cloud-Anbieter Port 25 blockieren enthält unsere Messungen. Catch-all-Domains, die jede Adresse annehmen, erkennt es ebenfalls nicht.

Lassen Sie die bereinigte Datei für diesen Schritt durch ein Verifizierungstool laufen. Weil Sie Duplikate und tote Domains bereits entfernt haben, bezahlen Sie für die Prüfung von weniger Zeilen.

Das Fazit

Normalisieren Sie vor dem Deduplizieren, verwenden Sie einen echten CSV-Parser, öffnen Sie Dateien mit utf-8-sig und behalten Sie abgelehnte Zeilen mit Begründung, statt sie zu löschen. Ergänzen Sie eine MX-Prüfung, die Null-MX und den A-Record-Fallback versteht. Damit sind Formatierungsfehler und tote Domains kostenlos beseitigt; Prüfungen auf Postfach-Ebene sind der einzige verbleibende Schritt, und der Rechner für Verifizierungskosten zeigt, was das für den Rest kostet.

Häufige Fragen

Wie entferne ich doppelte E-Mail-Adressen aus einer CSV-Datei?

Schreiben Sie die E-Mail-Spalte zuerst klein und entfernen Sie umgebende Leerzeichen. Entfernen Sie dann Duplikate anhand dieses bereinigten Werts und behalten Sie jeweils das erste Vorkommen. Wer die Rohspalte dedupliziert, übersieht Kopien, die sich nur durch Großbuchstaben oder versteckte Leerzeichen unterscheiden.

Wie entferne ich ungültige E-Mail-Adressen aus einer CSV-Datei?

Lassen Sie jede Adresse durch eine Syntaxprüfung laufen, um fehlerhaft formatierte zu entfernen, und prüfen Sie dann die MX-Einträge jeder Domain, um Domains zu entfernen, die keine E-Mails empfangen können. Um Adressen zu entfernen, deren Postfach nicht mehr existiert, brauchen Sie einen Verifizierungsdienst auf SMTP-Ebene; kein lokales Skript und keine Tabellenkalkulation kann das zuverlässig.

Warum meldet mein Skript, dass keine Zeile eine E-Mail-Adresse enthält?

Oft, weil die Datei mit einer Byte-Order-Mark beginnt, die manche Programme an den Anfang von UTF-8-Dateien schreiben. Python liest die erste Spaltenüberschrift dann als '\ufeffemail' statt als 'email'. Wenn Sie die Datei mit encoding='utf-8-sig' öffnen, wird die Markierung entfernt.

Ist es unbedenklich, E-Mail-Adressen kleinzuschreiben?

In der Praxis ja. RFC 5321 erlaubt zwar technisch, dass der Teil vor dem @ zwischen Groß- und Kleinschreibung unterscheidet, sagt aber, dass das Ausnutzen dieser Möglichkeit die Interoperabilität beeinträchtigt und nicht empfohlen wird. Gängige Anbieter vergeben keine Postfächer, die sich nur durch die Schreibweise unterscheiden.

Unbegrenzt Adressen verifizieren für 29,99 USD/Monat

Echte SMTP-Postfachprüfung. Keine Credits, keine Gebühren pro E-Mail.

Jetzt starten

Original auf Englisch lesen