[{"data":1,"prerenderedAt":2580},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl":2546,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl":2554,"children:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl":2559,"related:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl":2560},{"id":4,"title":5,"body":6,"dateModified":2512,"datePublished":2512,"description":2513,"extension":2514,"faq":2515,"keyword":2524,"kind":2525,"meta":2526,"navigation":238,"path":2535,"seo":2536,"slug":2539,"stem":2540,"topics":2541,"type":2544,"__hash__":2545},"docs\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl\u002Findex.md","Find and Replace Text in Excel with openpyxl",{"type":7,"value":8,"toc":2498},"minimark",[9,19,138,143,168,171,504,508,518,652,667,671,681,1147,1150,1283,1287,1312,1491,1575,1578,1582,1601,1825,1834,1838,1854,2040,2052,2056,2059,2273,2285,2289,2392,2396,2411,2415,2423,2427,2434,2440,2446,2452,2456,2494],[10,11,12,13,18],"p",{},"Ctrl+H is fine for one workbook. It is tedious for forty templates after a company rename, error-prone when\nonly whole-cell matches should change, and silent about what it touched. A short openpyxl script does the\nsame job across any number of files, keeps every cell's formatting, can leave formulas alone, and writes a\nlog of each change for someone to review. This guide, part of\n",[14,15,17],"a",{"href":16},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation",",\nbuilds that script step by step — including the one detail that quietly destroys formatting if you miss it.",[20,21,29,30,29,34,29,38,29,45,29,54,29,61,29,66,29,73,29,78,29,82,29,87,29,91,29,95,29,98,29,101,29,105,29,108,29,112,29,116,29,119,29,124,29,129,29,132],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"0 0 760 156","img",[25,26],"far-flow-t","far-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"A safe find-and-replace pass over a workbook",[35,36,37],"desc",{"id":26},"Load the workbook with rich text preserved, walk every cell on every sheet, skip formulas and non-text values, apply the replacement, record each change, and save to a new file.",[39,40],"rect",{"x":41,"y":41,"width":42,"height":43,"fill":44},"0","760","156","#ffffff",[39,46],{"x":47,"y":47,"width":48,"height":49,"rx":50,"fill":51,"stroke":52,"style":53},"20","150","86","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[55,56,60],"text",{"x":57,"y":58,"style":59},"95","50","font-size:14px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","load",[55,62,65],{"x":57,"y":63,"style":64},"72","font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","rich_text=True",[67,68],"line",{"x1":69,"y1":70,"x2":71,"y2":70,"stroke":72,"style":53},"175","63","204","var(--brand,#5b5cf0)",[74,75],"polygon",{"points":76,"fill":77},"207,63 198,58 198,68","#5b5cf0",[39,79],{"x":80,"y":47,"width":48,"height":49,"rx":50,"fill":81,"stroke":72,"style":53},"210","#f0f4ff",[55,83,86],{"x":84,"y":58,"style":85},"285","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","walk cells",[55,88,90],{"x":84,"y":63,"style":89},"font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","every sheet",[67,92],{"x1":93,"y1":70,"x2":94,"y2":70,"stroke":72,"style":53},"365","394",[74,96],{"points":97,"fill":77},"397,63 388,58 388,68",[39,99],{"x":100,"y":47,"width":48,"height":49,"rx":50,"fill":81,"stroke":72,"style":53},"400",[55,102,104],{"x":103,"y":58,"style":85},"475","match + replace",[55,106,107],{"x":103,"y":63,"style":89},"text only,",[55,109,111],{"x":103,"y":110,"style":89},"92","formulas skipped",[67,113],{"x1":114,"y1":70,"x2":115,"y2":70,"stroke":72,"style":53},"555","584",[74,117],{"points":118,"fill":77},"587,63 578,58 578,68",[39,120],{"x":121,"y":47,"width":48,"height":49,"rx":50,"fill":122,"stroke":123,"style":53},"590","#d9f4f1","var(--teal,#0f9488)",[55,125,128],{"x":126,"y":58,"style":127},"665","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","log + save",[55,130,131],{"x":126,"y":63,"style":89},"new file",[55,133,137],{"x":134,"y":135,"style":136},"380","140","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","assigning a new value keeps the cell style; losing rich text is the trap",[139,140,142],"h2",{"id":141},"prerequisites","Prerequisites",[144,145,150],"pre",{"className":146,"code":147,"language":148,"meta":149,"style":149},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[151,152,153],"code",{"__ignoreMap":149},[154,155,157,161,165],"span",{"class":67,"line":156},1,[154,158,160],{"class":159},"sMTad","pip",[154,162,164],{"class":163},"srMev"," install",[154,166,167],{"class":163}," openpyxl\n",[10,169,170],{},"This builds a small workbook with the kinds of cells a real replacement meets — plain text, a whole-cell match, a formula, a number, and a cell whose\ntext is partly bold:",[144,172,176],{"className":173,"code":174,"language":175,"meta":149,"style":149},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.cell.rich_text import CellRichText, TextBlock\nfrom openpyxl.cell.text import InlineFont\nfrom openpyxl.styles import Font, PatternFill\n\nwb = Workbook()\nws = wb.active\nws.title = \"Cover\"\nws[\"A1\"] = \"Acme Ltd — Quarterly Report\"\nws[\"A1\"].font = Font(size=16, bold=True)\nws[\"A2\"] = CellRichText([\"Prepared by \", TextBlock(InlineFont(b=True), \"Acme Ltd\"), \" finance\"])\nws[\"A3\"] = \"Acme\"\nws[\"A3\"].fill = PatternFill(\"solid\", start_color=\"FFEB9C\")\nws[\"A4\"] = '=CONCAT(\"Acme\", \" Ltd\")'\nws[\"A5\"] = 2025\ndata = wb.create_sheet(\"Data\")\ndata.append([\"Customer\", \"Supplier\"])\ndata.append([\"Northwind\", \"Acme Ltd\"])\nwb.save(\"report_template.xlsx\")\n","python",[151,177,178,194,207,220,233,240,252,263,274,291,330,372,387,417,432,447,463,479,493],{"__ignoreMap":149},[154,179,180,184,188,191],{"class":67,"line":156},[154,181,183],{"class":182},"s-kum","from",[154,185,187],{"class":186},"skGVy"," openpyxl ",[154,189,190],{"class":182},"import",[154,192,193],{"class":186}," Workbook\n",[154,195,197,199,202,204],{"class":67,"line":196},2,[154,198,183],{"class":182},[154,200,201],{"class":186}," openpyxl.cell.rich_text ",[154,203,190],{"class":182},[154,205,206],{"class":186}," CellRichText, TextBlock\n",[154,208,210,212,215,217],{"class":67,"line":209},3,[154,211,183],{"class":182},[154,213,214],{"class":186}," openpyxl.cell.text ",[154,216,190],{"class":182},[154,218,219],{"class":186}," InlineFont\n",[154,221,223,225,228,230],{"class":67,"line":222},4,[154,224,183],{"class":182},[154,226,227],{"class":186}," openpyxl.styles ",[154,229,190],{"class":182},[154,231,232],{"class":186}," Font, PatternFill\n",[154,234,236],{"class":67,"line":235},5,[154,237,239],{"emptyLinePlaceholder":238},true,"\n",[154,241,243,246,249],{"class":67,"line":242},6,[154,244,245],{"class":186},"wb ",[154,247,248],{"class":182},"=",[154,250,251],{"class":186}," Workbook()\n",[154,253,255,258,260],{"class":67,"line":254},7,[154,256,257],{"class":186},"ws ",[154,259,248],{"class":182},[154,261,262],{"class":186}," wb.active\n",[154,264,266,269,271],{"class":67,"line":265},8,[154,267,268],{"class":186},"ws.title ",[154,270,248],{"class":182},[154,272,273],{"class":163}," \"Cover\"\n",[154,275,277,280,283,286,288],{"class":67,"line":276},9,[154,278,279],{"class":186},"ws[",[154,281,282],{"class":163},"\"A1\"",[154,284,285],{"class":186},"] ",[154,287,248],{"class":182},[154,289,290],{"class":163}," \"Acme Ltd — Quarterly Report\"\n",[154,292,294,296,298,301,303,306,310,312,316,319,322,324,327],{"class":67,"line":293},10,[154,295,279],{"class":186},[154,297,282],{"class":163},[154,299,300],{"class":186},"].font ",[154,302,248],{"class":182},[154,304,305],{"class":186}," Font(",[154,307,309],{"class":308},"sa561","size",[154,311,248],{"class":182},[154,313,315],{"class":314},"sP0c6","16",[154,317,318],{"class":186},", ",[154,320,321],{"class":308},"bold",[154,323,248],{"class":182},[154,325,326],{"class":314},"True",[154,328,329],{"class":186},")\n",[154,331,333,335,338,340,342,345,348,351,354,356,358,361,364,366,369],{"class":67,"line":332},11,[154,334,279],{"class":186},[154,336,337],{"class":163},"\"A2\"",[154,339,285],{"class":186},[154,341,248],{"class":182},[154,343,344],{"class":186}," CellRichText([",[154,346,347],{"class":163},"\"Prepared by \"",[154,349,350],{"class":186},", TextBlock(InlineFont(",[154,352,353],{"class":308},"b",[154,355,248],{"class":182},[154,357,326],{"class":314},[154,359,360],{"class":186},"), ",[154,362,363],{"class":163},"\"Acme Ltd\"",[154,365,360],{"class":186},[154,367,368],{"class":163},"\" finance\"",[154,370,371],{"class":186},"])\n",[154,373,375,377,380,382,384],{"class":67,"line":374},12,[154,376,279],{"class":186},[154,378,379],{"class":163},"\"A3\"",[154,381,285],{"class":186},[154,383,248],{"class":182},[154,385,386],{"class":163}," \"Acme\"\n",[154,388,390,392,394,397,399,402,405,407,410,412,415],{"class":67,"line":389},13,[154,391,279],{"class":186},[154,393,379],{"class":163},[154,395,396],{"class":186},"].fill ",[154,398,248],{"class":182},[154,400,401],{"class":186}," PatternFill(",[154,403,404],{"class":163},"\"solid\"",[154,406,318],{"class":186},[154,408,409],{"class":308},"start_color",[154,411,248],{"class":182},[154,413,414],{"class":163},"\"FFEB9C\"",[154,416,329],{"class":186},[154,418,420,422,425,427,429],{"class":67,"line":419},14,[154,421,279],{"class":186},[154,423,424],{"class":163},"\"A4\"",[154,426,285],{"class":186},[154,428,248],{"class":182},[154,430,431],{"class":163}," '=CONCAT(\"Acme\", \" Ltd\")'\n",[154,433,435,437,440,442,444],{"class":67,"line":434},15,[154,436,279],{"class":186},[154,438,439],{"class":163},"\"A5\"",[154,441,285],{"class":186},[154,443,248],{"class":182},[154,445,446],{"class":314}," 2025\n",[154,448,450,453,455,458,461],{"class":67,"line":449},16,[154,451,452],{"class":186},"data ",[154,454,248],{"class":182},[154,456,457],{"class":186}," wb.create_sheet(",[154,459,460],{"class":163},"\"Data\"",[154,462,329],{"class":186},[154,464,466,469,472,474,477],{"class":67,"line":465},17,[154,467,468],{"class":186},"data.append([",[154,470,471],{"class":163},"\"Customer\"",[154,473,318],{"class":186},[154,475,476],{"class":163},"\"Supplier\"",[154,478,371],{"class":186},[154,480,482,484,487,489,491],{"class":67,"line":481},18,[154,483,468],{"class":186},[154,485,486],{"class":163},"\"Northwind\"",[154,488,318],{"class":186},[154,490,363],{"class":163},[154,492,371],{"class":186},[154,494,496,499,502],{"class":67,"line":495},19,[154,497,498],{"class":186},"wb.save(",[154,500,501],{"class":163},"\"report_template.xlsx\"",[154,503,329],{"class":186},[139,505,507],{"id":506},"step-1-load-with-rich-text-preserved","Step 1 — Load with rich text preserved",[10,509,510,511,513,514,517],{},"This is the step that matters most. By default openpyxl flattens a cell with mixed formatting — a bold word inside normal text — into a plain string,\nand saving the workbook writes it back without the bold. Nothing warns you. Load with ",[151,512,65],{}," and such cells come back as ",[151,515,516],{},"CellRichText","\nobjects whose formatted runs survive the round trip.",[144,519,521],{"className":173,"code":520,"language":175,"meta":149,"style":149},"from openpyxl import load_workbook\n\nplain = load_workbook(\"report_template.xlsx\")\nprint(type(plain[\"Cover\"][\"A2\"].value).__name__, repr(plain[\"Cover\"][\"A2\"].value))   # str — bold lost\n\nwb = load_workbook(\"report_template.xlsx\", rich_text=True)\nprint(type(wb[\"Cover\"][\"A2\"].value).__name__)                                          # CellRichText\n",[151,522,523,534,538,552,600,604,625],{"__ignoreMap":149},[154,524,525,527,529,531],{"class":67,"line":156},[154,526,183],{"class":182},[154,528,187],{"class":186},[154,530,190],{"class":182},[154,532,533],{"class":186}," load_workbook\n",[154,535,536],{"class":67,"line":196},[154,537,239],{"emptyLinePlaceholder":238},[154,539,540,543,545,548,550],{"class":67,"line":209},[154,541,542],{"class":186},"plain ",[154,544,248],{"class":182},[154,546,547],{"class":186}," load_workbook(",[154,549,501],{"class":163},[154,551,329],{"class":186},[154,553,554,557,560,563,566,569,572,574,577,580,582,585,587,589,591,593,596],{"class":67,"line":222},[154,555,556],{"class":314},"print",[154,558,559],{"class":186},"(",[154,561,562],{"class":314},"type",[154,564,565],{"class":186},"(plain[",[154,567,568],{"class":163},"\"Cover\"",[154,570,571],{"class":186},"][",[154,573,337],{"class":163},[154,575,576],{"class":186},"].value).",[154,578,579],{"class":314},"__name__",[154,581,318],{"class":186},[154,583,584],{"class":314},"repr",[154,586,565],{"class":186},[154,588,568],{"class":163},[154,590,571],{"class":186},[154,592,337],{"class":163},[154,594,595],{"class":186},"].value))   ",[154,597,599],{"class":598},"s-wDw","# str — bold lost\n",[154,601,602],{"class":67,"line":235},[154,603,239],{"emptyLinePlaceholder":238},[154,605,606,608,610,612,614,616,619,621,623],{"class":67,"line":242},[154,607,245],{"class":186},[154,609,248],{"class":182},[154,611,547],{"class":186},[154,613,501],{"class":163},[154,615,318],{"class":186},[154,617,618],{"class":308},"rich_text",[154,620,248],{"class":182},[154,622,326],{"class":314},[154,624,329],{"class":186},[154,626,627,629,631,633,636,638,640,642,644,646,649],{"class":67,"line":254},[154,628,556],{"class":314},[154,630,559],{"class":186},[154,632,562],{"class":314},[154,634,635],{"class":186},"(wb[",[154,637,568],{"class":163},[154,639,571],{"class":186},[154,641,337],{"class":163},[154,643,576],{"class":186},[154,645,579],{"class":314},[154,647,648],{"class":186},")                                          ",[154,650,651],{"class":598},"# CellRichText\n",[10,653,654,655,658,659,662,663,666],{},"Whole-cell styles — the font of ",[151,656,657],{},"A1",", the fill of ",[151,660,661],{},"A3"," — are unaffected either way, because they belong to the cell rather than to its text. Assigning a new\nstring to ",[151,664,665],{},"cell.value"," keeps them.",[139,668,670],{"id":669},"step-2-replace-substrings-in-text-cells","Step 2 — Replace substrings in text cells",[10,672,673,674,677,678,680],{},"Walk every worksheet with ",[151,675,676],{},"iter_rows()",", touch only string values, and skip anything that starts with ",[151,679,248],{}," — that is a formula, and a blind replacement\ninside it can break a reference such as a sheet name. Return a list of changes rather than printing them, so the caller decides what to do with the record.",[144,682,684],{"className":173,"code":683,"language":175,"meta":149,"style":149},"def replace_text(cell, old: str, new: str) -> bool:\n    value = cell.value\n    if isinstance(value, CellRichText):\n        changed = False\n        for index, part in enumerate(value):\n            if isinstance(part, str):\n                if old in part:\n                    value[index] = part.replace(old, new)\n                    changed = True\n            elif old in part.text:\n                part.text = part.text.replace(old, new)\n                changed = True\n        if changed:\n            cell.value = value\n        return changed\n    if isinstance(value, str) and not value.startswith(\"=\") and old in value:\n        cell.value = value.replace(old, new)\n        return True\n    return False\n\ndef find_and_replace(wb, old: str, new: str) -> list[dict]:\n    changes = []\n    for ws in wb.worksheets:\n        for row in ws.iter_rows():\n            for cell in row:\n                before = str(cell.value)\n                if replace_text(cell, old, new):\n                    changes.append({\"sheet\": ws.title, \"cell\": cell.coordinate,\n                                    \"before\": before, \"after\": str(cell.value)})\n    return changes\n\nwb = load_workbook(\"report_template.xlsx\", rich_text=True)\nfor change in find_and_replace(wb, \"Acme Ltd\", \"Contoso Limited\"):\n    print(change)\nwb.save(\"report_contoso.xlsx\")\n",[151,685,686,715,725,736,746,763,778,791,801,811,823,833,842,850,860,868,905,915,921,928,933,959,970,984,997,1011,1025,1033,1051,1071,1079,1084,1105,1128,1137],{"__ignoreMap":149},[154,687,688,691,695,698,701,704,706,709,712],{"class":67,"line":156},[154,689,690],{"class":182},"def",[154,692,694],{"class":693},"s_Opv"," replace_text",[154,696,697],{"class":186},"(cell, old: ",[154,699,700],{"class":314},"str",[154,702,703],{"class":186},", new: ",[154,705,700],{"class":314},[154,707,708],{"class":186},") -> ",[154,710,711],{"class":314},"bool",[154,713,714],{"class":186},":\n",[154,716,717,720,722],{"class":67,"line":196},[154,718,719],{"class":186},"    value ",[154,721,248],{"class":182},[154,723,724],{"class":186}," cell.value\n",[154,726,727,730,733],{"class":67,"line":209},[154,728,729],{"class":182},"    if",[154,731,732],{"class":314}," isinstance",[154,734,735],{"class":186},"(value, CellRichText):\n",[154,737,738,741,743],{"class":67,"line":222},[154,739,740],{"class":186},"        changed ",[154,742,248],{"class":182},[154,744,745],{"class":314}," False\n",[154,747,748,751,754,757,760],{"class":67,"line":235},[154,749,750],{"class":182},"        for",[154,752,753],{"class":186}," index, part ",[154,755,756],{"class":182},"in",[154,758,759],{"class":314}," enumerate",[154,761,762],{"class":186},"(value):\n",[154,764,765,768,770,773,775],{"class":67,"line":242},[154,766,767],{"class":182},"            if",[154,769,732],{"class":314},[154,771,772],{"class":186},"(part, ",[154,774,700],{"class":314},[154,776,777],{"class":186},"):\n",[154,779,780,783,786,788],{"class":67,"line":254},[154,781,782],{"class":182},"                if",[154,784,785],{"class":186}," old ",[154,787,756],{"class":182},[154,789,790],{"class":186}," part:\n",[154,792,793,796,798],{"class":67,"line":265},[154,794,795],{"class":186},"                    value[index] ",[154,797,248],{"class":182},[154,799,800],{"class":186}," part.replace(old, new)\n",[154,802,803,806,808],{"class":67,"line":276},[154,804,805],{"class":186},"                    changed ",[154,807,248],{"class":182},[154,809,810],{"class":314}," True\n",[154,812,813,816,818,820],{"class":67,"line":293},[154,814,815],{"class":182},"            elif",[154,817,785],{"class":186},[154,819,756],{"class":182},[154,821,822],{"class":186}," part.text:\n",[154,824,825,828,830],{"class":67,"line":332},[154,826,827],{"class":186},"                part.text ",[154,829,248],{"class":182},[154,831,832],{"class":186}," part.text.replace(old, new)\n",[154,834,835,838,840],{"class":67,"line":374},[154,836,837],{"class":186},"                changed ",[154,839,248],{"class":182},[154,841,810],{"class":314},[154,843,844,847],{"class":67,"line":389},[154,845,846],{"class":182},"        if",[154,848,849],{"class":186}," changed:\n",[154,851,852,855,857],{"class":67,"line":419},[154,853,854],{"class":186},"            cell.value ",[154,856,248],{"class":182},[154,858,859],{"class":186}," value\n",[154,861,862,865],{"class":67,"line":434},[154,863,864],{"class":182},"        return",[154,866,867],{"class":186}," changed\n",[154,869,870,872,874,877,879,882,885,888,891,894,896,898,900,902],{"class":67,"line":449},[154,871,729],{"class":182},[154,873,732],{"class":314},[154,875,876],{"class":186},"(value, ",[154,878,700],{"class":314},[154,880,881],{"class":186},") ",[154,883,884],{"class":182},"and",[154,886,887],{"class":182}," not",[154,889,890],{"class":186}," value.startswith(",[154,892,893],{"class":163},"\"=\"",[154,895,881],{"class":186},[154,897,884],{"class":182},[154,899,785],{"class":186},[154,901,756],{"class":182},[154,903,904],{"class":186}," value:\n",[154,906,907,910,912],{"class":67,"line":465},[154,908,909],{"class":186},"        cell.value ",[154,911,248],{"class":182},[154,913,914],{"class":186}," value.replace(old, new)\n",[154,916,917,919],{"class":67,"line":481},[154,918,864],{"class":182},[154,920,810],{"class":314},[154,922,923,926],{"class":67,"line":495},[154,924,925],{"class":182},"    return",[154,927,745],{"class":314},[154,929,931],{"class":67,"line":930},20,[154,932,239],{"emptyLinePlaceholder":238},[154,934,936,938,941,944,946,948,950,953,956],{"class":67,"line":935},21,[154,937,690],{"class":182},[154,939,940],{"class":693}," find_and_replace",[154,942,943],{"class":186},"(wb, old: ",[154,945,700],{"class":314},[154,947,703],{"class":186},[154,949,700],{"class":314},[154,951,952],{"class":186},") -> list[",[154,954,955],{"class":314},"dict",[154,957,958],{"class":186},"]:\n",[154,960,962,965,967],{"class":67,"line":961},22,[154,963,964],{"class":186},"    changes ",[154,966,248],{"class":182},[154,968,969],{"class":186}," []\n",[154,971,973,976,979,981],{"class":67,"line":972},23,[154,974,975],{"class":182},"    for",[154,977,978],{"class":186}," ws ",[154,980,756],{"class":182},[154,982,983],{"class":186}," wb.worksheets:\n",[154,985,987,989,992,994],{"class":67,"line":986},24,[154,988,750],{"class":182},[154,990,991],{"class":186}," row ",[154,993,756],{"class":182},[154,995,996],{"class":186}," ws.iter_rows():\n",[154,998,1000,1003,1006,1008],{"class":67,"line":999},25,[154,1001,1002],{"class":182},"            for",[154,1004,1005],{"class":186}," cell ",[154,1007,756],{"class":182},[154,1009,1010],{"class":186}," row:\n",[154,1012,1014,1017,1019,1022],{"class":67,"line":1013},26,[154,1015,1016],{"class":186},"                before ",[154,1018,248],{"class":182},[154,1020,1021],{"class":314}," str",[154,1023,1024],{"class":186},"(cell.value)\n",[154,1026,1028,1030],{"class":67,"line":1027},27,[154,1029,782],{"class":182},[154,1031,1032],{"class":186}," replace_text(cell, old, new):\n",[154,1034,1036,1039,1042,1045,1048],{"class":67,"line":1035},28,[154,1037,1038],{"class":186},"                    changes.append({",[154,1040,1041],{"class":163},"\"sheet\"",[154,1043,1044],{"class":186},": ws.title, ",[154,1046,1047],{"class":163},"\"cell\"",[154,1049,1050],{"class":186},": cell.coordinate,\n",[154,1052,1054,1057,1060,1063,1066,1068],{"class":67,"line":1053},29,[154,1055,1056],{"class":163},"                                    \"before\"",[154,1058,1059],{"class":186},": before, ",[154,1061,1062],{"class":163},"\"after\"",[154,1064,1065],{"class":186},": ",[154,1067,700],{"class":314},[154,1069,1070],{"class":186},"(cell.value)})\n",[154,1072,1074,1076],{"class":67,"line":1073},30,[154,1075,925],{"class":182},[154,1077,1078],{"class":186}," changes\n",[154,1080,1082],{"class":67,"line":1081},31,[154,1083,239],{"emptyLinePlaceholder":238},[154,1085,1087,1089,1091,1093,1095,1097,1099,1101,1103],{"class":67,"line":1086},32,[154,1088,245],{"class":186},[154,1090,248],{"class":182},[154,1092,547],{"class":186},[154,1094,501],{"class":163},[154,1096,318],{"class":186},[154,1098,618],{"class":308},[154,1100,248],{"class":182},[154,1102,326],{"class":314},[154,1104,329],{"class":186},[154,1106,1108,1111,1114,1116,1119,1121,1123,1126],{"class":67,"line":1107},33,[154,1109,1110],{"class":182},"for",[154,1112,1113],{"class":186}," change ",[154,1115,756],{"class":182},[154,1117,1118],{"class":186}," find_and_replace(wb, ",[154,1120,363],{"class":163},[154,1122,318],{"class":186},[154,1124,1125],{"class":163},"\"Contoso Limited\"",[154,1127,777],{"class":186},[154,1129,1131,1134],{"class":67,"line":1130},34,[154,1132,1133],{"class":314},"    print",[154,1135,1136],{"class":186},"(change)\n",[154,1138,1140,1142,1145],{"class":67,"line":1139},35,[154,1141,498],{"class":186},[154,1143,1144],{"class":163},"\"report_contoso.xlsx\"",[154,1146,329],{"class":186},[10,1148,1149],{},"Rich text is replaced run by run, which keeps each run's formatting. The limitation is that a search string split across two runs — \"Acme\" in normal\ntext and \"Ltd\" in bold — will not match, because no single run contains it. That is rare for names and codes, and the change log makes any miss easy to\nspot.",[20,1151,29,1157,29,1160,29,1163,29,1167,29,1172,29,1176,29,1182,29,1185,29,1189,29,1191,29,1196,29,1198,29,1202,29,1204,29,1207,29,1210,29,1214,29,1216,29,1220,29,1222,29,1225,29,1228,29,1232,29,1236,29,1240,29,1242,29,1245,29,1247,29,1251,29,1253,29,1256,29,1258,29,1261,29,1264,29,1268,29,1270,29,1273,29,1275,29,1278],{"viewBox":1152,"role":23,"ariaLabelledBy":1153,"xmlns":27,"style":1156},"0 0 446 254",[1154,1155],"far-cells-t","far-cells-d","width:100%;max-width:446px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1158,1159],{"id":1154},"How each kind of cell is treated",[35,1161,1162],{"id":1155},"A plain text cell has its substring replaced. A rich text cell is replaced run by run keeping bold formatting. A formula cell is skipped. A number cell is skipped. A whole-cell match changes only when the entire value matches.",[39,1164],{"x":41,"y":41,"width":1165,"height":1166,"fill":44},"446","254",[39,1168],{"x":47,"y":47,"width":1169,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},"36","30","stroke-width:1px",[39,1173],{"x":1174,"y":47,"width":1175,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},"56","220",[55,1177,1181],{"x":1178,"y":1179,"style":1180},"166","40","font-size:11.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[39,1183],{"x":1184,"y":47,"width":48,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},"276",[55,1186,1188],{"x":1187,"y":1179,"style":1180},"351","B",[39,1190],{"x":47,"y":58,"width":1169,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},[55,1192,1195],{"x":1193,"y":1194,"style":1180},"38","70","1",[39,1197],{"x":1174,"y":58,"width":1175,"height":1170,"rx":41,"fill":122,"stroke":123,"style":1171},[55,1199,1201],{"x":1178,"y":1194,"style":1200},"font-size:11.5px;font-weight:600;fill:var(--teal-ink,#0b6157);text-anchor:middle","Acme Ltd — Quarterly Report",[39,1203],{"x":1184,"y":58,"width":48,"height":1170,"rx":41,"fill":122,"stroke":123,"style":1171},[55,1205,1206],{"x":1187,"y":1194,"style":1200},"replaced",[39,1208],{"x":47,"y":1209,"width":1169,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},"80",[55,1211,1213],{"x":1193,"y":1212,"style":1180},"100","2",[39,1215],{"x":1174,"y":1209,"width":1175,"height":1170,"rx":41,"fill":81,"stroke":72,"style":1171},[55,1217,1219],{"x":1178,"y":1212,"style":1218},"font-size:11.5px;font-weight:600;fill:var(--brand-strong,#4338ca);text-anchor:middle","Prepared by Acme Ltd finance",[39,1221],{"x":1184,"y":1209,"width":48,"height":1170,"rx":41,"fill":81,"stroke":72,"style":1171},[55,1223,1224],{"x":1187,"y":1212,"style":1218},"replaced per run",[39,1226],{"x":47,"y":1227,"width":1169,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},"110",[55,1229,1231],{"x":1193,"y":1230,"style":1180},"130","3",[39,1233],{"x":1174,"y":1227,"width":1175,"height":1170,"rx":41,"fill":1234,"stroke":1235,"style":1171},"#fdefd8","var(--gold,#b4740a)",[55,1237,1239],{"x":1178,"y":1230,"style":1238},"font-size:11.5px;font-weight:600;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Acme",[39,1241],{"x":1184,"y":1227,"width":48,"height":1170,"rx":41,"fill":1234,"stroke":1235,"style":1171},[55,1243,1244],{"x":1187,"y":1230,"style":1238},"whole-cell rule",[39,1246],{"x":47,"y":135,"width":1169,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},[55,1248,1250],{"x":1193,"y":1249,"style":1180},"160","4",[39,1252],{"x":1174,"y":135,"width":1175,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},[55,1254,1255],{"x":1178,"y":1249,"style":64},"=CONCAT(\"Acme\", \" Ltd\")",[39,1257],{"x":1184,"y":135,"width":48,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},[55,1259,1260],{"x":1187,"y":1249,"style":64},"skipped: formula",[39,1262],{"x":47,"y":1263,"width":1169,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},"170",[55,1265,1267],{"x":1193,"y":1266,"style":1180},"190","5",[39,1269],{"x":1174,"y":1263,"width":1175,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},[55,1271,1272],{"x":1178,"y":1266,"style":64},"2025",[39,1274],{"x":1184,"y":1263,"width":48,"height":1170,"rx":41,"fill":51,"stroke":52,"style":1171},[55,1276,1277],{"x":1187,"y":1266,"style":64},"skipped: number",[55,1279,1282],{"x":1280,"y":1281,"style":136},"223","232","only text changes; formulas and numbers stay",[139,1284,1286],{"id":1285},"step-3-match-whole-cells-only","Step 3 — Match whole cells only",[10,1288,1289,1290,1292,1293,1296,1297,1300,1301,1304,1305,1300,1308,1311],{},"Substring replacement is wrong for codes and short names. Replacing ",[151,1291,1239],{}," with ",[151,1294,1295],{},"Contoso"," everywhere would also turn ",[151,1298,1299],{},"Acme Ltd"," into ",[151,1302,1303],{},"Contoso Ltd"," and a\ncustomer called ",[151,1306,1307],{},"Acmeworks",[151,1309,1310],{},"Contosoworks",". When the value in a cell is the thing being renamed, compare the whole stripped value instead.",[144,1313,1315],{"className":173,"code":1314,"language":175,"meta":149,"style":149},"def replace_whole(wb, mapping: dict[str, str]) -> list[tuple[str, str, str]]:\n    changes = []\n    for ws in wb.worksheets:\n        for row in ws.iter_rows():\n            for cell in row:\n                if isinstance(cell.value, str) and cell.value.strip() in mapping:\n                    old = cell.value\n                    cell.value = mapping[old.strip()]\n                    changes.append((ws.title, cell.coordinate, old))\n    return changes\n\nwb = load_workbook(\"report_template.xlsx\", rich_text=True)\nprint(replace_whole(wb, {\"Acme\": \"Contoso\", \"Northwind\": \"Northwind Traders\"}))\n",[151,1316,1317,1349,1357,1367,1377,1387,1410,1419,1429,1434,1440,1444,1464],{"__ignoreMap":149},[154,1318,1319,1321,1324,1327,1329,1331,1333,1336,1338,1340,1342,1344,1346],{"class":67,"line":156},[154,1320,690],{"class":182},[154,1322,1323],{"class":693}," replace_whole",[154,1325,1326],{"class":186},"(wb, mapping: dict[",[154,1328,700],{"class":314},[154,1330,318],{"class":186},[154,1332,700],{"class":314},[154,1334,1335],{"class":186},"]) -> list[tuple[",[154,1337,700],{"class":314},[154,1339,318],{"class":186},[154,1341,700],{"class":314},[154,1343,318],{"class":186},[154,1345,700],{"class":314},[154,1347,1348],{"class":186},"]]:\n",[154,1350,1351,1353,1355],{"class":67,"line":196},[154,1352,964],{"class":186},[154,1354,248],{"class":182},[154,1356,969],{"class":186},[154,1358,1359,1361,1363,1365],{"class":67,"line":209},[154,1360,975],{"class":182},[154,1362,978],{"class":186},[154,1364,756],{"class":182},[154,1366,983],{"class":186},[154,1368,1369,1371,1373,1375],{"class":67,"line":222},[154,1370,750],{"class":182},[154,1372,991],{"class":186},[154,1374,756],{"class":182},[154,1376,996],{"class":186},[154,1378,1379,1381,1383,1385],{"class":67,"line":235},[154,1380,1002],{"class":182},[154,1382,1005],{"class":186},[154,1384,756],{"class":182},[154,1386,1010],{"class":186},[154,1388,1389,1391,1393,1396,1398,1400,1402,1405,1407],{"class":67,"line":242},[154,1390,782],{"class":182},[154,1392,732],{"class":314},[154,1394,1395],{"class":186},"(cell.value, ",[154,1397,700],{"class":314},[154,1399,881],{"class":186},[154,1401,884],{"class":182},[154,1403,1404],{"class":186}," cell.value.strip() ",[154,1406,756],{"class":182},[154,1408,1409],{"class":186}," mapping:\n",[154,1411,1412,1415,1417],{"class":67,"line":254},[154,1413,1414],{"class":186},"                    old ",[154,1416,248],{"class":182},[154,1418,724],{"class":186},[154,1420,1421,1424,1426],{"class":67,"line":265},[154,1422,1423],{"class":186},"                    cell.value ",[154,1425,248],{"class":182},[154,1427,1428],{"class":186}," mapping[old.strip()]\n",[154,1430,1431],{"class":67,"line":276},[154,1432,1433],{"class":186},"                    changes.append((ws.title, cell.coordinate, old))\n",[154,1435,1436,1438],{"class":67,"line":293},[154,1437,925],{"class":182},[154,1439,1078],{"class":186},[154,1441,1442],{"class":67,"line":332},[154,1443,239],{"emptyLinePlaceholder":238},[154,1445,1446,1448,1450,1452,1454,1456,1458,1460,1462],{"class":67,"line":374},[154,1447,245],{"class":186},[154,1449,248],{"class":182},[154,1451,547],{"class":186},[154,1453,501],{"class":163},[154,1455,318],{"class":186},[154,1457,618],{"class":308},[154,1459,248],{"class":182},[154,1461,326],{"class":314},[154,1463,329],{"class":186},[154,1465,1466,1468,1471,1474,1476,1479,1481,1483,1485,1488],{"class":67,"line":389},[154,1467,556],{"class":314},[154,1469,1470],{"class":186},"(replace_whole(wb, {",[154,1472,1473],{"class":163},"\"Acme\"",[154,1475,1065],{"class":186},[154,1477,1478],{"class":163},"\"Contoso\"",[154,1480,318],{"class":186},[154,1482,486],{"class":163},[154,1484,1065],{"class":186},[154,1486,1487],{"class":163},"\"Northwind Traders\"",[154,1489,1490],{"class":186},"}))\n",[20,1492,29,1497,29,1500,29,1503,29,1506,29,1513,29,1519,29,1522,29,1526,29,1530,29,1534,29,1537,29,1541,29,1545,29,1547,29,1550,29,1553,29,1556,29,1561,29,1565,29,1567,29,1569,29,1571],{"viewBox":1493,"role":23,"ariaLabelledBy":1494,"xmlns":27,"style":28},"0 0 760 191",[1495,1496],"far-modes-t","far-modes-d",[31,1498,1499],{"id":1495},"Substring, whole-cell and regex replacement compared",[35,1501,1502],{"id":1496},"Replacing Acme with Contoso as a substring also changes Acme Ltd and Acmeworks. A whole-cell match changes only cells whose entire value is Acme. A regex with word boundaries and a lookahead changes a bare Acme inside longer text but not Acme Ltd.",[39,1504],{"x":41,"y":41,"width":42,"height":1505,"fill":44},"191",[39,1507],{"x":47,"y":47,"width":1508,"height":1509,"rx":1510,"fill":1511,"stroke":1512,"style":53},"226.7","127","14","#fee8f2","var(--accent,#f43f8f)",[55,1514,1518],{"x":1515,"y":1516,"style":1517},"133.3","46","font-size:13px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","substring",[67,1520],{"x1":1169,"y1":1174,"x2":1521,"y2":1174,"stroke":1512,"style":1171},"230.7",[55,1523,1525],{"x":1515,"y":1524,"style":89},"78","\"Acme\" → changed",[55,1527,1529],{"x":1515,"y":1528,"style":89},"101","\"Acme Ltd\" → changed",[55,1531,1533],{"x":1515,"y":1532,"style":89},"124","\"Acmeworks\" → changed",[39,1535],{"x":1536,"y":47,"width":1508,"height":1509,"rx":1510,"fill":122,"stroke":123,"style":53},"266.7",[55,1538,1540],{"x":134,"y":1516,"style":1539},"font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","whole cell",[67,1542],{"x1":1543,"y1":1174,"x2":1544,"y2":1174,"stroke":123,"style":1171},"282.7","477.3",[55,1546,1525],{"x":134,"y":1524,"style":89},[55,1548,1549],{"x":134,"y":1528,"style":89},"\"Acme Ltd\" → kept",[55,1551,1552],{"x":134,"y":1532,"style":89},"\"Acmeworks\" → kept",[39,1554],{"x":1555,"y":47,"width":1508,"height":1509,"rx":1510,"fill":81,"stroke":72,"style":53},"513.3",[55,1557,1560],{"x":1558,"y":1516,"style":1559},"626.7","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","regex rule",[67,1562],{"x1":1563,"y1":1174,"x2":1564,"y2":1174,"stroke":72,"style":1171},"529.3","724",[55,1566,1525],{"x":1558,"y":1524,"style":89},[55,1568,1549],{"x":1558,"y":1528,"style":89},[55,1570,1552],{"x":1558,"y":1532,"style":89},[55,1572,1574],{"x":134,"y":1573,"style":136},"177","short search terms need whole-cell or boundary-aware matching",[10,1576,1577],{},"A dictionary makes a bulk rename — dozens of old codes to new codes — a single pass through the workbook. Load the mapping from a CSV maintained by whoever\nowns the rename, rather than hard-coding it.",[139,1579,1581],{"id":1580},"step-4-use-regular-expressions-for-patterns","Step 4 — Use regular expressions for patterns",[10,1583,1584,1585,1588,1589,1592,1593,1596,1597,1600],{},"Some replacements are patterns rather than literals: every ",[151,1586,1587],{},"FY2025"," becoming ",[151,1590,1591],{},"FY2026",", or product codes of the form ",[151,1594,1595],{},"AB-1234"," gaining a new prefix. ",[151,1598,1599],{},"re.subn","\nreturns the new text and the number of substitutions, which doubles as the \"did anything change\" test.",[144,1602,1604],{"className":173,"code":1603,"language":175,"meta":149,"style":149},"import re\n\ndef regex_replace(wb, pattern: str, replacement: str) -> int:\n    compiled = re.compile(pattern)\n    total = 0\n    for ws in wb.worksheets:\n        for row in ws.iter_rows():\n            for cell in row:\n                if isinstance(cell.value, str) and not cell.value.startswith(\"=\"):\n                    new_value, count = compiled.subn(replacement, cell.value)\n                    if count:\n                        cell.value = new_value\n                        total += count\n    return total\n\nwb = load_workbook(\"report_template.xlsx\", rich_text=True)\nprint(regex_replace(wb, r\"\\bAcme\\b(?! Ltd)\", \"Contoso\"))     # only a bare \"Acme\"\n",[151,1605,1606,1613,1617,1641,1651,1661,1671,1681,1691,1714,1724,1732,1742,1753,1760,1764,1784],{"__ignoreMap":149},[154,1607,1608,1610],{"class":67,"line":156},[154,1609,190],{"class":182},[154,1611,1612],{"class":186}," re\n",[154,1614,1615],{"class":67,"line":196},[154,1616,239],{"emptyLinePlaceholder":238},[154,1618,1619,1621,1624,1627,1629,1632,1634,1636,1639],{"class":67,"line":209},[154,1620,690],{"class":182},[154,1622,1623],{"class":693}," regex_replace",[154,1625,1626],{"class":186},"(wb, pattern: ",[154,1628,700],{"class":314},[154,1630,1631],{"class":186},", replacement: ",[154,1633,700],{"class":314},[154,1635,708],{"class":186},[154,1637,1638],{"class":314},"int",[154,1640,714],{"class":186},[154,1642,1643,1646,1648],{"class":67,"line":222},[154,1644,1645],{"class":186},"    compiled ",[154,1647,248],{"class":182},[154,1649,1650],{"class":186}," re.compile(pattern)\n",[154,1652,1653,1656,1658],{"class":67,"line":235},[154,1654,1655],{"class":186},"    total ",[154,1657,248],{"class":182},[154,1659,1660],{"class":314}," 0\n",[154,1662,1663,1665,1667,1669],{"class":67,"line":242},[154,1664,975],{"class":182},[154,1666,978],{"class":186},[154,1668,756],{"class":182},[154,1670,983],{"class":186},[154,1672,1673,1675,1677,1679],{"class":67,"line":254},[154,1674,750],{"class":182},[154,1676,991],{"class":186},[154,1678,756],{"class":182},[154,1680,996],{"class":186},[154,1682,1683,1685,1687,1689],{"class":67,"line":265},[154,1684,1002],{"class":182},[154,1686,1005],{"class":186},[154,1688,756],{"class":182},[154,1690,1010],{"class":186},[154,1692,1693,1695,1697,1699,1701,1703,1705,1707,1710,1712],{"class":67,"line":276},[154,1694,782],{"class":182},[154,1696,732],{"class":314},[154,1698,1395],{"class":186},[154,1700,700],{"class":314},[154,1702,881],{"class":186},[154,1704,884],{"class":182},[154,1706,887],{"class":182},[154,1708,1709],{"class":186}," cell.value.startswith(",[154,1711,893],{"class":163},[154,1713,777],{"class":186},[154,1715,1716,1719,1721],{"class":67,"line":293},[154,1717,1718],{"class":186},"                    new_value, count ",[154,1720,248],{"class":182},[154,1722,1723],{"class":186}," compiled.subn(replacement, cell.value)\n",[154,1725,1726,1729],{"class":67,"line":332},[154,1727,1728],{"class":182},"                    if",[154,1730,1731],{"class":186}," count:\n",[154,1733,1734,1737,1739],{"class":67,"line":374},[154,1735,1736],{"class":186},"                        cell.value ",[154,1738,248],{"class":182},[154,1740,1741],{"class":186}," new_value\n",[154,1743,1744,1747,1750],{"class":67,"line":389},[154,1745,1746],{"class":186},"                        total ",[154,1748,1749],{"class":182},"+=",[154,1751,1752],{"class":186}," count\n",[154,1754,1755,1757],{"class":67,"line":419},[154,1756,925],{"class":182},[154,1758,1759],{"class":186}," total\n",[154,1761,1762],{"class":67,"line":434},[154,1763,239],{"emptyLinePlaceholder":238},[154,1765,1766,1768,1770,1772,1774,1776,1778,1780,1782],{"class":67,"line":449},[154,1767,245],{"class":186},[154,1769,248],{"class":182},[154,1771,547],{"class":186},[154,1773,501],{"class":163},[154,1775,318],{"class":186},[154,1777,618],{"class":308},[154,1779,248],{"class":182},[154,1781,326],{"class":314},[154,1783,329],{"class":186},[154,1785,1786,1788,1791,1794,1797,1800,1802,1804,1807,1810,1813,1815,1817,1819,1822],{"class":67,"line":465},[154,1787,556],{"class":314},[154,1789,1790],{"class":186},"(regex_replace(wb, ",[154,1792,1793],{"class":182},"r",[154,1795,1796],{"class":163},"\"",[154,1798,1799],{"class":314},"\\b",[154,1801,1239],{"class":163},[154,1803,1799],{"class":314},[154,1805,1806],{"class":182},"(?!",[154,1808,1809],{"class":163}," Ltd",[154,1811,1812],{"class":182},")",[154,1814,1796],{"class":163},[154,1816,318],{"class":186},[154,1818,1478],{"class":163},[154,1820,1821],{"class":186},"))     ",[154,1823,1824],{"class":598},"# only a bare \"Acme\"\n",[10,1826,1827,1828,1830,1831,1833],{},"The negative lookahead in that pattern replaces ",[151,1829,1239],{}," only when it is not followed by ",[151,1832,1809],{}," — the kind of rule that is impossible in Excel's dialog. Test a\npattern on a copy and read the change log before running it across a folder.",[139,1835,1837],{"id":1836},"step-5-replace-inside-formulas-deliberately","Step 5 — Replace inside formulas deliberately",[10,1839,1840,1841,1845,1846,1849,1850,1853],{},"Formulas were skipped so far, and usually that is right. When the rename ",[1842,1843,1844],"em",{},"should"," reach formulas — a sheet called ",[151,1847,1848],{},"Acme Data"," referenced as ",[151,1851,1852],{},"'Acme Data'!B2"," —\ntreat it as a separate, explicit step that only touches the text you intend.",[144,1855,1857],{"className":173,"code":1856,"language":175,"meta":149,"style":149},"def replace_in_formulas(wb, old: str, new: str) -> list[str]:\n    touched = []\n    for ws in wb.worksheets:\n        for row in ws.iter_rows():\n            for cell in row:\n                if isinstance(cell.value, str) and cell.value.startswith(\"=\") and old in cell.value:\n                    cell.value = cell.value.replace(old, new)\n                    touched.append(f\"{ws.title}!{cell.coordinate}\")\n    return touched\n\nwb = load_workbook(\"report_template.xlsx\", rich_text=True)\nprint(replace_in_formulas(wb, '\"Acme\"', '\"Contoso\"'))\n",[151,1858,1859,1880,1889,1899,1909,1919,1948,1957,1991,1998,2002,2022],{"__ignoreMap":149},[154,1860,1861,1863,1866,1868,1870,1872,1874,1876,1878],{"class":67,"line":156},[154,1862,690],{"class":182},[154,1864,1865],{"class":693}," replace_in_formulas",[154,1867,943],{"class":186},[154,1869,700],{"class":314},[154,1871,703],{"class":186},[154,1873,700],{"class":314},[154,1875,952],{"class":186},[154,1877,700],{"class":314},[154,1879,958],{"class":186},[154,1881,1882,1885,1887],{"class":67,"line":196},[154,1883,1884],{"class":186},"    touched ",[154,1886,248],{"class":182},[154,1888,969],{"class":186},[154,1890,1891,1893,1895,1897],{"class":67,"line":209},[154,1892,975],{"class":182},[154,1894,978],{"class":186},[154,1896,756],{"class":182},[154,1898,983],{"class":186},[154,1900,1901,1903,1905,1907],{"class":67,"line":222},[154,1902,750],{"class":182},[154,1904,991],{"class":186},[154,1906,756],{"class":182},[154,1908,996],{"class":186},[154,1910,1911,1913,1915,1917],{"class":67,"line":235},[154,1912,1002],{"class":182},[154,1914,1005],{"class":186},[154,1916,756],{"class":182},[154,1918,1010],{"class":186},[154,1920,1921,1923,1925,1927,1929,1931,1933,1935,1937,1939,1941,1943,1945],{"class":67,"line":242},[154,1922,782],{"class":182},[154,1924,732],{"class":314},[154,1926,1395],{"class":186},[154,1928,700],{"class":314},[154,1930,881],{"class":186},[154,1932,884],{"class":182},[154,1934,1709],{"class":186},[154,1936,893],{"class":163},[154,1938,881],{"class":186},[154,1940,884],{"class":182},[154,1942,785],{"class":186},[154,1944,756],{"class":182},[154,1946,1947],{"class":186}," cell.value:\n",[154,1949,1950,1952,1954],{"class":67,"line":254},[154,1951,1423],{"class":186},[154,1953,248],{"class":182},[154,1955,1956],{"class":186}," cell.value.replace(old, new)\n",[154,1958,1959,1962,1965,1967,1971,1974,1977,1980,1982,1985,1987,1989],{"class":67,"line":265},[154,1960,1961],{"class":186},"                    touched.append(",[154,1963,1964],{"class":182},"f",[154,1966,1796],{"class":163},[154,1968,1970],{"class":1969},"sSjpA","{",[154,1972,1973],{"class":186},"ws.title",[154,1975,1976],{"class":1969},"}",[154,1978,1979],{"class":163},"!",[154,1981,1970],{"class":1969},[154,1983,1984],{"class":186},"cell.coordinate",[154,1986,1976],{"class":1969},[154,1988,1796],{"class":163},[154,1990,329],{"class":186},[154,1992,1993,1995],{"class":67,"line":276},[154,1994,925],{"class":182},[154,1996,1997],{"class":186}," touched\n",[154,1999,2000],{"class":67,"line":293},[154,2001,239],{"emptyLinePlaceholder":238},[154,2003,2004,2006,2008,2010,2012,2014,2016,2018,2020],{"class":67,"line":332},[154,2005,245],{"class":186},[154,2007,248],{"class":182},[154,2009,547],{"class":186},[154,2011,501],{"class":163},[154,2013,318],{"class":186},[154,2015,618],{"class":308},[154,2017,248],{"class":182},[154,2019,326],{"class":314},[154,2021,329],{"class":186},[154,2023,2024,2026,2029,2032,2034,2037],{"class":67,"line":374},[154,2025,556],{"class":314},[154,2027,2028],{"class":186},"(replace_in_formulas(wb, ",[154,2030,2031],{"class":163},"'\"Acme\"'",[154,2033,318],{"class":186},[154,2035,2036],{"class":163},"'\"Contoso\"'",[154,2038,2039],{"class":186},"))\n",[10,2041,2042,2043,2046,2047,2051],{},"Renaming a sheet itself is ",[151,2044,2045],{},"ws.title = \"New name\"",", but openpyxl does not update formulas that refer to the old name — that is exactly what this step is for.\nFormula cells saved by openpyxl have no cached result until the workbook is recalculated, as explained in\n",[14,2048,2050],{"href":2049},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only\u002F","Read Formula Results with openpyxl data_only",".",[139,2053,2055],{"id":2054},"step-6-include-headers-footers-and-sheet-names","Step 6 — Include headers, footers and sheet names",[10,2057,2058],{},"Text also hides outside cells. Page headers and footers print on every page of a PDF export, and sheet names appear on every tab. A thorough rename checks them too.",[144,2060,2062],{"className":173,"code":2061,"language":175,"meta":149,"style":149},"def replace_outside_cells(wb, old: str, new: str) -> list[str]:\n    touched = []\n    for ws in wb.worksheets:\n        if old in ws.title:\n            ws.title = ws.title.replace(old, new)\n            touched.append(f\"sheet name -> {ws.title}\")\n        for part in (ws.oddHeader, ws.oddFooter):\n            for section in (part.left, part.center, part.right):\n                if section.text and old in section.text:\n                    section.text = section.text.replace(old, new)\n                    touched.append(f\"{ws.title} header\u002Ffooter\")\n    return touched\n\nwb = load_workbook(\"report_template.xlsx\", rich_text=True)\nwb[\"Cover\"].oddFooter.center.text = \"Acme Ltd — confidential\"\nprint(replace_outside_cells(wb, \"Acme Ltd\", \"Contoso Limited\"))\n",[151,2063,2064,2085,2093,2103,2114,2124,2144,2156,2168,2184,2194,2213,2219,2223,2243,2258],{"__ignoreMap":149},[154,2065,2066,2068,2071,2073,2075,2077,2079,2081,2083],{"class":67,"line":156},[154,2067,690],{"class":182},[154,2069,2070],{"class":693}," replace_outside_cells",[154,2072,943],{"class":186},[154,2074,700],{"class":314},[154,2076,703],{"class":186},[154,2078,700],{"class":314},[154,2080,952],{"class":186},[154,2082,700],{"class":314},[154,2084,958],{"class":186},[154,2086,2087,2089,2091],{"class":67,"line":196},[154,2088,1884],{"class":186},[154,2090,248],{"class":182},[154,2092,969],{"class":186},[154,2094,2095,2097,2099,2101],{"class":67,"line":209},[154,2096,975],{"class":182},[154,2098,978],{"class":186},[154,2100,756],{"class":182},[154,2102,983],{"class":186},[154,2104,2105,2107,2109,2111],{"class":67,"line":222},[154,2106,846],{"class":182},[154,2108,785],{"class":186},[154,2110,756],{"class":182},[154,2112,2113],{"class":186}," ws.title:\n",[154,2115,2116,2119,2121],{"class":67,"line":235},[154,2117,2118],{"class":186},"            ws.title ",[154,2120,248],{"class":182},[154,2122,2123],{"class":186}," ws.title.replace(old, new)\n",[154,2125,2126,2129,2131,2134,2136,2138,2140,2142],{"class":67,"line":242},[154,2127,2128],{"class":186},"            touched.append(",[154,2130,1964],{"class":182},[154,2132,2133],{"class":163},"\"sheet name -> ",[154,2135,1970],{"class":1969},[154,2137,1973],{"class":186},[154,2139,1976],{"class":1969},[154,2141,1796],{"class":163},[154,2143,329],{"class":186},[154,2145,2146,2148,2151,2153],{"class":67,"line":254},[154,2147,750],{"class":182},[154,2149,2150],{"class":186}," part ",[154,2152,756],{"class":182},[154,2154,2155],{"class":186}," (ws.oddHeader, ws.oddFooter):\n",[154,2157,2158,2160,2163,2165],{"class":67,"line":265},[154,2159,1002],{"class":182},[154,2161,2162],{"class":186}," section ",[154,2164,756],{"class":182},[154,2166,2167],{"class":186}," (part.left, part.center, part.right):\n",[154,2169,2170,2172,2175,2177,2179,2181],{"class":67,"line":276},[154,2171,782],{"class":182},[154,2173,2174],{"class":186}," section.text ",[154,2176,884],{"class":182},[154,2178,785],{"class":186},[154,2180,756],{"class":182},[154,2182,2183],{"class":186}," section.text:\n",[154,2185,2186,2189,2191],{"class":67,"line":293},[154,2187,2188],{"class":186},"                    section.text ",[154,2190,248],{"class":182},[154,2192,2193],{"class":186}," section.text.replace(old, new)\n",[154,2195,2196,2198,2200,2202,2204,2206,2208,2211],{"class":67,"line":332},[154,2197,1961],{"class":186},[154,2199,1964],{"class":182},[154,2201,1796],{"class":163},[154,2203,1970],{"class":1969},[154,2205,1973],{"class":186},[154,2207,1976],{"class":1969},[154,2209,2210],{"class":163}," header\u002Ffooter\"",[154,2212,329],{"class":186},[154,2214,2215,2217],{"class":67,"line":374},[154,2216,925],{"class":182},[154,2218,1997],{"class":186},[154,2220,2221],{"class":67,"line":389},[154,2222,239],{"emptyLinePlaceholder":238},[154,2224,2225,2227,2229,2231,2233,2235,2237,2239,2241],{"class":67,"line":419},[154,2226,245],{"class":186},[154,2228,248],{"class":182},[154,2230,547],{"class":186},[154,2232,501],{"class":163},[154,2234,318],{"class":186},[154,2236,618],{"class":308},[154,2238,248],{"class":182},[154,2240,326],{"class":314},[154,2242,329],{"class":186},[154,2244,2245,2248,2250,2253,2255],{"class":67,"line":434},[154,2246,2247],{"class":186},"wb[",[154,2249,568],{"class":163},[154,2251,2252],{"class":186},"].oddFooter.center.text ",[154,2254,248],{"class":182},[154,2256,2257],{"class":163}," \"Acme Ltd — confidential\"\n",[154,2259,2260,2262,2265,2267,2269,2271],{"class":67,"line":449},[154,2261,556],{"class":314},[154,2263,2264],{"class":186},"(replace_outside_cells(wb, ",[154,2266,363],{"class":163},[154,2268,318],{"class":186},[154,2270,1125],{"class":163},[154,2272,2039],{"class":186},[10,2274,2275,2276,2280,2281,2284],{},"Headers and footers are set up in ",[14,2277,2279],{"href":2278},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fset-print-area-and-page-setup-with-openpyxl\u002F","Set the Print Area and Page Setup with openpyxl",".\nChart titles, text boxes and comments are further places text can live; openpyxl exposes comments on each cell as ",[151,2282,2283],{},"cell.comment",", while shapes and text boxes are not loaded at all.",[139,2286,2288],{"id":2287},"common-pitfalls","Common pitfalls",[2290,2291,2292,2308],"table",{},[2293,2294,2295],"thead",{},[2296,2297,2298,2302,2305],"tr",{},[2299,2300,2301],"th",{},"Symptom",[2299,2303,2304],{},"Cause",[2299,2306,2307],{},"Fix",[2309,2310,2311,2327,2341,2352,2363,2381],"tbody",{},[2296,2312,2313,2317,2322],{},[2314,2315,2316],"td",{},"Bold or coloured words become plain after saving",[2314,2318,2319,2320],{},"Workbook loaded without ",[151,2321,65],{},[2314,2323,2324,2325],{},"Load with ",[151,2326,65],{},[2296,2328,2329,2332,2335],{},[2314,2330,2331],{},"A formula breaks after replacement",[2314,2333,2334],{},"Replacement ran inside formula text",[2314,2336,2337,2338,2340],{},"Skip strings starting with ",[151,2339,248],{},"; handle formulas explicitly",[2296,2342,2343,2346,2349],{},[2314,2344,2345],{},"Unrelated words changed",[2314,2347,2348],{},"Substring match on a short term",[2314,2350,2351],{},"Use whole-cell matching or a regex with word boundaries",[2296,2353,2354,2357,2360],{},[2314,2355,2356],{},"Numbers not replaced",[2314,2358,2359],{},"Only string values are checked",[2314,2361,2362],{},"Convert the rule to numbers explicitly if that is intended",[2296,2364,2365,2368,2371],{},[2314,2366,2367],{},"Charts, images or macros missing in the output",[2314,2369,2370],{},"openpyxl does not keep every workbook part",[2314,2372,2373,2374,2377,2378],{},"Test on a copy; use ",[151,2375,2376],{},"keep_vba=True"," for ",[151,2379,2380],{},".xlsm",[2296,2382,2383,2386,2389],{},[2314,2384,2385],{},"Match missed in a rich text cell",[2314,2387,2388],{},"Search text split across runs",[2314,2390,2391],{},"Inspect the log; edit that cell by hand",[139,2393,2395],{"id":2394},"performance-and-scale-notes","Performance and scale notes",[10,2397,2398,2399,2402,2403,2406,2407,2410],{},"A full ",[151,2400,2401],{},"load_workbook"," holds every cell in memory, and a replacement pass is a Python loop over all of them — about a second for tens of thousands of cells, and\nproportionally longer for large data sheets. Restrict the walk to the sheets that can contain the text, or to a column range with ",[151,2404,2405],{},"min_col"," and ",[151,2408,2409],{},"max_col",". For a\nfolder of templates, process files one at a time and write each result to a new path, so a failure halfway through never leaves a half-renamed original.",[139,2412,2414],{"id":2413},"conclusion","Conclusion",[10,2416,2324,2417,2419,2420,2422],{},[151,2418,65],{},", walk the cells, and change only string values that are not formulas — assigning ",[151,2421,665],{}," keeps each cell's style, and replacing rich text\nrun by run keeps partial formatting. Choose substring, whole-cell or regex matching deliberately, treat formulas, sheet names, headers and footers as separate explicit\nsteps, and log every change. Save to a new file and review the log before replacing the originals.",[139,2424,2426],{"id":2425},"frequently-asked-questions","Frequently asked questions",[10,2428,2429,2433],{},[2430,2431,2432],"strong",{},"Does openpyxl keep cell formatting when I change a value?","\nYes. Fonts, fills, borders and number formats belong to the cell, and assigning a new value leaves them in place. Only formatting inside the text of a rich text cell needs rich_text=True.",[10,2435,2436,2439],{},[2430,2437,2438],{},"Can I do a case-insensitive replace?","\nYes. Compile the pattern with re.IGNORECASE and use subn, as in the regex step. Keep the replacement text in the case you want to appear.",[10,2441,2442,2445],{},[2430,2443,2444],{},"Will this work on .xlsm files with macros?","\nLoad with keep_vba=True and save with the .xlsm extension, and the VBA project is carried through. Test on a copy, because some other parts such as form controls are not preserved.",[10,2447,2448,2451],{},[2430,2449,2450],{},"How do I replace text in many files at once?","\nLoop over the files with pathlib, run the same function on each workbook, save to an output folder, and collect the change logs into one CSV for review.",[139,2453,2455],{"id":2454},"related","Related",[2457,2458,2459,2466,2473,2480,2487],"ul",{},[2460,2461,2462,2463,2465],"li",{},"Up one level: ",[14,2464,17],{"href":16}," — the core workbook editing techniques.",[2460,2467,2468,2472],{},[14,2469,2471],{"href":2470},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fiterate-over-rows-and-columns-with-openpyxl\u002F","Iterate over Rows and Columns with openpyxl"," — the traversal patterns used here.",[2460,2474,2475,2479],{},[14,2476,2478],{"href":2477},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-the-last-row-with-data-in-openpyxl\u002F","Find the Last Row with Data in openpyxl"," — limiting the walk to real data.",[2460,2481,2482,2486],{},[14,2483,2485],{"href":2484},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Ffill-excel-template-with-python-openpyxl\u002F","Fill an Excel Template with Python and openpyxl"," — writing values into templates rather than replacing them.",[2460,2488,2489,2493],{},[14,2490,2492],{"href":2491},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Freplace-placeholders-in-an-excel-template-with-python\u002F","Replace Placeholders in an Excel Template with Python"," — the placeholder-driven version of this technique.",[2495,2496,2497],"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 .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":149,"searchDepth":196,"depth":196,"links":2499},[2500,2501,2502,2503,2504,2505,2506,2507,2508,2509,2510,2511],{"id":141,"depth":196,"text":142},{"id":506,"depth":196,"text":507},{"id":669,"depth":196,"text":670},{"id":1285,"depth":196,"text":1286},{"id":1580,"depth":196,"text":1581},{"id":1836,"depth":196,"text":1837},{"id":2054,"depth":196,"text":2055},{"id":2287,"depth":196,"text":2288},{"id":2394,"depth":196,"text":2395},{"id":2413,"depth":196,"text":2414},{"id":2425,"depth":196,"text":2426},{"id":2454,"depth":196,"text":2455},"2026-09-17","Replace a company name, a product code or a year across every sheet of a workbook without losing formatting: plain and regex replacement, formulas left intact, rich text preserved, and a log of every change.","md",[2516,2518,2520,2522],{"q":2432,"a":2517},"Yes. Fonts, fills, borders and number formats belong to the cell, and assigning a new value leaves them in place. Only formatting inside the text of a rich text cell needs rich_text=True.",{"q":2438,"a":2519},"Yes. Compile the pattern with re.IGNORECASE and use subn, as in the regex step. Keep the replacement text in the case you want to appear.",{"q":2444,"a":2521},"Load with keep_vba=True and save with the .xlsm extension, and the VBA project is carried through. Test on a copy, because some other parts such as form controls are not preserved.",{"q":2450,"a":2523},"Loop over the files with pathlib, run the same function on each workbook, save to an output folder, and collect the change logs into one CSV for review.","find and replace text in excel with openpyxl",null,{"breadcrumb":2527},[2528,2531,2534],{"name":2529,"item":2530},"Home","\u002F",{"name":2532,"item":2533},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":17,"item":16},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl",{"title":2537,"description":2538},"Find and Replace Text in Excel with Python and openpyxl","Find and replace in Excel files with openpyxl: whole-cell and substring matches, regular expressions, skipping formulas, preserving rich text formatting, sheet headers, and a change log.","find-and-replace-text-in-excel-with-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-and-replace-text-in-excel-with-openpyxl\u002Findex",[2542,2543],"writing-excel","openpyxl","how-to","6yA8qQG0NyiloXhwZnXIMT40tlcLpjdxB8i6rj9YdHo",[2547,2551],{"title":2548,"path":2549,"stem":2550,"children":-1},"Delete Empty Rows and Columns with openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fdelete-empty-rows-and-columns-with-openpyxl\u002Findex",{"title":2478,"path":2552,"stem":2553,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-the-last-row-with-data-in-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-the-last-row-with-data-in-openpyxl\u002Findex",[2555,2557],{"path":2556,"title":2532},"\u002Fgetting-started-with-python-excel-automation",{"path":2558,"title":17},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation",[],[2561,2564,2567,2571,2574,2577],{"path":2562,"title":2563,"kind":2544},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fwrite-multiple-dataframes-to-one-excel-file","Write Multiple DataFrames to One Excel File",{"path":2565,"title":2566,"kind":2544},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-formatted-excel-report-with-xlsxwriter","Write a Formatted Excel Report with xlsxwriter",{"path":2568,"title":2569,"kind":2570},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas","Writing DataFrames to Excel with Pandas","guide",{"path":2572,"title":2573,"kind":2544},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fappend-a-dataframe-to-an-existing-excel-file-with-pandas","Append a DataFrame to an Existing Excel File with pandas",{"path":2575,"title":2576,"kind":2544},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fcontrol-how-nan-and-infinity-appear-in-excel-output","Control How NaN, None and Infinity Appear in Excel Output",{"path":2578,"title":2579,"kind":2544},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fhow-pandas-data-types-are-written-to-excel-cells","How pandas Data Types Are Written to Excel Cells",1790972069837]