[{"data":1,"prerenderedAt":2470},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python":2435,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python":2443,"children:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python":2448,"related:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python":2449},{"id":4,"title":5,"body":6,"dateModified":2400,"datePublished":2400,"description":2401,"extension":2402,"faq":2403,"keyword":2412,"kind":2413,"meta":2414,"navigation":122,"path":2423,"seo":2424,"slug":2427,"stem":2428,"topics":2429,"type":2433,"__hash__":2434},"docs\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python\u002Findex.md","Save a Workbook as an Excel Template with Python",{"type":7,"value":8,"toc":2385},"minimark",[9,28,33,61,64,68,740,756,967,971,995,1004,1008,1022,1207,1226,1347,1358,1362,1375,1662,1679,1795,1799,1828,1832,1838,1914,1925,1929,1951,1955,1958,2236,2239,2243,2297,2301,2320,2324,2330,2336,2342,2348,2352,2381],[10,11,12,13,17,18,21,22,27],"p",{},"An Excel template (",[14,15,16],"code",{},".xltx",") is a workbook that opens as an untitled copy: double-click it and Excel creates ",[14,19,20],{},"Book1"," from it, so nobody overwrites the master by accident. Teams use templates for expense claims, order forms and monthly packs that people fill in by hand. Generating those templates from Python keeps them consistent — the same styles, dropdown lists and protected formulas in every version — and the reverse direction, filling a template into a finished report, is just as common. Both directions have one trap: a template is not a workbook with a different extension, and a mismatch between the extension and the file's internal content type gives you a file Excel will not open. This page belongs to ",[23,24,26],"a",{"href":25},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002F","Handling Excel File Formats and Conversions",".",[29,30,32],"h2",{"id":31},"prerequisites","Prerequisites",[34,35,40],"pre",{"className":36,"code":37,"language":38,"meta":39,"style":39},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pandas\n","bash","",[14,41,42],{"__ignoreMap":39},[43,44,47,51,55,58],"span",{"class":45,"line":46},"line",1,[43,48,50],{"class":49},"sMTad","pip",[43,52,54],{"class":53},"srMev"," install",[43,56,57],{"class":53}," openpyxl",[43,59,60],{"class":53}," pandas\n",[10,62,63],{},"openpyxl 3.1 was used for every example. Nothing here needs Excel; the files open in Excel, LibreOffice and other spreadsheet programs.",[29,65,67],{"id":66},"step-1-build-the-workbook-the-template-should-contain","Step 1 — Build the workbook the template should contain",[34,69,73],{"className":70,"code":71,"language":72,"meta":39,"style":39},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.styles import Font, PatternFill, Alignment, Protection\nfrom openpyxl.worksheet.datavalidation import DataValidation\n\nwb = Workbook()\nws = wb.active\nws.title = \"Expenses\"\nws[\"A1\"] = \"Expense claim\"\nws[\"A1\"].font = Font(size=16, bold=True, color=\"4338CA\")\nws.append([])\nws.append([\"Date\", \"Category\", \"Description\", \"Amount\"])\nfor c in ws[3]:\n    c.font = Font(bold=True, color=\"FFFFFF\")\n    c.fill = PatternFill(\"solid\", fgColor=\"4338CA\")\n    c.alignment = Alignment(horizontal=\"center\")\nfor r in range(4, 24):\n    for col in \"ABCD\":\n        ws[f\"{col}{r}\"].protection = Protection(locked=False)\n    ws[f\"A{r}\"].number_format = \"dd mmm yyyy\"\n    ws[f\"D{r}\"].number_format = \"#,##0.00\"\nws[\"C24\"], ws[\"D24\"] = \"Total\", \"=SUM(D4:D23)\"\nws[\"C24\"].font = ws[\"D24\"].font = Font(bold=True)\nws[\"D24\"].number_format = \"#,##0.00\"\ndv = DataValidation(type=\"list\", formula1='\"Travel,Meals,Lodging,Supplies\"', allow_blank=True)\nws.add_data_validation(dv)\ndv.add(\"B4:B23\")\nfor col, width in zip(\"ABCD\", (14, 14, 36, 12)):\n    ws.column_dimensions[col].width = width\nws.freeze_panes = \"A4\"\nws.protection.sheet = True\nws.print_area = \"A1:D24\"\nprint(\"input cells unlocked:\", not ws[\"B4\"].protection.locked, \"| total locked:\", ws[\"D24\"].protection.locked)\n","python",[14,74,75,91,104,117,124,136,147,158,175,224,230,257,278,305,330,351,378,395,443,470,495,521,550,563,603,609,620,661,672,683,694,705],{"__ignoreMap":39},[43,76,77,81,85,88],{"class":45,"line":46},[43,78,80],{"class":79},"s-kum","from",[43,82,84],{"class":83},"skGVy"," openpyxl ",[43,86,87],{"class":79},"import",[43,89,90],{"class":83}," Workbook\n",[43,92,94,96,99,101],{"class":45,"line":93},2,[43,95,80],{"class":79},[43,97,98],{"class":83}," openpyxl.styles ",[43,100,87],{"class":79},[43,102,103],{"class":83}," Font, PatternFill, Alignment, Protection\n",[43,105,107,109,112,114],{"class":45,"line":106},3,[43,108,80],{"class":79},[43,110,111],{"class":83}," openpyxl.worksheet.datavalidation ",[43,113,87],{"class":79},[43,115,116],{"class":83}," DataValidation\n",[43,118,120],{"class":45,"line":119},4,[43,121,123],{"emptyLinePlaceholder":122},true,"\n",[43,125,127,130,133],{"class":45,"line":126},5,[43,128,129],{"class":83},"wb ",[43,131,132],{"class":79},"=",[43,134,135],{"class":83}," Workbook()\n",[43,137,139,142,144],{"class":45,"line":138},6,[43,140,141],{"class":83},"ws ",[43,143,132],{"class":79},[43,145,146],{"class":83}," wb.active\n",[43,148,150,153,155],{"class":45,"line":149},7,[43,151,152],{"class":83},"ws.title ",[43,154,132],{"class":79},[43,156,157],{"class":53}," \"Expenses\"\n",[43,159,161,164,167,170,172],{"class":45,"line":160},8,[43,162,163],{"class":83},"ws[",[43,165,166],{"class":53},"\"A1\"",[43,168,169],{"class":83},"] ",[43,171,132],{"class":79},[43,173,174],{"class":53}," \"Expense claim\"\n",[43,176,178,180,182,185,187,190,194,196,200,203,206,208,211,213,216,218,221],{"class":45,"line":177},9,[43,179,163],{"class":83},[43,181,166],{"class":53},[43,183,184],{"class":83},"].font ",[43,186,132],{"class":79},[43,188,189],{"class":83}," Font(",[43,191,193],{"class":192},"sa561","size",[43,195,132],{"class":79},[43,197,199],{"class":198},"sP0c6","16",[43,201,202],{"class":83},", ",[43,204,205],{"class":192},"bold",[43,207,132],{"class":79},[43,209,210],{"class":198},"True",[43,212,202],{"class":83},[43,214,215],{"class":192},"color",[43,217,132],{"class":79},[43,219,220],{"class":53},"\"4338CA\"",[43,222,223],{"class":83},")\n",[43,225,227],{"class":45,"line":226},10,[43,228,229],{"class":83},"ws.append([])\n",[43,231,233,236,239,241,244,246,249,251,254],{"class":45,"line":232},11,[43,234,235],{"class":83},"ws.append([",[43,237,238],{"class":53},"\"Date\"",[43,240,202],{"class":83},[43,242,243],{"class":53},"\"Category\"",[43,245,202],{"class":83},[43,247,248],{"class":53},"\"Description\"",[43,250,202],{"class":83},[43,252,253],{"class":53},"\"Amount\"",[43,255,256],{"class":83},"])\n",[43,258,260,263,266,269,272,275],{"class":45,"line":259},12,[43,261,262],{"class":79},"for",[43,264,265],{"class":83}," c ",[43,267,268],{"class":79},"in",[43,270,271],{"class":83}," ws[",[43,273,274],{"class":198},"3",[43,276,277],{"class":83},"]:\n",[43,279,281,284,286,288,290,292,294,296,298,300,303],{"class":45,"line":280},13,[43,282,283],{"class":83},"    c.font ",[43,285,132],{"class":79},[43,287,189],{"class":83},[43,289,205],{"class":192},[43,291,132],{"class":79},[43,293,210],{"class":198},[43,295,202],{"class":83},[43,297,215],{"class":192},[43,299,132],{"class":79},[43,301,302],{"class":53},"\"FFFFFF\"",[43,304,223],{"class":83},[43,306,308,311,313,316,319,321,324,326,328],{"class":45,"line":307},14,[43,309,310],{"class":83},"    c.fill ",[43,312,132],{"class":79},[43,314,315],{"class":83}," PatternFill(",[43,317,318],{"class":53},"\"solid\"",[43,320,202],{"class":83},[43,322,323],{"class":192},"fgColor",[43,325,132],{"class":79},[43,327,220],{"class":53},[43,329,223],{"class":83},[43,331,333,336,338,341,344,346,349],{"class":45,"line":332},15,[43,334,335],{"class":83},"    c.alignment ",[43,337,132],{"class":79},[43,339,340],{"class":83}," Alignment(",[43,342,343],{"class":192},"horizontal",[43,345,132],{"class":79},[43,347,348],{"class":53},"\"center\"",[43,350,223],{"class":83},[43,352,354,356,359,361,364,367,370,372,375],{"class":45,"line":353},16,[43,355,262],{"class":79},[43,357,358],{"class":83}," r ",[43,360,268],{"class":79},[43,362,363],{"class":198}," range",[43,365,366],{"class":83},"(",[43,368,369],{"class":198},"4",[43,371,202],{"class":83},[43,373,374],{"class":198},"24",[43,376,377],{"class":83},"):\n",[43,379,381,384,387,389,392],{"class":45,"line":380},17,[43,382,383],{"class":79},"    for",[43,385,386],{"class":83}," col ",[43,388,268],{"class":79},[43,390,391],{"class":53}," \"ABCD\"",[43,393,394],{"class":83},":\n",[43,396,398,401,404,407,411,414,417,420,423,425,428,430,433,436,438,441],{"class":45,"line":397},18,[43,399,400],{"class":83},"        ws[",[43,402,403],{"class":79},"f",[43,405,406],{"class":53},"\"",[43,408,410],{"class":409},"sSjpA","{",[43,412,413],{"class":83},"col",[43,415,416],{"class":409},"}{",[43,418,419],{"class":83},"r",[43,421,422],{"class":409},"}",[43,424,406],{"class":53},[43,426,427],{"class":83},"].protection ",[43,429,132],{"class":79},[43,431,432],{"class":83}," Protection(",[43,434,435],{"class":192},"locked",[43,437,132],{"class":79},[43,439,440],{"class":198},"False",[43,442,223],{"class":83},[43,444,446,449,451,454,456,458,460,462,465,467],{"class":45,"line":445},19,[43,447,448],{"class":83},"    ws[",[43,450,403],{"class":79},[43,452,453],{"class":53},"\"A",[43,455,410],{"class":409},[43,457,419],{"class":83},[43,459,422],{"class":409},[43,461,406],{"class":53},[43,463,464],{"class":83},"].number_format ",[43,466,132],{"class":79},[43,468,469],{"class":53}," \"dd mmm yyyy\"\n",[43,471,473,475,477,480,482,484,486,488,490,492],{"class":45,"line":472},20,[43,474,448],{"class":83},[43,476,403],{"class":79},[43,478,479],{"class":53},"\"D",[43,481,410],{"class":409},[43,483,419],{"class":83},[43,485,422],{"class":409},[43,487,406],{"class":53},[43,489,464],{"class":83},[43,491,132],{"class":79},[43,493,494],{"class":53}," \"#,##0.00\"\n",[43,496,498,500,503,506,509,511,513,516,518],{"class":45,"line":497},21,[43,499,163],{"class":83},[43,501,502],{"class":53},"\"C24\"",[43,504,505],{"class":83},"], ws[",[43,507,508],{"class":53},"\"D24\"",[43,510,169],{"class":83},[43,512,132],{"class":79},[43,514,515],{"class":53}," \"Total\"",[43,517,202],{"class":83},[43,519,520],{"class":53},"\"=SUM(D4:D23)\"\n",[43,522,524,526,528,530,532,534,536,538,540,542,544,546,548],{"class":45,"line":523},22,[43,525,163],{"class":83},[43,527,502],{"class":53},[43,529,184],{"class":83},[43,531,132],{"class":79},[43,533,271],{"class":83},[43,535,508],{"class":53},[43,537,184],{"class":83},[43,539,132],{"class":79},[43,541,189],{"class":83},[43,543,205],{"class":192},[43,545,132],{"class":79},[43,547,210],{"class":198},[43,549,223],{"class":83},[43,551,553,555,557,559,561],{"class":45,"line":552},23,[43,554,163],{"class":83},[43,556,508],{"class":53},[43,558,464],{"class":83},[43,560,132],{"class":79},[43,562,494],{"class":53},[43,564,566,569,571,574,577,579,582,584,587,589,592,594,597,599,601],{"class":45,"line":565},24,[43,567,568],{"class":83},"dv ",[43,570,132],{"class":79},[43,572,573],{"class":83}," DataValidation(",[43,575,576],{"class":192},"type",[43,578,132],{"class":79},[43,580,581],{"class":53},"\"list\"",[43,583,202],{"class":83},[43,585,586],{"class":192},"formula1",[43,588,132],{"class":79},[43,590,591],{"class":53},"'\"Travel,Meals,Lodging,Supplies\"'",[43,593,202],{"class":83},[43,595,596],{"class":192},"allow_blank",[43,598,132],{"class":79},[43,600,210],{"class":198},[43,602,223],{"class":83},[43,604,606],{"class":45,"line":605},25,[43,607,608],{"class":83},"ws.add_data_validation(dv)\n",[43,610,612,615,618],{"class":45,"line":611},26,[43,613,614],{"class":83},"dv.add(",[43,616,617],{"class":53},"\"B4:B23\"",[43,619,223],{"class":83},[43,621,623,625,628,630,633,635,638,641,644,646,648,650,653,655,658],{"class":45,"line":622},27,[43,624,262],{"class":79},[43,626,627],{"class":83}," col, width ",[43,629,268],{"class":79},[43,631,632],{"class":198}," zip",[43,634,366],{"class":83},[43,636,637],{"class":53},"\"ABCD\"",[43,639,640],{"class":83},", (",[43,642,643],{"class":198},"14",[43,645,202],{"class":83},[43,647,643],{"class":198},[43,649,202],{"class":83},[43,651,652],{"class":198},"36",[43,654,202],{"class":83},[43,656,657],{"class":198},"12",[43,659,660],{"class":83},")):\n",[43,662,664,667,669],{"class":45,"line":663},28,[43,665,666],{"class":83},"    ws.column_dimensions[col].width ",[43,668,132],{"class":79},[43,670,671],{"class":83}," width\n",[43,673,675,678,680],{"class":45,"line":674},29,[43,676,677],{"class":83},"ws.freeze_panes ",[43,679,132],{"class":79},[43,681,682],{"class":53}," \"A4\"\n",[43,684,686,689,691],{"class":45,"line":685},30,[43,687,688],{"class":83},"ws.protection.sheet ",[43,690,132],{"class":79},[43,692,693],{"class":198}," True\n",[43,695,697,700,702],{"class":45,"line":696},31,[43,698,699],{"class":83},"ws.print_area ",[43,701,132],{"class":79},[43,703,704],{"class":53}," \"A1:D24\"\n",[43,706,708,711,713,716,718,721,723,726,729,732,735,737],{"class":45,"line":707},32,[43,709,710],{"class":198},"print",[43,712,366],{"class":83},[43,714,715],{"class":53},"\"input cells unlocked:\"",[43,717,202],{"class":83},[43,719,720],{"class":79},"not",[43,722,271],{"class":83},[43,724,725],{"class":53},"\"B4\"",[43,727,728],{"class":83},"].protection.locked, ",[43,730,731],{"class":53},"\"| total locked:\"",[43,733,734],{"class":83},", ws[",[43,736,508],{"class":53},[43,738,739],{"class":83},"].protection.locked)\n",[10,741,742,743,746,747,751,752,27],{},"The sheet has a title, a styled header row, twenty input rows whose cells are unlocked, a dropdown on the category column, a locked ",[14,744,745],{},"SUM"," total and sheet protection, so people can type into the input area but cannot break the formula. These are ordinary openpyxl features, covered in ",[23,748,750],{"href":749},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Flock-cells-and-protect-a-sheet-with-openpyxl\u002F","Lock Cells and Protect a Sheet with openpyxl"," and ",[23,753,755],{"href":754},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fadd-dropdown-data-validation-to-excel-with-openpyxl\u002F","Add Dropdown Data Validation to Excel with openpyxl",[757,758,766,767,766,771,766,775,766,783,766,791,766,795,766,802,766,807,766,812,766,816,766,822,766,826,766,830,766,834,766,838,766,842,766,846,766,849,766,854,766,856,766,860,766,862,766,864,766,866,766,869,766,873,766,875,766,877,766,879,766,881,766,884,766,887,766,892,766,895,766,897,766,900,766,902,766,905,766,907,766,910,766,913,766,916,766,918,766,920,766,924,766,926,766,928,766,931,766,935,766,937,766,939,766,941,766,943,766,946,766,950,766,952,766,954,766,958,766,962,766,964],"svg",{"viewBox":759,"role":760,"ariaLabelledBy":761,"xmlns":764,"style":765},"178 0 404 252","img",[762,763],"tp-layout-t","tp-layout-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:404px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[768,769,770],"title",{"id":762},"The expense claim the template contains",[772,773,774],"desc",{"id":763},"A sheet titled Expense claim with a coloured header row of Date, Category, Description and Amount, empty unlocked input rows with a dropdown on Category, and a locked Total row with a SUM formula.",[776,777],"rect",{"x":778,"y":779,"width":780,"height":781,"fill":782},"178","0","404","252","#ffffff",[776,784],{"x":785,"y":786,"width":787,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},"194.0","16.0","372.0","28.0","var(--line,#cdd5e6)","stroke-width:1px",[776,792],{"x":785,"y":786,"width":793,"height":788,"rx":779,"fill":794,"stroke":789,"style":790},"40.0","#e7ebef",[796,797,801],"text",{"x":798,"y":799,"style":800},"214.0","35.0","font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","fx",[796,803,806],{"x":804,"y":799,"style":805},"244.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","=SUM(D4:D23)",[776,808],{"x":785,"y":809,"width":810,"height":811,"rx":779,"fill":794,"stroke":789,"style":790},"44.0","32.0","24.0",[776,813],{"x":814,"y":809,"width":815,"height":811,"rx":779,"fill":794,"stroke":789,"style":790},"226.0","105.5",[796,817,821],{"x":818,"y":819,"style":820},"278.8","61.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[776,823],{"x":824,"y":809,"width":825,"height":811,"rx":779,"fill":794,"stroke":789,"style":790},"331.5","76.4",[796,827,829],{"x":828,"y":819,"style":820},"369.7","B",[776,831],{"x":832,"y":809,"width":833,"height":811,"rx":779,"fill":794,"stroke":789,"style":790},"407.9","89.2",[796,835,837],{"x":836,"y":819,"style":820},"452.5","C",[776,839],{"x":840,"y":809,"width":841,"height":811,"rx":779,"fill":794,"stroke":789,"style":790},"497.1","68.9",[796,843,845],{"x":844,"y":819,"style":820},"531.5","D",[776,847],{"x":785,"y":848,"width":810,"height":788,"rx":779,"fill":794,"stroke":789,"style":790},"68.0",[796,850,853],{"x":851,"y":852,"style":820},"210.0","87.0","1",[776,855],{"x":814,"y":848,"width":815,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[796,857,859],{"x":818,"y":852,"style":858},"font-size:11px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Expense claim",[776,861],{"x":824,"y":848,"width":825,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,863],{"x":832,"y":848,"width":833,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,865],{"x":840,"y":848,"width":841,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,867],{"x":785,"y":868,"width":810,"height":788,"rx":779,"fill":794,"stroke":789,"style":790},"96.0",[796,870,872],{"x":851,"y":871,"style":820},"115.0","2",[776,874],{"x":814,"y":868,"width":815,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,876],{"x":824,"y":868,"width":825,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,878],{"x":832,"y":868,"width":833,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,880],{"x":840,"y":868,"width":841,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,882],{"x":785,"y":883,"width":810,"height":788,"rx":779,"fill":794,"stroke":789,"style":790},"124.0",[796,885,274],{"x":851,"y":886,"style":820},"143.0",[776,888],{"x":814,"y":883,"width":815,"height":788,"rx":779,"fill":889,"stroke":890,"style":891},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[796,893,894],{"x":818,"y":886,"style":858},"Date",[776,896],{"x":824,"y":883,"width":825,"height":788,"rx":779,"fill":889,"stroke":890,"style":891},[796,898,899],{"x":828,"y":886,"style":858},"Category",[776,901],{"x":832,"y":883,"width":833,"height":788,"rx":779,"fill":889,"stroke":890,"style":891},[796,903,904],{"x":836,"y":886,"style":858},"Description",[776,906],{"x":840,"y":883,"width":841,"height":788,"rx":779,"fill":889,"stroke":890,"style":891},[796,908,909],{"x":844,"y":886,"style":858},"Amount",[776,911],{"x":785,"y":912,"width":810,"height":788,"rx":779,"fill":794,"stroke":789,"style":790},"152.0",[796,914,369],{"x":851,"y":915,"style":820},"171.0",[776,917],{"x":814,"y":912,"width":815,"height":788,"rx":779,"fill":782,"stroke":789,"style":891},[776,919],{"x":824,"y":912,"width":825,"height":788,"rx":779,"fill":782,"stroke":789,"style":891},[796,921,923],{"x":828,"y":915,"style":922},"font-size:11px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Travel ▾",[776,925],{"x":832,"y":912,"width":833,"height":788,"rx":779,"fill":782,"stroke":789,"style":891},[776,927],{"x":840,"y":912,"width":841,"height":788,"rx":779,"fill":782,"stroke":789,"style":891},[776,929],{"x":785,"y":930,"width":810,"height":788,"rx":779,"fill":794,"stroke":789,"style":790},"180.0",[796,932,934],{"x":851,"y":933,"style":820},"199.0","5",[776,936],{"x":814,"y":930,"width":815,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,938],{"x":824,"y":930,"width":825,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,940],{"x":832,"y":930,"width":833,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,942],{"x":840,"y":930,"width":841,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,944],{"x":785,"y":945,"width":810,"height":788,"rx":779,"fill":794,"stroke":789,"style":790},"208.0",[796,947,949],{"x":851,"y":948,"style":820},"227.0","6",[776,951],{"x":814,"y":945,"width":815,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,953],{"x":824,"y":945,"width":825,"height":788,"rx":779,"fill":782,"stroke":789,"style":790},[776,955],{"x":832,"y":945,"width":833,"height":788,"rx":779,"fill":956,"stroke":957,"style":891},"#d9f4f1","var(--teal,#0f9488)",[796,959,961],{"x":836,"y":948,"style":960},"font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Total",[776,963],{"x":840,"y":945,"width":841,"height":788,"rx":779,"fill":956,"stroke":957,"style":891},[796,965,966],{"x":844,"y":948,"style":960},"0.00",[29,968,970],{"id":969},"step-2-save-it-as-a-real-template","Step 2 — Save it as a real template",[34,972,974],{"className":70,"code":973,"language":72,"meta":39,"style":39},"wb.template = True\nwb.save(\"expense_claim.xltx\")\n",[14,975,976,985],{"__ignoreMap":39},[43,977,978,981,983],{"class":45,"line":46},[43,979,980],{"class":83},"wb.template ",[43,982,132],{"class":79},[43,984,693],{"class":198},[43,986,987,990,993],{"class":45,"line":93},[43,988,989],{"class":83},"wb.save(",[43,991,992],{"class":53},"\"expense_claim.xltx\"",[43,994,223],{"class":83},[10,996,997,1000,1001,1003],{},[14,998,999],{},"wb.template = True"," is the line that matters. It changes the content type openpyxl writes for the workbook part from the normal workbook type to the template type — the declaration Excel reads to decide whether to open the file as an untitled copy. Saving to a name ending in ",[14,1002,16],{}," without setting the flag produces a file whose extension says \"template\" and whose contents say \"workbook\".",[29,1005,1007],{"id":1006},"step-3-check-the-content-type-inside-the-file","Step 3 — Check the content type inside the file",[10,1009,1010,1011,1013,1014,1017,1018,1021],{},"An ",[14,1012,16],{}," is a ZIP package like an ",[14,1015,1016],{},".xlsx","; the difference is one line of ",[14,1019,1020],{},"[Content_Types].xml",". A tiny check makes the difference visible — and is worth keeping in a test:",[34,1023,1025],{"className":70,"code":1024,"language":72,"meta":39,"style":39},"import zipfile\n\ndef workbook_content_type(path):\n    xml = zipfile.ZipFile(path).read(\"[Content_Types].xml\").decode()\n    start = xml.index('PartName=\"\u002Fxl\u002Fworkbook.xml\"')\n    content_type = xml[start:].split('ContentType=\"', 1)[1].split('\"', 1)[0]\n    return content_type.split(\"spreadsheetml.\", 1)[1]\n\nWorkbook().save(\"renamed_only.xltx\")\nfor f in (\"expense_claim.xltx\", \"renamed_only.xltx\"):\n    print(f\"{f:20} {workbook_content_type(f)}\")\n",[14,1026,1027,1034,1038,1050,1066,1081,1120,1141,1145,1155,1175],{"__ignoreMap":39},[43,1028,1029,1031],{"class":45,"line":46},[43,1030,87],{"class":79},[43,1032,1033],{"class":83}," zipfile\n",[43,1035,1036],{"class":45,"line":93},[43,1037,123],{"emptyLinePlaceholder":122},[43,1039,1040,1043,1047],{"class":45,"line":106},[43,1041,1042],{"class":79},"def",[43,1044,1046],{"class":1045},"s_Opv"," workbook_content_type",[43,1048,1049],{"class":83},"(path):\n",[43,1051,1052,1055,1057,1060,1063],{"class":45,"line":119},[43,1053,1054],{"class":83},"    xml ",[43,1056,132],{"class":79},[43,1058,1059],{"class":83}," zipfile.ZipFile(path).read(",[43,1061,1062],{"class":53},"\"[Content_Types].xml\"",[43,1064,1065],{"class":83},").decode()\n",[43,1067,1068,1071,1073,1076,1079],{"class":45,"line":126},[43,1069,1070],{"class":83},"    start ",[43,1072,132],{"class":79},[43,1074,1075],{"class":83}," xml.index(",[43,1077,1078],{"class":53},"'PartName=\"\u002Fxl\u002Fworkbook.xml\"'",[43,1080,223],{"class":83},[43,1082,1083,1086,1088,1091,1094,1096,1098,1101,1103,1106,1109,1111,1113,1115,1117],{"class":45,"line":138},[43,1084,1085],{"class":83},"    content_type ",[43,1087,132],{"class":79},[43,1089,1090],{"class":83}," xml[start:].split(",[43,1092,1093],{"class":53},"'ContentType=\"'",[43,1095,202],{"class":83},[43,1097,853],{"class":198},[43,1099,1100],{"class":83},")[",[43,1102,853],{"class":198},[43,1104,1105],{"class":83},"].split(",[43,1107,1108],{"class":53},"'\"'",[43,1110,202],{"class":83},[43,1112,853],{"class":198},[43,1114,1100],{"class":83},[43,1116,779],{"class":198},[43,1118,1119],{"class":83},"]\n",[43,1121,1122,1125,1128,1131,1133,1135,1137,1139],{"class":45,"line":149},[43,1123,1124],{"class":79},"    return",[43,1126,1127],{"class":83}," content_type.split(",[43,1129,1130],{"class":53},"\"spreadsheetml.\"",[43,1132,202],{"class":83},[43,1134,853],{"class":198},[43,1136,1100],{"class":83},[43,1138,853],{"class":198},[43,1140,1119],{"class":83},[43,1142,1143],{"class":45,"line":160},[43,1144,123],{"emptyLinePlaceholder":122},[43,1146,1147,1150,1153],{"class":45,"line":177},[43,1148,1149],{"class":83},"Workbook().save(",[43,1151,1152],{"class":53},"\"renamed_only.xltx\"",[43,1154,223],{"class":83},[43,1156,1157,1159,1162,1164,1167,1169,1171,1173],{"class":45,"line":226},[43,1158,262],{"class":79},[43,1160,1161],{"class":83}," f ",[43,1163,268],{"class":79},[43,1165,1166],{"class":83}," (",[43,1168,992],{"class":53},[43,1170,202],{"class":83},[43,1172,1152],{"class":53},[43,1174,377],{"class":83},[43,1176,1177,1180,1182,1184,1186,1188,1190,1193,1195,1198,1201,1203,1205],{"class":45,"line":232},[43,1178,1179],{"class":198},"    print",[43,1181,366],{"class":83},[43,1183,403],{"class":79},[43,1185,406],{"class":53},[43,1187,410],{"class":409},[43,1189,403],{"class":83},[43,1191,1192],{"class":79},":20",[43,1194,422],{"class":409},[43,1196,1197],{"class":409}," {",[43,1199,1200],{"class":83},"workbook_content_type(f)",[43,1202,422],{"class":409},[43,1204,406],{"class":53},[43,1206,223],{"class":83},[10,1208,1209,1210,1213,1214,1217,1218,1221,1222,27],{},"The real template reports ",[14,1211,1212],{},"template.main+xml","; the renamed file reports ",[14,1215,1216],{},"sheet.main+xml",", the ordinary workbook type (the helper prints the part after ",[14,1219,1220],{},"application\u002Fvnd.openxmlformats-officedocument.spreadsheetml.","). Nothing else in the two packages needs to differ — the sheets, styles and relationships are identical. Excel compares the extension with this type and refuses mismatches with \"Excel cannot open the file because the file format or file extension is not valid\" — the error explained in ",[23,1223,1225],{"href":1224},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-file-format-or-extension-is-not-valid\u002F","Fix \"Excel Cannot Open the File Because the File Format or Extension Is Not Valid\"",[757,1227,766,1233,766,1236,766,1239,766,1243,766,1249,766,1260,766,1265,766,1270,766,1276,766,1282,766,1286,766,1291,766,1295,766,1300,766,1305,766,1312,766,1317,766,1321,766,1325,766,1331,766,1334,766,1339,766,1342],{"viewBox":1228,"role":760,"ariaLabelledBy":1229,"xmlns":764,"style":1232},"4 0 752 207",[1230,1231],"tp-types-t","tp-types-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[768,1234,1235],{"id":1230},"Same extension, different content types",[772,1237,1238],{"id":1231},"A file saved with workbook.template set to True declares the template content type and opens in Excel as an untitled copy. A workbook merely saved with an xltx name declares the normal workbook type, and Excel refuses to open it because the extension and content do not match.",[776,1240],{"x":369,"y":779,"width":1241,"height":1242,"fill":782},"752","207",[776,1244],{"x":1245,"y":786,"width":1246,"height":1247,"rx":643,"fill":956,"stroke":957,"style":1248},"20.0","351.0","175.0","stroke-width:2px",[1250,1251,1253],"g",{"transform":1252},"translate(182.5,29.0) scale(1.083)",[1254,1255],"path",{"d":1256,"fill":1257,"stroke":1258,"style":1259},"M5 12.5l4.5 4.5L19 7","none","var(--teal-ink,#0b6157)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[796,1261,999],{"x":1262,"y":1263,"style":1264},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[45,1266],{"x1":1267,"y1":1268,"x2":1269,"y2":1268,"stroke":957,"style":790},"36.0","91.0","355.0",[796,1271,1275],{"x":1272,"y":1273,"style":1274},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","content type: spreadsheetml.template.main",[1250,1277,1279],{"transform":1278},"translate(38.5,99.5) scale(0.625)",[1254,1280],{"d":1256,"fill":1257,"stroke":957,"style":1281},"stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[796,1283,1285],{"x":1272,"y":1284,"style":1274},"137.0","opens as an untitled copy",[1250,1287,1289],{"transform":1288},"translate(38.5,125.5) scale(0.625)",[1254,1290],{"d":1256,"fill":1257,"stroke":957,"style":1281},[796,1292,1294],{"x":1272,"y":1293,"style":1274},"163.0","the master cannot be overwritten by Save",[1250,1296,1298],{"transform":1297},"translate(38.5,151.5) scale(0.625)",[1254,1299],{"d":1256,"fill":1257,"stroke":957,"style":1281},[776,1301],{"x":1302,"y":786,"width":1246,"height":1247,"rx":643,"fill":1303,"stroke":1304,"style":1248},"389.0","#fce9e9","var(--danger,#dc2626)",[1250,1306,1308],{"transform":1307},"translate(551.5,29.0) scale(1.083)",[1254,1309],{"d":1310,"fill":1257,"stroke":1311,"style":1259},"M6 6l12 12 M18 6L6 18","var(--accent-ink,#be185d)",[796,1313,1316],{"x":1314,"y":1263,"style":1315},"564.5","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","renamed to .xltx",[45,1318],{"x1":1319,"y1":1268,"x2":1320,"y2":1268,"stroke":1304,"style":790},"405.0","724.0",[796,1322,1324],{"x":1323,"y":1273,"style":1274},"429.0","content type: spreadsheetml.sheet.main",[1250,1326,1328],{"transform":1327},"translate(407.5,99.5) scale(0.625)",[1254,1329],{"d":1310,"fill":1257,"stroke":1330,"style":1281},"var(--accent,#f43f8f)",[796,1332,1333],{"x":1323,"y":1284,"style":1274},"extension and contents disagree",[1250,1335,1337],{"transform":1336},"translate(407.5,125.5) scale(0.625)",[1254,1338],{"d":1310,"fill":1257,"stroke":1330,"style":1281},[796,1340,1341],{"x":1323,"y":1293,"style":1274},"Excel: format or extension is not valid",[1250,1343,1345],{"transform":1344},"translate(407.5,151.5) scale(0.625)",[1254,1346],{"d":1310,"fill":1257,"stroke":1330,"style":1281},[10,1348,1349,1350,1353,1354,1357],{},"The same applies in the other direction and to other writers: xlsxwriter, for example, always writes the workbook content type, so ",[14,1351,1352],{},"xlsxwriter.Workbook(\"form.xltx\")"," produces a mislabelled file. Build templates with openpyxl, or convert an xlsxwriter output by loading it in openpyxl, setting ",[14,1355,1356],{},"template = True"," and saving.",[29,1359,1361],{"id":1360},"step-4-fill-the-template-into-a-normal-workbook","Step 4 — Fill the template into a normal workbook",[10,1363,1364,1365,1368,1369,1371,1372,1374],{},"Reports are often produced ",[1366,1367,80],"em",{}," a template: load the ",[14,1370,16],{},", write the data, save an ",[14,1373,1016],{},". openpyxl remembers that the source was a template, so the flag must be switched off before saving under a workbook name:",[34,1376,1378],{"className":70,"code":1377,"language":72,"meta":39,"style":39},"from openpyxl import load_workbook\nimport datetime as dt\n\nclaims = [(dt.date(2026, 9, 2), \"Travel\", \"Train to Leeds\", 64.4),\n          (dt.date(2026, 9, 2), \"Meals\", \"Client lunch\", 38.9),\n          (dt.date(2026, 9, 3), \"Lodging\", \"Hotel, one night\", 119.0)]\nwb = load_workbook(\"expense_claim.xltx\")\nprint(\"loaded as template:\", wb.template)\nws = wb[\"Expenses\"]\nfor i, row in enumerate(claims, start=4):\n    for col, value in zip(\"ABCD\", row):\n        ws[f\"{col}{i}\"] = value\nwb.template = False\nwb.save(\"claim_jsmith_2026-09.xlsx\")\nprint(workbook_content_type(\"claim_jsmith_2026-09.xlsx\"), \"| dropdowns kept:\", len(ws.data_validations.dataValidation))\n",[14,1379,1380,1391,1404,1408,1449,1481,1513,1526,1538,1552,1576,1594,1622,1631,1640],{"__ignoreMap":39},[43,1381,1382,1384,1386,1388],{"class":45,"line":46},[43,1383,80],{"class":79},[43,1385,84],{"class":83},[43,1387,87],{"class":79},[43,1389,1390],{"class":83}," load_workbook\n",[43,1392,1393,1395,1398,1401],{"class":45,"line":93},[43,1394,87],{"class":79},[43,1396,1397],{"class":83}," datetime ",[43,1399,1400],{"class":79},"as",[43,1402,1403],{"class":83}," dt\n",[43,1405,1406],{"class":45,"line":106},[43,1407,123],{"emptyLinePlaceholder":122},[43,1409,1410,1413,1415,1418,1421,1423,1426,1428,1430,1433,1436,1438,1441,1443,1446],{"class":45,"line":119},[43,1411,1412],{"class":83},"claims ",[43,1414,132],{"class":79},[43,1416,1417],{"class":83}," [(dt.date(",[43,1419,1420],{"class":198},"2026",[43,1422,202],{"class":83},[43,1424,1425],{"class":198},"9",[43,1427,202],{"class":83},[43,1429,872],{"class":198},[43,1431,1432],{"class":83},"), ",[43,1434,1435],{"class":53},"\"Travel\"",[43,1437,202],{"class":83},[43,1439,1440],{"class":53},"\"Train to Leeds\"",[43,1442,202],{"class":83},[43,1444,1445],{"class":198},"64.4",[43,1447,1448],{"class":83},"),\n",[43,1450,1451,1454,1456,1458,1460,1462,1464,1466,1469,1471,1474,1476,1479],{"class":45,"line":126},[43,1452,1453],{"class":83},"          (dt.date(",[43,1455,1420],{"class":198},[43,1457,202],{"class":83},[43,1459,1425],{"class":198},[43,1461,202],{"class":83},[43,1463,872],{"class":198},[43,1465,1432],{"class":83},[43,1467,1468],{"class":53},"\"Meals\"",[43,1470,202],{"class":83},[43,1472,1473],{"class":53},"\"Client lunch\"",[43,1475,202],{"class":83},[43,1477,1478],{"class":198},"38.9",[43,1480,1448],{"class":83},[43,1482,1483,1485,1487,1489,1491,1493,1495,1497,1500,1502,1505,1507,1510],{"class":45,"line":138},[43,1484,1453],{"class":83},[43,1486,1420],{"class":198},[43,1488,202],{"class":83},[43,1490,1425],{"class":198},[43,1492,202],{"class":83},[43,1494,274],{"class":198},[43,1496,1432],{"class":83},[43,1498,1499],{"class":53},"\"Lodging\"",[43,1501,202],{"class":83},[43,1503,1504],{"class":53},"\"Hotel, one night\"",[43,1506,202],{"class":83},[43,1508,1509],{"class":198},"119.0",[43,1511,1512],{"class":83},")]\n",[43,1514,1515,1517,1519,1522,1524],{"class":45,"line":149},[43,1516,129],{"class":83},[43,1518,132],{"class":79},[43,1520,1521],{"class":83}," load_workbook(",[43,1523,992],{"class":53},[43,1525,223],{"class":83},[43,1527,1528,1530,1532,1535],{"class":45,"line":160},[43,1529,710],{"class":198},[43,1531,366],{"class":83},[43,1533,1534],{"class":53},"\"loaded as template:\"",[43,1536,1537],{"class":83},", wb.template)\n",[43,1539,1540,1542,1544,1547,1550],{"class":45,"line":177},[43,1541,141],{"class":83},[43,1543,132],{"class":79},[43,1545,1546],{"class":83}," wb[",[43,1548,1549],{"class":53},"\"Expenses\"",[43,1551,1119],{"class":83},[43,1553,1554,1556,1559,1561,1564,1567,1570,1572,1574],{"class":45,"line":226},[43,1555,262],{"class":79},[43,1557,1558],{"class":83}," i, row ",[43,1560,268],{"class":79},[43,1562,1563],{"class":198}," enumerate",[43,1565,1566],{"class":83},"(claims, ",[43,1568,1569],{"class":192},"start",[43,1571,132],{"class":79},[43,1573,369],{"class":198},[43,1575,377],{"class":83},[43,1577,1578,1580,1583,1585,1587,1589,1591],{"class":45,"line":232},[43,1579,383],{"class":79},[43,1581,1582],{"class":83}," col, value ",[43,1584,268],{"class":79},[43,1586,632],{"class":198},[43,1588,366],{"class":83},[43,1590,637],{"class":53},[43,1592,1593],{"class":83},", row):\n",[43,1595,1596,1598,1600,1602,1604,1606,1608,1611,1613,1615,1617,1619],{"class":45,"line":259},[43,1597,400],{"class":83},[43,1599,403],{"class":79},[43,1601,406],{"class":53},[43,1603,410],{"class":409},[43,1605,413],{"class":83},[43,1607,416],{"class":409},[43,1609,1610],{"class":83},"i",[43,1612,422],{"class":409},[43,1614,406],{"class":53},[43,1616,169],{"class":83},[43,1618,132],{"class":79},[43,1620,1621],{"class":83}," value\n",[43,1623,1624,1626,1628],{"class":45,"line":280},[43,1625,980],{"class":83},[43,1627,132],{"class":79},[43,1629,1630],{"class":198}," False\n",[43,1632,1633,1635,1638],{"class":45,"line":307},[43,1634,989],{"class":83},[43,1636,1637],{"class":53},"\"claim_jsmith_2026-09.xlsx\"",[43,1639,223],{"class":83},[43,1641,1642,1644,1647,1649,1651,1654,1656,1659],{"class":45,"line":332},[43,1643,710],{"class":198},[43,1645,1646],{"class":83},"(workbook_content_type(",[43,1648,1637],{"class":53},[43,1650,1432],{"class":83},[43,1652,1653],{"class":53},"\"| dropdowns kept:\"",[43,1655,202],{"class":83},[43,1657,1658],{"class":198},"len",[43,1660,1661],{"class":83},"(ws.data_validations.dataValidation))\n",[10,1663,1664,1665,1668,1669,1671,1672,1674,1675,27],{},"Without ",[14,1666,1667],{},"wb.template = False",", the ",[14,1670,1016],{}," would carry the template content type and hit the same \"not valid\" error. Styles, number formats, protection, the dropdown and the ",[14,1673,745],{}," formula all survive the round trip; openpyxl's general losses on load-and-save — charts and images among them — apply to templates too, as listed in ",[23,1676,1678],{"href":1677},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook\u002F","What openpyxl Loses When Saving an Existing Workbook",[757,1680,766,1685,766,1688,766,1691,766,1694,766,1698,766,1706,766,1712,766,1717,766,1721,766,1726,766,1731,766,1734,766,1740,766,1745,766,1748,766,1751,766,1754,766,1757,766,1764,766,1769,766,1772,766,1776,766,1779,766,1782,766,1788,766,1793],{"viewBox":1681,"role":760,"ariaLabelledBy":1682,"xmlns":764,"style":1232},"4 0 752 142",[1683,1684],"tp-cycle-t","tp-cycle-d",[768,1686,1687],{"id":1683},"From script to template to filled report",[772,1689,1690],{"id":1684},"A script builds the workbook and saves it as expense_claim.xltx with template set to True. People open it as an untitled copy, or a second script loads it, fills rows, sets template to False and saves a dated xlsx.",[776,1692],{"x":369,"y":779,"width":1241,"height":1693,"fill":782},"142",[776,1695],{"x":1245,"y":786,"width":1696,"height":1697,"rx":657,"fill":889,"stroke":890,"style":1248},"153.0","110.5",[1250,1699,1701],{"transform":1700},"translate(84.5,29.0) scale(1.000)",[1254,1702],{"d":1703,"fill":1257,"stroke":1704,"style":1705},"M12 3c-4 0-4 1.6-4 3v2h4.2v1H6c-2 0-3 1.4-3 4s1 4 3 4h2v-2.6c0-1.6 1.3-2.9 2.9-2.9h4.2c1.3 0 2.4-1.1 2.4-2.4V6c0-1.6-1.8-3-5.5-3z M10 5.5h.01 M12 21c4 0 4-1.6 4-3v-2h-4.2v-1H18c2 0 3-1.4 3-4s-1-4-3-4h-2v2.6c0 1.6-1.3 2.9-2.9 2.9H8.9c-1.3 0-2.4 1.1-2.4 2.4V18c0 1.6 1.8 3 5.5 3z M14 18.5h.01","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[796,1707,1711],{"x":1708,"y":1709,"style":1710},"96.5","75.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Build in openpyxl",[796,1713,1716],{"x":1708,"y":1714,"style":1715},"95.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","styles, validation,",[796,1718,1720],{"x":1708,"y":1719,"style":1715},"112.5","protection",[45,1722],{"x1":1723,"y1":1724,"x2":1725,"y2":1724,"stroke":890,"style":1248},"178.0","71.2","200.0",[1727,1728],"polygon",{"points":1729,"fill":1730},"204.0,71.2 195.0,76.2 195.0,66.2","#5b5cf0",[776,1732],{"x":1733,"y":786,"width":1696,"height":1697,"rx":657,"fill":889,"stroke":890,"style":1248},"209.0",[1250,1735,1737],{"transform":1736},"translate(273.5,37.8) scale(1.000)",[1254,1738],{"d":1739,"fill":1257,"stroke":1704,"style":1705},"M6 3h9l4 4v14H6z M15 3v4h4",[796,1741,1744],{"x":1742,"y":1743,"style":1710},"285.5","84.2","expense_claim.xltx",[796,1746,999],{"x":1742,"y":1747,"style":1715},"103.8",[45,1749],{"x1":1750,"y1":1724,"x2":1302,"y2":1724,"stroke":890,"style":1248},"367.0",[1727,1752],{"points":1753,"fill":1730},"393.0,71.2 384.0,76.2 384.0,66.2",[776,1755],{"x":1756,"y":786,"width":1696,"height":1697,"rx":657,"fill":794,"stroke":789,"style":1248},"398.0",[1250,1758,1760],{"transform":1759},"translate(462.5,37.8) scale(1.000)",[1254,1761],{"d":1762,"fill":1257,"stroke":1763,"style":1705},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","var(--text,#172033)",[796,1765,1768],{"x":1766,"y":1743,"style":1767},"474.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Load and fill",[796,1770,1771],{"x":1766,"y":1747,"style":1715},"write rows",[45,1773],{"x1":1774,"y1":1724,"x2":1775,"y2":1724,"stroke":890,"style":1248},"556.0","578.0",[1727,1777],{"points":1778,"fill":1730},"582.0,71.2 573.0,76.2 573.0,66.2",[776,1780],{"x":1781,"y":786,"width":1696,"height":1697,"rx":657,"fill":956,"stroke":957,"style":1248},"587.0",[1250,1783,1785],{"transform":1784},"translate(651.5,37.8) scale(1.000)",[1254,1786],{"d":1787,"fill":1257,"stroke":1258,"style":1705},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6",[796,1789,1792],{"x":1790,"y":1743,"style":1791},"663.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","claim_….xlsx",[796,1794,1667],{"x":1790,"y":1747,"style":1715},[29,1796,1798],{"id":1797},"step-5-templates-with-macros","Step 5 — Templates with macros",[10,1800,1801,1802,1805,1806,1809,1810,1812,1813,1815,1816,1819,1820,1823,1824,27],{},"A macro-enabled template has the extension ",[14,1803,1804],{},".xltm",". Load the source with ",[14,1807,1808],{},"keep_vba=True"," so the VBA project is preserved, and keep ",[14,1811,1356],{}," when saving back to ",[14,1814,1804],{},"; to produce a filled, macro-enabled workbook, save as ",[14,1817,1818],{},".xlsm"," with ",[14,1821,1822],{},"template = False",". openpyxl cannot create or edit the macros themselves — it carries an existing VBA project through unchanged. Macro files are covered in ",[23,1825,1827],{"href":1826},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fwork-with-macro-enabled-xlsm-files-in-openpyxl\u002F","Work with Macro-Enabled .xlsm Files in openpyxl",[29,1829,1831],{"id":1830},"step-6-read-a-templates-data-with-pandas","Step 6 — Read a template's data with pandas",[10,1833,1834,1835,1837],{},"pandas reads ",[14,1836,16],{}," files like any workbook, which is handy for checking what a template contains or for reading lookup lists stored in it:",[34,1839,1841],{"className":70,"code":1840,"language":72,"meta":39,"style":39},"import pandas as pd\n\nprint(pd.read_excel(\"claim_jsmith_2026-09.xlsx\", header=2, nrows=3))\nwith pd.ExcelFile(\"expense_claim.xltx\") as xf:\n    print(xf.sheet_names)\n",[14,1842,1843,1855,1859,1889,1907],{"__ignoreMap":39},[43,1844,1845,1847,1850,1852],{"class":45,"line":46},[43,1846,87],{"class":79},[43,1848,1849],{"class":83}," pandas ",[43,1851,1400],{"class":79},[43,1853,1854],{"class":83}," pd\n",[43,1856,1857],{"class":45,"line":93},[43,1858,123],{"emptyLinePlaceholder":122},[43,1860,1861,1863,1866,1868,1870,1873,1875,1877,1879,1882,1884,1886],{"class":45,"line":106},[43,1862,710],{"class":198},[43,1864,1865],{"class":83},"(pd.read_excel(",[43,1867,1637],{"class":53},[43,1869,202],{"class":83},[43,1871,1872],{"class":192},"header",[43,1874,132],{"class":79},[43,1876,872],{"class":198},[43,1878,202],{"class":83},[43,1880,1881],{"class":192},"nrows",[43,1883,132],{"class":79},[43,1885,274],{"class":198},[43,1887,1888],{"class":83},"))\n",[43,1890,1891,1894,1897,1899,1902,1904],{"class":45,"line":119},[43,1892,1893],{"class":79},"with",[43,1895,1896],{"class":83}," pd.ExcelFile(",[43,1898,992],{"class":53},[43,1900,1901],{"class":83},") ",[43,1903,1400],{"class":79},[43,1905,1906],{"class":83}," xf:\n",[43,1908,1909,1911],{"class":45,"line":126},[43,1910,1179],{"class":198},[43,1912,1913],{"class":83},"(xf.sheet_names)\n",[10,1915,1916,1917,1920,1921,1924],{},"Reading a filled claim uses ",[14,1918,1919],{},"header=2"," to skip the title rows and ",[14,1922,1923],{},"nrows=3"," to stop before the empty input rows and the total.",[29,1926,1928],{"id":1927},"step-7-put-the-template-where-people-find-it","Step 7 — Put the template where people find it",[10,1930,1931,1932,1935,1936,1939,1940,1943,1944,1946,1947,1950],{},"A template on a shared drive is opened by double-click. To make it appear in Excel's ",[1366,1933,1934],{},"File › New › Personal"," list instead, copy it into the folder Excel uses for personal templates (on Windows, by default ",[14,1937,1938],{},"Documents\\Custom Office Templates","; the location is set under ",[1366,1941,1942],{},"File › Options › Save › Default personal templates location","). A deployment script can copy the generated ",[14,1945,16],{}," there with ",[14,1948,1949],{},"shutil.copy",", overwriting last month's version so everyone starts from the current layout.",[29,1952,1954],{"id":1953},"step-8-regenerate-the-template-from-one-script","Step 8 — Regenerate the template from one script",[10,1956,1957],{},"Treat the template as build output. Keep the code from Steps 1 and 2 in a function that takes the things that change — the category list, the number of input rows, the company name in the title — and run it whenever one of them changes:",[34,1959,1961],{"className":70,"code":1960,"language":72,"meta":39,"style":39},"def build_template(path, categories, rows=20, title=\"Expense claim\"):\n    wb = Workbook()\n    ws = wb.active\n    ws.title = \"Expenses\"\n    ws[\"A1\"] = title\n    ws.append([])\n    ws.append([\"Date\", \"Category\", \"Description\", \"Amount\"])\n    dv = DataValidation(type=\"list\", formula1='\"' + \",\".join(categories) + '\"', allow_blank=True)\n    ws.add_data_validation(dv)\n    dv.add(f\"B4:B{3 + rows}\")\n    ws[f\"D{4 + rows}\"] = f\"=SUM(D4:D{3 + rows})\"\n    wb.template = True\n    wb.save(path)\n    return workbook_content_type(path)\n\nprint(build_template(\"claim_v2.xltx\", [\"Travel\", \"Meals\", \"Lodging\", \"Supplies\", \"Training\"]))\n",[14,1962,1963,1988,1997,2006,2015,2028,2033,2054,2102,2107,2132,2175,2184,2189,2196,2200],{"__ignoreMap":39},[43,1964,1965,1967,1970,1973,1975,1978,1981,1983,1986],{"class":45,"line":46},[43,1966,1042],{"class":79},[43,1968,1969],{"class":1045}," build_template",[43,1971,1972],{"class":83},"(path, categories, rows",[43,1974,132],{"class":79},[43,1976,1977],{"class":198},"20",[43,1979,1980],{"class":83},", title",[43,1982,132],{"class":79},[43,1984,1985],{"class":53},"\"Expense claim\"",[43,1987,377],{"class":83},[43,1989,1990,1993,1995],{"class":45,"line":93},[43,1991,1992],{"class":83},"    wb ",[43,1994,132],{"class":79},[43,1996,135],{"class":83},[43,1998,1999,2002,2004],{"class":45,"line":106},[43,2000,2001],{"class":83},"    ws ",[43,2003,132],{"class":79},[43,2005,146],{"class":83},[43,2007,2008,2011,2013],{"class":45,"line":119},[43,2009,2010],{"class":83},"    ws.title ",[43,2012,132],{"class":79},[43,2014,157],{"class":53},[43,2016,2017,2019,2021,2023,2025],{"class":45,"line":126},[43,2018,448],{"class":83},[43,2020,166],{"class":53},[43,2022,169],{"class":83},[43,2024,132],{"class":79},[43,2026,2027],{"class":83}," title\n",[43,2029,2030],{"class":45,"line":138},[43,2031,2032],{"class":83},"    ws.append([])\n",[43,2034,2035,2038,2040,2042,2044,2046,2048,2050,2052],{"class":45,"line":149},[43,2036,2037],{"class":83},"    ws.append([",[43,2039,238],{"class":53},[43,2041,202],{"class":83},[43,2043,243],{"class":53},[43,2045,202],{"class":83},[43,2047,248],{"class":53},[43,2049,202],{"class":83},[43,2051,253],{"class":53},[43,2053,256],{"class":83},[43,2055,2056,2059,2061,2063,2065,2067,2069,2071,2073,2075,2077,2080,2083,2086,2089,2092,2094,2096,2098,2100],{"class":45,"line":160},[43,2057,2058],{"class":83},"    dv ",[43,2060,132],{"class":79},[43,2062,573],{"class":83},[43,2064,576],{"class":192},[43,2066,132],{"class":79},[43,2068,581],{"class":53},[43,2070,202],{"class":83},[43,2072,586],{"class":192},[43,2074,132],{"class":79},[43,2076,1108],{"class":53},[43,2078,2079],{"class":79}," +",[43,2081,2082],{"class":53}," \",\"",[43,2084,2085],{"class":83},".join(categories) ",[43,2087,2088],{"class":79},"+",[43,2090,2091],{"class":53}," '\"'",[43,2093,202],{"class":83},[43,2095,596],{"class":192},[43,2097,132],{"class":79},[43,2099,210],{"class":198},[43,2101,223],{"class":83},[43,2103,2104],{"class":45,"line":177},[43,2105,2106],{"class":83},"    ws.add_data_validation(dv)\n",[43,2108,2109,2112,2114,2117,2119,2121,2123,2126,2128,2130],{"class":45,"line":226},[43,2110,2111],{"class":83},"    dv.add(",[43,2113,403],{"class":79},[43,2115,2116],{"class":53},"\"B4:B",[43,2118,410],{"class":409},[43,2120,274],{"class":198},[43,2122,2079],{"class":79},[43,2124,2125],{"class":83}," rows",[43,2127,422],{"class":409},[43,2129,406],{"class":53},[43,2131,223],{"class":83},[43,2133,2134,2136,2138,2140,2142,2144,2146,2148,2150,2152,2154,2156,2159,2162,2164,2166,2168,2170,2172],{"class":45,"line":232},[43,2135,448],{"class":83},[43,2137,403],{"class":79},[43,2139,479],{"class":53},[43,2141,410],{"class":409},[43,2143,369],{"class":198},[43,2145,2079],{"class":79},[43,2147,2125],{"class":83},[43,2149,422],{"class":409},[43,2151,406],{"class":53},[43,2153,169],{"class":83},[43,2155,132],{"class":79},[43,2157,2158],{"class":79}," f",[43,2160,2161],{"class":53},"\"=SUM(D4:D",[43,2163,410],{"class":409},[43,2165,274],{"class":198},[43,2167,2079],{"class":79},[43,2169,2125],{"class":83},[43,2171,422],{"class":409},[43,2173,2174],{"class":53},")\"\n",[43,2176,2177,2180,2182],{"class":45,"line":259},[43,2178,2179],{"class":83},"    wb.template ",[43,2181,132],{"class":79},[43,2183,693],{"class":198},[43,2185,2186],{"class":45,"line":280},[43,2187,2188],{"class":83},"    wb.save(path)\n",[43,2190,2191,2193],{"class":45,"line":307},[43,2192,1124],{"class":79},[43,2194,2195],{"class":83}," workbook_content_type(path)\n",[43,2197,2198],{"class":45,"line":332},[43,2199,123],{"emptyLinePlaceholder":122},[43,2201,2202,2204,2207,2210,2213,2215,2217,2219,2221,2223,2225,2228,2230,2233],{"class":45,"line":353},[43,2203,710],{"class":198},[43,2205,2206],{"class":83},"(build_template(",[43,2208,2209],{"class":53},"\"claim_v2.xltx\"",[43,2211,2212],{"class":83},", [",[43,2214,1435],{"class":53},[43,2216,202],{"class":83},[43,2218,1468],{"class":53},[43,2220,202],{"class":83},[43,2222,1499],{"class":53},[43,2224,202],{"class":83},[43,2226,2227],{"class":53},"\"Supplies\"",[43,2229,202],{"class":83},[43,2231,2232],{"class":53},"\"Training\"",[43,2234,2235],{"class":83},"]))\n",[10,2237,2238],{},"Generating the template means a new category is one list entry, not an afternoon of editing validation ranges by hand, and the content-type check at the end confirms the output is still a genuine template. A list-type validation written inline like this is limited to 255 characters; for longer lists, put the values on a hidden sheet and point the validation at that range.",[29,2240,2242],{"id":2241},"common-pitfalls","Common pitfalls",[2244,2245,2246,2260,2271,2277,2283],"ul",{},[2247,2248,2249,2253,2254,2256,2257,2259],"li",{},[2250,2251,2252],"strong",{},"Renaming instead of flagging."," An ",[14,2255,16],{}," name without ",[14,2258,999],{}," gives a mislabelled file.",[2247,2261,2262,2267,2268,2270],{},[2250,2263,2264,2265,27],{},"Loading a template and saving ",[14,2266,1016],{}," The flag sticks; set ",[14,2269,1667],{}," first.",[2247,2272,2273,2276],{},[2250,2274,2275],{},"Expecting xlsxwriter to write templates."," It writes the workbook type regardless of the name.",[2247,2278,2279,2282],{},[2250,2280,2281],{},"Unprotected input areas."," If the sheet is protected, unlock the input cells first or nobody can type into the template.",[2247,2284,2285,2288,2289,751,2291,2293,2294,2296],{},[2250,2286,2287],{},"Macro templates."," Use ",[14,2290,1804],{},[14,2292,1808],{},"; plain ",[14,2295,16],{}," cannot hold macros.",[29,2298,2300],{"id":2299},"conclusion","Conclusion",[10,2302,2303,2304,2306,2307,2309,2310,2306,2312,2314,2315,1819,2317,2319],{},"A template is a workbook with a different content type, not a different extension. Build the layout in openpyxl, set ",[14,2305,999],{}," and save as ",[14,2308,16],{},"; when filling a template into a report, load it, write the data, set ",[14,2311,1667],{},[14,2313,1016],{},". Check the content type in a test, use ",[14,2316,1804],{},[14,2318,1808],{}," for macros, and remember that xlsxwriter cannot produce a real template on its own.",[29,2321,2323],{"id":2322},"frequently-asked-questions","Frequently asked questions",[10,2325,2326,2329],{},[2250,2327,2328],{},"How do I save an Excel template (.xltx) with openpyxl?","\nSet wb.template = True before calling wb.save(\"name.xltx\"). The flag makes openpyxl write the template content type that Excel checks when opening the file.",[10,2331,2332,2335],{},[2250,2333,2334],{},"Why does Excel say the file format or extension is not valid for my .xltx?","\nThe file's internal content type does not match its extension. Either it was saved with an .xltx name without wb.template = True, or a template was loaded and saved as .xlsx without setting wb.template = False.",[10,2337,2338,2341],{},[2250,2339,2340],{},"Can xlsxwriter create .xltx templates?","\nNo. xlsxwriter always writes the normal workbook content type, whatever the file name. Load its output with openpyxl, set template = True and save it again to turn it into a real template.",[10,2343,2344,2347],{},[2250,2345,2346],{},"Can pandas read .xltx files?","\nYes. pd.read_excel and pd.ExcelFile read .xltx templates with the openpyxl engine just like .xlsx workbooks.",[29,2349,2351],{"id":2350},"related","Related",[2244,2353,2354,2360,2367,2374],{},[2247,2355,2356,2357,2359],{},"Up one level: ",[23,2358,26],{"href":25}," — every workbook format and how to move between them.",[2247,2361,2362,2366],{},[23,2363,2365],{"href":2364},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Ffill-excel-template-with-python-openpyxl\u002F","Fill an Excel Template with Python and openpyxl"," — filling templates at scale.",[2247,2368,2369,2373],{},[23,2370,2372],{"href":2371},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fpopulate-excel-template-without-losing-formatting\u002F","Populate an Excel Template Without Losing Formatting"," — keeping the template's look intact.",[2247,2375,2376,2380],{},[23,2377,2379],{"href":2378},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fapply-a-reusable-style-theme-across-an-excel-report\u002F","Apply a Reusable Style Theme Across an Excel Report"," — consistent styling for generated templates.",[2382,2383,2384],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}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 .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":39,"searchDepth":93,"depth":93,"links":2386},[2387,2388,2389,2390,2391,2392,2393,2394,2395,2396,2397,2398,2399],{"id":31,"depth":93,"text":32},{"id":66,"depth":93,"text":67},{"id":969,"depth":93,"text":970},{"id":1006,"depth":93,"text":1007},{"id":1360,"depth":93,"text":1361},{"id":1797,"depth":93,"text":1798},{"id":1830,"depth":93,"text":1831},{"id":1927,"depth":93,"text":1928},{"id":1953,"depth":93,"text":1954},{"id":2241,"depth":93,"text":2242},{"id":2299,"depth":93,"text":2300},{"id":2322,"depth":93,"text":2323},{"id":2350,"depth":93,"text":2351},"2026-10-02","Create a real .xltx template with openpyxl — styled headers, input cells, dropdowns, formulas and protection — by setting workbook.template = True, check the content type that makes Excel treat it as a template, and avoid the trap of loading an .xltx and saving it as .xlsx without switching the flag back, which produces a file Excel refuses to open.","md",[2404,2406,2408,2410],{"q":2328,"a":2405},"Set wb.template = True before calling wb.save(\"name.xltx\"). The flag makes openpyxl write the template content type that Excel checks when opening the file.",{"q":2334,"a":2407},"The file's internal content type does not match its extension. Either it was saved with an .xltx name without wb.template = True, or a template was loaded and saved as .xlsx without setting wb.template = False.",{"q":2340,"a":2409},"No. xlsxwriter always writes the normal workbook content type, whatever the file name. Load its output with openpyxl, set template = True and save it again to turn it into a real template.",{"q":2346,"a":2411},"Yes. pd.read_excel and pd.ExcelFile read .xltx templates with the openpyxl engine just like .xlsx workbooks.","save a workbook as an excel template with python",null,{"breadcrumb":2415},[2416,2419,2422],{"name":2417,"item":2418},"Home","\u002F",{"name":2420,"item":2421},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":26,"item":25},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python",{"title":2425,"description":2426},"Save an Excel Template (.xltx) with Python and openpyxl","Write a genuine .xltx with openpyxl using wb.template = True, verify the template content type, fill a template into a normal .xlsx without the \"file format or extension is not valid\" error, and why renaming or xlsxwriter is not enough.","save-a-workbook-as-an-excel-template-with-python","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python\u002Findex",[2430,2431,2432],"file-formats","report-layouts","openpyxl","how-to","fdfqf03pja43DEZhFtHirpdfdBbsXGEKKKyUVt5qG7U",[2436,2440],{"title":2437,"path":2438,"stem":2439,"children":-1},"Read Binary .xlsb Excel Files in Python","\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python\u002Findex",{"title":1827,"path":2441,"stem":2442,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fwork-with-macro-enabled-xlsm-files-in-openpyxl","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fwork-with-macro-enabled-xlsm-files-in-openpyxl\u002Findex",[2444,2446],{"path":2445,"title":26},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions",{"path":2447,"title":2420},"\u002Fgetting-started-with-python-excel-automation",[],[2450,2453,2456,2460,2463,2467],{"path":2451,"title":2452,"kind":2433},"\u002Fadvanced-data-transformation-and-cleaning\u002Freading-excel-with-polars-and-arrow\u002Fconvert-excel-files-to-parquet-with-python","Convert Excel Files to Parquet with Python",{"path":2454,"title":2455,"kind":2433},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python","Convert Excel to CSV with Python",{"path":2457,"title":2458,"kind":2459},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fexcel-vs-csv-vs-parquet-for-python-data-pipelines","Excel vs CSV vs Parquet for Python Data Pipelines","compare",{"path":2461,"title":2462,"kind":2459},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpick-an-excel-engine-for-xlsx-xlsm-xls-xlsb-and-ods","Pick an Excel Engine for .xlsx, .xlsm, .xls, .xlsb and .ods",{"path":2464,"title":2465,"kind":2466},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Ffix-garbled-characters-in-excel-data","Fix Garbled Characters Like \"CafÃ©\" and \"â€™\" in Excel Data","fix",{"path":2468,"title":2469,"kind":2433},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fextract-vba-macro-code-from-an-excel-file","Extract VBA Macro Code from an Excel File",1790972064917]