달나라 노트

Python openpyxl : Python에서 Excel 다루기, BytesIO xlsx 객체 읽어오기 본문

Python/Python ETC

Python openpyxl : Python에서 Excel 다루기, BytesIO xlsx 객체 읽어오기

CosmosProject 2023. 12. 13. 01:37
728x90
반응형

 

 

 

openpyxl이라는 라이브러리는 Python에서 Excel을 다룰 수 있게 해주는 기능을 가지고 있습니다.

 

pandas에서도 read_excel() method를 사용할 때 등 openpyxl을 아주 밀접하게 사용하고있습니다.

이렇게 일반적으로 openpyxl은 다른 라이브러리 내부에서 엔진으로서 사용되고 있어서 이것을 직접 사용하는 경우는 아주 많지는 않을 수 있습니다만 그래도 엑셀 데이터를 다룰 수 있게 해주는 강력한 툴 중 하나이므로 그 방법을 알아봅시다.

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기

 

일단 시작은 BytesIO객체로 시작합니다.

test용 DataFrame 2개 df_test_1, df_test_2를 만들고

이 두 개의 DataFrame을 하나의 xlsx 파일의 형태로 만들었습니다.

그리고 xlsx 파일을 BytesIO 객체의 형태로 저장했죠.

 

이 객체에 저장된 정보는 df_test_1, df_test_2라는 2개의 탭을 가진 하나의 엑셀 파일과 동일합니다.

 

BytesIO 객체를 이용해서 xlsx 파일을 다루는 방법은 아래 링크를 참고하면 됩니다.

https://cosmosproject.tistory.com/794

 

Python io : BytesIO (메모리에 엑셀 파일 저장하기, BytesIO로 xlsx 파일 객체 생성하기)

DataFrame을 xlsx 파일로 생성하려면 to_excel() method를 사용합니다. import pandas as pd df_test = pd.DataFrame({ 'item_id': [1, 2, 3, 4, 5], 'name': ['a', 'b', 'c', 'd', 'e'] }) print(df_test) dir = 'output/df_test.xlsx' df_test.to_excel(dir,

cosmosproject.tistory.com

 

 

 

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)

 

openpyxl을 사용하여 xlsx를 다루려면 당연히 먼저 xlsx 데이터를 불러와야 합니다.

xlsx 데이터는 openpyxl.lead_workbook() method를 이용하여 할 수 있습니다.

 

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)

 

위처럼 사용할 수 있으며 이렇게 workbook 객체를 생성할 수 있습니다.

 

여기서 용어 정리를 한번 하고 가면

주로 workbook이란 xlsx 파일 전체를 의미하고,

workbook에 있는 우리가 흔히 탭이라고 부르는 것은 worksheet(또는 sheet)라고 합니다.

 

 

 

추가로 load_workbook() method는 oepnpyxl version에 따라

_io.Bytes 객체를 받을 수도 있고

bytes 객체를 받을 수도 있습니다.

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

bytes_xlsx = io.BytesIO(xlsx_file)
bytes_xlsx_value = io.BytesIO(xlsx_file).getvalue()
print(type(bytes_xlsx), bytes_xlsx)
print(type(bytes_xlsx_value), bytes_xlsx_value)

workbook = openpyxl.load_workbook(bytes_xlsx, data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)





-- Result
<class '_io.BytesIO'> <_io.BytesIO object at 0x7fe9ff032610>
<class 'bytes'> b'PK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dWFZ\xc1\x0c\x82\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x00\x00docProps/app.xmlM\x8eM\x0b\xc20\x10D\xffJ\xe9\xddnU\xf0 1 \xd4\xa3\xe0\xc9{H76\x90dCv\x85\xfc|S\xc1\x8f\xdb<\xde0\x8c\xba\x15\xcaX\xc4#w5\x86\xc4\xa7~\x11\xc9G\x00\xb6\x0bF\xc3C\xd3\xa9\x19G%\x1aiX\x1e@\xcey\x8b\x13\xd9g\xc4$\xb0\x1b\xc7\x03`\x15L3\xce\x9b\xfc\x1d\xec\xb5:\xe7\x1c\xbc5\xe2)\xe9\xab\xb7\x85\x98\x9ct\x97j1(\xf8\x97k\xf3\x8e\x85\xd7\xbc\x1f\xb6o\xf9a\x05\xbf\x93\xfa\x05PK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\x96\x1dF\xe9\xeb\x00\x00\x00\xcb\x01\x00\x00\x11\x00\x00\x00docProps/core.xml\xa5\x91\xcbj\xc30\x10E\x7f%ho\x8f\x1f\xd4\x14\xe1x\x93\xd0U\x0b\x85\x06Z\xba\x13\xd2$\x11\xb1\x1eHS\xec\xfc}e7qZ\xda]A\x1b\xcd=sf\x84Z\xe9\xb9t\x01\x9f\x83\xf3\x18Hc\\\x8d\xa6\xb7\x91K\xbffG"\xcf\x01\xa2<\xa2\x111O\x84M\xe1\xde\x05#(]\xc3\x01\xbc\x90\'q@\xa8\x8a\xa2\x01\x83$\x94 \x01\x930\xf3\x8b\x91]\x94J.J\xff\x11\xfaY\xa0$`\x8f\x06-E(\xf3\x12n,a0\xf1\xcf\x869Y\xc81\xea\x85\x1a\x86!\x1f\xea\x99K\x1b\x95\xf0\xf6\xf4\xf82/\x9fi\x1bIX\x89\xack\x95\xe42\xa0 \x17\xba\xe9E\xfe<\xf6-|+\xb6\x97\xd9_\x05T\xab4\x81\xd3\xd9\xe3\x9a]\x93\xd7z\xb3\xdd=\xb0\xae*\xaa:+\xabtve\xc3\xef\x1a^\xdd\xbfO\xae\x1f\xfd7\xa1qJ\xef\xf5?\x8cWA\xd7\xc2\xaf\x7f\xeb>\x01PK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\x99\\\x9c#\x10\x06\x00\x00\x9c\'\x00\x00\x13\x00\x00\x00xl/theme/theme1.xml\xedZ[s\xda8\x14~\xef\xaf\xd0xg\xf6m\x0b\xc66\x81\xb6\xb4\x13siv\xdb\xb4\x99\x84\xedN\x1f\x85\x11X\x8dlyd\x91\x84\x7f\xbfG6\x10\xcb\x96\r\xed\x92M\xba\x9b<\x04,\xe9\xfb\xceEG\xe7\xe88y\xf3\xee.b\xe8\x86\x88\x94\xf2x`\xd9/\xdb\xd6\xbb\xb7/\xde\xe0W2$\x11A0\x19\xa7\xaf\xf0\xc0\n\xa5L^\xb5Zi\x00\xc38}\xc9\x13\x12\xc3\xdc\x82\x8b\x08Kx\x14\xcb\xd6\\\xe0[\x1a/#\xd6\xea\xb4\xdb\xddV\x84il\xa1\x18Gd`}^,h@\xd0TQZo_ \xb4\xe5\x1f3\xf8\x15\xcbT\x8de\xa3\x01\x13WA&\xb9\x88\xb4\xf2\xf9l\xc5\xfc\xda\xde>e\xcf\xe9:\x1d2\x81n0\x1bX \x7f\xceo\xa7\xe4NZ\x88\xe1T\xc2\xc4\xc0jg?Vk\xc7\xd1\xd2H\x80\x82\xc9}\x94\x05\xbaI\xf6\xa3\xd3\x15\x082\r;:\x9dX\xcev|\xf6\xc4\xed\x9f\x8c\xca\xdat4m\x1a\xe0\xe3\xf1x8\xb6\xcb\xd2\x8bp\x1c\x04\xe0Q\xbb\x9e\xc2\x9d\xf4l\xbf\xa4A\t\xb4\xa3i\xd0d\xd8\xf6\xda\xae\x91\xa6\xaa\x8dSO\xd3\xf7}\xdf\xeb\x9bh\x9c\n\x8d[O\xd3kw\xdd\xd3\x8e\x89\xc6\xad\xd0x\r\xbe\xf1O\x87\xc3\xae\x89\xc6\xab\xd0t\xebi&\'\xfd\xaek\xa4\xe9\x16hBF\xe3\xebz\x12\x15\xb5\xe5@\xd3 \x00Xpv\xd6\xcc\xd2\x03\x96^)\xfau\x94\x1a\xd9\x1d\xbb\xddA\\\xf0X\xee9\x89\x11\xfe\xc6\xc5\x04\xd6i\xd2\x19\x964Fr\x9d\x90\x05\x0e\x007\xc4\xd1LP|\xafA\xb6\x8a\xe0\xc2\x92\xd2\\\x90\xd6\xcf)\xb5P\x1a\x08\x9a\xc8\x81\xf5G\x82!\xc5\xdc\xaf\xfd\xf5\x97\xbb\xc9\xa43z\x9d}:\xcek\x94\x7fi\xab\x01\xa7\xed\xbb\x9b\xcf\x93\xfcs\xe8\xe4\x9f\xa7\x93\xd7MB\xcep\xbc,\t\xf1\xfb#[a\x87\'n;\x13r:\x1cgB|\xcf\xf6\xf6\x91\xa5%2\xcf\xef\xf9\n\xebN<g\x1fV\x96\xb0]\xcf\xcf\xe4\x9e\x8cr#\xbb\xdd\xf6X}\xf6OGn#\xd7\xa9\xc0\xb3"\xd7\x94F$E\x9f\xc8-\xba\xe4\x118\xb5I\r2\x13?\x08\x9d\x86\x98jP\x1c\x02\xa4\t1\x96\xa1\x86\xf8\xb4\xc6\xac\x11\xe0\x13}\xb7\xbe\x08\xc8\xdf\x8d\x88\xf7\xabo\x9a=W\xa1XI\xda\x84\xf8\x10F\x1a\xe2\x9cs\xe6s\xd1l\xfb\x07\xa5F\xd1\xf6U\xbc\xdc\xa3\x97X\x15\x01\x97\x18\xdf4\xaa5,\xc5\xd6x\x95\xc0\xf1\xad\x9c<\x1d\x13\x12\xcd\x94\x0b\x06A\x86\x97$&\x12\xa99~MH\x13\xfe+\xa5\xda\xfe\x9c\xd3@\xf0\x94/$\xfaJ\x91\x8fi\xb3#\xa7t&\xcd\xe83\x1a\xc1F\xaf\x1bu\x87h\xd2<z\xfe\x05\xf9\x9c5\n\x1c\x91\x1b\x1d\x02g\x1b\xb3F!\x84i\xbb\xf0\x1e\xaf$\x8e\x9a\xad\xc2\x11+B>b\x196\x1ar\xb5\x16\x81\xb6q\xa9\x84`Z\x12\xc6\xd1xN\xd2\xb4\x11\xfcY\xac5\x93>`\xc8\xec\xcd\x91u\xce\xd6\x91\x0e\x11\x92^7B>b\xce\x8b\x90\x11\xbf\x1e\x868J\x9a\xed\xa2qX\x04\xfd\x9e^\xc3I\xc1\xe8\x82\xcbf\xfd\xb8~\x86\xd53l,\x8e\xf7G\xd4\x17J\xe4\x0f&\xa7?\xe924\x07\xa3\x9aY\t\xbd\x84Vj\x9f\xaa\x874>\xa8\x1e2\n\x05\xf1\xb9\x1e>\xe5zx\n7\x96\xc6\xbcP\xae\x82{\x01\xff\xd1\xda7\xc2\xab\xf8\x82\xc09\x7f.}\xcf\xa5\xef\xb9\xf4=\xa1\xd2\xb77#}g\xc1\xd3\x8b[\xdeFn[\xc4\xfb\xae1\xda\xd74.(cWr\xcd\xc8\xc7T\xaf\x93)\xd89\x9f\xc0\xec\xfdh>\x9e\xf1\xed\xfa\xd9$\x84\xaf\x9aY-#\x16\x90K\x81\xb3A$\xb8\xfc\x8b\xca\xf0*\xc4\t\xe8d[%\t\xcbT\xd3e7\x8a\x12\x9eB\x1bn\xe9S\xf5J\x95\xd7\xe5\xaf\xb9(\xb8<[\xe4\xe9\xaf\xa1t>,\xcf\xf9<_\xe7\xb4\xcd\x0b3C\xb7rK\xea\xb6\x94\xbe\xb5&8J\xf4\xb1\xccpN\x1e\xcb\x0c;g<\x92\x1d\xb6w\xa0\x1d5\xfb\xf6]v\xe4#\xa50S\x97C\xb8\x1aB\xbe\x03m\xba\x9d\xdc:8\x9e\x98\x91\xb9\n\xd3R\x90o\xc3\xf9\xe9\xc5x\x1a\xe29\xd9\x04\xb9}\x98Wm\xe7\xd8\xd1\xd1\xfb\xe7\xc1Q\xb0\xa3\xef<\x96\x1d\xc7\x88\xf2\xa2!\xee\xa1\x86\x98\xcf\xc3C\x87y{_\x98g\x95\xc6P4\x14ml\xac$,F\xb7`\xb8\xd7\xf1,\x14\xe0d`-\xa0\x07\x83\xafQ\x02\xf2RU`1[\xc6\x03+\x90\xa2|L\x8cE\xe8p\xe7\x97\\_\xe3\xd1\x92\xe3\xdb\xa6e\xb5n\xaf)w\x19m"R9\xc2i\x98\x13g\xab\xca\xdee\xb1\xc1U\x1d\xcfU[\xf2\xb0\xbej=\xb4\x15N\xcf\xfeY\xad\xc8\x9f\x0c\x11N\x16\x0b\x12Hc\x94\x17\xa6J\xa2\xf3\x19S\xbe\xe7+I\xc4U8\xbfE3\xb6\x12\x97\x18\xbc\xe3\xe6\xc7qNS\xb8\x12v\xb6\x0f\x022\xb9\xbb9\xa9ze1g\xa6\xf2\xdf-\x0c\t,[\x88Y\x12\xe2M]\xed\xd5\xe7\x9b\x9c\xaez"v\xfa\x97w\xc1`\xf2\xfdp\xc9G\x0f\xe5;\xe7_\xf4]C\xae~\xf6\xdd\xe3\xfan\x93;HL\x9cy\xc5\x11\x01tE\x02#\x95\x1c\x06\x16\x172\xe4P\xee\x92\x90\x06\x13\x01\xcd\x94\xc9D\xf0\x02\x82d\xa6\x1c\x80\x98\xfa\x0b\xbd\xf2\x0c\xb9)\x15\xce\xad>9\x7fE,\x83\x86N^\xd2%\x12\x14\x8a\xb0\x0c\x05!\x17r\xe3\xef\xef\x93jw\x8c\xd7\xfa,\x81m\x84T2d\xd5\x17\xcaC\x89\xc1=3rC\xd8T%\xf3\xae\xda&\x0b\x85\xdb\xe2T\xcd\xbb\x1a\xbe&`K\xc3zn\x9d-\'\xff\xdb^\xd4=\xb4\x17=F\xf3\xa3\x99\xe0\x1e\xb3\x87s\x9bz\xb8\xc2E\xac\xffX\xd6\x1e\xf92\xdf9p\xdb:\xde\x03^\xe6\x13,C\xa4~\xc1}\x8a\x8a\x80\x11\xabb\xbe\xba\xafO\xf9%\x9c;\xb4{\xf1\x81 \x9b\xfc\xd6\xdb\xa4\xf6\xdd\xe0\x0c|\xd4\xabZ\xa5d+\x11?K\x07|\x1f\x92\x06c\x8c[\xf44_\x8f\x14b\xad\xa6\xb1\xad\xc6\xda1\x0cy\x80X\xf3\x0c\xa1f8\xdf\x87E\x9a\x1a3\xd5\x8b\xac9\x8d\noA\xd5@\xe5?\xdb\xd4\rh\xf6\r4\x1c\x91\x05^1\x99\xb66\xa3\xe4N\n<\xdc\xfe\xef\r\xb0\xc2\xc4\x8e\xe1\xed\x8b\xbf\x01PK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\x83\xc0c\x06\x9c\x01\x00\x00\x14\x04\x00\x00\x18\x00\x00\x00xl/worksheets/sheet1.xml}\x94\xddN\xeb0\x0c\xc7_\xa5\xca\x03\x90\xad[\x0bBm%\x06Bp\x81\x84@\xe7\x9cK\x94\xb5\xee\x1a\x91\x8f\x92x+\xbc\xfdI\xc2\x08e\xa2\xbd\xaa\xed\xc4\xbf\xbf\xdd8)\x06m^m\x07\x80\xc9\xbb\x14\xca\x96\xa4C\xec/)\xb5u\x07\x92\xd93\xdd\x83r+\xad6\x92\xa1s\xcd\x8e\xda\xde\x00kB\x92\x144],r*\x19W\xa4*B\xec\xd1T\x85\xde\xa3\xe0\n\x1eMb\xf7R2\xf3\xb1\x01\xa1\x87\x92,\xc9W\xe0\x89\xef:\x0c\x01Z\x15=\xdb\xc13\xe0\x9f\xde%8\x97FN\xc3%(\xcb\xb5J\x0c\xb4%\xb9Z^n\xf2\x90\x11v\xfc\xe50\xd8\x91\x9d\xf8f\xb6Z\xbfz\xe7\xbe)\xc9\xc2\xd7\x04\x02j\xf4\x08\xe6>\x07\xb8\x06!<\xc9U\xf2v\x84\x92oQ\x9f9\xb6\xbf\xf0\xb7\xa1\x7fW\xde\x96Y\xb8\xd6\xe2\x1fo\xb0+\xc9\x05I\x1ah\xd9^\xe0\x93\x1e\xee\xe0\xd8S\xf6]\xe2\rCV\x15F\x0f\x89\xf1\xcdVE\xed\x8d\xa0\x1e\x9aw\xdb\xb9\xf2\xbf\xea\x19\x8d[\xe5N\x0f+\x8e _xSPt\xa5\xf8\x10\xad\x8f\x89\x9b\xf9D\xc5$\xfc\xcc\xa2N:\xea\xa7Q?\r\x00\x7fd\x87jY\xd0\xc3X"\x9d\x80\xb3\x19\xf2*\x92W#rzB^M\x90\xb73\xe4u$\xafG\xe4\xd5\ty=A\xaeg\xc8Y$g#\xf2\xfa\x84\x9cM\x90\x9b\x19r\x1e\xc9\xf9\x88\x9c\x9d\x90\xf3\t\xf2\xaf\'HG\xd3\xe4o\xcb\x033;\xael"\xa0u\x90\xc5\xd9\xb9\xab\xd3|\x8e\xdf\xa7\x83\xba\x0fs\xb2\xd5\x88Z\x06\xb3s\xb7\x16\x8c\xdf\xe0\xd6[\xad1:~\xfc\xe3CP\xfd\x07PK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dWQMT:\xf3\x01\x00\x009\x06\x00\x00\x18\x00\x00\x00xl/worksheets/sheet2.xml}\x95\xddn\xa30\x10\x85_\x05\xf1\x005!\x7f\xa8\x02\xa4\x06\xb4\xda^\xacT\xb5\xda\xdd\xcb\xca\x81!X\xb515N\xd8\xbe\xfd\xda&q\x81\xe2^\xc5\xf6\xe1\xccwfH\x9c\xb8\xe7\xe2\xad\xab\x01\xa4\xf7\x8f\xd1\xa6K\xfcZ\xca\xf6\x1e\xa1\xae\xa8\x81\xe1\xee\x8e\xb7\xd0(\xa5\xe2\x82a\xa9\xb6\xe2\x84\xbaV\x00.\x8d\x89Q\x14\x06\xc1\x0e1L\x1a?\x8d\xcd\xd9\x93Hc~\x96\x944\xf0$\xbc\xee\xcc\x18\x16\x1f\x07\xa0\xbcO\xfc\x95\x7f;x&\xa7Z\x9a\x03\x94\xc6->\xc1\x0b\xc8\xdf\xad2\xa8-\xb2uJ\xc2\xa0\xe9\x08o<\x01U\xe2?\xac\xee\xf3\xc88\xcc\x13\x7f\x08\xf4\xddh\xed\xe9f\x8e\x9c\xbf\xe9\xcdc\x99\xf8\x81\xce\x04\x14\n\xa9K`\xf5q\x81\x0c(\xd5\x95T\x92\xf7kQ\xff\x13\xaa\x9d\xe3\xf5\xad\xfc\x0f\xd3\xbf\x8aw\xc4\x1dd\x9c\xfe%\xa5\xac\x13_\x85)\xa1\xc2g*\x9fy\xff\x13\xae=m?#\xe6X\xe24\x16\xbc\xf7\x84n6\x8d\x0b\xbd0t\xd3\xbcz\x9c4zT/R(\x95(\x9eL\x89\x04\xf6J\xca\x18I\x15E\x1f\xa1\xe2j<|ol\x05)`\xc1\x96}o\x03\xd6\xca\x8f\xd7\x82\xd3\x05k\xbel5\x13Sm\xd9\xdeB\xdb[h\x1e\xd5_\x87K\xba\x8a\xd1e\x1c\x7f\xa2\x05A0\x95\xb3p\x812\xa4XR&\xfc\xb5\xe5\xafG\x8cp\xc6\x1fk\xd1\x17\xfc\xda\x89\x9f+\xb7\xc9y\xd3\x89M\x12ml\xa2\x8d\xab\xeea3\xca\xb3\xfe:\x0f\xa71_R&\xf4\xad\xa5oG\x8c\xcdl\x1e[\x17 s*\xf9\x922A\xef,z\xe7l|\xae8\x06:dq\x96\xc9\x97\x94I\x96\xbd\xcd\xb2wfq*\x99S\xc9\x97\x94\t8\xb2\xe0h\xfc\x8eg\xf3\x8f\x9ch\xa7\x92/)\x03\x1a\x8d\xae\x1c}\xa5\xfe\xc2\xe2D\x9a\xce\xa3P)Gp\xb7W\xafN\x0cw\xd4\xb0\x91\xbc5?\xed#\x97\x923\xb3\xac\xd5\xd5\x0eB?\xa0\xf4\x8asi7\x1ac\xff-\xd2\xffPK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\xf7\xf6\x8f\t\xa7\x02\x00\x00m\x0b\x00\x00\r\x00\x00\x00xl/styles.xml\xddV\xdb\x8e\x9b0\x10\xfd\x15\xc4\x07\x94MPQ\xa8B\xa46\xd2J\x95\xdaj\xa5\xdd\x87\xbe\x9a`\x82%_\xa81\xabd\xbf\xbe3\xb6\x13\x92\xec\x0e\xdd\xf6\xb1\xa0\x84\xf1\x1c\x9f\xb9;d=\xb8\xa3\xe4\x8f\x1d\xe7.9(\xa9\x87*\xed\x9c\xeb?e\xd9\xb0\xeb\xb8b\xc3\x07\xd3s\rHk\xacb\x0e\x96v\x9f\r\xbd\xe5\xac\x19\x90\xa4d\xb6\xbc\xbb+2\xc5\x84N7k=\xaa{\xe5\x86dgF\xed\xaa\xf4.M\xb2\xcd\xba5zR-\xd3\xa0\x80\xbdL\xf1\xe4\x99\xc9*\xdd2)j+\xc2f\xa6\x84<\x06\xfd\xd2kvF\x1a\x9b8\x88\x86W\xe9\xc2\xab\x86\x97\xb0a\x11\x97\x18j\xb4\xa5\x846\xd6k\xb3\xe0&|\xd7\x91p\x89\xf8\xc7\x00;\x84\x94\xd7\xf1\x81b\xb3\xee\x99s\xdc\xea{X\x04\x92\xd7\xbe\xc6\xa2\xfct\xec!\xbe\xbde\xc7\xc5\xf2cz\xc1\xf0\x0fpS\x1b\xdbp{\xe5(\xa86k\xc9[\x87\x0c+\xf6\x9d\x17\x9c\xe9\xf1Q\x1b\xe7\x8cB\xa9\x11lo4\x0b\x91\x9chWt\xdf\xc8*u\x1d4b\xb2u\xabE\xc3\xb7\xba\xe8\xe5V}v\x13\x05Ha\xc7\xa5|\xc4m?\xdbs\x1e\x0b\xc8\xe3\xd0&\xa1\xf3_\x1b\xdft,\xecI\x84\xe4\xa3\x18\xcc\xc4\x05:\xb84\x17\x8c_\xd8]\xfe\x9b\xdd^<\x1b\xf7e\x84\x8c\xb4_\xff\x1a\x8d\xe3\x0f\x96\xb7\xe2\xe0\xd7\x87v\n\x802\xbf \xcc\x83\x9e\xf5\xbd<~\x96b\xaf\x15\x0f\xd9\xbf\xdb\xe3f\xcdN\xbc\xa43V\xbc\x807\x9c\xc9\x1d(8\x8c\xec3\xb7N\xecP\x03M\n\x05:\xb4\xb1J\xe7\x02\xf9r]\xd5\xfe\xacM\xf0@U\xe9\x0f<\xa8\xf2"\xd1z\x14\xd2\t\x1dW\x9dh\x1a\xae_\xb7\x00\xec;V\xc3O\xc1\x95\x03\xd8\xd5\xf0\x96\x8d\xd2=\x9d\xc1*\x9d\xe4\xef\xbc\x11\xa3*\xcf\xbb\x1e\xb0\x16q\xd7$\x7f\xc3Q\\\x14\xd3i\x06gB7\xfc\xc0\x9bm\\\xda}\xed\xc5\x04\x04p\x1b\xaf0\xc87\xd0\xbd\xbf\x08\x88d\x05\x90\x80\x10$}\x91a\x90\xac\xc0#}\xfd\x8fy\xad\xe8\xbc\x02HF\xb8z\x1bZ\xd1\xac\x15\xcd\n\xbc7\xa1\xad\xbfI_\x04\xab\x84\x8bH\xb9,\xf3\xbc(\xc8\xf2n\xb7o\x87\xb1%kX\x14\xf8!\x0c\x92\x11"\x87\xf4\x85\xde\xfe\xb6\xf23\x03036\x7f\x98\r\xb2\xcb\xb3cC\xa6<3\xa2d\xca3\x95G\x88\xa8!r\xca\x92\x18\x00\xd2\x17r\xc8\xa6\x90\x13\x85A\x10\xbep\xd4\x08V\x9ec\x9f\xc9\x08\xc9c>\x03\x95%\t\xe1\x90\x12\xd3[\x14T\xa1\n\xbc\x89~\x91\x87(\xcf\xcb\x92\x80\x10$\xc2\xc8s\x12\xc2\x03;\x03\x91a` $\x94\xe7\xe1Ez\xf3>\xcbN\xef\xb9l\xfa\x83\xbd\xf9\rPK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\xb7G\xeb\x8a\xc0\x00\x00\x00\x16\x02\x00\x00\x0b\x00\x00\x00_rels/.rels\x9d\x92Kn\x021\x0c@\xaf\x12e_L\xa9\xc4\x021\xac\xd8\xb0C\x88\x0b\xb8\x89\xe7\xa3\x99\xc4\x91c\xc4\xf4\xf6\x8d\xd8\xc0 h\x11K\xff\x9e\x9e-\xaf\x0f4\xa0v\x1cs\xdb\xa5l\xc60\xc4\\\xd9V5\xad\x00\xb2k)`\x9eq\xa2X*5K@-\xa14\x90\xd0\xf5\xd8\x10,\xe6\xf3%\xc8-\xc3n\xd6\xb7Ls\xfcI\xf4\n\x91\xeb\xbas\xb4ew\n\x14\xf5\x01\xf8\xae\xc3\x9a#JCZ\xd9q\x803K\xff\xcd\xdc\xcf\n\xd4\x9a\x9d\xaf\xac\xec\xfc\xa75\xf0\xa6\xcc\xf3\xf5 \x90\xa2GEp,\xf4\x91\xa4L\x8bv\x94\xaf>\x9e\xdd\xbe\xa4\xf3\xa5cb\xb4x\xdf\xe8\xff\xf3\xd0\xa8\x14=\xf9\xbf\x9d0\xa5\x89\xd2\xd7E\t&o\xb0\xf9\x05PK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dWf!1\xaaB\x01\x00\x00n\x02\x00\x00\x0f\x00\x00\x00xl/workbook.xml\x8d\x91aK\xc30\x10\x86\xffJ\xc9\x0f\xb0]\xd1\x81c\xf5\x8bC\x1d\x88\x8a\x93}\x1dis]\x8f%\xb9\x92\\7\xdd\xaf7M\xa9\x16\x14\xf1Sr\xef\x1dO\xde\xf7\xb2<\x91;\x94D\x87\xe4\xddh\xeb\x17\xae\x10\rs\xbbHS_5`\xa4\xbf\xa0\x16l\xe8\xd5\xe4\x8c\xe4P\xba}Ju\x8d\x15\xac\xa8\xea\x0cXN\xf3,\x9b\xa7\x0e\xb4d$\xeb\x1bl\xbd\x18h\xffa\xf9\xd6\x81T\xbe\x01`\xa3\x07\x94\x91h\xc5\xcdrt\xf6\xe2\x92tZ\x11C\xd5\xbf\xd4\xab\xbd\xb2E8\xf9\xef\x81\xbeL\x8e\xe8\xb1D\x8d\xfcQ\x88x\xd7 \x12\x83\x16\r\x9eA\x15"\x13\x89o\xe8\xf4@\x0e\xcfdY\xeaM\xe5H\xebB\xcc\x86\xc6\x16\x1cc\xf5C\xde\xf46\xdfd\xe9\xa3\xc2\xb2|\xed3\x17b\x9e\x05`\x8d\xces\x9c\x88|\x19L\x1e!\x0c\x0fU\xc7t\x87\x9a\xc1\xad$\xc3\xbd\xa3\xaeE\xbb\x8f\x98\x10#\x9d\xe4\x88\xab\x18\xcf\xc4J\x03\x85P\xf5\x8e\xc1\xf3.\xda\x08\xf2Z\r\x968\xb0&\x01\xdd\x02C\xc3\xad\xd5@\xfd\x8d\x90O\x08\xf9\x1f\x84|\xf05\x9aQP\xa3\x05\xf5\x14X\xbeo\x84\xd5T\xe1_\xfa#z\xc9/\xaff\xd7a\x05\x9d\xd6\xb7A{\xb6\x8f$\xd5W\xba\xf1kn>\x01PK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\xab^r.\xb4\x00\x00\x00\x8d\x02\x00\x00\x1a\x00\x00\x00xl/_rels/workbook.xml.rels\xc5\x92M\n\x830\x10F\xaf\x12r\x00Gm\xe9\xa2\xa8\xabn\xdc\x16/\x10t\xfc\xc1\xc4\x84\xcc\x94\xea\xed+\xbaP\xa1\x8bn\xa4\xab\xf0M\xc8\xfb\x1eL\x92\'j\xc5\x9d\x1d\xa8\xed\x1c\x89\xd1\xe8\x81R\xd92\xbb;\x00\x95-\x1aE\x81u8\xcc7\xb5\xf5F\xf1\x1c}\x03N\x95\xbdj\x10\xe20\xbc\x81\xdf3d\x96\xec\x99\xa2\x98\x1c\xfeB\xb4u\xdd\x95\xf8\xb0\xe5\xcb\xe0\xc0_\xc0\xf0\xb6\xbe\xa7\x16\x91\xa5(\x94o\x90S\t\xa3\xde\xc6\x04\xcb\x11\x053Y\x8a\xbcJ\xa5\xcf\xabH\n\xf8\xb7Q|0\x8a\xcf4"\x9e4\xd2\xa6\xb3\xe6C\xff\xe5\xcc~\x9e\xdf\xe2V\xbf\xc4ux\\\xcbu\x91\x80\xc3\xef\xcb>PK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\xa5\xe1\x1bX\x1f\x01\x00\x00`\x04\x00\x00\x13\x00\x00\x00[Content_Types].xml\xc5T\xcbN\xc30\x10\xfc\x95\xc8\xd7*v\xe9\x81\x03jz\xa1\\\xa1\x07~\xc0$\x9b\xc6\x8a_\xf2nK\xfa\xf7l\x12Z\tTZ\xaa q\x89\x15\xef\xec\xccx\xc7\xf2\xf2\xf5\x10\x01\xb3\xceY\x8f\x85h\x88\xe2\x83RX6\xe04\xca\x10\xc1s\xa5\x0e\xc9i\xe2\xdf\xb4UQ\x97\xad\xde\x82Z\xcc\xe7\xf7\xaa\x0c\x9e\xc0SN=\x87X-\xd7P\xeb\x9d\xa5\xec\xa9\xe3m4\xc1\x17"\x81E\x91=\x8e\xc0^\xab\x10:FkJM\\W{_}S\xc9?\x15$w\x0e\x18lL\xc4\x19\x03D\xa6\xceJ\x0c\xa5\x1f\x15\x8e\x8d/{H\xc9T\x90mt\xa2g\xed\x18\xa6:\xab\x90\x0e\x16P^\xe68\xe32\xd4\xb5)\xa1\n\xe5\xceq\x8b\xc4\x98@W\xd8\x00\x90\xb3r$\x9d]\x91&\x1e2\x8c\xdf\xbb\xc9\x06\x06\x9a\x8b\x8a\x0c\xdd\xa4\x10\x91SKp\xbb\xde1\x96\xbe;\x8fL\x04\x89\xcc\x95C\x9e$\x99{\xf2\t\xa1O\xbc\x82\xea\xb7\xe2<\xe1\xf7\x90\xda!\x13T\xc32}\xcc_s>\xf1\xdfjd\xf1\x9fF\xdeBh\xff\xfa\xc2\xf7\xabt\xda\xf8\x93\x015<,\xab\x0fPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dWFZ\xc1\x0c\x82\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00docProps/app.xmlPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\x96\x1dF\xe9\xeb\x00\x00\x00\xcb\x01\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\xb0\x00\x00\x00docProps/core.xmlPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\x99\\\x9c#\x10\x06\x00\x00\x9c\'\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\xca\x01\x00\x00xl/theme/theme1.xmlPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\x83\xc0c\x06\x9c\x01\x00\x00\x14\x04\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x81\x0b\x08\x00\x00xl/worksheets/sheet1.xmlPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dWQMT:\xf3\x01\x00\x009\x06\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x81\xdd\t\x00\x00xl/worksheets/sheet2.xmlPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\xf7\xf6\x8f\t\xa7\x02\x00\x00m\x0b\x00\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x06\x0c\x00\x00xl/styles.xmlPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\xb7G\xeb\x8a\xc0\x00\x00\x00\x16\x02\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\xd8\x0e\x00\x00_rels/.relsPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dWf!1\xaaB\x01\x00\x00n\x02\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\xc1\x0f\x00\x00xl/workbook.xmlPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\xab^r.\xb4\x00\x00\x00\x8d\x02\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x010\x11\x00\x00xl/_rels/workbook.xml.relsPK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dW\xa5\xe1\x1bX\x1f\x01\x00\x00`\x04\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x1c\x12\x00\x00[Content_Types].xmlPK\x05\x06\x00\x00\x00\x00\n\x00\n\x00\x84\x02\x00\x00l\x13\x00\x00\x00\x00'
Show workbook object = <openpyxl.workbook.workbook.Workbook object at 0x7fe9ff377e80>

 

두 개의 차이가 뭔지 봐봅시다.

load_workbook() method를 사용하기 전에 bytes_xlsx, bytes_xlsx_value부분을 보면 그 차이를 알 수 있습니다..

 

<class '_io.BytesIO'> <_io.BytesIO object at 0x7fe9ff032610>

_io.BytesIO 객체는 말그대로 bytes 데이터를 _io.BytesIO 객체의 형태로 구성한 것입니다.

 

_io.BytesIO 객체에 getvalue() method를 이용하면 bytes 객체를 얻을 수 있습니다.

<class 'bytes'> b'PK\x03\x04\x14\x00\x00\x00\x08\x00\x0e\x0f\x8dWFZ\...

그 결과를 보면 위와 같으며, bytes 객체는 메모리에 저장된 bytes 데이터를 그대로 보여주는 bytes 객체입니다.

 

두 데이터는 근본적으로는 동일한 데이터를 의미하지만 그 형태가 다릅니다.

 

좀 오래된 version의 openpyxl에서는 _io.BytesIO 객체를 load_workbook() method에 전달하면 에러가 발생합니다.

이런 경우 _io.BytesIO 객체의 getvalue() method를 사용해서 bytes 객체를 load_workbook()에 전달해야 한다는 것에 주의합시다.

 

 

 

 

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)

list_worksheet_objects = workbook.worksheets  # workbook 객체에 존재하는 모든 worksheet 객체를 불러옴.
print('Show Worksheet objects =', list_worksheet_objects)



-- Result
Show workbook object = <openpyxl.workbook.workbook.Workbook object at 0x7fb54dd6beb0>
Show Worksheet objects = [<Worksheet "df_test_1">, <Worksheet "df_test_2">]

 

- list_worksheet_objects = workbook.worksheets

 

workbook 객체에는 worksheets라는 attribute가 있는데 이 attribute는 workbook에 존재하는 worksheet 객체를 list에 담아서 return해줍니다.

 

workbook은 하나의 엑셀 파일이고

worksheet는 하나의 엑셀 파일에 있는 각각의 sheet 정보를 담은 객체라고 했습니다.

 

따라서 worksheet를 이용하면 sheet의 정보를 얻어올 수 있다는 것이죠.

 

 

 

 

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)

list_worksheet_objects = workbook.worksheets  # workbook 객체에 존재하는 모든 worksheet 객체를 불러옴.
print('Show Worksheet objects =', list_worksheet_objects)

list_worksheet_names = workbook.sheetnames  # workbook 객체에 존재하는 모든 worksheet의 이름을 불러옴.
print('Show Worksheet names =', list_worksheet_names)




-- Result
Show workbook object = <openpyxl.workbook.workbook.Workbook object at 0x7fc1e137ffd0>
Show Worksheet objects = [<Worksheet "df_test_1">, <Worksheet "df_test_2">]
Show Worksheet names = ['df_test_1', 'df_test_2']

 

- list_worksheet_names = workbook.sheetnames

workbook 객체의 sheetnames attribute는 workbook에 존재하는 모든 worksheet의 이름을 list에 담아서 return해줍니다.

위 예시에서는 workbook이 df_test_1, df_test_2라는 2개의 worksheet를 가지고 있는 걸 알 수 있네요.

 

 

 

 

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)

list_worksheet_objects = workbook.worksheets  # workbook 객체에 존재하는 모든 worksheet 객체를 불러옴.
print('Show Worksheet objects =', list_worksheet_objects)

list_worksheet_names = workbook.sheetnames  # workbook 객체에 존재하는 모든 worksheet의 이름을 불러옴.
print('Show Worksheet names =', list_worksheet_names)

active_worksheet = workbook.active  # 현재 active한 worksheet의 worksheet 객체를 불러옴.
print('Show Active Worksheet objects =', active_worksheet)





-- Result
Show workbook object = <openpyxl.workbook.workbook.Workbook object at 0x7fc1e137ffd0>
Show Worksheet objects = [<Worksheet "df_test_1">, <Worksheet "df_test_2">]
Show Worksheet names = ['df_test_1', 'df_test_2']
Show Active Worksheet objects = <Worksheet "df_test_1">

 

- active_worksheet = workbook.active

workbook 객체에는 active라는 attribute도 있는데,

이것은 현재 active 상태인 worksheet의 객체를 return해줍니다.

 

여기서 active란 우리가 엑셀을 틀어서 여러 탭 중 하나를 선택하면 그게 바로 active 상태인 탭이 됩니다.

즉, 현재 선택된(= active 상태인) worksheet의 객체를 얻어오는 것입니다.

 

 

 

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)

list_worksheet_objects = workbook.worksheets  # workbook 객체에 존재하는 모든 worksheet 객체를 불러옴.
print('Show Worksheet objects =', list_worksheet_objects)

list_worksheet_names = workbook.sheetnames  # workbook 객체에 존재하는 모든 worksheet의 이름을 불러옴.
print('Show Worksheet names =', list_worksheet_names)

active_worksheet = workbook.active  # 현재 active한 worksheet의 worksheet 객체를 불러옴.
print('Show Active Worksheet objects =', active_worksheet)

workbook.active = list_worksheet_names.index('df_test_2')  # active attribute에 active상태로 만들 worksheet의 index 입력.
active_worksheet = workbook.active
print('Show Active Worksheet objects =', active_worksheet)





-- Result
Show workbook object = <openpyxl.workbook.workbook.Workbook object at 0x7f9a5ac97e20>
Show Worksheet objects = [<Worksheet "df_test_1">, <Worksheet "df_test_2">]
Show Worksheet names = ['df_test_1', 'df_test_2']
Show Active Worksheet objects = <Worksheet "df_test_1">
Show Active Worksheet objects = <Worksheet "df_test_2">

 

- workbook.active = list_worksheet_names.index('df_test_2')

workbook의 active attribute에 worksheet의 index를 입력해주면 내가 원하는 worksheet를 active상태로 만들 수 있습니다.

 

현재 workbook에는 2개의 sheet가 존재한다고 했습니다.

첫 번째 worksheet는 df_test_1이며

두 번째 worksheet는 df_test_2입니다.

 

각 worksheet의 index는 가장 첫 번째 worksheet가 0부터 시작해서 1씩 늘어나는 방식입니다.

즉, 첫 번째 worksheet인 df_test_1의 index는 0입니다.

두 번째 worksheet인 df_test_2의 index는 1입니다.

 

list_worksheet_names.index('df_test_2') -> list 속에서 df_test_2라는 element의 index를 return합니다. 따라서 1이 return됩니다.

 

즉 위 코드는 workbook.active = 1 과 같습니다.

index = 1인 worksheet를 active하라는 것입니다.

index = 1인 worksheet는 df_test_2라는 sheet이구요.

결국 df_test_2라는 이름의 sheet를 active한다는 의미입니다.

 

그래서 다시 active sheet를 출력해보면 df_test_2로 바뀐 것을 알 수 있습니다.

 

 

 

 

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)

list_worksheet_objects = workbook.worksheets  # workbook 객체에 존재하는 모든 worksheet 객체를 불러옴.
print('Show Worksheet objects =', list_worksheet_objects)

list_worksheet_names = workbook.sheetnames  # workbook 객체에 존재하는 모든 worksheet의 이름을 불러옴.
print('Show Worksheet names =', list_worksheet_names)

active_worksheet = workbook.active  # 현재 active한 worksheet의 worksheet 객체를 불러옴.
print('Show Active Worksheet objects =', active_worksheet)

workbook.active = list_worksheet_names.index('df_test_2')  # active attribute에 active상태로 만들 worksheet의 index 입력.
active_worksheet = workbook.active
print('Show Active Worksheet objects =', active_worksheet)


active_worksheet = workbook.active
print('Show Worksheet values object =', active_worksheet.values)
for row in active_worksheet.values:  # worksheet에 존재하는 모든 value를 동일한 row 별로 묶어서 return합니다. value는 하나의 row별로 저장되어있습니다.
    print('Worksheet row =', row)





-- Result
Show workbook object = <openpyxl.workbook.workbook.Workbook object at 0x7fde75193e80>
Show Worksheet objects = [<Worksheet "df_test_1">, <Worksheet "df_test_2">]
Show Worksheet names = ['df_test_1', 'df_test_2']
Show Active Worksheet objects = <Worksheet "df_test_1">
Show Active Worksheet objects = <Worksheet "df_test_2">
Show Worksheet values = <generator object Worksheet.values at 0x7fde75199f50>
Worksheet row = ('item_id', 'price', 'empty_col', None)
Worksheet row = (1, 1000, None, None)
Worksheet row = (2, 800, None, ' ')
Worksheet row = (None, 3000, None, None)
Worksheet row = (4, None, None, None)
Worksheet row = (None, ' ', None, None)
Worksheet row = (None, None, None, None)
Worksheet row = (3, None, None, None)

 

active_worksheet = workbook.active
for row in active_worksheet.values:
    print('Worksheet row =', row)

 

worksheet 객체의 values attribute는 worksheet 객체에 있는 모든 값을 가지고 있는 attribute입니다.


Show Worksheet values = <generator object Worksheet.values at 0x7fde75199f50>

그리고 이 객체는 generator 형태의 객체입니다.

따라서 for loop를 통해서 그 내용을 살펴봐야합니다.

 

for loop를 돌린 결과를 보면 하나의 행(row)이 출력되는 것을 볼 수 있습니다.

 

 

 

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)

list_worksheet_objects = workbook.worksheets  # workbook 객체에 존재하는 모든 worksheet 객체를 불러옴.
print('Show Worksheet objects =', list_worksheet_objects)

list_worksheet_names = workbook.sheetnames  # workbook 객체에 존재하는 모든 worksheet의 이름을 불러옴.
print('Show Worksheet names =', list_worksheet_names)

active_worksheet = workbook.active  # 현재 active한 worksheet의 worksheet 객체를 불러옴.
print('Show Active Worksheet objects =', active_worksheet)

workbook.active = list_worksheet_names.index('df_test_2')  # active attribute에 active상태로 만들 worksheet의 index 입력.
active_worksheet = workbook.active
print('Show Active Worksheet objects =', active_worksheet)


active_worksheet = workbook.active
print('Show Worksheet values object =', active_worksheet.values)
for row in active_worksheet.values:  # worksheet에 존재하는 모든 value를 동일한 row 별로 묶어서 return합니다. value는 하나의 row별로 저장되어있습니다.
    print('Worksheet row =', row)


active_worksheet = workbook.active
print('Show Worksheet columns object =', active_worksheet.columns)
for col in active_worksheet.columns:  # worksheet에 존재하는 모든 값의 정보를 동일한 column별로 묶어서 return합니다. value는 하나의 row별로 저장되어있습니다.
    print('Worksheet columns =', col)





-- Result
Show workbook object = <openpyxl.workbook.workbook.Workbook object at 0x7fec8bcdc160>
Show Worksheet objects = [<Worksheet "df_test_1">, <Worksheet "df_test_2">]
Show Worksheet names = ['df_test_1', 'df_test_2']
Show Active Worksheet objects = <Worksheet "df_test_1">
Show Active Worksheet objects = <Worksheet "df_test_2">
Show Worksheet values object = <generator object Worksheet.values at 0x7fec8bc79e70>
Worksheet row = ('item_id', 'price', 'empty_col', None)
Worksheet row = (1, 1000, None, None)
Worksheet row = (2, 800, None, ' ')
Worksheet row = (None, 3000, None, None)
Worksheet row = (4, None, None, None)
Worksheet row = (None, ' ', None, None)
Worksheet row = (None, None, None, None)
Worksheet row = (3, None, None, None)
Show Worksheet columns object = <generator object Worksheet._cells_by_col at 0x7fec8bc79e70>
Worksheet columns = (<Cell 'df_test_2'.A1>, <Cell 'df_test_2'.A2>, <Cell 'df_test_2'.A3>, <Cell 'df_test_2'.A4>, <Cell 'df_test_2'.A5>, <Cell 'df_test_2'.A6>, <Cell 'df_test_2'.A7>, <Cell 'df_test_2'.A8>)
Worksheet columns = (<Cell 'df_test_2'.B1>, <Cell 'df_test_2'.B2>, <Cell 'df_test_2'.B3>, <Cell 'df_test_2'.B4>, <Cell 'df_test_2'.B5>, <Cell 'df_test_2'.B6>, <Cell 'df_test_2'.B7>, <Cell 'df_test_2'.B8>)
Worksheet columns = (<Cell 'df_test_2'.C1>, <Cell 'df_test_2'.C2>, <Cell 'df_test_2'.C3>, <Cell 'df_test_2'.C4>, <Cell 'df_test_2'.C5>, <Cell 'df_test_2'.C6>, <Cell 'df_test_2'.C7>, <Cell 'df_test_2'.C8>)
Worksheet columns = (<Cell 'df_test_2'.D1>, <Cell 'df_test_2'.D2>, <Cell 'df_test_2'.D3>, <Cell 'df_test_2'.D4>, <Cell 'df_test_2'.D5>, <Cell 'df_test_2'.D6>, <Cell 'df_test_2'.D7>, <Cell 'df_test_2'.D8>)

 

active_worksheet = workbook.active
print('Show Worksheet columns object =', active_worksheet.columns)
for col in active_worksheet.columns:
    print('Worksheet columns =', col)

 

worksheet 객체의 columns attribute는 worksheet 객체에 있는 모든 셀 객체를 가지고 있는 attribute입니다.


Show Worksheet columns object = <generator object Worksheet._cells_by_col at 0x7fec8bc79e70>

그리고 이 객체는 generator 형태의 객체입니다.

따라서 for loop를 통해서 그 내용을 살펴봐야합니다.

 

for loop를 돌린 결과를 보면 하나의 동일한 column이 하나의 tuple에 묶여서 출력되는 것을 볼 수 있습니다.

근데 결과가 좀 특이합니다.

 

Worksheet columns = (<Cell 'df_test_2'.A1>, <Cell 'df_test_2'.A2>, <Cell 'df_test_2'.A3>, <Cell 'df_test_2'.A4>, <Cell 'df_test_2'.A5>, <Cell 'df_test_2'.A6>, <Cell 'df_test_2'.A7>, <Cell 'df_test_2'.A8>)

 

이런 결과를 보여주죠.

openpyxl은 단순히 각 위치에 있는 값을 가진 것 뿐 아니라 해당 값이 엑셀의 어느 셀에 있는지에 대한 정보까지 가지고 있습니다.

그래서 위 내용을 보면

A column에 있는 A1, A2, A3, ..., A8 셀의 객체가 하나의 tuple에 묶여서 return되는거죠.

 

 

 

 

 

 

 

...

active_worksheet = workbook.active
print('Show Worksheet columns object =', active_worksheet.columns)
for col in active_worksheet.columns:  # worksheet에 존재하는 모든 값의 정보를 동일한 column별로 묶어서 return합니다. value는 하나의 row별로 저장되어있습니다.
    print('Worksheet columns =', col)
    for cell in col:
        print('Cell value =', cell.value)





-- Result
Show Worksheet columns object = <generator object Worksheet._cells_by_col at 0x7fbe96289e70>
Worksheet columns = (<Cell 'df_test_2'.A1>, <Cell 'df_test_2'.A2>, <Cell 'df_test_2'.A3>, <Cell 'df_test_2'.A4>, <Cell 'df_test_2'.A5>, <Cell 'df_test_2'.A6>, <Cell 'df_test_2'.A7>, <Cell 'df_test_2'.A8>)
Cell value = item_id
Cell value = 1
Cell value = 2
Cell value = None
Cell value = 4
Cell value = None
Cell value = None
Cell value = 3
Worksheet columns = (<Cell 'df_test_2'.B1>, <Cell 'df_test_2'.B2>, <Cell 'df_test_2'.B3>, <Cell 'df_test_2'.B4>, <Cell 'df_test_2'.B5>, <Cell 'df_test_2'.B6>, <Cell 'df_test_2'.B7>, <Cell 'df_test_2'.B8>)
Cell value = price
Cell value = 1000
Cell value = 800
Cell value = 3000
Cell value = None
Cell value =  
Cell value = None
Cell value = None
Worksheet columns = (<Cell 'df_test_2'.C1>, <Cell 'df_test_2'.C2>, <Cell 'df_test_2'.C3>, <Cell 'df_test_2'.C4>, <Cell 'df_test_2'.C5>, <Cell 'df_test_2'.C6>, <Cell 'df_test_2'.C7>, <Cell 'df_test_2'.C8>)
Cell value = empty_col
Cell value = None
Cell value = None
Cell value = None
Cell value = None
Cell value = None
Cell value = None
Cell value = None
Worksheet columns = (<Cell 'df_test_2'.D1>, <Cell 'df_test_2'.D2>, <Cell 'df_test_2'.D3>, <Cell 'df_test_2'.D4>, <Cell 'df_test_2'.D5>, <Cell 'df_test_2'.D6>, <Cell 'df_test_2'.D7>, <Cell 'df_test_2'.D8>)
Cell value = None
Cell value = None
Cell value =  
Cell value = None
Cell value = None
Cell value = None
Cell value = None
Cell value = None

 

위처럼 cell 객체는 cell.value와 같이 value attribute를 이용하여 각 cell에 어떤 값이 있는지를 얻어올 수 있습니다.

 

 

 

 

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)

list_worksheet_objects = workbook.worksheets  # workbook 객체에 존재하는 모든 worksheet 객체를 불러옴.
print('Show Worksheet objects =', list_worksheet_objects)

list_worksheet_names = workbook.sheetnames  # workbook 객체에 존재하는 모든 worksheet의 이름을 불러옴.
print('Show Worksheet names =', list_worksheet_names)

active_worksheet = workbook.active  # 현재 active한 worksheet의 worksheet 객체를 불러옴.
print('Show Active Worksheet objects =', active_worksheet)

workbook.active = list_worksheet_names.index('df_test_2')  # active attribute에 active상태로 만들 worksheet의 index 입력.
active_worksheet = workbook.active
print('Show Active Worksheet objects =', active_worksheet)


active_worksheet = workbook.active
print('Show Worksheet values object =', active_worksheet.values)
for row in active_worksheet.values:  # worksheet에 존재하는 모든 value를 동일한 row 별로 묶어서 return합니다. value는 하나의 row별로 저장되어있습니다.
    print('Worksheet row =', row)


active_worksheet = workbook.active
print('Show Worksheet columns object =', active_worksheet.columns)
for col in active_worksheet.columns:  # worksheet에 존재하는 모든 값의 정보를 동일한 column별로 묶어서 return합니다. value는 하나의 row별로 저장되어있습니다.
    print('Worksheet columns =', col)


print('Show max row count =', active_worksheet.max_row)  # total row 개수
print('Show max column count =', active_worksheet.max_column)  # total column 개수





-- Result
Show workbook object = <openpyxl.workbook.workbook.Workbook object at 0x7fa1807c8340>
Show Worksheet objects = [<Worksheet "df_test_1">, <Worksheet "df_test_2">]
Show Worksheet names = ['df_test_1', 'df_test_2']
Show Active Worksheet objects = <Worksheet "df_test_1">
Show Active Worksheet objects = <Worksheet "df_test_2">
Show Worksheet values object = <generator object Worksheet.values at 0x7fa18078de70>
Worksheet row = ('item_id', 'price', 'empty_col', None)
Worksheet row = (1, 1000, None, None)
Worksheet row = (2, 800, None, ' ')
Worksheet row = (None, 3000, None, None)
Worksheet row = (4, None, None, None)
Worksheet row = (None, ' ', None, None)
Worksheet row = (None, None, None, None)
Worksheet row = (3, None, None, None)
Show Worksheet columns object = <generator object Worksheet._cells_by_col at 0x7fa18078de70>
Worksheet columns = (<Cell 'df_test_2'.A1>, <Cell 'df_test_2'.A2>, <Cell 'df_test_2'.A3>, <Cell 'df_test_2'.A4>, <Cell 'df_test_2'.A5>, <Cell 'df_test_2'.A6>, <Cell 'df_test_2'.A7>, <Cell 'df_test_2'.A8>)
Worksheet columns = (<Cell 'df_test_2'.B1>, <Cell 'df_test_2'.B2>, <Cell 'df_test_2'.B3>, <Cell 'df_test_2'.B4>, <Cell 'df_test_2'.B5>, <Cell 'df_test_2'.B6>, <Cell 'df_test_2'.B7>, <Cell 'df_test_2'.B8>)
Worksheet columns = (<Cell 'df_test_2'.C1>, <Cell 'df_test_2'.C2>, <Cell 'df_test_2'.C3>, <Cell 'df_test_2'.C4>, <Cell 'df_test_2'.C5>, <Cell 'df_test_2'.C6>, <Cell 'df_test_2'.C7>, <Cell 'df_test_2'.C8>)
Worksheet columns = (<Cell 'df_test_2'.D1>, <Cell 'df_test_2'.D2>, <Cell 'df_test_2'.D3>, <Cell 'df_test_2'.D4>, <Cell 'df_test_2'.D5>, <Cell 'df_test_2'.D6>, <Cell 'df_test_2'.D7>, <Cell 'df_test_2'.D8>)
Show max row count = 8
Show max column count = 4

 

print('Show max row count =', active_worksheet.max_row)
print('Show max column count =', active_worksheet.max_column)

 

worksheet 객체의 max_row attribute는 worksheet에 존재하는 row의 총 개수를 return합니다.

worksheet 객체의 max_column attribute는 worksheet에 존재하는 column의 총 개수를 return합니다.

 

 

 

 

 

 

 

import pandas as pd
import io

df_test_1 = pd.DataFrame({
    'item_id': [1, 2, 3, 4, 5],
    'name': ['a', 'b', 'c', 'd', 'e']
})

df_test_2 = pd.DataFrame({
    'item_id': [1, 2, None, 4, None, None, 3],
    'price': [1000, 800, 3000, None, ' ', None, None],
    'empty_col': [None, None, None, None, None, None, None],
    None: [None, None, None, None, None, None, None],
    None: [None, ' ', None, None, None, None, None]
})


bio = io.BytesIO()  # ByesIO 객체 생성
xlsx_writer = pd.ExcelWriter(bio, engine='openpyxl')  # ExcelWirter를 BytesIO 객체를 대상으로 생성
df_test_1.to_excel(xlsx_writer, index=False, sheet_name='df_test_1')  # xlsx에 원하는 sheet 생성
df_test_2.to_excel(xlsx_writer, index=False, sheet_name='df_test_2')  # xlsx에 원하는 sheet 생성
xlsx_writer.save()  # BytesIO 메모리에 xlsx 정보 저장
bio.seek(0)
xlsx_file = bio.read()  # 방금 저장했던 BytesIO() 객체에 담긴 내용을 읽어오기


import openpyxl

workbook = openpyxl.load_workbook(io.BytesIO(xlsx_file), data_only=True)  # BytesIO 객체를 workbook 객체로 전환
print('Show workbook object =', workbook)

list_worksheet_objects = workbook.worksheets  # workbook 객체에 존재하는 모든 worksheet 객체를 불러옴.
print('Show Worksheet objects =', list_worksheet_objects)

list_worksheet_names = workbook.sheetnames  # workbook 객체에 존재하는 모든 worksheet의 이름을 불러옴.
print('Show Worksheet names =', list_worksheet_names)

active_worksheet = workbook.active  # 현재 active한 worksheet의 worksheet 객체를 불러옴.
print('Show Active Worksheet objects =', active_worksheet)

workbook.active = list_worksheet_names.index('df_test_2')  # active attribute에 active상태로 만들 worksheet의 index 입력.
active_worksheet = workbook.active
print('Show Active Worksheet objects =', active_worksheet)


active_worksheet = workbook.active
print('Show Worksheet values object =', active_worksheet.values)
for row in active_worksheet.values:  # worksheet에 존재하는 모든 value를 동일한 row 별로 묶어서 return합니다. value는 하나의 row별로 저장되어있습니다.
    print('Worksheet row =', row)


active_worksheet = workbook.active
print('Show Worksheet columns object =', active_worksheet.columns)
for col in active_worksheet.columns:  # worksheet에 존재하는 모든 값의 정보를 동일한 column별로 묶어서 return합니다. value는 하나의 row별로 저장되어있습니다.
    print('Worksheet columns =', col)


print('Show max row count =', active_worksheet.max_row)  # total row 개수
print('Show max column count =', active_worksheet.max_column)  # total column 개수


active_worksheet = workbook.active
worksheet_generator = active_worksheet.iter_rows(min_row=1, max_row=active_worksheet.max_row,
                                                 min_col=1, max_col=active_worksheet.max_column,
                                                 values_only=False)
print('Show Worksheet generator =', worksheet_generator)

for row in worksheet_generator:
    print('Worksheet iter =', row)





-- Result
Show workbook object = <openpyxl.workbook.workbook.Workbook object at 0x7f9fdb494730>
Show Worksheet objects = [<Worksheet "df_test_1">, <Worksheet "df_test_2">]
Show Worksheet names = ['df_test_1', 'df_test_2']
Show Active Worksheet objects = <Worksheet "df_test_1">
Show Active Worksheet objects = <Worksheet "df_test_2">
Show Worksheet values object = <generator object Worksheet.values at 0x7f9fdb471ee0>
Worksheet row = ('item_id', 'price', 'empty_col', None)
Worksheet row = (1, 1000, None, None)
Worksheet row = (2, 800, None, ' ')
Worksheet row = (None, 3000, None, None)
Worksheet row = (4, None, None, None)
Worksheet row = (None, ' ', None, None)
Worksheet row = (None, None, None, None)
Worksheet row = (3, None, None, None)
Show Worksheet columns object = <generator object Worksheet._cells_by_col at 0x7f9fdb471ee0>
Worksheet columns = (<Cell 'df_test_2'.A1>, <Cell 'df_test_2'.A2>, <Cell 'df_test_2'.A3>, <Cell 'df_test_2'.A4>, <Cell 'df_test_2'.A5>, <Cell 'df_test_2'.A6>, <Cell 'df_test_2'.A7>, <Cell 'df_test_2'.A8>)
Worksheet columns = (<Cell 'df_test_2'.B1>, <Cell 'df_test_2'.B2>, <Cell 'df_test_2'.B3>, <Cell 'df_test_2'.B4>, <Cell 'df_test_2'.B5>, <Cell 'df_test_2'.B6>, <Cell 'df_test_2'.B7>, <Cell 'df_test_2'.B8>)
Worksheet columns = (<Cell 'df_test_2'.C1>, <Cell 'df_test_2'.C2>, <Cell 'df_test_2'.C3>, <Cell 'df_test_2'.C4>, <Cell 'df_test_2'.C5>, <Cell 'df_test_2'.C6>, <Cell 'df_test_2'.C7>, <Cell 'df_test_2'.C8>)
Worksheet columns = (<Cell 'df_test_2'.D1>, <Cell 'df_test_2'.D2>, <Cell 'df_test_2'.D3>, <Cell 'df_test_2'.D4>, <Cell 'df_test_2'.D5>, <Cell 'df_test_2'.D6>, <Cell 'df_test_2'.D7>, <Cell 'df_test_2'.D8>)
Show max row count = 8
Show max column count = 4
Show Worksheet generator = <generator object Worksheet._cells_by_row at 0x7f9fdb471ee0>
Worksheet iter = (<Cell 'df_test_2'.A1>, <Cell 'df_test_2'.B1>, <Cell 'df_test_2'.C1>, <Cell 'df_test_2'.D1>)
Worksheet iter = (<Cell 'df_test_2'.A2>, <Cell 'df_test_2'.B2>, <Cell 'df_test_2'.C2>, <Cell 'df_test_2'.D2>)
Worksheet iter = (<Cell 'df_test_2'.A3>, <Cell 'df_test_2'.B3>, <Cell 'df_test_2'.C3>, <Cell 'df_test_2'.D3>)
Worksheet iter = (<Cell 'df_test_2'.A4>, <Cell 'df_test_2'.B4>, <Cell 'df_test_2'.C4>, <Cell 'df_test_2'.D4>)
Worksheet iter = (<Cell 'df_test_2'.A5>, <Cell 'df_test_2'.B5>, <Cell 'df_test_2'.C5>, <Cell 'df_test_2'.D5>)
Worksheet iter = (<Cell 'df_test_2'.A6>, <Cell 'df_test_2'.B6>, <Cell 'df_test_2'.C6>, <Cell 'df_test_2'.D6>)
Worksheet iter = (<Cell 'df_test_2'.A7>, <Cell 'df_test_2'.B7>, <Cell 'df_test_2'.C7>, <Cell 'df_test_2'.D7>)
Worksheet iter = (<Cell 'df_test_2'.A8>, <Cell 'df_test_2'.B8>, <Cell 'df_test_2'.C8>, <Cell 'df_test_2'.D8>)

 

이번에는 Workbook의 iterration을 해봅시다.

 

active_worksheet = workbook.active
worksheet_generator = active_worksheet.iter_rows(min_row=1, max_row=active_worksheet.max_row,
                                                 min_col=1, max_col=active_worksheet.max_column,
                                                 values_only=False)
print('Show Worksheet generator =', worksheet_generator)

for row in worksheet_generator:
    print('Worksheet iter =', row)




-- Result
Show Worksheet generator = <generator object Worksheet._cells_by_row at 0x7f9fdb471ee0>
Worksheet iter = (<Cell 'df_test_2'.A1>, <Cell 'df_test_2'.B1>, <Cell 'df_test_2'.C1>, <Cell 'df_test_2'.D1>)
Worksheet iter = (<Cell 'df_test_2'.A2>, <Cell 'df_test_2'.B2>, <Cell 'df_test_2'.C2>, <Cell 'df_test_2'.D2>)
Worksheet iter = (<Cell 'df_test_2'.A3>, <Cell 'df_test_2'.B3>, <Cell 'df_test_2'.C3>, <Cell 'df_test_2'.D3>)
Worksheet iter = (<Cell 'df_test_2'.A4>, <Cell 'df_test_2'.B4>, <Cell 'df_test_2'.C4>, <Cell 'df_test_2'.D4>)
Worksheet iter = (<Cell 'df_test_2'.A5>, <Cell 'df_test_2'.B5>, <Cell 'df_test_2'.C5>, <Cell 'df_test_2'.D5>)
Worksheet iter = (<Cell 'df_test_2'.A6>, <Cell 'df_test_2'.B6>, <Cell 'df_test_2'.C6>, <Cell 'df_test_2'.D6>)
Worksheet iter = (<Cell 'df_test_2'.A7>, <Cell 'df_test_2'.B7>, <Cell 'df_test_2'.C7>, <Cell 'df_test_2'.D7>)
Worksheet iter = (<Cell 'df_test_2'.A8>, <Cell 'df_test_2'.B8>, <Cell 'df_test_2'.C8>, <Cell 'df_test_2'.D8>)

 

worksheet 객체는 iter_rows() 라는 method가 존재합니다.

이 method는 worksheet에 있는 데이터 중 특정 범위에 있는 cell들의 객체만을 동일 row에 있는 cell끼리 묶어서 return해줍니다.

 

min_row는 최소 몇 번째 행부터 읽을거냐는 의미입니다. min_row=1은 가장 첫 번째 행부터 읽겠다는 의미입니다.

max_row는 최대 몇 번째 행까지 읽어올거냐 라는 의미인데 active_worksheet.max_row를 사용했으므로 값이 존재하는 최대 행까지 읽어오겠다는 의미입니다.

 

min_col은 최소 몇 번째 컬럼부터 읽을거냐는 의미입니다. min_col=1은 가장 첫 번째 컬럼부터 읽겠다는 의미입니다.

max_col 최대 몇 번째 컬럼까지 읽어올거냐 라는 의미인데 active_worksheet.max_column을 사용했으므로 값이 존재하는 최대 컬럼까지 읽어오겠다는 의미입니다.

 

따라서 이 값을 조절하면 엑셀의 어디서부터 어디까지만 읽어올지를 판단할 수 있습니다.

 

일단 iter_rows() method의 결과는 generator입니다. 따라서 for loop로 조회를 해야하죠.

 

for loop로 조회한 결과를 보면

A1, B1, C1, D1 cell 객체가 하나의 tuple에 묶여있습니다.

 

 

 

 

 

 

...

active_worksheet = workbook.active
worksheet_generator = active_worksheet.iter_rows(min_row=3, max_row=active_worksheet.max_row,
                                                 min_col=2, max_col=active_worksheet.max_column,
                                                 values_only=False)
print('Show Worksheet generator =', worksheet_generator)

for row in worksheet_generator:
    print('Worksheet iter =', row)





-- Result
Show Worksheet generator = <generator object Worksheet._cells_by_row at 0x7fa832685ee0>
Worksheet iter = (<Cell 'df_test_2'.B3>, <Cell 'df_test_2'.C3>, <Cell 'df_test_2'.D3>)
Worksheet iter = (<Cell 'df_test_2'.B4>, <Cell 'df_test_2'.C4>, <Cell 'df_test_2'.D4>)
Worksheet iter = (<Cell 'df_test_2'.B5>, <Cell 'df_test_2'.C5>, <Cell 'df_test_2'.D5>)
Worksheet iter = (<Cell 'df_test_2'.B6>, <Cell 'df_test_2'.C6>, <Cell 'df_test_2'.D6>)
Worksheet iter = (<Cell 'df_test_2'.B7>, <Cell 'df_test_2'.C7>, <Cell 'df_test_2'.D7>)
Worksheet iter = (<Cell 'df_test_2'.B8>, <Cell 'df_test_2'.C8>, <Cell 'df_test_2'.D8>)

 

min_row, min_col 값을 변경해 보았니다.

min_row=3으로 적었으므로 첫 번째 행과 두 번째 행이 skip되고 읽히지 않습니다.

따라서 N1, N2 cell이 없고 3번 행에 존재하는 cell부터 출력되는 것을 볼 수 있습니다.

 

또한 min_col=2로 적었으므로 첫 번째 column인 A컬럼이 skip되고 읽히지 않습니다.

따라서 결과에 A column에 있는 cell이 없습니다.

 

 

 

그리고 예시를 보면 values_only=False로 설정해놨는데

이것은 iter_rows() 객체를 생성할 때 그 객체에 담긴 정보가 cell에 있는 값 뿐 아니라 cell 객체 그 자체를 담고있게 하라는 의미입니다.

 

values_only=True로 설정하면 iter_rows() 객체에 cell에 있는 값만 담기게 됩니다.

 

 

 

active_worksheet = workbook.active
worksheet_generator = active_worksheet.iter_rows(min_row=1, max_row=active_worksheet.max_row,
                                                 min_col=1, max_col=active_worksheet.max_column,
                                                 values_only=True)
print('Show Worksheet generator =', worksheet_generator)

for row in worksheet_generator:
    print('Worksheet iter =', row)





-- Result
Show Worksheet generator = <generator object Worksheet._cells_by_row at 0x7f862dd85ee0>
Worksheet iter = ('item_id', 'price', 'empty_col', None)
Worksheet iter = (1, 1000, None, None)
Worksheet iter = (2, 800, None, ' ')
Worksheet iter = (None, 3000, None, None)
Worksheet iter = (4, None, None, None)
Worksheet iter = (None, ' ', None, None)
Worksheet iter = (None, None, None, None)
Worksheet iter = (3, None, None, None)

 

위 예시처럼 values_only=True로 바꾸면 iter_rows() 객체의 for loop 결과가 오로지 값만을 담고있는 것을 알 수 있습니다.

 

단 values_only 옵션은 좀 상위 openpyxl version에서 가능합니다.

따라서 openpyxl version이 무엇이냐에 따라 사용하지 못할 수도 있는 옵션이라는 점을 참고해주시면 좋습니다.

 

 

 

active_worksheet = workbook.active
worksheet_generator = active_worksheet.iter_rows(min_row=1, max_row=active_worksheet.max_row,
                                                 min_col=1, max_col=active_worksheet.max_column)
print('Show Worksheet generator =', worksheet_generator)

for row in worksheet_generator:
    list_values = []
    for cell in row:
        list_values.append(cell.value)
    print(list_values)




-- Result
Show Worksheet generator = <generator object Worksheet._cells_by_row at 0x7f9e41391ee0>
['item_id', 'price', 'empty_col', None]
[1, 1000, None, None]
[2, 800, None, ' ']
[None, 3000, None, None]
[4, None, None, None]
[None, ' ', None, None]
[None, None, None, None]
[3, None, None, None]

 

values_only옵션을 사용할 수 없는 경우 이런식으로 이중 for loop를 사용해서 cell의 값만을 불러올 수 있습니다.

 

 

 

 

 

- 참고

openpyxl에 관한 괜찮은 예시가 담긴 링크입니다.

https://mistonline.in/wp/python-how-to-read-excel-file-from-aws-s3/

 

How to read spreadsheet in S3 using python

Last updated on June 2nd, 2022 at 08:03 am In this tutorial we will focus on how to read a spreadsheet (excel) in an AWS S3 bucket using Python. Before you jump on to the script please make sure that the below pre-requisites are met IAM user created with s

mistonline.in

 

 

 

 

 

 

728x90
반응형
Comments