O módulo poplib implementa um cliente POP3 na biblioteca padrão do Python. Ele permite consultar uma caixa, listar mensagens, baixar conteúdo e marcar itens para exclusão. POP3 ainda aparece em contas antigas, dispositivos e integrações simples que precisam retirar e-mails de um servidor.
O próprio manual do Python considera POP3 obsolescente e recomenda IMAP quando disponível. POP3 oferece uma visão mais simples da caixa, normalmente sem pastas, busca rica, sincronização de flags ou acesso parcial confiável. Use-o apenas quando o provedor ou sistema legado exigir.
Conexão segura com POP3_SSL
import poplib
import ssl
contexto = ssl.create_default_context()
cliente = poplib.POP3_SSL(
"pop.example.com",
port=995,
timeout=15,
context=contexto,
)
try:
cliente.user("usuario@example.com")
cliente.pass_("senha")
print(cliente.stat())
finally:
cliente.quit()
Use POP3_SSL ou STARTTLS antes de autenticar. FTP e e-mail compartilham um erro comum: proteger apenas parte do fluxo. No POP3 simples, usuário, senha e mensagens podem trafegar sem criptografia.
O contexto criado por ssl.create_default_context() valida certificado e hostname. Não desative essa verificação para contornar certificados expirados ou nomes divergentes. Consulte ssl no Python.
STARTTLS na porta 110
Quando o servidor usa POP3 explícito com upgrade TLS, conecte sem autenticar, consulte capabilities e chame stls() com um contexto verificado.
cliente = poplib.POP3("pop.example.com", timeout=15)
cliente.stls(context=contexto)
cliente.user(USER)
cliente.pass_(PASSWORD)
STARTTLS deve ocorrer antes de USER e PASS. Recuse a conexão quando o servidor não oferecer a capacidade esperada; não faça downgrade silencioso para texto claro.
Credenciais
Não fixe senha no código-fonte. Use variáveis de ambiente, gerenciador de segredos ou credenciais de aplicativo. Muitos provedores modernos exigem OAuth e podem não aceitar POP3 por senha.
Não use set_debuglevel(2) em produção. O debug registra comandos e respostas e pode revelar metadados, identificadores ou autenticação.
Consultando capabilities
capacidades = cliente.capa()
for nome, parametros in capacidades.items():
print(nome, parametros)
Capabilities podem indicar STLS, UIDL, TOP, UTF8 e mecanismos de autenticação. Implementações POP3 variam bastante; a presença de um método no Python não garante que o servidor o suporte corretamente.
Status da caixa
quantidade, bytes_totais = cliente.stat()
print(quantidade, bytes_totais)
stat() retorna quantidade e tamanho total informado. Use isso como estimativa e aplique limites próprios. Uma caixa pode crescer entre a consulta e o download.
Listando mensagens
resposta, linhas, octetos = cliente.list()
mensagens = []
for linha in linhas:
numero_texto, tamanho_texto = linha.split(maxsplit=1)
mensagens.append((int(numero_texto), int(tamanho_texto)))
Valide cada linha. O servidor é remoto e pode enviar respostas inesperadas. Não tente baixar mensagens acima do limite definido pela aplicação.
UIDL para identificar mensagens
Números POP3 são posições temporárias da sessão. Use uidl() para obter um identificador estável fornecido pelo servidor e evitar processar novamente o mesmo e-mail.
resposta, linhas, octetos = cliente.uidl()
uids = {}
for linha in linhas:
numero, uid = linha.split(maxsplit=1)
uids[int(numero)] = uid.decode("ascii", errors="strict")
Armazene os UIDs já concluídos em banco ou arquivo transacional. O identificador depende do servidor e não substitui Message-ID; os dois podem ser úteis para deduplicação.
Baixando uma mensagem
resposta, linhas, octetos = cliente.retr(numero)
if octetos > LIMITE_MENSAGEM:
raise ValueError("mensagem grande demais")
raw_email = b"\r\n".join(linhas) + b"\r\n"
retr() retorna linhas sem o terminador final. Ao reconstruir, use CRLF. A resposta já pode ocupar memória; verifique tamanho informado antes quando possível e limite quantidade de mensagens por execução.
Parseando com o pacote email
from email import policy
from email.parser import BytesParser
mensagem = BytesParser(policy=policy.default).parsebytes(raw_email)
print(mensagem.get("Subject"))
print(mensagem.get("From"))
Assuntos, remetentes, corpos HTML e nomes de anexos são conteúdo não confiável. Faça escape ao exibir e sanitize HTML quando realmente permitir renderização.
TOP para headers e amostra
top(numero, linhas) tenta buscar headers e algumas linhas do corpo sem marcar a mensagem como lida. Porém a documentação alerta que TOP é mal especificado e frequentemente quebrado em servidores alternativos.
resposta, linhas, octetos = cliente.top(numero, 0)
headers = b"\r\n".join(linhas) + b"\r\n\r\n"
Teste manualmente contra cada servidor antes de depender de TOP. Quando a implementação é inconsistente, pode ser necessário usar RETR com limites ou escolher IMAP.
Extraindo texto com limite
def partes_texto(mensagem, limite=1_000_000):
total = 0
for parte in mensagem.walk():
if parte.get_content_maintype() == "multipart":
continue
if parte.get_content_disposition() == "attachment":
continue
dados = parte.get_payload(decode=True) or b""
total += len(dados)
if total > limite:
raise ValueError("conteúdo excede limite")
yield parte.get_content_type(), dados
Use o charset declarado com fallback. Para estratégias de decoding, veja codecs no Python.
Adjuntos seguros
Nunca use o filename MIME como caminho direto. Gere um nome interno, limite bytes, inspecione o formato e salve primeiro em área temporária. Use tempfile no Python e hashlib no Python.
Exclusão é confirmada no QUIT
dele(numero) apenas marca a mensagem durante a sessão. Em servidores conformes, a exclusão é efetivada quando quit() termina corretamente.
cliente.dele(numero)
# outras validações
cliente.quit() # confirma mudanças
Isso torna o fluxo perigoso: um código pode marcar mensagens erradas e confirmá-las ao fechar. Separe download de exclusão, confirme UID, hash ou Message-ID e registre a decisão antes de chamar DELE.
Desfazendo marcações com RSET
cliente.rset()
rset() remove marcações de exclusão da sessão, desde que ainda não tenham sido confirmadas. Use-o ao detectar falha antes do QUIT.
Fechamento inesperado
A maioria dos servidores cancela exclusões se a conexão cair sem QUIT, mas a documentação menciona implementações históricas que violam essa regra. Não dependa do disconnect como rollback. Prefira evitar DELE até o último estágio.
Pipeline seguro de processamento
Um fluxo robusto pode seguir estas etapas:
- Conectar com TLS validado.
- Listar UIDL e tamanhos.
- Ignorar UIDs já concluídos.
- Baixar no máximo N mensagens e M bytes.
- Parsear e validar.
- Persistir o resultado e confirmar transação.
- Somente então, opcionalmente, marcar para exclusão.
- Executar QUIT e registrar conclusão.
Se a persistência falhar, não marque o e-mail.
UTF-8
O método utf8() solicita o modo definido no RFC 6856 quando o servidor suporta. Teste capabilities e trate error_proto. Mesmo com UTF-8 no protocolo, mensagens MIME podem usar vários charsets.
Keep-alive
noop() mantém a sessão ativa, mas não substitui um deadline total. Processamentos demorados devem limitar o tempo e reconectar quando necessário.
Tratando erros
poplib.error_proto representa respostas de protocolo. Falhas de socket e TLS podem chegar como OSError, TimeoutError ou ssl.SSLError.
try:
cliente = poplib.POP3_SSL(HOST, context=contexto, timeout=15)
cliente.user(USER)
cliente.pass_(PASSWORD)
except poplib.error_proto as erro:
raise RuntimeError("servidor POP3 rejeitou a operação") from erro
except OSError as erro:
raise RuntimeError("falha de transporte POP3") from erro
Não registre a senha nem a resposta completa de autenticação.
POP3 ou IMAP
POP3 é orientado a baixar mensagens de uma caixa simples. IMAP oferece pastas, busca no servidor, UIDs com UIDVALIDITY, flags e sincronização. Para automações que precisam preservar estado ou trabalhar com várias caixas, consulte imaplib no Python.
Testes recomendados
Teste certificado inválido, STARTTLS ausente, login rejeitado, UIDL não suportado, TOP quebrado, mensagem grande, MIME malformado, anexo perigoso, DELE seguido de RSET, falha antes de QUIT, timeout, caixa vazia e deduplicação.
Erros comuns
Os erros frequentes são usar POP3 simples com senha, desativar TLS, confiar em TOP, identificar apenas pelo número, baixar a caixa inteira, guardar tudo em memória, salvar filenames externos, marcar para exclusão antes de persistir, confirmar DELE automaticamente no QUIT e registrar conteúdo sensível.
Conclusão
poplib oferece acesso direto a servidores POP3, mas o protocolo é limitado e obsoleto. Use POP3_SSL ou STARTTLS com contexto verificado, identifique mensagens com UIDL, imponha limites, parseie MIME como conteúdo não confiável e mantenha exclusão como uma etapa final e explicitamente autorizada.
Consulte a documentação oficial de poplib e o RFC 1939 do POP3. Quando houver opção, prefira IMAP para sincronização e controle mais previsíveis.







