[{"data":1,"prerenderedAt":1871},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python":1838,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python":1846,"children:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python":1851,"related:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python":1852},{"id":4,"title":5,"body":6,"dateModified":1803,"datePublished":1803,"description":1804,"extension":1805,"faq":1806,"keyword":1815,"kind":1816,"meta":1817,"navigation":291,"path":1826,"seo":1827,"slug":1830,"stem":1831,"topics":1832,"type":1836,"__hash__":1837},"docs\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python\u002Findex.md","Read Binary .xlsb Excel Files in Python",{"type":7,"value":8,"toc":1788},"minimark",[9,35,194,199,211,215,245,256,260,263,366,394,398,414,553,568,742,748,752,763,922,932,936,942,1158,1168,1172,1181,1313,1327,1463,1467,1473,1617,1625,1629,1684,1688,1704,1708,1723,1727,1733,1739,1745,1751,1755,1784],[10,11,12,13,17,18,21,22,25,26,31,32,34],"p",{},"The Excel Binary Workbook format, ",[14,15,16],"code",{},".xlsb",", stores the same content as ",[14,19,20],{},".xlsx"," in a compact binary encoding instead of XML. Finance teams and anyone with very large workbooks use it because it opens and saves faster in Excel and produces smaller files. For Python it is a stumbling block: openpyxl cannot read it, and the first ",[14,23,24],{},"pd.read_excel(\"report.xlsb\")"," a person tries may fail for a missing dependency. Two engines read it well, with one important difference in how they return dates. This guide, part of ",[27,28,30],"a",{"href":29},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002F","Handling Excel File Formats and Conversions",", covers both, plus conversion to ",[14,33,20],{}," when the rest of your pipeline needs it.",[36,37,45,46,45,50,45,54,45,62,45,72,45,83,45,90,45,97,45,103,45,110,45,114,45,119,45,123,45,128,45,132,45,139,45,143,45,150,45,155,45,159,45,163,45,168,45,171,45,178,45,181,45,186,45,189],"svg",{"viewBox":38,"role":39,"ariaLabelledBy":40,"xmlns":43,"style":44},"4 0 752 233","img",[41,42],"xlsb-engines-t","xlsb-engines-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  ",[47,48,49],"title",{"id":41},"The two engines that read .xlsb in pandas",[51,52,53],"desc",{"id":42},"The calamine engine, a Rust reader, is fast, converts date cells to datetimes and returns missing values for error cells. The pyxlsb engine is pure Python, returns dates as serial numbers and errors as hex codes, and offers a streaming API for reading row by row.",[55,56],"rect",{"x":57,"y":58,"width":59,"height":60,"fill":61},"4","0","752","233","#ffffff",[55,63],{"x":64,"y":65,"width":66,"height":67,"rx":68,"fill":69,"stroke":70,"style":71},"20.0","16.0","351.0","201.0","14","#d9f4f1","var(--teal,#0f9488)","stroke-width:2px",[73,74,76],"g",{"transform":75},"translate(182.5,29.0) scale(1.083)",[77,78],"path",{"d":79,"fill":80,"stroke":81,"style":82},"M13 2L4 14h7l-1 8 9-12h-7z","none","var(--teal-ink,#0b6157)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[84,85,89],"text",{"x":86,"y":87,"style":88},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","calamine",[91,92],"line",{"x1":93,"y1":94,"x2":95,"y2":94,"stroke":70,"style":96},"36.0","91.0","355.0","stroke-width:1px",[84,98,102],{"x":99,"y":100,"style":101},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","fast (Rust)",[73,104,106],{"transform":105},"translate(38.5,99.5) scale(0.625)",[77,107],{"d":108,"fill":80,"stroke":70,"style":109},"M5 12.5l4.5 4.5L19 7","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[84,111,113],{"x":99,"y":112,"style":101},"137.0","dates become datetimes",[73,115,117],{"transform":116},"translate(38.5,125.5) scale(0.625)",[77,118],{"d":108,"fill":80,"stroke":70,"style":109},[84,120,122],{"x":99,"y":121,"style":101},"163.0","error cells become NaN",[73,124,126],{"transform":125},"translate(38.5,151.5) scale(0.625)",[77,127],{"d":108,"fill":80,"stroke":70,"style":109},[84,129,131],{"x":99,"y":130,"style":101},"189.0","loads the whole sheet",[73,133,135],{"transform":134},"translate(38.5,177.5) scale(0.625)",[77,136],{"d":137,"fill":80,"stroke":138,"style":109},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold,#b4740a)",[55,140],{"x":141,"y":65,"width":66,"height":67,"rx":68,"fill":142,"stroke":138,"style":71},"389.0","#fdefd8",[73,144,146],{"transform":145},"translate(551.5,29.0) scale(1.083)",[77,147],{"d":148,"fill":80,"stroke":149,"style":82},"M12 3c-4 0-4 1.6-4 3v2h4.2v1H6c-2 0-3 1.4-3 4s1 4 3 4h2v-2.6c0-1.6 1.3-2.9 2.9-2.9h4.2c1.3 0 2.4-1.1 2.4-2.4V6c0-1.6-1.8-3-5.5-3z M10 5.5h.01 M12 21c4 0 4-1.6 4-3v-2h-4.2v-1H18c2 0 3-1.4 3-4s-1-4-3-4h-2v2.6c0 1.6-1.3 2.9-2.9 2.9H8.9c-1.3 0-2.4 1.1-2.4 2.4V18c0 1.6 1.8 3 5.5 3z M14 18.5h.01","var(--gold-ink,#7a4e06)",[84,151,154],{"x":152,"y":87,"style":153},"564.5","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","pyxlsb",[91,156],{"x1":157,"y1":94,"x2":158,"y2":94,"stroke":138,"style":96},"405.0","724.0",[84,160,162],{"x":161,"y":100,"style":101},"429.0","pure Python, slower",[73,164,166],{"transform":165},"translate(407.5,99.5) scale(0.625)",[77,167],{"d":137,"fill":80,"stroke":138,"style":109},[84,169,170],{"x":161,"y":112,"style":101},"dates stay serial numbers",[73,172,174],{"transform":173},"translate(407.5,125.5) scale(0.625)",[77,175],{"d":176,"fill":80,"stroke":177,"style":109},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)",[84,179,180],{"x":161,"y":121,"style":101},"errors as codes like 0x7",[73,182,184],{"transform":183},"translate(407.5,151.5) scale(0.625)",[77,185],{"d":176,"fill":80,"stroke":177,"style":109},[84,187,188],{"x":161,"y":130,"style":101},"streams rows one by one",[73,190,192],{"transform":191},"translate(407.5,177.5) scale(0.625)",[77,193],{"d":108,"fill":80,"stroke":70,"style":109},[195,196,198],"h2",{"id":197},"why-teams-use-xlsb","Why teams use .xlsb",[10,200,201,202,204,205,207,208,210],{},"The binary format holds the same things as ",[14,203,20],{}," — sheets, formulas, formatting, charts, even VBA macros — with the same limits of 1,048,576 rows and 16,384 columns. What changes is the encoding: records are written in a compact binary form instead of verbose XML. Large workbooks typically shrink noticeably on disk and open and save faster in Excel, which is why teams with heavy models and long histories adopt it. The costs are interoperability and transparency: fewer tools read it, no Python library writes it, and you cannot unzip it and inspect the XML the way you can with ",[14,206,20],{},". When you control the producer, ask for ",[14,209,20],{}," or CSV; when the file comes from a system or a team you do not control, the techniques below cover reading it as it is.",[195,212,214],{"id":213},"prerequisites","Prerequisites",[216,217,222],"pre",{"className":218,"code":219,"language":220,"meta":221,"style":221},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas python-calamine pyxlsb\n","bash","",[14,223,224],{"__ignoreMap":221},[225,226,228,232,236,239,242],"span",{"class":91,"line":227},1,[225,229,231],{"class":230},"sMTad","pip",[225,233,235],{"class":234},"srMev"," install",[225,237,238],{"class":234}," pandas",[225,240,241],{"class":234}," python-calamine",[225,243,244],{"class":234}," pyxlsb\n",[10,246,247,248,251,252,255],{},"You only need one of the two engines; installing both lets you compare. The examples read a workbook called ",[14,249,250],{},"report.xlsb"," with a sheet named ",[14,253,254],{},"Test"," — substitute your own file.",[195,257,259],{"id":258},"step-1-read-with-the-calamine-engine","Step 1 — Read with the calamine engine",[10,261,262],{},"calamine is the simplest choice: pass the engine name and read as you would any workbook. Date cells come back as datetimes and error cells as missing values:",[216,264,268],{"className":265,"code":266,"language":267,"meta":221,"style":221},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nbook = pd.ExcelFile(\"report.xlsb\", engine=\"calamine\")\nprint(book.sheet_names)                     # ['Test']\ndf = pd.read_excel(book, sheet_name=\"Test\")\nprint(df.dtypes)\n","python",[14,269,270,286,293,323,337,358],{"__ignoreMap":221},[225,271,272,276,280,283],{"class":91,"line":227},[225,273,275],{"class":274},"s-kum","import",[225,277,279],{"class":278},"skGVy"," pandas ",[225,281,282],{"class":274},"as",[225,284,285],{"class":278}," pd\n",[225,287,289],{"class":91,"line":288},2,[225,290,292],{"emptyLinePlaceholder":291},true,"\n",[225,294,296,299,302,305,308,311,315,317,320],{"class":91,"line":295},3,[225,297,298],{"class":278},"book ",[225,300,301],{"class":274},"=",[225,303,304],{"class":278}," pd.ExcelFile(",[225,306,307],{"class":234},"\"report.xlsb\"",[225,309,310],{"class":278},", ",[225,312,314],{"class":313},"sa561","engine",[225,316,301],{"class":274},[225,318,319],{"class":234},"\"calamine\"",[225,321,322],{"class":278},")\n",[225,324,326,330,333],{"class":91,"line":325},4,[225,327,329],{"class":328},"sP0c6","print",[225,331,332],{"class":278},"(book.sheet_names)                     ",[225,334,336],{"class":335},"s-wDw","# ['Test']\n",[225,338,340,343,345,348,351,353,356],{"class":91,"line":339},5,[225,341,342],{"class":278},"df ",[225,344,301],{"class":274},[225,346,347],{"class":278}," pd.read_excel(book, ",[225,349,350],{"class":313},"sheet_name",[225,352,301],{"class":274},[225,354,355],{"class":234},"\"Test\"",[225,357,322],{"class":278},[225,359,361,363],{"class":91,"line":360},6,[225,362,329],{"class":328},[225,364,365],{"class":278},"(df.dtypes)\n",[10,367,368,369,372,373,310,376,310,379,310,382,385,386,388,389,393],{},"All the usual ",[14,370,371],{},"read_excel"," options — ",[14,374,375],{},"header",[14,377,378],{},"usecols",[14,380,381],{},"nrows",[14,383,384],{},"dtype"," — work the same way with calamine as with any other engine. It is also the fastest engine for ",[14,387,20],{},", so the same code serves both formats; see ",[27,390,392],{"href":391},"\u002Fadvanced-data-transformation-and-cleaning\u002Freading-excel-with-polars-and-arrow\u002Fspeed-up-pandas-excel-reads-with-the-calamine-engine\u002F","Speed Up pandas Excel Reads with the calamine Engine",".",[195,395,397],{"id":396},"step-2-read-with-the-pyxlsb-engine-and-fix-the-dates","Step 2 — Read with the pyxlsb engine, and fix the dates",[10,399,400,401,405,406,409,410,413],{},"The pyxlsb engine returns exactly what the file stores. For dates, that is Excel's ",[402,403,404],"em",{},"serial day number",": the count of days since 30 December 1899, with the time as a fraction of a day. Reading a workbook whose first column holds the date 27 December 2017 gives ",[14,407,408],{},"43096",", and a time of 18:06 gives ",[14,411,412],{},"0.7541…",". Convert those columns explicitly:",[216,415,417],{"className":265,"code":416,"language":267,"meta":221,"style":221},"raw = pd.read_excel(\"report.xlsb\", engine=\"pyxlsb\", sheet_name=\"Test\")\n\ndef excel_serial_to_datetime(series: pd.Series) -> pd.Series:\n    numbers = pd.to_numeric(series, errors=\"coerce\")\n    return pd.to_datetime(numbers, unit=\"D\", origin=\"1899-12-30\")\n\nraw[\"booked\"] = excel_serial_to_datetime(raw[\"booked\"])\nprint(raw[\"booked\"].head())\n",[14,418,419,450,454,466,486,514,518,540],{"__ignoreMap":221},[225,420,421,424,426,429,431,433,435,437,440,442,444,446,448],{"class":91,"line":227},[225,422,423],{"class":278},"raw ",[225,425,301],{"class":274},[225,427,428],{"class":278}," pd.read_excel(",[225,430,307],{"class":234},[225,432,310],{"class":278},[225,434,314],{"class":313},[225,436,301],{"class":274},[225,438,439],{"class":234},"\"pyxlsb\"",[225,441,310],{"class":278},[225,443,350],{"class":313},[225,445,301],{"class":274},[225,447,355],{"class":234},[225,449,322],{"class":278},[225,451,452],{"class":91,"line":288},[225,453,292],{"emptyLinePlaceholder":291},[225,455,456,459,463],{"class":91,"line":295},[225,457,458],{"class":274},"def",[225,460,462],{"class":461},"s_Opv"," excel_serial_to_datetime",[225,464,465],{"class":278},"(series: pd.Series) -> pd.Series:\n",[225,467,468,471,473,476,479,481,484],{"class":91,"line":325},[225,469,470],{"class":278},"    numbers ",[225,472,301],{"class":274},[225,474,475],{"class":278}," pd.to_numeric(series, ",[225,477,478],{"class":313},"errors",[225,480,301],{"class":274},[225,482,483],{"class":234},"\"coerce\"",[225,485,322],{"class":278},[225,487,488,491,494,497,499,502,504,507,509,512],{"class":91,"line":339},[225,489,490],{"class":274},"    return",[225,492,493],{"class":278}," pd.to_datetime(numbers, ",[225,495,496],{"class":313},"unit",[225,498,301],{"class":274},[225,500,501],{"class":234},"\"D\"",[225,503,310],{"class":278},[225,505,506],{"class":313},"origin",[225,508,301],{"class":274},[225,510,511],{"class":234},"\"1899-12-30\"",[225,513,322],{"class":278},[225,515,516],{"class":91,"line":360},[225,517,292],{"emptyLinePlaceholder":291},[225,519,521,524,527,530,532,535,537],{"class":91,"line":520},7,[225,522,523],{"class":278},"raw[",[225,525,526],{"class":234},"\"booked\"",[225,528,529],{"class":278},"] ",[225,531,301],{"class":274},[225,533,534],{"class":278}," excel_serial_to_datetime(raw[",[225,536,526],{"class":234},[225,538,539],{"class":278},"])\n",[225,541,543,545,548,550],{"class":91,"line":542},8,[225,544,329],{"class":328},[225,546,547],{"class":278},"(raw[",[225,549,526],{"class":234},[225,551,552],{"class":278},"].head())\n",[10,554,555,556,559,560,563,564,393],{},"The origin ",[14,557,558],{},"1899-12-30"," rather than ",[14,561,562],{},"1900-01-01"," accounts for Excel's historical leap-year quirk, and it is correct for every date from March 1900 onwards. The same conversion applies to serial numbers from any source, as covered in ",[27,565,567],{"href":566},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffix-excel-serial-numbers-showing-instead-of-dates\u002F","Fix Excel Serial Numbers Showing Instead of Dates",[36,569,45,575,45,578,45,581,45,586,45,594,45,598,45,604,45,608,45,613,45,616,45,620,45,623,45,626,45,628,45,631,45,634,45,638,45,640,45,643,45,646,45,649,45,651,45,654,45,657,45,661,45,664,45,666,45,669,45,671,45,673,45,675,45,677,45,679,45,683,45,685,45,687,45,689,45,692,45,694,45,696,45,698,45,701,45,707,45,711,45,717,45,722,45,730,45,736],{"viewBox":570,"role":39,"ariaLabelledBy":571,"xmlns":43,"style":574},"36 0 687 218",[572,573],"xlsb-dates-t","xlsb-dates-d","width:100%;max-width:687px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[47,576,577],{"id":572},"Serial numbers from pyxlsb converted into dates",[51,579,580],{"id":573},"The pyxlsb engine returns 43096 for 27 December 2017 and 43096.7556 for the same date at 18:08. Converting with unit D and origin 1899-12-30 turns them into proper datetime values.",[55,582],{"x":583,"y":58,"width":584,"height":585,"fill":61},"36","687","218",[55,587],{"x":588,"y":589,"width":590,"height":591,"rx":58,"fill":592,"stroke":593,"style":96},"103.5","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)",[55,595],{"x":596,"y":589,"width":597,"height":591,"rx":58,"fill":592,"stroke":593,"style":96},"135.5","116.6",[84,599,603],{"x":600,"y":601,"style":602},"193.8","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[55,605],{"x":588,"y":606,"width":590,"height":607,"rx":58,"fill":592,"stroke":593,"style":96},"64.0","28.0",[84,609,612],{"x":610,"y":611,"style":602},"119.5","83.0","1",[55,614],{"x":596,"y":606,"width":597,"height":607,"rx":58,"fill":592,"stroke":593,"style":615},"stroke-width:1.5px",[84,617,619],{"x":600,"y":611,"style":618},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","booked",[55,621],{"x":588,"y":622,"width":590,"height":607,"rx":58,"fill":592,"stroke":593,"style":96},"92.0",[84,624,625],{"x":610,"y":100,"style":602},"2",[55,627],{"x":596,"y":622,"width":597,"height":607,"rx":58,"fill":142,"stroke":138,"style":615},[84,629,408],{"x":600,"y":100,"style":630},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[55,632],{"x":588,"y":633,"width":590,"height":607,"rx":58,"fill":592,"stroke":593,"style":96},"120.0",[84,635,637],{"x":610,"y":636,"style":602},"139.0","3",[55,639],{"x":596,"y":633,"width":597,"height":607,"rx":58,"fill":142,"stroke":138,"style":615},[84,641,642],{"x":600,"y":636,"style":630},"43096.755556",[55,644],{"x":588,"y":645,"width":590,"height":607,"rx":58,"fill":592,"stroke":593,"style":96},"148.0",[84,647,57],{"x":610,"y":648,"style":602},"167.0",[55,650],{"x":596,"y":645,"width":597,"height":607,"rx":58,"fill":142,"stroke":138,"style":615},[84,652,653],{"x":600,"y":648,"style":630},"0.754167",[55,655],{"x":656,"y":589,"width":590,"height":591,"rx":58,"fill":592,"stroke":593,"style":96},"489.7",[55,658],{"x":659,"y":589,"width":660,"height":591,"rx":58,"fill":592,"stroke":593,"style":96},"521.7","134.8",[84,662,603],{"x":663,"y":601,"style":602},"589.1",[55,665],{"x":656,"y":606,"width":590,"height":607,"rx":58,"fill":592,"stroke":593,"style":96},[84,667,612],{"x":668,"y":611,"style":602},"505.7",[55,670],{"x":659,"y":606,"width":660,"height":607,"rx":58,"fill":592,"stroke":593,"style":615},[84,672,619],{"x":663,"y":611,"style":618},[55,674],{"x":656,"y":622,"width":590,"height":607,"rx":58,"fill":592,"stroke":593,"style":96},[84,676,625],{"x":668,"y":100,"style":602},[55,678],{"x":659,"y":622,"width":660,"height":607,"rx":58,"fill":69,"stroke":70,"style":615},[84,680,682],{"x":663,"y":100,"style":681},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","2017-12-27 00:00",[55,684],{"x":656,"y":633,"width":590,"height":607,"rx":58,"fill":592,"stroke":593,"style":96},[84,686,637],{"x":668,"y":636,"style":602},[55,688],{"x":659,"y":633,"width":660,"height":607,"rx":58,"fill":69,"stroke":70,"style":615},[84,690,691],{"x":663,"y":636,"style":681},"2017-12-27 18:08",[55,693],{"x":656,"y":645,"width":590,"height":607,"rx":58,"fill":592,"stroke":593,"style":96},[84,695,57],{"x":668,"y":648,"style":602},[55,697],{"x":659,"y":645,"width":660,"height":607,"rx":58,"fill":69,"stroke":70,"style":615},[84,699,700],{"x":663,"y":648,"style":681},"1899-12-30 18:06",[84,702,706],{"x":703,"y":704,"style":705},"177.8","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","pyxlsb engine",[84,708,710],{"x":709,"y":704,"style":705},"573.1","after conversion",[91,712],{"x1":713,"y1":714,"x2":715,"y2":714,"stroke":716,"style":71},"262.1","108.0","475.7","var(--brand,#5b5cf0)",[718,719],"polygon",{"points":720,"fill":721},"479.7,108.0 470.7,113.0 470.7,103.0","#5b5cf0",[55,723],{"x":724,"y":725,"width":726,"height":727,"rx":728,"fill":61,"stroke":593,"style":729},"291.8","75.0","158.1","22.0","11","stroke-width:1.2px",[84,731,735],{"x":732,"y":733,"style":734},"370.9","90.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","origin='1899-12-30'",[84,737,741],{"x":738,"y":739,"style":740},"380.0","198.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","a pure time value becomes a time on 30 December 1899 — keep time-only columns as timedeltas instead",[10,743,744,745,393],{},"The last row shows a trap: a cell holding only a time has no date part, so it converts to a time on 30 December 1899. For time-only columns, convert to durations instead with ",[14,746,747],{},"pd.to_timedelta(numbers, unit=\"D\")",[195,749,751],{"id":750},"step-3-recognise-error-cells","Step 3 — Recognise error cells",[10,753,754,755,758,759,762],{},"Cells showing ",[14,756,757],{},"#DIV\u002F0!"," or ",[14,760,761],{},"#REF!"," in Excel store error codes. pyxlsb returns them as hexadecimal strings; calamine returns missing values. With pyxlsb you can map the codes to names and count them before deciding what to do:",[216,764,766],{"className":265,"code":765,"language":267,"meta":221,"style":221},"ERRORS = {\"0x0\": \"#NULL!\", \"0x7\": \"#DIV\u002F0!\", \"0xf\": \"#VALUE!\", \"0x17\": \"#REF!\",\n          \"0x1d\": \"#NAME?\", \"0x24\": \"#NUM!\", \"0x2a\": \"#N\u002FA\"}\n\ndef error_report(frame: pd.DataFrame) -> pd.Series:\n    as_text = frame.astype(str)\n    hits = as_text.isin(ERRORS.keys())\n    return as_text[hits].stack().map(ERRORS).value_counts()\n\nprint(error_report(raw))\n",[14,767,768,821,854,858,868,883,898,910,914],{"__ignoreMap":221},[225,769,770,773,776,779,782,785,788,790,793,795,798,800,803,805,808,810,813,815,818],{"class":91,"line":227},[225,771,772],{"class":328},"ERRORS",[225,774,775],{"class":274}," =",[225,777,778],{"class":278}," {",[225,780,781],{"class":234},"\"0x0\"",[225,783,784],{"class":278},": ",[225,786,787],{"class":234},"\"#NULL!\"",[225,789,310],{"class":278},[225,791,792],{"class":234},"\"0x7\"",[225,794,784],{"class":278},[225,796,797],{"class":234},"\"#DIV\u002F0!\"",[225,799,310],{"class":278},[225,801,802],{"class":234},"\"0xf\"",[225,804,784],{"class":278},[225,806,807],{"class":234},"\"#VALUE!\"",[225,809,310],{"class":278},[225,811,812],{"class":234},"\"0x17\"",[225,814,784],{"class":278},[225,816,817],{"class":234},"\"#REF!\"",[225,819,820],{"class":278},",\n",[225,822,823,826,828,831,833,836,838,841,843,846,848,851],{"class":91,"line":288},[225,824,825],{"class":234},"          \"0x1d\"",[225,827,784],{"class":278},[225,829,830],{"class":234},"\"#NAME?\"",[225,832,310],{"class":278},[225,834,835],{"class":234},"\"0x24\"",[225,837,784],{"class":278},[225,839,840],{"class":234},"\"#NUM!\"",[225,842,310],{"class":278},[225,844,845],{"class":234},"\"0x2a\"",[225,847,784],{"class":278},[225,849,850],{"class":234},"\"#N\u002FA\"",[225,852,853],{"class":278},"}\n",[225,855,856],{"class":91,"line":295},[225,857,292],{"emptyLinePlaceholder":291},[225,859,860,862,865],{"class":91,"line":325},[225,861,458],{"class":274},[225,863,864],{"class":461}," error_report",[225,866,867],{"class":278},"(frame: pd.DataFrame) -> pd.Series:\n",[225,869,870,873,875,878,881],{"class":91,"line":339},[225,871,872],{"class":278},"    as_text ",[225,874,301],{"class":274},[225,876,877],{"class":278}," frame.astype(",[225,879,880],{"class":328},"str",[225,882,322],{"class":278},[225,884,885,888,890,893,895],{"class":91,"line":360},[225,886,887],{"class":278},"    hits ",[225,889,301],{"class":274},[225,891,892],{"class":278}," as_text.isin(",[225,894,772],{"class":328},[225,896,897],{"class":278},".keys())\n",[225,899,900,902,905,907],{"class":91,"line":520},[225,901,490],{"class":274},[225,903,904],{"class":278}," as_text[hits].stack().map(",[225,906,772],{"class":328},[225,908,909],{"class":278},").value_counts()\n",[225,911,912],{"class":91,"line":542},[225,913,292],{"emptyLinePlaceholder":291},[225,915,917,919],{"class":91,"line":916},9,[225,918,329],{"class":328},[225,920,921],{"class":278},"(error_report(raw))\n",[10,923,924,925,927,928,393],{},"Counting errors is the right first step whichever engine you use: silently turning ",[14,926,761],{}," into NaN hides a broken workbook, and a report built on it inherits the damage. The sources of those errors are discussed in ",[27,929,931],{"href":930},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Ffind-external-links-and-broken-references-in-a-workbook\u002F","Find External Links and Broken References in a Workbook with Python",[195,933,935],{"id":934},"step-4-stream-rows-with-pyxlsb-directly","Step 4 — Stream rows with pyxlsb directly",[10,937,938,939,941],{},"For very large ",[14,940,16],{}," sheets, or when you want to process rows as they are read, use pyxlsb's own API. It yields one row at a time, so memory stays flat however big the sheet is:",[216,943,945],{"className":265,"code":944,"language":267,"meta":221,"style":221},"from pyxlsb import convert_date, open_workbook\n\ntotal = 0.0\nwith open_workbook(\"report.xlsb\") as wb:\n    print(wb.sheets)                          # ['Test']\n    with wb.get_sheet(\"Test\") as sheet:\n        for i, row in enumerate(sheet.rows()):\n            values = [cell.v for cell in row]\n            if i == 0:\n                header = values\n                continue\n            amount = values[header.index(\"amount\")]\n            if isinstance(amount, (int, float)):\n                total += amount\nprint(convert_date(43096))                    # 2017-12-27 00:00:00\n",[14,946,947,960,964,974,992,1002,1019,1036,1057,1074,1085,1091,1108,1130,1142],{"__ignoreMap":221},[225,948,949,952,955,957],{"class":91,"line":227},[225,950,951],{"class":274},"from",[225,953,954],{"class":278}," pyxlsb ",[225,956,275],{"class":274},[225,958,959],{"class":278}," convert_date, open_workbook\n",[225,961,962],{"class":91,"line":288},[225,963,292],{"emptyLinePlaceholder":291},[225,965,966,969,971],{"class":91,"line":295},[225,967,968],{"class":278},"total ",[225,970,301],{"class":274},[225,972,973],{"class":328}," 0.0\n",[225,975,976,979,982,984,987,989],{"class":91,"line":325},[225,977,978],{"class":274},"with",[225,980,981],{"class":278}," open_workbook(",[225,983,307],{"class":234},[225,985,986],{"class":278},") ",[225,988,282],{"class":274},[225,990,991],{"class":278}," wb:\n",[225,993,994,997,1000],{"class":91,"line":339},[225,995,996],{"class":328},"    print",[225,998,999],{"class":278},"(wb.sheets)                          ",[225,1001,336],{"class":335},[225,1003,1004,1007,1010,1012,1014,1016],{"class":91,"line":360},[225,1005,1006],{"class":274},"    with",[225,1008,1009],{"class":278}," wb.get_sheet(",[225,1011,355],{"class":234},[225,1013,986],{"class":278},[225,1015,282],{"class":274},[225,1017,1018],{"class":278}," sheet:\n",[225,1020,1021,1024,1027,1030,1033],{"class":91,"line":520},[225,1022,1023],{"class":274},"        for",[225,1025,1026],{"class":278}," i, row ",[225,1028,1029],{"class":274},"in",[225,1031,1032],{"class":328}," enumerate",[225,1034,1035],{"class":278},"(sheet.rows()):\n",[225,1037,1038,1041,1043,1046,1049,1052,1054],{"class":91,"line":542},[225,1039,1040],{"class":278},"            values ",[225,1042,301],{"class":274},[225,1044,1045],{"class":278}," [cell.v ",[225,1047,1048],{"class":274},"for",[225,1050,1051],{"class":278}," cell ",[225,1053,1029],{"class":274},[225,1055,1056],{"class":278}," row]\n",[225,1058,1059,1062,1065,1068,1071],{"class":91,"line":916},[225,1060,1061],{"class":274},"            if",[225,1063,1064],{"class":278}," i ",[225,1066,1067],{"class":274},"==",[225,1069,1070],{"class":328}," 0",[225,1072,1073],{"class":278},":\n",[225,1075,1077,1080,1082],{"class":91,"line":1076},10,[225,1078,1079],{"class":278},"                header ",[225,1081,301],{"class":274},[225,1083,1084],{"class":278}," values\n",[225,1086,1088],{"class":91,"line":1087},11,[225,1089,1090],{"class":274},"                continue\n",[225,1092,1094,1097,1099,1102,1105],{"class":91,"line":1093},12,[225,1095,1096],{"class":278},"            amount ",[225,1098,301],{"class":274},[225,1100,1101],{"class":278}," values[header.index(",[225,1103,1104],{"class":234},"\"amount\"",[225,1106,1107],{"class":278},")]\n",[225,1109,1111,1113,1116,1119,1122,1124,1127],{"class":91,"line":1110},13,[225,1112,1061],{"class":274},[225,1114,1115],{"class":328}," isinstance",[225,1117,1118],{"class":278},"(amount, (",[225,1120,1121],{"class":328},"int",[225,1123,310],{"class":278},[225,1125,1126],{"class":328},"float",[225,1128,1129],{"class":278},")):\n",[225,1131,1133,1136,1139],{"class":91,"line":1132},14,[225,1134,1135],{"class":278},"                total ",[225,1137,1138],{"class":274},"+=",[225,1140,1141],{"class":278}," amount\n",[225,1143,1145,1147,1150,1152,1155],{"class":91,"line":1144},15,[225,1146,329],{"class":328},[225,1148,1149],{"class":278},"(convert_date(",[225,1151,408],{"class":328},[225,1153,1154],{"class":278},"))                    ",[225,1156,1157],{"class":335},"# 2017-12-27 00:00:00\n",[10,1159,1160,1163,1164,1167],{},[14,1161,1162],{},"convert_date"," turns a serial number into a Python ",[14,1165,1166],{},"datetime",", the same conversion as Step 2 for single values. Sheets can be opened by name or by one-based index.",[195,1169,1171],{"id":1170},"step-5-convert-xlsb-to-xlsx","Step 5 — Convert .xlsb to .xlsx",[10,1173,1174,1175,1177,1178,1180],{},"Python libraries cannot write ",[14,1176,16],{},", and some tools in a pipeline cannot read it. When you need an ",[14,1179,20],{}," copy, LibreOffice converts headlessly and keeps values, dates and formatting:",[216,1182,1184],{"className":265,"code":1183,"language":267,"meta":221,"style":221},"import subprocess\nfrom pathlib import Path\n\nPath(\"converted\").mkdir(exist_ok=True)\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"xlsx\", \"--outdir\", \"converted\", \"report.xlsb\"],\n               check=True, capture_output=True, timeout=300)\nconverted = pd.read_excel(\"converted\u002Freport.xlsx\")\n",[14,1185,1186,1193,1205,1209,1230,1269,1299],{"__ignoreMap":221},[225,1187,1188,1190],{"class":91,"line":227},[225,1189,275],{"class":274},[225,1191,1192],{"class":278}," subprocess\n",[225,1194,1195,1197,1200,1202],{"class":91,"line":288},[225,1196,951],{"class":274},[225,1198,1199],{"class":278}," pathlib ",[225,1201,275],{"class":274},[225,1203,1204],{"class":278}," Path\n",[225,1206,1207],{"class":91,"line":295},[225,1208,292],{"emptyLinePlaceholder":291},[225,1210,1211,1214,1217,1220,1223,1225,1228],{"class":91,"line":325},[225,1212,1213],{"class":278},"Path(",[225,1215,1216],{"class":234},"\"converted\"",[225,1218,1219],{"class":278},").mkdir(",[225,1221,1222],{"class":313},"exist_ok",[225,1224,301],{"class":274},[225,1226,1227],{"class":328},"True",[225,1229,322],{"class":278},[225,1231,1232,1235,1238,1240,1243,1245,1248,1250,1253,1255,1258,1260,1262,1264,1266],{"class":91,"line":339},[225,1233,1234],{"class":278},"subprocess.run([",[225,1236,1237],{"class":234},"\"soffice\"",[225,1239,310],{"class":278},[225,1241,1242],{"class":234},"\"--headless\"",[225,1244,310],{"class":278},[225,1246,1247],{"class":234},"\"--convert-to\"",[225,1249,310],{"class":278},[225,1251,1252],{"class":234},"\"xlsx\"",[225,1254,310],{"class":278},[225,1256,1257],{"class":234},"\"--outdir\"",[225,1259,310],{"class":278},[225,1261,1216],{"class":234},[225,1263,310],{"class":278},[225,1265,307],{"class":234},[225,1267,1268],{"class":278},"],\n",[225,1270,1271,1274,1276,1278,1280,1283,1285,1287,1289,1292,1294,1297],{"class":91,"line":360},[225,1272,1273],{"class":313},"               check",[225,1275,301],{"class":274},[225,1277,1227],{"class":328},[225,1279,310],{"class":278},[225,1281,1282],{"class":313},"capture_output",[225,1284,301],{"class":274},[225,1286,1227],{"class":328},[225,1288,310],{"class":278},[225,1290,1291],{"class":313},"timeout",[225,1293,301],{"class":274},[225,1295,1296],{"class":328},"300",[225,1298,322],{"class":278},[225,1300,1301,1304,1306,1308,1311],{"class":91,"line":520},[225,1302,1303],{"class":278},"converted ",[225,1305,301],{"class":274},[225,1307,428],{"class":278},[225,1309,1310],{"class":234},"\"converted\u002Freport.xlsx\"",[225,1312,322],{"class":278},[10,1314,1315,1316,1319,1320,1322,1323,393],{},"On Windows with Excel installed, Excel itself can do the conversion through COM (",[14,1317,1318],{},"SaveAs"," with the ",[14,1321,20],{}," file format), which is the most faithful option for files with macros or unusual features; see ",[27,1324,1326],{"href":1325},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fsave-a-workbook-in-another-format-with-win32com\u002F","Save a Workbook in Another Format with win32com",[36,1328,45,1333,45,1336,45,1339,45,1342,45,1349,45,1357,45,1361,45,1365,45,1369,45,1372,45,1375,45,1382,45,1387,45,1392,45,1398,45,1403,45,1408,45,1410,45,1413,45,1417,45,1420,45,1423,45,1429,45,1433,45,1436,45,1438,45,1441,45,1445,45,1448,45,1451,45,1457,45,1460],{"viewBox":1329,"role":39,"ariaLabelledBy":1330,"xmlns":43,"style":44},"4 0 752 272",[1331,1332],"xlsb-which-t","xlsb-which-d",[47,1334,1335],{"id":1331},"Choosing how to handle an .xlsb file",[51,1337,1338],{"id":1332},"To analyse the data, read it with pandas and the calamine engine. To process a very large sheet row by row, use pyxlsb's streaming API. If other tools need an xlsx file, convert with LibreOffice or with Excel through COM.",[55,1340],{"x":57,"y":58,"width":59,"height":1341,"fill":61},"272",[55,1343],{"x":1344,"y":65,"width":1345,"height":1346,"rx":1347,"fill":1348,"stroke":716,"style":71},"221.1","317.7","75.5","12","#f0f4ff",[73,1350,1352],{"transform":1351},"translate(368.0,29.0) scale(1.000)",[77,1353],{"d":1354,"fill":80,"stroke":1355,"style":1356},"M6 3h9l4 4v14H6z M15 3v4h4","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[84,1358,1360],{"x":738,"y":1346,"style":1359},"font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","What do you need from the .xlsb file?",[91,1362],{"x1":738,"y1":1363,"x2":738,"y2":1364,"stroke":716,"style":71},"91.5","111.5",[91,1366],{"x1":1367,"y1":1364,"x2":1368,"y2":1364,"stroke":716,"style":71},"134.7","625.3",[91,1370],{"x1":1367,"y1":1364,"x2":1367,"y2":1371,"stroke":716,"style":71},"157.5",[718,1373],{"points":1374,"fill":721},"134.7,161.5 129.7,152.5 139.7,152.5",[55,1376],{"x":1377,"y":1378,"width":1379,"height":1380,"rx":1381,"fill":61,"stroke":593,"style":729},"88.5","121.5","92.3","21.0","10",[84,1383,1386],{"x":1367,"y":1384,"style":1385},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","a DataFrame",[55,1388],{"x":64,"y":1389,"width":1390,"height":1391,"rx":1347,"fill":69,"stroke":70,"style":71},"163.5","229.3","93.0",[73,1393,1395],{"transform":1394},"translate(122.7,176.5) scale(1.000)",[77,1396],{"d":1397,"fill":80,"stroke":81,"style":1356},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14",[84,1399,1402],{"x":1367,"y":1400,"style":1401},"223.0","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","pandas + calamine",[84,1404,1407],{"x":1367,"y":1405,"style":1406},"242.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","dates converted for you",[91,1409],{"x1":738,"y1":1364,"x2":738,"y2":1371,"stroke":716,"style":71},[718,1411],{"points":1412,"fill":721},"380.0,161.5 375.0,152.5 385.0,152.5",[55,1414],{"x":1415,"y":1378,"width":1416,"height":1380,"rx":1381,"fill":61,"stroke":593,"style":729},"338.1","83.8",[84,1418,1419],{"x":738,"y":1384,"style":1385},"row by row",[55,1421],{"x":1422,"y":1389,"width":1390,"height":1391,"rx":1347,"fill":142,"stroke":138,"style":71},"265.3",[73,1424,1426],{"transform":1425},"translate(368.0,176.5) scale(1.000)",[77,1427],{"d":1428,"fill":80,"stroke":149,"style":1356},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5",[84,1430,1432],{"x":738,"y":1400,"style":1431},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","pyxlsb API",[84,1434,1435],{"x":738,"y":1405,"style":1406},"flat memory",[91,1437],{"x1":1368,"y1":1364,"x2":1368,"y2":1371,"stroke":716,"style":71},[718,1439],{"points":1440,"fill":721},"625.3,161.5 620.3,152.5 630.3,152.5",[55,1442],{"x":1443,"y":1378,"width":1444,"height":1380,"rx":1381,"fill":61,"stroke":593,"style":729},"577.5","95.7",[84,1446,1447],{"x":1368,"y":1384,"style":1385},"an .xlsx copy",[55,1449],{"x":1450,"y":1389,"width":1390,"height":1391,"rx":1347,"fill":1348,"stroke":716,"style":71},"510.7",[73,1452,1454],{"transform":1453},"translate(613.3,176.5) scale(1.000)",[77,1455],{"d":1456,"fill":80,"stroke":1355,"style":1356},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7",[84,1458,1459],{"x":1368,"y":1400,"style":1359},"LibreOffice or Excel",[84,1461,1462],{"x":1368,"y":1405,"style":1406},"headless conversion",[195,1464,1466],{"id":1465},"step-6-read-a-folder-of-xlsb-files","Step 6 — Read a folder of .xlsb files",[10,1468,1469,1470,1472],{},"Monthly ",[14,1471,16],{}," exports usually arrive as a series. Read them all with the same settings and stack them, keeping the file name so every row can be traced back to its source:",[216,1474,1476],{"className":265,"code":1475,"language":267,"meta":221,"style":221},"from pathlib import Path\n\nframes = []\nfor path in sorted(Path(\"exports\").glob(\"*.xlsb\")):\n    frame = pd.read_excel(path, engine=\"calamine\", sheet_name=0)\n    frame[\"source_file\"] = path.name\n    frames.append(frame)\ncombined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()\nprint(combined.groupby(\"source_file\").size())\n",[14,1477,1478,1488,1492,1502,1528,1554,1569,1574,1605],{"__ignoreMap":221},[225,1479,1480,1482,1484,1486],{"class":91,"line":227},[225,1481,951],{"class":274},[225,1483,1199],{"class":278},[225,1485,275],{"class":274},[225,1487,1204],{"class":278},[225,1489,1490],{"class":91,"line":288},[225,1491,292],{"emptyLinePlaceholder":291},[225,1493,1494,1497,1499],{"class":91,"line":295},[225,1495,1496],{"class":278},"frames ",[225,1498,301],{"class":274},[225,1500,1501],{"class":278}," []\n",[225,1503,1504,1506,1509,1511,1514,1517,1520,1523,1526],{"class":91,"line":325},[225,1505,1048],{"class":274},[225,1507,1508],{"class":278}," path ",[225,1510,1029],{"class":274},[225,1512,1513],{"class":328}," sorted",[225,1515,1516],{"class":278},"(Path(",[225,1518,1519],{"class":234},"\"exports\"",[225,1521,1522],{"class":278},").glob(",[225,1524,1525],{"class":234},"\"*.xlsb\"",[225,1527,1129],{"class":278},[225,1529,1530,1533,1535,1538,1540,1542,1544,1546,1548,1550,1552],{"class":91,"line":339},[225,1531,1532],{"class":278},"    frame ",[225,1534,301],{"class":274},[225,1536,1537],{"class":278}," pd.read_excel(path, ",[225,1539,314],{"class":313},[225,1541,301],{"class":274},[225,1543,319],{"class":234},[225,1545,310],{"class":278},[225,1547,350],{"class":313},[225,1549,301],{"class":274},[225,1551,58],{"class":328},[225,1553,322],{"class":278},[225,1555,1556,1559,1562,1564,1566],{"class":91,"line":360},[225,1557,1558],{"class":278},"    frame[",[225,1560,1561],{"class":234},"\"source_file\"",[225,1563,529],{"class":278},[225,1565,301],{"class":274},[225,1567,1568],{"class":278}," path.name\n",[225,1570,1571],{"class":91,"line":520},[225,1572,1573],{"class":278},"    frames.append(frame)\n",[225,1575,1576,1579,1581,1584,1587,1589,1591,1593,1596,1599,1602],{"class":91,"line":542},[225,1577,1578],{"class":278},"combined ",[225,1580,301],{"class":274},[225,1582,1583],{"class":278}," pd.concat(frames, ",[225,1585,1586],{"class":313},"ignore_index",[225,1588,301],{"class":274},[225,1590,1227],{"class":328},[225,1592,986],{"class":278},[225,1594,1595],{"class":274},"if",[225,1597,1598],{"class":278}," frames ",[225,1600,1601],{"class":274},"else",[225,1603,1604],{"class":278}," pd.DataFrame()\n",[225,1606,1607,1609,1612,1614],{"class":91,"line":916},[225,1608,329],{"class":328},[225,1610,1611],{"class":278},"(combined.groupby(",[225,1613,1561],{"class":234},[225,1615,1616],{"class":278},").size())\n",[10,1618,1619,1620,1624],{},"The per-file row counts are a quick sanity check: a month with an unusually small count usually means the export was cut short or saved from the wrong sheet. For large folders, the parallel pattern in ",[27,1621,1623],{"href":1622},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fprocess-multiple-excel-files-in-parallel-with-python\u002F","Process Multiple Excel Files in Parallel with Python"," applies unchanged — each file is read independently.",[195,1626,1628],{"id":1627},"common-pitfalls","Common pitfalls",[1630,1631,1632,1644,1658,1678],"ul",{},[1633,1634,1635,1639,1640,1643],"li",{},[1636,1637,1638],"strong",{},"Formulas are not available."," Both engines return the ",[402,1641,1642],{},"cached results"," of formulas, not the formulas themselves. If a workbook was saved without recalculation, the values may be stale.",[1633,1645,1646,1649,1650,1653,1654,1657],{},[1636,1647,1648],{},"Mixed columns."," A column with numbers and error codes arrives as ",[14,1651,1652],{},"object"," dtype with pyxlsb. Convert with ",[14,1655,1656],{},"pd.to_numeric(..., errors=\"coerce\")"," after counting the errors.",[1633,1659,1660,1663,1664,1667,1668,758,1671,1673,1674,393],{},[1636,1661,1662],{},"Engine not installed."," ",[14,1665,1666],{},"ImportError: Missing optional dependency"," means the engine package is missing. Install ",[14,1669,1670],{},"python-calamine",[14,1672,154],{}," in the same environment — the general fix is in ",[27,1675,1677],{"href":1676},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-missing-optional-dependency-openpyxl\u002F","Fix \"Missing optional dependency 'openpyxl'\" in pandas",[1633,1679,1680,1683],{},[1636,1681,1682],{},"Treating times as dates."," Time-only cells become times on 30 December 1899 when converted as dates. Convert them to timedeltas.",[195,1685,1687],{"id":1686},"performance-and-scale-notes","Performance and scale notes",[10,1689,1690,1691,1693,1694,1696,1697,1699,1700,393],{},"calamine reads ",[14,1692,16],{}," roughly as fast as it reads ",[14,1695,20],{}," — both are far faster than pure-Python parsing — so for analysis it is the right default. pyxlsb is slower per row but constant in memory when streamed, which matters for sheets with millions of cells on a small machine. If the same large ",[14,1698,16],{}," is read repeatedly, convert it once to Parquet and read that instead, as described in ",[27,1701,1703],{"href":1702},"\u002Fadvanced-data-transformation-and-cleaning\u002Freading-excel-with-polars-and-arrow\u002Fconvert-excel-files-to-parquet-with-python\u002F","Convert Excel Files to Parquet with Python",[195,1705,1707],{"id":1706},"conclusion","Conclusion",[10,1709,1710,1711,1713,1714,1716,1717,1719,1720,1722],{},"Read ",[14,1712,16],{}," with pandas and the calamine engine for a DataFrame with dates already converted and errors as missing values. Use the pyxlsb engine or its streaming API when you need raw values or flat memory, converting serial numbers with origin ",[14,1715,558],{}," and mapping error codes before discarding them. Python cannot write ",[14,1718,16],{},"; convert to ",[14,1721,20],{}," with LibreOffice or Excel when the rest of the pipeline needs it.",[195,1724,1726],{"id":1725},"frequently-asked-questions","Frequently asked questions",[10,1728,1729,1732],{},[1636,1730,1731],{},"Can openpyxl read .xlsb files?","\nNo. openpyxl reads and writes the XML-based formats (.xlsx, .xlsm). For the binary .xlsb format use pandas with the calamine or pyxlsb engine, or the pyxlsb package directly.",[10,1734,1735,1738],{},[1636,1736,1737],{},"Why are my dates numbers when I read an .xlsb file?","\nThe pyxlsb engine returns the raw stored value, and Excel stores dates as serial day numbers. Convert them with pd.to_datetime(col, unit=\"D\", origin=\"1899-12-30\"), or use the calamine engine, which converts date cells for you.",[10,1740,1741,1744],{},[1636,1742,1743],{},"Can Python write .xlsb files?","\nNot with the common libraries — pandas, openpyxl and xlsxwriter write .xlsx only. Write .xlsx, or have Excel itself save as .xlsb through COM or xlwings if the binary format is required.",[10,1746,1747,1750],{},[1636,1748,1749],{},"Why does an .xlsb read show 0x7 or 0x17 in some cells?","\nThose are Excel error codes returned by pyxlsb — 0x7 is #DIV\u002F0! and 0x17 is #REF!. The calamine engine returns missing values for error cells instead.",[195,1752,1754],{"id":1753},"related","Related",[1630,1756,1757,1763,1770,1777],{},[1633,1758,1759,1760,1762],{},"Up one level: ",[27,1761,30],{"href":29}," — every format in and out.",[1633,1764,1765,1769],{},[27,1766,1768],{"href":1767},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpick-an-excel-engine-for-xlsx-xlsm-xls-xlsb-and-ods\u002F","Pick an Excel Engine for .xlsx, .xlsm, .xls, .xlsb and .ods"," — engines at a glance.",[1633,1771,1772,1776],{},[27,1773,1775],{"href":1774},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xls-files-in-python-with-xlrd-and-pandas\u002F","Read .xls Files in Python with xlrd and pandas"," — the older binary format.",[1633,1778,1779,1783],{},[27,1780,1782],{"href":1781},"\u002Fadvanced-data-transformation-and-cleaning\u002Freading-excel-with-polars-and-arrow\u002F","Reading Excel with Polars and Arrow"," — calamine-based reading in Polars.",[1785,1786,1787],"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}",{"title":221,"searchDepth":288,"depth":288,"links":1789},[1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1800,1801,1802],{"id":197,"depth":288,"text":198},{"id":213,"depth":288,"text":214},{"id":258,"depth":288,"text":259},{"id":396,"depth":288,"text":397},{"id":750,"depth":288,"text":751},{"id":934,"depth":288,"text":935},{"id":1170,"depth":288,"text":1171},{"id":1465,"depth":288,"text":1466},{"id":1627,"depth":288,"text":1628},{"id":1686,"depth":288,"text":1687},{"id":1706,"depth":288,"text":1707},{"id":1725,"depth":288,"text":1726},{"id":1753,"depth":288,"text":1754},"2026-09-23","Read Excel Binary Workbooks (.xlsb) with pandas — the pyxlsb and calamine engines, why dates arrive as numbers with pyxlsb, how error cells look, reading sheet by sheet with pyxlsb directly, and converting .xlsb to .xlsx.","md",[1807,1809,1811,1813],{"q":1731,"a":1808},"No. openpyxl reads and writes the XML-based formats (.xlsx, .xlsm). For the binary .xlsb format use pandas with the calamine or pyxlsb engine, or the pyxlsb package directly.",{"q":1737,"a":1810},"The pyxlsb engine returns the raw stored value, and Excel stores dates as serial day numbers. Convert them with pd.to_datetime(col, unit=\"D\", origin=\"1899-12-30\"), or use the calamine engine, which converts date cells for you.",{"q":1743,"a":1812},"Not with the common libraries — pandas, openpyxl and xlsxwriter write .xlsx only. Write .xlsx, or have Excel itself save as .xlsb through COM or xlwings if the binary format is required.",{"q":1749,"a":1814},"Those are Excel error codes returned by pyxlsb — 0x7 is #DIV\u002F0! and 0x17 is #REF!. The calamine engine returns missing values for error cells instead.","read binary .xlsb excel files in python",null,{"breadcrumb":1818},[1819,1822,1825],{"name":1820,"item":1821},"Home","\u002F",{"name":1823,"item":1824},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":30,"item":29},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python",{"title":1828,"description":1829},"Read .xlsb Files in Python with pandas (pyxlsb, calamine)","Open Excel Binary Workbook .xlsb files in Python — pandas with the calamine or pyxlsb engine, converting serial-number dates, handling error codes like 0x7, streaming rows, and converting to .xlsx.","read-xlsb-files-in-python","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xlsb-files-in-python\u002Findex",[1833,1834,1835],"file-formats","reading-excel","pandas","how-to","xyTAZ1QLmAjCQNtu6GKZFBAF8lLSZuOz9GJhGcVA1zQ",[1839,1842],{"title":1775,"path":1840,"stem":1841,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xls-files-in-python-with-xlrd-and-pandas","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xls-files-in-python-with-xlrd-and-pandas\u002Findex",{"title":1843,"path":1844,"stem":1845,"children":-1},"Save a Workbook as an Excel Template with Python","\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fsave-a-workbook-as-an-excel-template-with-python\u002Findex",[1847,1849],{"path":1848,"title":30},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions",{"path":1850,"title":1823},"\u002Fgetting-started-with-python-excel-automation",[],[1853,1855,1858,1862,1864,1868],{"path":1854,"title":1703,"kind":1836},"\u002Fadvanced-data-transformation-and-cleaning\u002Freading-excel-with-polars-and-arrow\u002Fconvert-excel-files-to-parquet-with-python",{"path":1856,"title":1857,"kind":1836},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python","Convert Excel to CSV with Python",{"path":1859,"title":1860,"kind":1861},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fexcel-vs-csv-vs-parquet-for-python-data-pipelines","Excel vs CSV vs Parquet for Python Data Pipelines","compare",{"path":1863,"title":1768,"kind":1861},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpick-an-excel-engine-for-xlsx-xlsm-xls-xlsb-and-ods",{"path":1865,"title":1866,"kind":1867},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Ffix-garbled-characters-in-excel-data","Fix Garbled Characters Like \"CafÃ©\" and \"â€™\" in Excel Data","fix",{"path":1869,"title":1870,"kind":1836},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fextract-vba-macro-code-from-an-excel-file","Extract VBA Macro Code from an Excel File",1790972064894]