Excel e documentos de trabalho

Combine planilhas do Excel em uma única tabela com uma coluna de planilha de origem

Combine planilhas que usam as mesmas colunas em uma única tabela do Excel, registrando de qual planilha cada linha veio. Uma pequena pasta de trabalho sintética permite verificar manualmente a ordem das linhas, as contagens e os totais.

Ver o sumário

A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. English

Para quem éEste guia é voltado a pessoas que recebem várias planilhas do Excel com estrutura semelhante e precisam de um único conjunto de dados consolidado sem alterar a pasta de trabalho original.

Preparação
  • Python 3.12 e um comando de terminal que inicie essa versão.
  • openpyxl instalado com python -m pip install openpyxl.
  • Uma pasta de trabalho em que os scripts possam criar pastas dentro de outputs.
  • As planilhas a serem combinadas devem usar os mesmos nomes de coluna na mesma ordem.

01Defina exatamente o que será combinado

O fluxo de trabalho combina planilhas selecionadas de uma pasta de trabalho em uma nova pasta de trabalho. Cada planilha de origem deve ter exatamente a mesma linha de cabeçalho, incluindo a ordem das colunas e o uso de maiúsculas e minúsculas. A saída adiciona source_sheet como primeira coluna para que cada registro preserve sua origem.

Linhas em branco são ignoradas. Linhas não vazias são copiadas na ordem das planilhas e, em seguida, na ordem original das linhas dentro de cada planilha. A pasta de trabalho de origem é aberta em modo somente leitura, e o resultado é gravado em uma pasta separada dentro de outputs.

02Crie uma pasta de trabalho sintética

A pasta de trabalho abaixo é sintética e foi criada especificamente para este artigo. Salve o script de preparação como create_excel_merge_demo.py. Ele cria três planilhas chamadas East, West e Online. Cada uma usa as colunas order_id, customer e amount.

python
from pathlib import Path
from openpyxl import Workbook

SOURCE_DIR = Path("outputs") / "excel_merge_demo"
SOURCE = SOURCE_DIR / "regional_orders.xlsx"
HEADERS = ["order_id", "customer", "amount"]
DATA = {
    "East": [
        ["E001", "Ana", 120],
        ["E002", "Ben", 80],
    ],
    "West": [
        ["W001", "Cara", 150],
        ["W002", "Dan", 70],
    ],
    "Online": [
        ["O001", "Emi", 90],
        [None, None, None],
        ["O002", "Finn", 110],
    ],
}

SOURCE_DIR.parent.mkdir(parents=True, exist_ok=True)
SOURCE_DIR.mkdir()  # Stop if the synthetic source folder already exists.

workbook = Workbook()
for index, (sheet_name, rows) in enumerate(DATA.items()):
    if index == 0:
        sheet = workbook.active
        sheet.title = sheet_name
    else:
        sheet = workbook.create_sheet(sheet_name)
    sheet.append(HEADERS)
    for row in rows:
        sheet.append(row)

workbook.save(SOURCE)
text
python create_excel_merge_demo.py
PlanilhaLinhas de dados não vaziasTotal de amount
East2200
West2220
Online2200

Há 6 registros de dados não vazios no total. Os valores de amount somam 620: 120 + 80 + 150 + 70 + 90 + 110. Online contém deliberadamente uma linha completamente vazia entre seus dois registros para demonstrar que a lógica de combinação ignora linhas em branco.

03Determine manualmente a ordem esperada das linhas combinadas

O script lista explicitamente as planilhas como East, West e Online. Essa ordem determina a ordem dos registros combinados. Dentro de cada planilha, os registros permanecem na ordem original de cima para baixo.

source_sheetorder_idcustomeramount
EastE001Ana120
EastE002Ben80
WestW001Cara150
WestW002Dan70
OnlineO001Emi90
OnlineO002Finn110

A saída, portanto, tem 4 colunas e 7 linhas na planilha quando o cabeçalho é incluído: 1 linha de cabeçalho mais 6 linhas de dados. Os valores de source_sheet também oferecem uma verificação manual útil: East aparece duas vezes, West aparece duas vezes e Online aparece duas vezes.

04Combine as planilhas em uma nova pasta de trabalho

Salve o script a seguir como excel_merge_sheets.py. Ele abre a pasta de trabalho de origem em modo somente leitura, valida os nomes e cabeçalhos das planilhas selecionadas, ignora linhas completamente vazias e rejeita células com fórmulas para que as fórmulas não sejam silenciosamente separadas do contexto da pasta de trabalho original.

python
from pathlib import Path
from openpyxl import Workbook, load_workbook

SOURCE = Path("outputs") / "excel_merge_demo" / "regional_orders.xlsx"
OUTPUT_DIR = Path("outputs") / "excel_merge_result"
OUTPUT = OUTPUT_DIR / "merged_orders.xlsx"
SHEETS = ("East", "West", "Online")
OUTPUT_SHEET = "Combined"
SOURCE_COLUMN = "source_sheet"


def read_sheet_rows(sheet, expected_header=None):
    first_row = next(
        sheet.iter_rows(min_row=1, max_row=1, values_only=True),
        None,
    )
    if first_row is None:
        raise ValueError(f"Sheet is empty: {sheet.title}")

    header = tuple(first_row)
    if any(not isinstance(name, str) or not name.strip() for name in header):
        raise ValueError(f"Invalid header in sheet: {sheet.title}")
    if len(set(header)) != len(header):
        raise ValueError(f"Duplicate header name in sheet: {sheet.title}")
    if SOURCE_COLUMN in header:
        raise ValueError(f"Reserved column already exists: {SOURCE_COLUMN}")
    if expected_header is not None and header != expected_header:
        raise ValueError(f"Header mismatch in sheet: {sheet.title}")

    rows = []
    for excel_row, values in enumerate(
        sheet.iter_rows(min_row=2, max_col=len(header), values_only=True),
        start=2,
    ):
        if all(value is None for value in values):
            continue
        if any(isinstance(value, str) and value.startswith("=") for value in values):
            raise ValueError(
                f"Formula found in {sheet.title} row {excel_row}; "
                "this example merges literal values only."
            )
        rows.append(tuple(values))

    return header, rows


def main() -> None:
    source_path = SOURCE.resolve(strict=True)
    if not source_path.is_file():
        raise ValueError("SOURCE must be an Excel file.")
    if len(set(SHEETS)) != len(SHEETS):
        raise ValueError("SHEETS contains a duplicate sheet name.")

    source_workbook = load_workbook(
        source_path,
        read_only=True,
        data_only=False,
    )
    try:
        missing = [name for name in SHEETS if name not in source_workbook.sheetnames]
        if missing:
            raise ValueError(f"Missing sheets: {missing}")

        expected_header = None
        combined_rows = []
        for sheet_name in SHEETS:
            sheet = source_workbook[sheet_name]
            header, rows = read_sheet_rows(sheet, expected_header)
            if expected_header is None:
                expected_header = header
            for row in rows:
                combined_rows.append((sheet_name, *row))
    finally:
        source_workbook.close()

    OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_DIR.mkdir()  # Refuse to reuse an existing output folder.

    output_workbook = Workbook()
    output_sheet = output_workbook.active
    output_sheet.title = OUTPUT_SHEET
    output_sheet.append([SOURCE_COLUMN, *expected_header])
    for row in combined_rows:
        output_sheet.append(row)
    output_workbook.save(OUTPUT)

    check_workbook = load_workbook(OUTPUT, read_only=True, data_only=False)
    try:
        if check_workbook.sheetnames != [OUTPUT_SHEET]:
            raise RuntimeError("Unexpected output worksheet structure.")
        check_sheet = check_workbook[OUTPUT_SHEET]
        written = list(check_sheet.iter_rows(values_only=True))
    finally:
        check_workbook.close()

    expected = [(SOURCE_COLUMN, *expected_header), *combined_rows]
    if written != expected:
        raise RuntimeError("Saved workbook does not match the expected rows.")

    print(f"Merged {len(SHEETS)} sheets into {len(combined_rows)} data rows.")
    print(f"Verified {len(expected_header) + 1} columns and {len(combined_rows)} data rows.")
    print(f"Output: {OUTPUT.as_posix()}")


if __name__ == "__main__":
    main()
text
python excel_merge_sheets.py

05Compare o resultado esperado

Para a pasta de trabalho sintética, a saída deve conter exatamente uma planilha chamada Combined. Sua primeira linha deve ser source_sheet, order_id, customer, amount, seguida pelos 6 registros calculados anteriormente.

A saída esperada do console abaixo foi derivada manualmente a partir do código e dos dados sintéticos. Não é um log capturado de execução.

text
Merged 3 sheets into 6 data rows.
Verified 4 columns and 6 data rows.
Output: outputs/excel_merge_result/merged_orders.xlsx
VerificaçãoResultado esperado
Planilhas de origem selecionadas3
Linhas de dados combinadas6
Colunas de saída4
Linhas de East2
Linhas de West2
Linhas de Online2
Total de amount620

06Verifique a pasta de trabalho antes de usar dados reais

  1. Abra merged_orders.xlsx e confirme que existe uma única planilha chamada Combined.
  2. Confirme que source_sheet é a primeira coluna e que as três colunas originais vêm em seguida na mesma ordem.
  3. Conte os registros por source_sheet. East, West e Online devem aparecer exatamente duas vezes cada.
  4. Some a coluna amount manualmente ou com o Excel. O total esperado é 620.
  5. Confirme que a linha em branco de Online não se tornou um registro vazio na saída combinada.
  6. Execute o script novamente sem alterar OUTPUT_DIR. Ele deve parar com FileExistsError em vez de substituir o resultado anterior.

Em pastas de trabalho reais, altere SHEETS explicitamente em vez de combinar automaticamente todas as planilhas. Isso reduz a chance de incluir acidentalmente tabelas de consulta, anotações, planilhas ocultas ou planilhas de resumo que estejam armazenadas no mesmo arquivo.

07Reconheça erros comuns

SintomaO que verificar
ModuleNotFoundError: No module named openpyxlInstale openpyxl no mesmo ambiente Python com python -m pip install openpyxl.
FileNotFoundErrorVerifique SOURCE e o diretório de trabalho do terminal. Neste exemplo, execute primeiro a preparação da pasta de trabalho sintética.
Missing sheetsVerifique exatamente os nomes em SHEETS, incluindo espaços e o uso de maiúsculas e minúsculas.
Header mismatchConfirme que todas as planilhas selecionadas usam nomes de coluna idênticos na mesma ordem.
Reserved column already existsRenomeie a coluna de origem chamada source_sheet ou altere SOURCE_COLUMN para um nome que não entre em conflito.
Formula foundDecida como as fórmulas devem ser tratadas antes da combinação. Este tutorial para deliberadamente em vez de copiar expressões de fórmulas sem contexto.
FileExistsErrorA pasta de saída já existe. Revise o resultado anterior e use um novo destino em vez de sobrescrevê-lo.

Se o salvamento ou a verificação falharem depois que OUTPUT_DIR tiver sido criado, a pasta ou a pasta de trabalho poderá permanecer como um resultado parcial. Trate-o como não verificado e use um novo destino depois de corrigir o problema.

08Entenda os limites

O script é destinado a planilhas de dados retangulares com uma única linha de cabeçalho. Ele não lida com cabeçalhos de várias linhas, células de cabeçalho mescladas, tabelas dinâmicas, gráficos, imagens ou planilhas em que várias tabelas não relacionadas compartilham a mesma planilha.

Somente os valores das células são consolidados. Formatos numéricos, fontes, preenchimentos, formatação condicional, regras de validação, hiperlinks, comentários, fórmulas, alturas de linha, larguras de coluna e configurações em nível de planilha não são reproduzidos. Valores literais de data e numéricos podem ser mantidos como valores, mas a formatação de exibição original não é copiada.

O script também carrega todas as tuplas de linhas combinadas na memória do Python antes de criar a pasta de trabalho de saída. Isso é conveniente para este pequeno fluxo de trabalho verificado, mas pode não ser adequado para pastas de trabalho muito grandes. Em arquivos grandes, um projeto de streaming com saída write-only e validação incremental separada reduziria o uso de memória.

Registro de execução e verificação

2026-09-20 · exemplo verificado manualmente · alvo: Python 3.12 · openpyxl necessário · sem execução

  • Foram contados manualmente 2 registros não vazios em East, 2 em West e 2 em Online, totalizando 6 registros combinados.
  • Foram calculados manualmente os totais de amount das planilhas como 200, 220 e 200, resultando em 620 no total.
  • Foi derivada manualmente a ordem esperada da saída: primeiro as linhas de East, depois West e, por fim, Online, com a linha completamente vazia de Online ignorada.
  • O código foi inspecionado para confirmar que a pasta de trabalho de origem é aberta em modo somente leitura e o resultado é gravado em uma pasta outputs separada.
  • Foram inspecionadas as verificações de cabeçalho, a verificação da coluna reservada source_sheet, o tratamento de linhas em branco, a rejeição de fórmulas e a comparação de linhas após o salvamento.
  • Foram derivadas manualmente as 4 colunas de saída, as 6 linhas de dados e as mensagens esperadas do console.
Limites da verificação
  • O código não foi executado pelo autor desta resposta; nenhum arquivo XLSX foi criado nem aberto aqui.
  • Pastas de trabalho com fórmulas, células mescladas, formatação, datas, hiperlinks, planilhas ocultas, arquivos corrompidos e pastas de trabalho muito grandes não foram testados.
  • O comportamento do openpyxl na pasta de trabalho sintética foi inferido a partir do código, mas não foi confirmado em um ambiente de execução Python.
  • As URLs da documentação oficial foram fornecidas com base em locais de documentação conhecidos, mas não foram verificadas ao vivo.

Princípios de redação e verificação de todo o site

Fontes de referência

As explicações e os exemplos são de elaboração própria. Os comportamentos e conceitos relacionados podem ser consultados nas fontes oficiais abaixo.