[{"data":1,"prerenderedAt":1143},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcombine-multiple-excel-files-into-one-python":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcombine-multiple-excel-files-into-one-python":1135},{"id":4,"title":5,"body":6,"description":1128,"extension":1129,"meta":1130,"navigation":147,"path":1131,"seo":1132,"stem":1133,"__hash__":1134},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcombine-multiple-excel-files-into-one-python\u002Findex.md","How to Combine Multiple Excel Files into One in Python",{"type":7,"value":8,"toc":1120},"minimark",[9,13,30,35,63,101,105,572,579,586,856,860,873,927,937,970,988,994,1056,1069,1073,1116],[10,11,5],"h1",{"id":12},"how-to-combine-multiple-excel-files-into-one-in-python",[14,15,16,17,21,22,25,26,29],"p",{},"Use ",[18,19,20],"code",{},"pandas.concat()"," with ",[18,23,24],{},"pathlib"," to batch-read ",[18,27,28],{},".xlsx"," files, align mismatched columns automatically, and export a unified workbook. This approach is the standard for automated reporting pipelines because it handles schema drift, skips empty files, and executes in seconds without manual iteration.",[31,32,34],"h2",{"id":33},"prerequisites","Prerequisites",[36,37,42],"pre",{"className":38,"code":39,"language":40,"meta":41,"style":41},"language-bash shiki shiki-themes github-light github-dark","pip install pandas openpyxl\n","bash","",[18,43,44],{"__ignoreMap":41},[45,46,49,53,57,60],"span",{"class":47,"line":48},"line",1,[45,50,52],{"class":51},"sScJk","pip",[45,54,56],{"class":55},"sZZnC"," install",[45,58,59],{"class":55}," pandas",[45,61,62],{"class":55}," openpyxl\n",[64,65,66,74,84],"ul",{},[67,68,69,73],"li",{},[70,71,72],"strong",{},"Python:"," 3.8+",[67,75,76,79,80,83],{},[70,77,78],{},"pandas:"," 2.0+ (column alignment defaults to name-based; legacy ",[18,81,82],{},"sort=False"," parameter is removed)",[67,85,86,89,90,93,94,96,97,100],{},[70,87,88],{},"Engine:"," ",[18,91,92],{},"openpyxl"," is mandatory for ",[18,95,28],{}," I\u002FO. Convert legacy ",[18,98,99],{},".xls"," files first.",[31,102,104],{"id":103},"primary-method-dataframe-concatenation","Primary Method: DataFrame Concatenation",[36,106,110],{"className":107,"code":108,"language":109,"meta":41,"style":41},"language-python shiki shiki-themes github-light github-dark","import pandas as pd\nfrom pathlib import Path\n\ndef combine_excel_files(source_dir: str, output_path: str) -> None:\n \"\"\"Combine all .xlsx files in a directory into a single workbook.\"\"\"\n source = Path(source_dir)\n # Exclude Excel temporary lock files\n files = [f for f in source.glob(\"*.xlsx\") if not f.name.startswith(\"~$\")]\n \n if not files:\n raise FileNotFoundError(f\"No .xlsx files found in {source_dir}\")\n\n dfs = []\n for file in files:\n try:\n df = pd.read_excel(file, engine=\"openpyxl\")\n if not df.empty:\n dfs.append(df)\n except Exception as e:\n print(f\"Skipping {file.name}: {e}\")\n\n if not dfs:\n raise ValueError(\"No valid data found across provided files.\")\n\n # Aligns columns by name; ignores original row indices\n combined = pd.concat(dfs, ignore_index=True)\n combined.to_excel(output_path, index=False, engine=\"openpyxl\")\n print(f\"Combined {len(dfs)} files into {output_path}\")\n\ncombine_excel_files(\".\u002Fmonthly_reports\", \".\u002Fconsolidated_report.xlsx\")\n","python",[18,111,112,128,142,149,179,185,197,204,248,254,265,298,303,314,329,337,364,374,380,395,431,436,446,461,466,472,493,517,551,556],{"__ignoreMap":41},[45,113,114,118,122,125],{"class":47,"line":48},[45,115,117],{"class":116},"szBVR","import",[45,119,121],{"class":120},"sVt8B"," pandas ",[45,123,124],{"class":116},"as",[45,126,127],{"class":120}," pd\n",[45,129,131,134,137,139],{"class":47,"line":130},2,[45,132,133],{"class":116},"from",[45,135,136],{"class":120}," pathlib ",[45,138,117],{"class":116},[45,140,141],{"class":120}," Path\n",[45,143,145],{"class":47,"line":144},3,[45,146,148],{"emptyLinePlaceholder":147},true,"\n",[45,150,152,155,158,161,165,168,170,173,176],{"class":47,"line":151},4,[45,153,154],{"class":116},"def",[45,156,157],{"class":51}," combine_excel_files",[45,159,160],{"class":120},"(source_dir: ",[45,162,164],{"class":163},"sj4cs","str",[45,166,167],{"class":120},", output_path: ",[45,169,164],{"class":163},[45,171,172],{"class":120},") -> ",[45,174,175],{"class":163},"None",[45,177,178],{"class":120},":\n",[45,180,182],{"class":47,"line":181},5,[45,183,184],{"class":55}," \"\"\"Combine all .xlsx files in a directory into a single workbook.\"\"\"\n",[45,186,188,191,194],{"class":47,"line":187},6,[45,189,190],{"class":120}," source ",[45,192,193],{"class":116},"=",[45,195,196],{"class":120}," Path(source_dir)\n",[45,198,200],{"class":47,"line":199},7,[45,201,203],{"class":202},"sJ8bj"," # Exclude Excel temporary lock files\n",[45,205,207,210,212,215,218,221,224,227,230,233,236,239,242,245],{"class":47,"line":206},8,[45,208,209],{"class":120}," files ",[45,211,193],{"class":116},[45,213,214],{"class":120}," [f ",[45,216,217],{"class":116},"for",[45,219,220],{"class":120}," f ",[45,222,223],{"class":116},"in",[45,225,226],{"class":120}," source.glob(",[45,228,229],{"class":55},"\"*.xlsx\"",[45,231,232],{"class":120},") ",[45,234,235],{"class":116},"if",[45,237,238],{"class":116}," not",[45,240,241],{"class":120}," f.name.startswith(",[45,243,244],{"class":55},"\"~$\"",[45,246,247],{"class":120},")]\n",[45,249,251],{"class":47,"line":250},9,[45,252,253],{"class":120}," \n",[45,255,257,260,262],{"class":47,"line":256},10,[45,258,259],{"class":116}," if",[45,261,238],{"class":116},[45,263,264],{"class":120}," files:\n",[45,266,268,271,274,277,280,283,286,289,292,295],{"class":47,"line":267},11,[45,269,270],{"class":116}," raise",[45,272,273],{"class":163}," FileNotFoundError",[45,275,276],{"class":120},"(",[45,278,279],{"class":116},"f",[45,281,282],{"class":55},"\"No .xlsx files found in ",[45,284,285],{"class":163},"{",[45,287,288],{"class":120},"source_dir",[45,290,291],{"class":163},"}",[45,293,294],{"class":55},"\"",[45,296,297],{"class":120},")\n",[45,299,301],{"class":47,"line":300},12,[45,302,148],{"emptyLinePlaceholder":147},[45,304,306,309,311],{"class":47,"line":305},13,[45,307,308],{"class":120}," dfs ",[45,310,193],{"class":116},[45,312,313],{"class":120}," []\n",[45,315,317,320,324,327],{"class":47,"line":316},14,[45,318,319],{"class":116}," for",[45,321,323],{"class":322},"s4XuR"," file",[45,325,326],{"class":116}," in",[45,328,264],{"class":120},[45,330,332,335],{"class":47,"line":331},15,[45,333,334],{"class":116}," try",[45,336,178],{"class":120},[45,338,340,343,345,348,351,354,357,359,362],{"class":47,"line":339},16,[45,341,342],{"class":120}," df ",[45,344,193],{"class":116},[45,346,347],{"class":120}," pd.read_excel(",[45,349,350],{"class":322},"file",[45,352,353],{"class":120},", ",[45,355,356],{"class":322},"engine",[45,358,193],{"class":116},[45,360,361],{"class":55},"\"openpyxl\"",[45,363,297],{"class":120},[45,365,367,369,371],{"class":47,"line":366},17,[45,368,259],{"class":116},[45,370,238],{"class":116},[45,372,373],{"class":120}," df.empty:\n",[45,375,377],{"class":47,"line":376},18,[45,378,379],{"class":120}," dfs.append(df)\n",[45,381,383,386,389,392],{"class":47,"line":382},19,[45,384,385],{"class":116}," except",[45,387,388],{"class":163}," Exception",[45,390,391],{"class":116}," as",[45,393,394],{"class":120}," e:\n",[45,396,398,401,403,405,408,410,412,415,417,420,422,425,427,429],{"class":47,"line":397},20,[45,399,400],{"class":163}," print",[45,402,276],{"class":120},[45,404,279],{"class":116},[45,406,407],{"class":55},"\"Skipping ",[45,409,285],{"class":163},[45,411,350],{"class":322},[45,413,414],{"class":120},".name",[45,416,291],{"class":163},[45,418,419],{"class":55},": ",[45,421,285],{"class":163},[45,423,424],{"class":120},"e",[45,426,291],{"class":163},[45,428,294],{"class":55},[45,430,297],{"class":120},[45,432,434],{"class":47,"line":433},21,[45,435,148],{"emptyLinePlaceholder":147},[45,437,439,441,443],{"class":47,"line":438},22,[45,440,259],{"class":116},[45,442,238],{"class":116},[45,444,445],{"class":120}," dfs:\n",[45,447,449,451,454,456,459],{"class":47,"line":448},23,[45,450,270],{"class":116},[45,452,453],{"class":163}," ValueError",[45,455,276],{"class":120},[45,457,458],{"class":55},"\"No valid data found across provided files.\"",[45,460,297],{"class":120},[45,462,464],{"class":47,"line":463},24,[45,465,148],{"emptyLinePlaceholder":147},[45,467,469],{"class":47,"line":468},25,[45,470,471],{"class":202}," # Aligns columns by name; ignores original row indices\n",[45,473,475,478,480,483,486,488,491],{"class":47,"line":474},26,[45,476,477],{"class":120}," combined ",[45,479,193],{"class":116},[45,481,482],{"class":120}," pd.concat(dfs, ",[45,484,485],{"class":322},"ignore_index",[45,487,193],{"class":116},[45,489,490],{"class":163},"True",[45,492,297],{"class":120},[45,494,496,499,502,504,507,509,511,513,515],{"class":47,"line":495},27,[45,497,498],{"class":120}," combined.to_excel(output_path, ",[45,500,501],{"class":322},"index",[45,503,193],{"class":116},[45,505,506],{"class":163},"False",[45,508,353],{"class":120},[45,510,356],{"class":322},[45,512,193],{"class":116},[45,514,361],{"class":55},[45,516,297],{"class":120},[45,518,520,522,524,526,529,532,535,537,540,542,545,547,549],{"class":47,"line":519},28,[45,521,400],{"class":163},[45,523,276],{"class":120},[45,525,279],{"class":116},[45,527,528],{"class":55},"\"Combined ",[45,530,531],{"class":163},"{len",[45,533,534],{"class":120},"(dfs)",[45,536,291],{"class":163},[45,538,539],{"class":55}," files into ",[45,541,285],{"class":163},[45,543,544],{"class":120},"output_path",[45,546,291],{"class":163},[45,548,294],{"class":55},[45,550,297],{"class":120},[45,552,554],{"class":47,"line":553},29,[45,555,148],{"emptyLinePlaceholder":147},[45,557,559,562,565,567,570],{"class":47,"line":558},30,[45,560,561],{"class":120},"combine_excel_files(",[45,563,564],{"class":55},"\".\u002Fmonthly_reports\"",[45,566,353],{"class":120},[45,568,569],{"class":55},"\".\u002Fconsolidated_report.xlsx\"",[45,571,297],{"class":120},[31,573,575,576,578],{"id":574},"fallback-raw-cell-append-openpyxl","Fallback: Raw Cell Append (",[18,577,92],{},")",[14,580,581,582,585],{},"When source files contain merged cells, password protection, or heavily inconsistent headers, ",[18,583,584],{},"pandas"," fails to parse correctly. Bypass DataFrame conversion and append rows directly. This preserves raw cell values but sacrifices automatic type casting and column alignment.",[36,587,589],{"className":107,"code":588,"language":109,"meta":41,"style":41},"from openpyxl import load_workbook\nfrom pathlib import Path\n\ndef fallback_combine(source_dir: str, output_path: str) -> None:\n source = Path(source_dir)\n files = [f for f in source.glob(\"*.xlsx\") if not f.name.startswith(\"~$\")]\n if not files:\n return\n\n wb_out = load_workbook(files[0])\n ws_out = wb_out.active\n \n for file in files[1:]:\n wb_in = load_workbook(file, data_only=True)\n ws_in = wb_in.active\n \n # Skip header row (min_row=2), append only non-empty rows\n for row in ws_in.iter_rows(min_row=2, values_only=True):\n if any(cell is not None for cell in row):\n ws_out.append(row)\n \n wb_out.save(output_path)\n",[18,590,591,603,613,617,638,646,676,684,689,693,709,719,723,740,763,773,777,782,814,842,847,851],{"__ignoreMap":41},[45,592,593,595,598,600],{"class":47,"line":48},[45,594,133],{"class":116},[45,596,597],{"class":120}," openpyxl ",[45,599,117],{"class":116},[45,601,602],{"class":120}," load_workbook\n",[45,604,605,607,609,611],{"class":47,"line":130},[45,606,133],{"class":116},[45,608,136],{"class":120},[45,610,117],{"class":116},[45,612,141],{"class":120},[45,614,615],{"class":47,"line":144},[45,616,148],{"emptyLinePlaceholder":147},[45,618,619,621,624,626,628,630,632,634,636],{"class":47,"line":151},[45,620,154],{"class":116},[45,622,623],{"class":51}," fallback_combine",[45,625,160],{"class":120},[45,627,164],{"class":163},[45,629,167],{"class":120},[45,631,164],{"class":163},[45,633,172],{"class":120},[45,635,175],{"class":163},[45,637,178],{"class":120},[45,639,640,642,644],{"class":47,"line":181},[45,641,190],{"class":120},[45,643,193],{"class":116},[45,645,196],{"class":120},[45,647,648,650,652,654,656,658,660,662,664,666,668,670,672,674],{"class":47,"line":187},[45,649,209],{"class":120},[45,651,193],{"class":116},[45,653,214],{"class":120},[45,655,217],{"class":116},[45,657,220],{"class":120},[45,659,223],{"class":116},[45,661,226],{"class":120},[45,663,229],{"class":55},[45,665,232],{"class":120},[45,667,235],{"class":116},[45,669,238],{"class":116},[45,671,241],{"class":120},[45,673,244],{"class":55},[45,675,247],{"class":120},[45,677,678,680,682],{"class":47,"line":199},[45,679,259],{"class":116},[45,681,238],{"class":116},[45,683,264],{"class":120},[45,685,686],{"class":47,"line":206},[45,687,688],{"class":116}," return\n",[45,690,691],{"class":47,"line":250},[45,692,148],{"emptyLinePlaceholder":147},[45,694,695,698,700,703,706],{"class":47,"line":256},[45,696,697],{"class":120}," wb_out ",[45,699,193],{"class":116},[45,701,702],{"class":120}," load_workbook(files[",[45,704,705],{"class":163},"0",[45,707,708],{"class":120},"])\n",[45,710,711,714,716],{"class":47,"line":267},[45,712,713],{"class":120}," ws_out ",[45,715,193],{"class":116},[45,717,718],{"class":120}," wb_out.active\n",[45,720,721],{"class":47,"line":300},[45,722,253],{"class":120},[45,724,725,727,729,731,734,737],{"class":47,"line":305},[45,726,319],{"class":116},[45,728,323],{"class":322},[45,730,326],{"class":116},[45,732,733],{"class":120}," files[",[45,735,736],{"class":163},"1",[45,738,739],{"class":120},":]:\n",[45,741,742,745,747,750,752,754,757,759,761],{"class":47,"line":316},[45,743,744],{"class":120}," wb_in ",[45,746,193],{"class":116},[45,748,749],{"class":120}," load_workbook(",[45,751,350],{"class":322},[45,753,353],{"class":120},[45,755,756],{"class":322},"data_only",[45,758,193],{"class":116},[45,760,490],{"class":163},[45,762,297],{"class":120},[45,764,765,768,770],{"class":47,"line":331},[45,766,767],{"class":120}," ws_in ",[45,769,193],{"class":116},[45,771,772],{"class":120}," wb_in.active\n",[45,774,775],{"class":47,"line":339},[45,776,253],{"class":120},[45,778,779],{"class":47,"line":366},[45,780,781],{"class":202}," # Skip header row (min_row=2), append only non-empty rows\n",[45,783,784,786,789,791,794,797,799,802,804,807,809,811],{"class":47,"line":376},[45,785,319],{"class":116},[45,787,788],{"class":120}," row ",[45,790,223],{"class":116},[45,792,793],{"class":120}," ws_in.iter_rows(",[45,795,796],{"class":322},"min_row",[45,798,193],{"class":116},[45,800,801],{"class":163},"2",[45,803,353],{"class":120},[45,805,806],{"class":322},"values_only",[45,808,193],{"class":116},[45,810,490],{"class":163},[45,812,813],{"class":120},"):\n",[45,815,816,818,821,824,827,829,832,834,837,839],{"class":47,"line":382},[45,817,259],{"class":116},[45,819,820],{"class":163}," any",[45,822,823],{"class":120},"(cell ",[45,825,826],{"class":116},"is",[45,828,238],{"class":116},[45,830,831],{"class":163}," None",[45,833,319],{"class":116},[45,835,836],{"class":120}," cell ",[45,838,223],{"class":116},[45,840,841],{"class":120}," row):\n",[45,843,844],{"class":47,"line":397},[45,845,846],{"class":120}," ws_out.append(row)\n",[45,848,849],{"class":47,"line":433},[45,850,253],{"class":120},[45,852,853],{"class":47,"line":438},[45,854,855],{"class":120}," wb_out.save(output_path)\n",[31,857,859],{"id":858},"pipeline-hardening-troubleshooting","Pipeline Hardening & Troubleshooting",[14,861,862,865,868,869,872],{},[70,863,864],{},"Schema Drift & Column Mismatch",[18,866,867],{},"pd.concat()"," aligns by column name, not position. Missing columns fill with ",[18,870,871],{},"NaN",". Enforce a strict schema post-concatenation:",[36,874,876],{"className":107,"code":875,"language":109,"meta":41,"style":41},"master_cols = [\"date\", \"region\", \"revenue\", \"cost\"]\ncombined = combined.reindex(columns=master_cols)\n",[18,877,878,909],{"__ignoreMap":41},[45,879,880,883,885,888,891,893,896,898,901,903,906],{"class":47,"line":48},[45,881,882],{"class":120},"master_cols ",[45,884,193],{"class":116},[45,886,887],{"class":120}," [",[45,889,890],{"class":55},"\"date\"",[45,892,353],{"class":120},[45,894,895],{"class":55},"\"region\"",[45,897,353],{"class":120},[45,899,900],{"class":55},"\"revenue\"",[45,902,353],{"class":120},[45,904,905],{"class":55},"\"cost\"",[45,907,908],{"class":120},"]\n",[45,910,911,914,916,919,922,924],{"class":47,"line":130},[45,912,913],{"class":120},"combined ",[45,915,193],{"class":116},[45,917,918],{"class":120}," combined.reindex(",[45,920,921],{"class":322},"columns",[45,923,193],{"class":116},[45,925,926],{"class":120},"master_cols)\n",[14,928,929,932,933,936],{},[70,930,931],{},"Data Type Conflicts","\nMixed numeric\u002Fstring columns trigger ",[18,934,935],{},"DtypeWarning",". Suppress with explicit casting:",[36,938,940],{"className":107,"code":939,"language":109,"meta":41,"style":41},"combined = combined.astype({\"revenue\": \"float64\", \"region\": \"string\"})\n",[18,941,942],{"__ignoreMap":41},[45,943,944,946,948,951,953,955,958,960,962,964,967],{"class":47,"line":48},[45,945,913],{"class":120},[45,947,193],{"class":116},[45,949,950],{"class":120}," combined.astype({",[45,952,900],{"class":55},[45,954,419],{"class":120},[45,956,957],{"class":55},"\"float64\"",[45,959,353],{"class":120},[45,961,895],{"class":55},[45,963,419],{"class":120},[45,965,966],{"class":55},"\"string\"",[45,968,969],{"class":120},"})\n",[14,971,972,975,977,978,981,982,987],{},[70,973,974],{},"Performance & Memory Limits",[18,976,584],{}," loads files entirely into RAM. For datasets >2GB or 50+ files, avoid repeated I\u002FO. Process in batches, write to intermediate Parquet files, and export to Excel only at the final step. When building your first reporting pipeline, lock dependency versions in ",[18,979,980],{},"requirements.txt"," and review ",[983,984,986],"a",{"href":985},"\u002Fgetting-started-with-python-excel-automation\u002F","Getting Started with Python Excel Automation"," to configure virtual environments and structured logging that prevents silent data corruption.",[14,989,990,993],{},[70,991,992],{},"Validation & Automation","\nSchedule via cron, GitHub Actions, or Windows Task Scheduler. Validate output programmatically before deployment:",[36,995,997],{"className":107,"code":996,"language":109,"meta":41,"style":41},"expected_rows = sum(pd.read_excel(f, engine=\"openpyxl\").shape[0] for f in files)\nassert combined.shape[0] == expected_rows, \"Row count mismatch detected\"\n",[18,998,999,1035],{"__ignoreMap":41},[45,1000,1001,1004,1006,1009,1012,1014,1016,1018,1021,1023,1026,1028,1030,1032],{"class":47,"line":48},[45,1002,1003],{"class":120},"expected_rows ",[45,1005,193],{"class":116},[45,1007,1008],{"class":163}," sum",[45,1010,1011],{"class":120},"(pd.read_excel(f, ",[45,1013,356],{"class":322},[45,1015,193],{"class":116},[45,1017,361],{"class":55},[45,1019,1020],{"class":120},").shape[",[45,1022,705],{"class":163},[45,1024,1025],{"class":120},"] ",[45,1027,217],{"class":116},[45,1029,220],{"class":120},[45,1031,223],{"class":116},[45,1033,1034],{"class":120}," files)\n",[45,1036,1037,1040,1043,1045,1047,1050,1053],{"class":47,"line":130},[45,1038,1039],{"class":116},"assert",[45,1041,1042],{"class":120}," combined.shape[",[45,1044,705],{"class":163},[45,1046,1025],{"class":120},[45,1048,1049],{"class":116},"==",[45,1051,1052],{"class":120}," expected_rows, ",[45,1054,1055],{"class":55},"\"Row count mismatch detected\"\n",[14,1057,1058,1061,1063,1064,1068],{},[70,1059,1060],{},"Formatting & Template Preservation",[18,1062,584],{}," exports raw data only. To retain conditional formatting, pivot tables, or macros, generate consolidated data first, then inject values into a pre-built template. This aligns with established patterns for ",[983,1065,1067],{"href":1066},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002F","Working with Multiple Excel Sheets in Python"," where sheet-level operations require direct workbook manipulation rather than DataFrame abstraction.",[31,1070,1072],{"id":1071},"deployment-checklist","Deployment Checklist",[64,1074,1077,1089,1098,1104,1110],{"className":1075},[1076],"contains-task-list",[67,1078,1081,1085,1086,1088],{"className":1079},[1080],"task-list-item",[1082,1083],"input",{"disabled":147,"type":1084},"checkbox"," Verify all source files use ",[18,1087,28],{}," extension",[67,1090,1092,1094,1095,1097],{"className":1091},[1080],[1082,1093],{"disabled":147,"type":1084}," Confirm ",[18,1096,92],{}," version matches pandas compatibility matrix",[67,1099,1101,1103],{"className":1100},[1080],[1082,1102],{"disabled":147,"type":1084}," Test with empty files, single-row files, and trailing whitespace",[67,1105,1107,1109],{"className":1106},[1080],[1082,1108],{"disabled":147,"type":1084}," Validate output against expected row count",[67,1111,1113,1115],{"className":1112},[1080],[1082,1114],{"disabled":147,"type":1084}," Add structured logging to track skipped files and dtype warnings",[1117,1118,1119],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":41,"searchDepth":130,"depth":130,"links":1121},[1122,1123,1124,1126,1127],{"id":33,"depth":130,"text":34},{"id":103,"depth":130,"text":104},{"id":574,"depth":130,"text":1125},"Fallback: Raw Cell Append (openpyxl)",{"id":858,"depth":130,"text":859},{"id":1071,"depth":130,"text":1072},"Use pandas.concat() with pathlib to batch-read .xlsx files, align mismatched columns automatically, and export a unified workbook. This approach is the standard for automated reporting pipelines because it handles schema drift, skips empty files, and executes in seconds without manual iteration.","md",{},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcombine-multiple-excel-files-into-one-python",{"title":5,"description":1128},"getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcombine-multiple-excel-files-into-one-python\u002Findex","M7cY39kJA1U_337xkwms8PTF0b3vbYiZJkcB47qsxwM",[1136,1139],{"title":1067,"path":1137,"stem":1138,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Findex",{"title":1140,"path":1141,"stem":1142,"children":-1},"Writing DataFrames to Excel with Pandas","\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Findex",1777830515176]