Python e Word com python-docx: Criar e Editar Documentos

Aprenda a criar e editar documentos Word (.docx) em Python com a biblioteca python-docx: parágrafos, formatação, tabelas, imagens, cabeçalho e rodapé, e geração de contratos e propostas em lote a partir de um template. Guia prático em português.

8 min de leitura Equipe Python Brasil

Gerar documentos do Word automaticamente é uma das automações de escritório que mais economizam tempo. Em vez de abrir o Word, copiar um modelo, trocar o nome do cliente, a data e o valor, e salvar como um novo arquivo, um script Python pode produzir dezenas de contratos, propostas comerciais, certificados ou relatórios em segundos — cada um personalizado, formatado e pronto para enviar. A biblioteca que faz isso é a python-docx, e neste tutorial você vai aprender a criar, editar e automatizar documentos .docx com exemplos práticos voltados para a realidade brasileira.

Se você já automatiza planilhas Excel com Python ou gera relatórios em PDF, este artigo fecha o trio de automação de documentos mais pedido em escritórios, financeiras e consultorias. Para quem está começando, vale revisar o guia de Python para iniciantes antes; aqui o foco é uma tarefa específica, mas recorrente.

Instalação e primeiros passos

A python-docx é uma biblioteca de terceiros. Instale com o pip dentro do seu ambiente virtual:

pip install python-docx

O pacote é importado como docx. O fluxo básico é sempre o mesmo: criar (ou abrir) um documento, adicionar conteúdo e salvar. Veja o “hello world” dos documentos Word:

from docx import Document
from docx.shared import Pt

doc = Document()                       # documento novo e vazio
doc.add_heading("Proposta Comercial", level=1)
doc.add_paragraph("Prezado cliente, segue a nossa proposta para o projeto.")

doc.save("proposta.docx")              # grava o arquivo .docx

Ao rodar esse script, surge um arquivo proposta.docx válido, abrível no Word, no LibreOffice ou no Google Docs. Nenhum Microsoft Word precisa estar instalado — a biblioteca lê e escreve o XML interno do formato .docx diretamente, o que permite rodar até em servidor Linux.

Parágrafos, títulos e formatação de texto

O conteúdo de um documento é organizado em parágrafos (Paragraph). Dentro de cada parágrafo, os trechos com formatação própria são chamados de runs. Para aplicar negrito, itálico, cor ou tamanho a apenas uma parte do texto, adicione um run:

from docx import Document
from docx.shared import Pt, RGBColor

doc = Document()

p = doc.add_paragraph()
p.add_run("Valor total: ")                      # texto normal
valor = p.add_run("R$ 12.500,00")               # trecho destacado
valor.bold = True
valor.font.size = Pt(14)
valor.font.color.rgb = RGBColor(0x1F, 0x4E, 0x79)  # azul corporativo

# Títulos com níveis de 0 (Title) a 9
doc.add_heading("1. Escopo do serviço", level=2)
doc.add_heading("2. Cronograma", level=2)

A regra prática: forme o texto sempre pelos runs, nunca tente “misturar negrito” em um add_paragraph("texto") único. Um parágrafo pode ter quantos runs forem necessários, cada um com sua formatação.

Fontes, tamanhos e estilo padrão

Para definir uma fonte e um tamanho padrão para o documento inteiro, altere o estilo Normal:

from docx import Document
from docx.shared import Pt

doc = Document()
estilo = doc.styles["Normal"]
estilo.font.name = "Calibri"
estilo.font.size = Pt(11)

# Agora todo add_paragraph() usa Calibri 11 por padrão
doc.add_paragraph("Este texto segue o estilo Normal definido acima.")

Isso evita repetir a fonte em cada parágrafo. Para trocar a fonte de todo o documento de uma vez — útil ao padronizar relatórios com a identidade visual da empresa — mexer no estilo Normal é mais eficiente do que percorrer parágrafo por parágrafo.

Listas com marcadores e numeradas

A python-docx cria listas aplicando estilos prontos. Não há método add_list; usamos add_paragraph com o estilo apropriado:

doc.add_paragraph("Levantamento de requisitos", style="List Bullet")
doc.add_paragraph("Desenvolvimento", style="List Bullet")
doc.add_paragraph("Homologação e entrega", style="List Bullet")

doc.add_paragraph("Primeira parcela na assinatura", style="List Number")
doc.add_paragraph("Segunda parcela na entrega", style="List Number")

List Bullet gera a lista com marcadores; List Number, a lista numerada. Esses estilos existem no modelo padrão do Word; se você partir de um template próprio, confirme que os estilos de lista estão presentes nele.

Tabelas

Tabelas são perfeitas para cronogramas, tabelas de preço e resumos financeiros. Crie com add_table e preencha célula a célula:

from docx import Document

doc = Document()

dados = [
    ("Etapa", "Entrega", "Valor"),
    ("Descoberta", "Semana 2", "R$ 3.000,00"),
    ("Desenvolvimento", "Semana 6", "R$ 6.500,00"),
    ("Implantação", "Semana 8", "R$ 3.000,00"),
]

tabela = doc.add_table(rows=len(dados), cols=3)
tabela.style = "Light Grid Accent 1"   # estilo com bordas e cabeçalho

for i, linha in enumerate(dados):
    for j, valor in enumerate(linha):
        tabela.rows[i].cells[j].text = valor

# Negrito na linha de cabeçalho
for celula in tabela.rows[0].cells:
    for paragrafo in celula.paragraphs:
        for run in paragrafo.runs:
            run.bold = True

O estilo "Light Grid Accent 1" (e outros da família Light Grid / Light List) já vem com o Word e aplica bordas e cores. Preencher a tabela a partir de uma lista de dicionários — por exemplo, lida de uma planilha ou de um banco de dados — é o caminho natural para gerar relatórios dinâmicos.

Inserindo imagens

Adicione logotipos, gráficos ou assinaturas digitalizadas com add_picture:

from docx.shared import Cm

doc.add_picture("logo.png", width=Cm(5))
doc.add_paragraph()  # espaço após a imagem

Sempre defina width ou height para controlar o tamanho; sem isso, a imagem entra na resolução original, podendo ultrapassar a margem da página. Para imagens geradas dinamicamente — um gráfico do matplotlib, por exemplo — salve o PNG em disco com plt.savefig() e depois chame add_picture.

Cabeçalho, rodapé e seções

Cada documento tem uma ou mais seções (Section), e cada seção traz seu próprio cabeçalho e rodapé. É neles que ficam logotipo, nome do documento e numeração:

section = doc.sections[0]

# Cabeçalho
header = section.header
header.is_linked_to_previous = False
hp = header.paragraphs[0]
hp.text = "Minha Empresa Ltda. — CNPJ 00.000.000/0001-00"

# Rodapé
footer = section.footer
fp = footer.paragraphs[0]
fp.text = "Documento confidencial — Proposta Comercial"

Numeração automática de páginas exige inserir um campo de página no rodapé, o que é feito em nível de XML e sai do escopo deste tutorial. Para documentos que precisam desse recurso de forma confiável, o atalho mais produtivo é manter um template .docx já com o rodapé configurado e só preencher o conteúdo via script.

Mini-projeto: gerando contratos a partir de um template

O uso mais valioso da biblioteca é transformar um modelo fixo em dezenas de documentos personalizados. A técnica é abrir o template, substituir marcadores e salvar cada cópia. Veja um gerador de contratos de prestação de serviços:

from docx import Document
from datetime import date

clientes = [
    {"nome": "Ana Souza", "servico": "desenvolvimento de site", "valor": "R$ 8.000,00"},
    {"nome": "Bruno Lima", "servico": "integração de API", "valor": "R$ 4.500,00"},
    {"nome": "Carla Dias", "servico": "automação de relatórios", "valor": "R$ 3.200,00"},
}

hoje = date.today().strftime("%d/%m/%Y")

for cliente in clientes:
    doc = Document("template_contrato.docx")

    for paragrafo in doc.paragraphs:
        # Substitui cada marcador no texto do parágrafo
        texto = paragrafo.text
        texto = texto.replace("{{nome}}", cliente["nome"])
        texto = texto.replace("{{servico}}", cliente["servico"])
        texto = texto.replace("{{valor}}", cliente["valor"])
        texto = texto.replace("{{data}}", hoje)

        # Reescreve preservando o estilo do parágrafo
        for run in paragrafo.runs:
            run.text = ""
        if paragrafo.runs:
            paragrafo.runs[0].text = texto
        else:
            paragrafo.text = texto

    nome_arquivo = f"contrato_{cliente['nome'].split()[0].lower()}.docx"
    doc.save(nome_arquivo)
    print(f"Gerado: {nome_arquivo}")

O modelo template_contrato.docx contém texto normal com marcadores {{nome}}, {{servico}}, {{valor}} e {{data}}. O script gera três contratos prontos, cada um com seus dados. Para volumes maiores ou substituições que envolvam tabelas dinâmicas, a biblioteca docxtpl (que aplica a sintaxe Jinja2 sobre o .docx) é mais robusta e evita o trabalho manual de limpar runs.

Atenção ao detalhe dos runs: quando você digita {{nome}} no Word, ele pode quebrar o texto em vários runs internos (por correção ortográfica, por exemplo). Por isso, ao montar o template, desligue a revisão ou digite os marcadores de uma vez; caso contrário, o replace pode não encontrar o trecho inteiro. A docxtpl lida com isso automaticamente.

Lendo e editando documentos existentes

Abrir um .docx para extrair ou modificar conteúdo é igualmente simples:

from docx import Document

doc = Document("relatorio.docx")

# Listar todos os parágrafos
for paragrafo in doc.paragraphs:
    if paragrafo.text.strip():
        print(paragrafo.text)

# Contar tabelas e ler a primeira
print(f"Total de tabelas: {len(doc.tables)}")
for linha in doc.tables[0].rows:
    print([celula.text for celula in linha.cells])

Essa leitura é a base de pipelines de extração de dados — por exemplo, varrer um diretório de arquivos .docx de notas fiscais ou contratos para alimentar uma planilha ou um banco de dados, sem depender de digitação manual.

Boas práticas

  • Prefira templates a código para o layout. Desenhar cabeçalhos complexos, margens e identidade visual pelo código é trabalhoso; faça isso uma vez no Word, salve como template .docx e use o script só para preencher.
  • Versione seus modelos. Guarde template_v1.docx, template_v2.docx no repositório ao lado do script, para rastrear mudanças de layout.
  • Cuidado com runs quebrados. Ao substituir texto em templates, lembre-se de que o Word divide palavras em vários runs; limpe os runs ou use a docxtpl.
  • Valide antes de enviar. Em automações que enviam o documento por e-mail, abra uma amostra para conferir antes do envio em massa.
  • Use try/except em operações de arquivo. Envolver doc.save() e a leitura de templates em tratamento de erros evita que um arquivo travado interrompa um lote inteiro — veja as práticas do nosso guia de tratamento de erros em Python.

Próximos passos

Para evoluir depois deste tutorial:

  1. Combine documentos e planilhas — leia uma planilha Excel com a lista de clientes e gere um contrato .docx para cada linha.
  2. Exporte para PDF — encadeie a geração do .docx com uma conversão para PDF quando o documento for assinado.
  3. Automatize o envio — conecte a geração do documento ao envio de e-mails com Python para entregar propostas e contratos sem toque manual.

Automatizar a geração de documentos Word troca uma tarefa repetitiva e propensa a erros de digitação por um script de poucas linhas. Comece com um template simples, valide o resultado e expanda para listas, tabelas e geração em lote. Para acompanhar as oportunidades do mercado de desenvolvimento Python no Brasil, confira nossas vagas de Python e o guia de primeiro emprego como programador Python.

E

Equipe Python Brasil

Contribuidor do Python Brasil — Aprenda Python em Português