[{"data":1,"prerenderedAt":1423},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel":1388,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel":1397,"children:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel":1402,"related:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel":1403},{"id":4,"title":5,"body":6,"dateModified":1354,"datePublished":1354,"description":1355,"extension":1356,"faq":1357,"keyword":1366,"kind":1367,"meta":1368,"navigation":270,"path":1377,"seo":1378,"slug":1381,"stem":1382,"topics":1383,"type":1386,"__hash__":1387},"docs\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel\u002Findex.md","Read an Excel File That Is Open in Excel",{"type":7,"value":8,"toc":1342},"minimark",[9,33,38,80,83,87,90,201,208,212,221,641,652,656,659,790,809,925,929,932,1059,1069,1073,1085,1210,1214,1255,1259,1275,1279,1285,1291,1301,1307,1311,1338],[10,11,12,13,17,18,22,23,26,27,32],"p",{},"Shared workbooks are open in Excel most of the working day, and the scripts that read them run during the working day too. That raises three practical questions. Can Python read a file while Excel has it open? Usually, yes — but what it reads is the ",[14,15,16],"em",{},"last saved"," version, not the figures on the user's screen. What happens if the read coincides with a save? It can fail with ",[19,20,21],"code",{},"PermissionError"," or with a ",[19,24,25],{},"BadZipFile"," error from a half-written file. And what if the script needs the values the user is looking at right now, including changes they have not saved? That needs a different tool altogether. This page covers all three: reading a consistent snapshot of the saved file with retries, telling whether a workbook is open, and reading live values from the running Excel with xlwings. It belongs to ",[28,29,31],"a",{"href":30},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002F","Reading Excel Files with pandas",".",[34,35,37],"h2",{"id":36},"prerequisites","Prerequisites",[39,40,45],"pre",{"className":41,"code":42,"language":43,"meta":44,"style":44},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\npip install xlwings          # only for reading live values, Windows or macOS with Excel\n","bash","",[19,46,47,66],{"__ignoreMap":44},[48,49,52,56,60,63],"span",{"class":50,"line":51},"line",1,[48,53,55],{"class":54},"sMTad","pip",[48,57,59],{"class":58},"srMev"," install",[48,61,62],{"class":58}," pandas",[48,64,65],{"class":58}," openpyxl\n",[48,67,69,71,73,76],{"class":50,"line":68},2,[48,70,55],{"class":54},[48,72,59],{"class":58},[48,74,75],{"class":58}," xlwings",[48,77,79],{"class":78},"s-wDw","          # only for reading live values, Windows or macOS with Excel\n",[10,81,82],{},"The snapshot and retry code runs anywhere; the sample run simulates a lock and a half-saved file. Reading live values needs desktop Excel and is shown as it runs there.",[34,84,86],{"id":85},"step-1-what-you-read-is-the-last-save","Step 1 — What you read is the last save",[10,88,89],{},"Excel works on a copy of the workbook in memory and writes the file only when the user saves — or, with AutoSave on a OneDrive or SharePoint file, every few seconds. Between saves, the file on disk and the screen differ. pandas, openpyxl and every other file-based library read the file on disk:",[91,92,100,101,100,105,100,109,100,117,100,127,100,138,100,145,100,151,100,156,100,160,100,164,100,168,100,173,100,180,100,185,100,189,100,192,100,195,100,198],"svg",{"viewBox":93,"role":94,"ariaLabelledBy":95,"xmlns":98,"style":99},"4 0 752 233","img",[96,97],"oe-saved-t","oe-saved-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[102,103,104],"title",{"id":96},"The saved file or the open workbook",[106,107,108],"desc",{"id":97},"Reading the file with pandas or openpyxl returns the last saved version, which may lag behind what the user sees. Reading through the running Excel with xlwings returns the current values, including unsaved edits, but needs Excel on the same machine.",[110,111],"rect",{"x":112,"y":113,"width":114,"height":115,"fill":116},"4","0","752","233","#ffffff",[110,118],{"x":119,"y":120,"width":121,"height":122,"rx":123,"fill":124,"stroke":125,"style":126},"20.0","16.0","351.0","201.0","14","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[128,129,131],"g",{"transform":130},"translate(182.5,29.0) scale(1.083)",[132,133],"path",{"d":134,"fill":135,"stroke":136,"style":137},"M6 3h9l4 4v14H6z M15 3v4h4","none","var(--brand-strong,#4338ca)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[139,140,144],"text",{"x":141,"y":142,"style":143},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Read the file (pandas, openpyxl)",[50,146],{"x1":147,"y1":148,"x2":149,"y2":148,"stroke":125,"style":150},"36.0","91.0","355.0","stroke-width:1px",[139,152,155],{"x":141,"y":153,"style":154},"111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","last saved version",[139,157,159],{"x":141,"y":158,"style":154},"137.0","works anywhere",[139,161,163],{"x":141,"y":162,"style":154},"163.0","no Excel needed",[139,165,167],{"x":141,"y":166,"style":154},"189.0","misses unsaved edits",[110,169],{"x":170,"y":120,"width":121,"height":122,"rx":123,"fill":171,"stroke":172,"style":126},"389.0","#d9f4f1","var(--teal,#0f9488)",[128,174,176],{"transform":175},"translate(551.5,29.0) scale(1.083)",[132,177],{"d":178,"fill":135,"stroke":179,"style":137},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","var(--teal-ink,#0b6157)",[139,181,184],{"x":182,"y":142,"style":183},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Read the open workbook (xlwings)",[50,186],{"x1":187,"y1":148,"x2":188,"y2":148,"stroke":172,"style":150},"405.0","724.0",[139,190,191],{"x":182,"y":153,"style":154},"values on screen now",[139,193,194],{"x":182,"y":158,"style":154},"includes unsaved edits",[139,196,197],{"x":182,"y":162,"style":154},"needs Excel running",[139,199,200],{"x":182,"y":166,"style":154},"same machine as the user",[10,202,203,204,32],{},"For a scheduled report that is usually what you want: the saved file is the version the owner has committed to. When it is not — a dashboard that should show today's edits — ask whether the workbook can be saved before the run, switch on AutoSave for it, or read it live as in Step 4. On Windows, Excel normally lets other programs read an open workbook; writing to it is what fails, as covered in ",[28,205,207],{"href":206},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fhandle-permission-denied-when-writing-excel-in-python\u002F","Handle Permission Denied When Writing Excel in Python",[34,209,211],{"id":210},"step-2-read-a-snapshot-and-retry-when-the-timing-is-bad","Step 2 — Read a snapshot, and retry when the timing is bad",[10,213,214,215,217,218,220],{},"Two things can still go wrong with a read. On some network shares and synced folders the open file cannot be opened at all and the read raises ",[19,216,21],{},". And if Excel is in the middle of writing the file, the read can see an incomplete zip archive and raise ",[19,219,25],{},". Copy the file to a private location first, check the copy is a complete workbook, and retry a few times with a pause:",[39,222,226],{"className":223,"code":224,"language":225,"meta":44,"style":44},"language-python shiki shiki-themes github-light github-dark-high-contrast","import shutil, tempfile, time, zipfile\nfrom pathlib import Path\nimport pandas as pd\n\ndef read_snapshot(path, attempts=4, wait=0.5, **read_kwargs):\n    path = Path(path)\n    for attempt in range(1, attempts + 1):\n        try:\n            with tempfile.TemporaryDirectory() as tmp:\n                copy = Path(tmp) \u002F path.name\n                shutil.copy2(path, copy)\n                with zipfile.ZipFile(copy) as z:\n                    if z.testzip() is not None:\n                        raise zipfile.BadZipFile(\"corrupt member\")\n                return pd.read_excel(copy, **read_kwargs), attempt\n        except (PermissionError, zipfile.BadZipFile) as e:\n            print(f\"  attempt {attempt}: {type(e).__name__} — retrying in {wait:.1f}s\")\n            time.sleep(wait)\n            wait *= 2\n    raise RuntimeError(f\"could not read a complete copy of {path} after {attempts} attempts\")\n\ndf, attempt = read_snapshot(\"tracker.xlsx\")\nprint(df.to_string(index=False), \"| read on attempt\", attempt)\n","python",[19,227,228,238,251,265,272,308,319,352,361,375,392,398,412,432,447,461,480,538,544,556,593,598,614],{"__ignoreMap":44},[48,229,230,234],{"class":50,"line":51},[48,231,233],{"class":232},"s-kum","import",[48,235,237],{"class":236},"skGVy"," shutil, tempfile, time, zipfile\n",[48,239,240,243,246,248],{"class":50,"line":68},[48,241,242],{"class":232},"from",[48,244,245],{"class":236}," pathlib ",[48,247,233],{"class":232},[48,249,250],{"class":236}," Path\n",[48,252,254,256,259,262],{"class":50,"line":253},3,[48,255,233],{"class":232},[48,257,258],{"class":236}," pandas ",[48,260,261],{"class":232},"as",[48,263,264],{"class":236}," pd\n",[48,266,268],{"class":50,"line":267},4,[48,269,271],{"emptyLinePlaceholder":270},true,"\n",[48,273,275,278,282,285,288,291,294,296,299,302,305],{"class":50,"line":274},5,[48,276,277],{"class":232},"def",[48,279,281],{"class":280},"s_Opv"," read_snapshot",[48,283,284],{"class":236},"(path, attempts",[48,286,287],{"class":232},"=",[48,289,112],{"class":290},"sP0c6",[48,292,293],{"class":236},", wait",[48,295,287],{"class":232},[48,297,298],{"class":290},"0.5",[48,300,301],{"class":236},", ",[48,303,304],{"class":232},"**",[48,306,307],{"class":236},"read_kwargs):\n",[48,309,311,314,316],{"class":50,"line":310},6,[48,312,313],{"class":236},"    path ",[48,315,287],{"class":232},[48,317,318],{"class":236}," Path(path)\n",[48,320,322,325,328,331,334,337,340,343,346,349],{"class":50,"line":321},7,[48,323,324],{"class":232},"    for",[48,326,327],{"class":236}," attempt ",[48,329,330],{"class":232},"in",[48,332,333],{"class":290}," range",[48,335,336],{"class":236},"(",[48,338,339],{"class":290},"1",[48,341,342],{"class":236},", attempts ",[48,344,345],{"class":232},"+",[48,347,348],{"class":290}," 1",[48,350,351],{"class":236},"):\n",[48,353,355,358],{"class":50,"line":354},8,[48,356,357],{"class":232},"        try",[48,359,360],{"class":236},":\n",[48,362,364,367,370,372],{"class":50,"line":363},9,[48,365,366],{"class":232},"            with",[48,368,369],{"class":236}," tempfile.TemporaryDirectory() ",[48,371,261],{"class":232},[48,373,374],{"class":236}," tmp:\n",[48,376,378,381,383,386,389],{"class":50,"line":377},10,[48,379,380],{"class":236},"                copy ",[48,382,287],{"class":232},[48,384,385],{"class":236}," Path(tmp) ",[48,387,388],{"class":232},"\u002F",[48,390,391],{"class":236}," path.name\n",[48,393,395],{"class":50,"line":394},11,[48,396,397],{"class":236},"                shutil.copy2(path, copy)\n",[48,399,401,404,407,409],{"class":50,"line":400},12,[48,402,403],{"class":232},"                with",[48,405,406],{"class":236}," zipfile.ZipFile(copy) ",[48,408,261],{"class":232},[48,410,411],{"class":236}," z:\n",[48,413,415,418,421,424,427,430],{"class":50,"line":414},13,[48,416,417],{"class":232},"                    if",[48,419,420],{"class":236}," z.testzip() ",[48,422,423],{"class":232},"is",[48,425,426],{"class":232}," not",[48,428,429],{"class":290}," None",[48,431,360],{"class":236},[48,433,435,438,441,444],{"class":50,"line":434},14,[48,436,437],{"class":232},"                        raise",[48,439,440],{"class":236}," zipfile.BadZipFile(",[48,442,443],{"class":58},"\"corrupt member\"",[48,445,446],{"class":236},")\n",[48,448,450,453,456,458],{"class":50,"line":449},15,[48,451,452],{"class":232},"                return",[48,454,455],{"class":236}," pd.read_excel(copy, ",[48,457,304],{"class":232},[48,459,460],{"class":236},"read_kwargs), attempt\n",[48,462,464,467,470,472,475,477],{"class":50,"line":463},16,[48,465,466],{"class":232},"        except",[48,468,469],{"class":236}," (",[48,471,21],{"class":290},[48,473,474],{"class":236},", zipfile.BadZipFile) ",[48,476,261],{"class":232},[48,478,479],{"class":236}," e:\n",[48,481,483,486,488,491,494,498,501,504,507,509,512,515,518,520,523,525,528,531,533,536],{"class":50,"line":482},17,[48,484,485],{"class":290},"            print",[48,487,336],{"class":236},[48,489,490],{"class":232},"f",[48,492,493],{"class":58},"\"  attempt ",[48,495,497],{"class":496},"sSjpA","{",[48,499,500],{"class":236},"attempt",[48,502,503],{"class":496},"}",[48,505,506],{"class":58},": ",[48,508,497],{"class":496},[48,510,511],{"class":290},"type",[48,513,514],{"class":236},"(e).",[48,516,517],{"class":290},"__name__",[48,519,503],{"class":496},[48,521,522],{"class":58}," — retrying in ",[48,524,497],{"class":496},[48,526,527],{"class":236},"wait",[48,529,530],{"class":232},":.1f",[48,532,503],{"class":496},[48,534,535],{"class":58},"s\"",[48,537,446],{"class":236},[48,539,541],{"class":50,"line":540},18,[48,542,543],{"class":236},"            time.sleep(wait)\n",[48,545,547,550,553],{"class":50,"line":546},19,[48,548,549],{"class":236},"            wait ",[48,551,552],{"class":232},"*=",[48,554,555],{"class":290}," 2\n",[48,557,559,562,565,567,569,572,574,576,578,581,583,586,588,591],{"class":50,"line":558},20,[48,560,561],{"class":232},"    raise",[48,563,564],{"class":290}," RuntimeError",[48,566,336],{"class":236},[48,568,490],{"class":232},[48,570,571],{"class":58},"\"could not read a complete copy of ",[48,573,497],{"class":496},[48,575,132],{"class":236},[48,577,503],{"class":496},[48,579,580],{"class":58}," after ",[48,582,497],{"class":496},[48,584,585],{"class":236},"attempts",[48,587,503],{"class":496},[48,589,590],{"class":58}," attempts\"",[48,592,446],{"class":236},[48,594,596],{"class":50,"line":595},21,[48,597,271],{"emptyLinePlaceholder":270},[48,599,601,604,606,609,612],{"class":50,"line":600},22,[48,602,603],{"class":236},"df, attempt ",[48,605,287],{"class":232},[48,607,608],{"class":236}," read_snapshot(",[48,610,611],{"class":58},"\"tracker.xlsx\"",[48,613,446],{"class":236},[48,615,617,620,623,627,629,632,635,638],{"class":50,"line":616},23,[48,618,619],{"class":290},"print",[48,621,622],{"class":236},"(df.to_string(",[48,624,626],{"class":625},"sa561","index",[48,628,287],{"class":232},[48,630,631],{"class":290},"False",[48,633,634],{"class":236},"), ",[48,636,637],{"class":58},"\"| read on attempt\"",[48,639,640],{"class":236},", attempt)\n",[10,642,643,644,647,648,651],{},"Reading from a copy has a second benefit: the script holds the original open only for the instant of the copy, so it never gets in the way of the user saving. ",[19,645,646],{},"testzip()"," reads every member of the archive and reports the first damaged one, which catches a half-written file before pandas produces a more confusing error. Doubling the wait between attempts gives a slow save on a network drive time to finish. The temporary directory is removed when the ",[19,649,650],{},"with"," block ends, so snapshots never accumulate, and because the read happens inside the block the DataFrame is complete before the copy disappears. For very large workbooks the copy costs a moment of disk time, which is small next to parsing the file; if it matters, copy once and read several sheets from the same snapshot rather than copying per sheet.",[34,653,655],{"id":654},"step-3-simulate-the-bad-timing","Step 3 — Simulate the bad timing",[10,657,658],{},"Test the retry path without Excel by making the file look mid-save for the first attempt and complete afterwards:",[39,660,662],{"className":223,"code":661,"language":225,"meta":44,"style":44},"import threading\n\ngood = Path(\"tracker.xlsx\").read_bytes()\nPath(\"tracker.xlsx\").write_bytes(good[: len(good) \u002F\u002F 2])          # a save in progress\nthreading.Timer(0.3, lambda: Path(\"tracker.xlsx\").write_bytes(good)).start()   # the save finishes\ndf, attempt = read_snapshot(\"tracker.xlsx\", wait=0.4)\nprint(\"recovered on attempt\", attempt, \"with\", len(df), \"rows\")\n",[19,663,664,671,675,690,718,742,763],{"__ignoreMap":44},[48,665,666,668],{"class":50,"line":51},[48,667,233],{"class":232},[48,669,670],{"class":236}," threading\n",[48,672,673],{"class":50,"line":68},[48,674,271],{"emptyLinePlaceholder":270},[48,676,677,680,682,685,687],{"class":50,"line":253},[48,678,679],{"class":236},"good ",[48,681,287],{"class":232},[48,683,684],{"class":236}," Path(",[48,686,611],{"class":58},[48,688,689],{"class":236},").read_bytes()\n",[48,691,692,695,697,700,703,706,709,712,715],{"class":50,"line":267},[48,693,694],{"class":236},"Path(",[48,696,611],{"class":58},[48,698,699],{"class":236},").write_bytes(good[: ",[48,701,702],{"class":290},"len",[48,704,705],{"class":236},"(good) ",[48,707,708],{"class":232},"\u002F\u002F",[48,710,711],{"class":290}," 2",[48,713,714],{"class":236},"])          ",[48,716,717],{"class":78},"# a save in progress\n",[48,719,720,723,726,728,731,734,736,739],{"class":50,"line":274},[48,721,722],{"class":236},"threading.Timer(",[48,724,725],{"class":290},"0.3",[48,727,301],{"class":236},[48,729,730],{"class":232},"lambda",[48,732,733],{"class":236},": Path(",[48,735,611],{"class":58},[48,737,738],{"class":236},").write_bytes(good)).start()   ",[48,740,741],{"class":78},"# the save finishes\n",[48,743,744,746,748,750,752,754,756,758,761],{"class":50,"line":310},[48,745,603],{"class":236},[48,747,287],{"class":232},[48,749,608],{"class":236},[48,751,611],{"class":58},[48,753,301],{"class":236},[48,755,527],{"class":625},[48,757,287],{"class":232},[48,759,760],{"class":290},"0.4",[48,762,446],{"class":236},[48,764,765,767,769,772,775,778,780,782,785,788],{"class":50,"line":321},[48,766,619],{"class":290},[48,768,336],{"class":236},[48,770,771],{"class":58},"\"recovered on attempt\"",[48,773,774],{"class":236},", attempt, ",[48,776,777],{"class":58},"\"with\"",[48,779,301],{"class":236},[48,781,702],{"class":290},[48,783,784],{"class":236},"(df), ",[48,786,787],{"class":58},"\"rows\"",[48,789,446],{"class":236},[10,791,792,793,795,796,799,800,803,804,808],{},"The first attempt finds half a workbook and raises ",[19,794,25],{},"; by the second attempt the \"save\" has finished and the snapshot reads all three projects. If every attempt fails the function raises a clear error naming the file, which a scheduled job can log and alert on. Whether a workbook is open at all — Excel's ",[19,797,798],{},"~$"," owner file next to it, LibreOffice's ",[19,801,802],{},".~lock"," file — can be checked first, as shown in ",[28,805,807],{"href":806},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fignore-excel-lock-files-when-processing-a-folder\u002F","Ignore Excel Lock Files When Processing a Folder","; a job might log \"read while open by another user\" so that stale figures can be explained later.",[91,810,100,815,100,818,100,821,100,824,100,831,100,839,100,845,100,850,100,855,100,860,100,865,100,872,100,876,100,879,100,882,100,885,100,888,100,894,100,899,100,901,100,905,100,908,100,911,100,917,100,922],{"viewBox":811,"role":94,"ariaLabelledBy":812,"xmlns":98,"style":99},"4 0 752 125",[813,814],"oe-snapshot-t","oe-snapshot-d",[102,816,817],{"id":813},"Reading an open workbook safely",[106,819,820],{"id":814},"Copy the workbook to a temporary folder, check the copy is a complete zip archive, read the copy with pandas, and on PermissionError or BadZipFile wait and retry with a doubling delay, failing with a clear error after the last attempt.",[110,822],{"x":112,"y":113,"width":114,"height":823,"fill":116},"125",[110,825],{"x":119,"y":120,"width":826,"height":827,"rx":828,"fill":829,"stroke":830,"style":126},"153.0","93.0","12","#e7ebef","var(--line,#cdd5e6)",[128,832,834],{"transform":833},"translate(84.5,29.0) scale(1.000)",[132,835],{"d":836,"fill":135,"stroke":837,"style":838},"M3 6h6l2 2h10v11H3z","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[139,840,844],{"x":841,"y":842,"style":843},"96.5","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Copy to temp",[139,846,849],{"x":841,"y":847,"style":848},"95.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","hold the original briefly",[50,851],{"x1":852,"y1":853,"x2":854,"y2":853,"stroke":125,"style":126},"178.0","62.5","200.0",[856,857],"polygon",{"points":858,"fill":859},"204.0,62.5 195.0,67.5 195.0,57.5","#5b5cf0",[110,861],{"x":862,"y":120,"width":826,"height":827,"rx":828,"fill":863,"stroke":864,"style":126},"209.0","#fdefd8","var(--gold,#b4740a)",[128,866,868],{"transform":867},"translate(273.5,29.0) scale(1.000)",[132,869],{"d":870,"fill":135,"stroke":871,"style":838},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","var(--gold-ink,#7a4e06)",[139,873,646],{"x":874,"y":842,"style":875},"285.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[139,877,878],{"x":874,"y":847,"style":848},"complete archive?",[50,880],{"x1":881,"y1":853,"x2":170,"y2":853,"stroke":125,"style":126},"367.0",[856,883],{"points":884,"fill":859},"393.0,62.5 384.0,67.5 384.0,57.5",[110,886],{"x":887,"y":120,"width":826,"height":827,"rx":828,"fill":124,"stroke":125,"style":126},"398.0",[128,889,891],{"transform":890},"translate(462.5,29.0) scale(1.000)",[132,892],{"d":893,"fill":135,"stroke":136,"style":838},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14",[139,895,898],{"x":896,"y":842,"style":897},"474.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","read_excel(copy)",[139,900,155],{"x":896,"y":847,"style":848},[50,902],{"x1":903,"y1":853,"x2":904,"y2":853,"stroke":125,"style":126},"556.0","578.0",[856,906],{"points":907,"fill":859},"582.0,62.5 573.0,67.5 573.0,57.5",[110,909],{"x":910,"y":120,"width":826,"height":827,"rx":828,"fill":171,"stroke":172,"style":126},"587.0",[128,912,914],{"transform":913},"translate(651.5,29.0) scale(1.000)",[132,915],{"d":916,"fill":135,"stroke":179,"style":838},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7",[139,918,921],{"x":919,"y":842,"style":920},"663.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Retry on failure",[139,923,924],{"x":919,"y":847,"style":848},"0.5 s, 1 s, 2 s …",[34,926,928],{"id":927},"step-4-read-the-live-values-with-xlwings","Step 4 — Read the live values with xlwings",[10,930,931],{},"When the script must see what is on the user's screen, read through Excel itself. xlwings attaches to a workbook that is already open in the running Excel instead of opening the file again:",[39,933,935],{"className":223,"code":934,"language":225,"meta":44,"style":44},"import xlwings as xw\n\nbook = xw.books[\"tracker.xlsx\"]                   # the workbook as currently open in Excel\nsheet = book.sheets[0]\nlive = sheet[\"A1\"].options(pd.DataFrame, expand=\"table\", index=False).value\nsaved = pd.read_excel(book.fullname)\nchanged = live.compare(saved) if live.shape == saved.shape else \"shape differs\"\n",[19,936,937,949,953,971,986,1021,1031],{"__ignoreMap":44},[48,938,939,941,944,946],{"class":50,"line":51},[48,940,233],{"class":232},[48,942,943],{"class":236}," xlwings ",[48,945,261],{"class":232},[48,947,948],{"class":236}," xw\n",[48,950,951],{"class":50,"line":68},[48,952,271],{"emptyLinePlaceholder":270},[48,954,955,958,960,963,965,968],{"class":50,"line":253},[48,956,957],{"class":236},"book ",[48,959,287],{"class":232},[48,961,962],{"class":236}," xw.books[",[48,964,611],{"class":58},[48,966,967],{"class":236},"]                   ",[48,969,970],{"class":78},"# the workbook as currently open in Excel\n",[48,972,973,976,978,981,983],{"class":50,"line":267},[48,974,975],{"class":236},"sheet ",[48,977,287],{"class":232},[48,979,980],{"class":236}," book.sheets[",[48,982,113],{"class":290},[48,984,985],{"class":236},"]\n",[48,987,988,991,993,996,999,1002,1005,1007,1010,1012,1014,1016,1018],{"class":50,"line":274},[48,989,990],{"class":236},"live ",[48,992,287],{"class":232},[48,994,995],{"class":236}," sheet[",[48,997,998],{"class":58},"\"A1\"",[48,1000,1001],{"class":236},"].options(pd.DataFrame, ",[48,1003,1004],{"class":625},"expand",[48,1006,287],{"class":232},[48,1008,1009],{"class":58},"\"table\"",[48,1011,301],{"class":236},[48,1013,626],{"class":625},[48,1015,287],{"class":232},[48,1017,631],{"class":290},[48,1019,1020],{"class":236},").value\n",[48,1022,1023,1026,1028],{"class":50,"line":310},[48,1024,1025],{"class":236},"saved ",[48,1027,287],{"class":232},[48,1029,1030],{"class":236}," pd.read_excel(book.fullname)\n",[48,1032,1033,1036,1038,1041,1044,1047,1050,1053,1056],{"class":50,"line":321},[48,1034,1035],{"class":236},"changed ",[48,1037,287],{"class":232},[48,1039,1040],{"class":236}," live.compare(saved) ",[48,1042,1043],{"class":232},"if",[48,1045,1046],{"class":236}," live.shape ",[48,1048,1049],{"class":232},"==",[48,1051,1052],{"class":236}," saved.shape ",[48,1054,1055],{"class":232},"else",[48,1057,1058],{"class":58}," \"shape differs\"\n",[10,1060,1061,1064,1065,32],{},[19,1062,1063],{},"xw.books"," lists the workbooks open in the active Excel instance, by file name; reading a range from one returns the current cell values, including edits the user has not saved and formulas Excel has just recalculated. Comparing with a read of the saved file shows exactly what the user has changed since their last save. The limits are practical: this needs Excel running on the same machine, it reads a single user's session — not other people's open copies on other machines — and it should be read-only, because writing into a workbook a person is actively editing risks overwriting their work. More on xlwings in ",[28,1066,1068],{"href":1067},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fread-and-write-a-live-excel-workbook-with-xlwings\u002F","Read and Write a Live Excel Workbook with xlwings",[34,1070,1072],{"id":1071},"reduce-the-problem-at-the-source","Reduce the problem at the source",[10,1074,1075,1076,1079,1080,1084],{},"Reading around people's editing sessions works, but the cleaner fix is often organisational. If a script reads a workbook that people edit all day, agree a moment when it is \"published\": the owner saves, or a macro saves a copy to a ",[14,1077,1078],{},"published"," folder, and the script reads only that copy. For workbooks in OneDrive or SharePoint, reading through Microsoft Graph returns the version the service holds — with AutoSave, close to what users see — without touching anyone's desktop session, as described in ",[28,1081,1083],{"href":1082},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fread-an-excel-file-from-sharepoint-or-onedrive-into-pandas\u002F","Read an Excel File from SharePoint or OneDrive into pandas",". And if the script needs only a few figures, keeping them in a small, separate input workbook that is edited rarely removes most of the contention.",[91,1086,100,1091,100,1094,100,1097,100,1100,100,1104,100,1109,100,1113,100,1117,100,1121,100,1124,100,1127,100,1135,100,1139,100,1143,100,1148,100,1152,100,1156,100,1158,100,1161,100,1165,100,1168,100,1171,100,1177,100,1180,100,1183,100,1185,100,1188,100,1192,100,1195,100,1198,100,1204,100,1207],{"viewBox":1087,"role":94,"ariaLabelledBy":1088,"xmlns":98,"style":99},"4 0 752 272",[1089,1090],"oe-choose-t","oe-choose-d",[102,1092,1093],{"id":1089},"Which way to read a workbook people have open",[106,1095,1096],{"id":1090},"If the saved version is good enough, read a snapshot copy with retries. If the script must see unsaved edits on the same machine, read the open workbook through xlwings. If many people edit it all day, read a published copy or read it from SharePoint through Microsoft Graph.",[110,1098],{"x":112,"y":113,"width":114,"height":1099,"fill":116},"272",[110,1101],{"x":1102,"y":120,"width":1103,"height":842,"rx":828,"fill":124,"stroke":125,"style":126},"240.0","280.0",[128,1105,1107],{"transform":1106},"translate(368.0,29.0) scale(1.000)",[132,1108],{"d":178,"fill":135,"stroke":136,"style":838},[139,1110,1112],{"x":1111,"y":842,"style":897},"380.0","What does the script need?",[50,1114],{"x1":1111,"y1":1115,"x2":1111,"y2":1116,"stroke":125,"style":126},"91.5","111.5",[50,1118],{"x1":1119,"y1":1116,"x2":1120,"y2":1116,"stroke":125,"style":126},"134.7","625.3",[50,1122],{"x1":1119,"y1":1116,"x2":1119,"y2":1123,"stroke":125,"style":126},"157.5",[856,1125],{"points":1126,"fill":859},"134.7,161.5 129.7,152.5 139.7,152.5",[110,1128],{"x":1129,"y":1130,"width":1131,"height":1132,"rx":1133,"fill":116,"stroke":830,"style":1134},"72.0","121.5","125.3","21.0","10","stroke-width:1.2px",[139,1136,155],{"x":1119,"y":1137,"style":1138},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle",[110,1140],{"x":119,"y":1141,"width":1142,"height":827,"rx":828,"fill":171,"stroke":172,"style":126},"163.5","229.3",[128,1144,1146],{"transform":1145},"translate(122.7,176.5) scale(1.000)",[132,1147],{"d":134,"fill":135,"stroke":179,"style":838},[139,1149,1151],{"x":1119,"y":1150,"style":920},"223.0","snapshot + retry",[139,1153,1155],{"x":1119,"y":1154,"style":848},"242.5","pandas on a copy",[50,1157],{"x1":1111,"y1":1116,"x2":1111,"y2":1123,"stroke":125,"style":126},[856,1159],{"points":1160,"fill":859},"380.0,161.5 375.0,152.5 385.0,152.5",[110,1162],{"x":1163,"y":1130,"width":1164,"height":1132,"rx":1133,"fill":116,"stroke":830,"style":1134},"329.5","101.0",[139,1166,1167],{"x":1111,"y":1137,"style":1138},"unsaved edits",[110,1169],{"x":1170,"y":1141,"width":1142,"height":827,"rx":828,"fill":124,"stroke":125,"style":126},"265.3",[128,1172,1174],{"transform":1173},"translate(368.0,176.5) scale(1.000)",[132,1175],{"d":1176,"fill":135,"stroke":136,"style":838},"M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z M12 9a3 3 0 1 0 .01 0z",[139,1178,1179],{"x":1111,"y":1150,"style":897},"xlwings",[139,1181,1182],{"x":1111,"y":1154,"style":848},"same machine as Excel",[50,1184],{"x1":1120,"y1":1116,"x2":1120,"y2":1123,"stroke":125,"style":126},[856,1186],{"points":1187,"fill":859},"625.3,161.5 620.3,152.5 630.3,152.5",[110,1189],{"x":1190,"y":1130,"width":1191,"height":1132,"rx":1133,"fill":116,"stroke":830,"style":1134},"569.1","112.5",[139,1193,1194],{"x":1120,"y":1137,"style":1138},"busy shared file",[110,1196],{"x":1197,"y":1141,"width":1142,"height":827,"rx":828,"fill":863,"stroke":864,"style":126},"510.7",[128,1199,1201],{"transform":1200},"translate(613.3,176.5) scale(1.000)",[132,1202],{"d":1203,"fill":135,"stroke":871,"style":838},"M7 18h10a4 4 0 0 0 .6-8A6 6 0 0 0 6.1 11.4 3.4 3.4 0 0 0 7 18z",[139,1205,1206],{"x":1120,"y":1150,"style":875},"published copy or Graph",[139,1208,1209],{"x":1120,"y":1154,"style":848},"no desktop contention",[34,1211,1213],{"id":1212},"common-pitfalls","Common pitfalls",[1215,1216,1217,1225,1234,1240,1246],"ul",{},[1218,1219,1220,1224],"li",{},[1221,1222,1223],"strong",{},"Expecting unsaved edits in pandas."," File-based reads see the last save only.",[1218,1226,1227,1230,1231,1233],{},[1221,1228,1229],{},"Reading the original during a save."," Copy, check with ",[19,1232,646],{},", then read.",[1218,1235,1236,1239],{},[1221,1237,1238],{},"Retrying forever."," Limit attempts and fail with a clear message.",[1218,1241,1242,1245],{},[1221,1243,1244],{},"Writing to a workbook someone has open."," Read live values, but leave the writing to them.",[1218,1247,1248,1254],{},[1221,1249,1250,1251,1253],{},"Reading ",[19,1252,798],{}," files."," They are lock files, not workbooks.",[34,1256,1258],{"id":1257},"conclusion","Conclusion",[10,1260,1261,1262,1264,1265,1268,1269,1271,1272,1274],{},"A workbook open in Excel can usually be read with pandas, but what pandas reads is the last saved version. Read a snapshot — copy, check the copy with ",[19,1263,646],{},", then ",[19,1266,1267],{},"read_excel"," — and retry with a growing delay on ",[19,1270,21],{}," or ",[19,1273,25],{},", failing clearly after a few attempts. When the current, unsaved values are needed, attach to the open workbook with xlwings and read from Excel itself.",[34,1276,1278],{"id":1277},"frequently-asked-questions","Frequently asked questions",[10,1280,1281,1284],{},[1221,1282,1283],{},"Can pandas read an Excel file while it is open in Excel?","\nUsually yes, on Windows and macOS; pandas reads the last saved version of the file, not unsaved changes.",[10,1286,1287,1290],{},[1221,1288,1289],{},"Why do I get PermissionError when reading an open Excel file?","\nSome file shares and synced folders do not allow reading a file another program has open; copy the file first and retry with a short delay.",[10,1292,1293,1296,1297,1300],{},[1221,1294,1295],{},"How do I read the values a user has not saved yet?","\nAttach to the open workbook in the running Excel with xlwings, for example xw.books",[48,1298,1299],{},"\"file.xlsx\"",", and read the range from there.",[10,1302,1303,1306],{},[1221,1304,1305],{},"How can I tell whether a workbook is open in Excel?","\nLook for Excel's ~$ owner file or LibreOffice's .~lock file in the same folder as the workbook.",[34,1308,1310],{"id":1309},"related","Related",[1215,1312,1313,1319,1324,1331],{},[1218,1314,1315,1316,1318],{},"Up one level: ",[28,1317,31],{"href":30}," — reading workbooks into DataFrames.",[1218,1320,1321,1323],{},[28,1322,807],{"href":806}," — lock files in folder loops.",[1218,1325,1326,1330],{},[28,1327,1329],{"href":1328},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-badzipfile-error-when-reading-excel-in-python\u002F","Fix BadZipFile Error When Reading Excel in Python"," — the error a half-saved file gives.",[1218,1332,1333,1337],{},[28,1334,1336],{"href":1335},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fwrite-excel-files-atomically-in-python\u002F","Write Excel Files Atomically in Python"," — avoiding half-written files in your own jobs.",[1339,1340,1341],"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 pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":44,"searchDepth":68,"depth":68,"links":1343},[1344,1345,1346,1347,1348,1349,1350,1351,1352,1353],{"id":36,"depth":68,"text":37},{"id":85,"depth":68,"text":86},{"id":210,"depth":68,"text":211},{"id":654,"depth":68,"text":655},{"id":927,"depth":68,"text":928},{"id":1071,"depth":68,"text":1072},{"id":1212,"depth":68,"text":1213},{"id":1257,"depth":68,"text":1258},{"id":1277,"depth":68,"text":1278},{"id":1309,"depth":68,"text":1310},"2026-10-02","Read a workbook that someone has open in Excel without errors or surprises: why pandas sees the last saved version and not what is on screen, how to take a consistent snapshot copy and retry when the file is locked or caught mid-save, how to tell when a file is open, and how to read the live, unsaved values from the running Excel with xlwings.","md",[1358,1360,1362,1364],{"q":1283,"a":1359},"Usually yes, on Windows and macOS; pandas reads the last saved version of the file, not unsaved changes.",{"q":1289,"a":1361},"Some file shares and synced folders do not allow reading a file another program has open; copy the file first and retry with a short delay.",{"q":1295,"a":1363},"Attach to the open workbook in the running Excel with xlwings, for example xw.books[\"file.xlsx\"], and read the range from there.",{"q":1305,"a":1365},"Look for Excel's ~$ owner file or LibreOffice's .~lock file in the same folder as the workbook.","read an excel file that is open in excel python",null,{"breadcrumb":1369},[1370,1372,1375],{"name":1371,"item":388},"Home",{"name":1373,"item":1374},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":1376,"item":30},"Reading Excel Files with Pandas","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel",{"title":1379,"description":1380},"Read an Excel File That Is Open in Excel with Python","Reading open workbooks with pandas: saved versus on-screen values, snapshot copies with retries for PermissionError and half-saved files, checking for lock files, and reading unsaved values from running Excel with xlwings.","read-an-excel-file-that-is-open-in-excel","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel\u002Findex",[1384,1385],"reading-excel","troubleshooting","how-to","D0EZHo-z9vfaUyEpFeyhV6dg-DIGxfH9vM9UdQPbcMk",[1389,1393],{"title":1390,"path":1391,"stem":1392,"children":-1},"Read a Named Range into pandas","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas\u002Findex",{"title":1394,"path":1395,"stem":1396,"children":-1},"Read Yes\u002FNo and TRUE\u002FFALSE Columns as Booleans with pandas","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas\u002Findex",[1398,1400],{"path":1399,"title":1373},"\u002Fgetting-started-with-python-excel-automation",{"path":1401,"title":1376},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas",[],[1404,1407,1410,1413,1416,1419],{"path":1405,"title":1406,"kind":1386},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-an-excel-file-from-a-url-or-bytes-in-python","Read an Excel File from a URL or Bytes in Python",{"path":1408,"title":1409,"kind":1386},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-the-last-row-with-data-in-openpyxl","Find the Last Row with Data in openpyxl",{"path":1411,"title":1412,"kind":1386},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fiterate-over-rows-and-columns-with-openpyxl","Iterate over Rows and Columns with openpyxl",{"path":1414,"title":1415,"kind":1386},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fread-cell-value-from-excel-with-openpyxl","Read a Cell Value From Excel With openpyxl",{"path":1417,"title":1418,"kind":1386},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas","Standardise Excel Column Names with pandas",{"path":1420,"title":1421,"kind":1422},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets","Extracting Data from Messy Excel Sheets","guide",1790972065163]