엑셀 파일 생성 중에 cannot be used in worksheets 에러 발생
626
작성한 질문수 25
안녕하세요.
강의와는 무관한 질문이지만 본 강의 수강 완료 후 혼자서 프로젝트를 하고 있습니다.
현재 구글 리뷰 크롤링 & 스크랩중인데요. 해당 에러가 발생하는 이유를 도무지 찾을 수 가 없어서 질문 드립니다...
여러 사이트를 크롤링 해보고 엑셀을 생성 해 보았지만 왜 이런 에러가 발생하는지 로그를 봐도 제대로 표시가 안되니깐 찾기가 힘드네요.
구글 리뷰 사이트만 20여개 스크랩 했었고 엑셀도 제대로 생성 되었으니 스크랩 코드 자체에는 문제가 없는거 같습니다. 다만 이 부분에서만 문제가 생깁니다.
### 에러 발생 로그
[2024-06-12 09:42:59,954] [ERROR utils.py:179] >>
Traceback (most recent call last):
File "scraper\scrap_crawlers.py", line 1365, in get_review_details
File "scraper\utils.py", line 202, in create_xlsx_file
File "scraper\utils.py", line 181, in create_xlsx_file
File "pandas\util\_decorators.py", line 333, in wrapper
File "pandas\core\generic.py", line 2417, in to_excel
File "pandas\io\formats\excel.py", line 952, in write
File "pandas\io\excel\_openpyxl.py", line 490, in writecells
File "openpyxl\cell\cell.py", line 218, in value
File "openpyxl\cell\cell.py", line 197, in bindvalue
File "openpyxl\cell\cell.py", line 165, in check_string
openpyxl.utils.exceptions.IllegalCharacterError: 동생한테추천받았는데이렇게편한어플이있다니너무좋아요.현금비율은좋지않지만 신경많이안써도되서괜찮네요~ cannot be used in worksheets.
During handling of the above exception, another exception occurred:
###
cannot be used in worksheets. 이놈이 말썽이네요...
아래와 같이 테스트 케이스 만들어서 적용했을 때는 제대로 작동했었습니다.
import asyncio
from scraper.utils import create_xlsx_file, save_to_xlsx
DEFAULT_NAME = "test"
async def main():
data = {
"message": "동생한테추천받았는데이렇게편한어플이있다니너무좋아요.현금비율은좋지않지만 신경많이안써도되서괜찮네요~"
}
xlsx_file = await create_xlsx_file(
data, file_name=DEFAULT_NAME, sheet_name=DEFAULT_NAME
)
await save_to_xlsx(xlsx_file, DEFAULT_NAME)
asyncio.run(main())
# utils.py
# 엑셀 가로 폭 조정하는 함수
async def calculate_dimension(worksheet: Worksheet) -> None:
try:
for column_cells in worksheet.iter_cols():
length = max(len(str(cell.value)) for cell in column_cells)
adjusted_width = (length + 2) * 1.2 # 조정된 폭 계산
column_letter = get_column_letter(column_cells[0].column)
worksheet.column_dimensions[column_letter].width = adjusted_width
except Exception as e:
message = f"엑셀 폭 조정 중에 예외 발생: '\n{e}"
logger = await get_logger()
logger.error(message)
print(message)
raise e
# 엑셀에 서식 스타일 지정하는 함수
async def cell_pattern_fill(
df: pd.DataFrame,
worksheet: Worksheet,
head_fill_color: str = "4472C4",
head_font_color: str = "FFFFFF",
body_fill_color: str = "D9E1F2",
body_font_color: str = "000000",
head_border_color: str = "2E5C99",
body_border_color: str = "B4C6E7",
fill_type: fills = "solid",
) -> None:
try:
# Define border styles
thin_border_head = Border(
left=Side(border_style="thin", color=head_border_color),
right=Side(border_style="thin", color=head_border_color),
top=Side(border_style="thin", color=head_border_color),
bottom=Side(border_style="thin", color=head_border_color),
)
thin_border_body = Border(
left=Side(border_style="thin", color=body_border_color),
right=Side(border_style="thin", color=body_border_color),
top=Side(border_style="thin", color=body_border_color),
bottom=Side(border_style="thin", color=body_border_color),
)
# Set header row style
for row in worksheet.iter_rows(
min_row=1, max_row=1, min_col=1, max_col=df.shape[1]
):
for cell in row:
cell.fill = PatternFill(
start_color=head_fill_color,
end_color=head_fill_color,
fill_type=fill_type,
)
cell.font = Font(color=head_font_color, bold=True)
cell.border = thin_border_head
# Set body row style
for i, row in enumerate(
worksheet.iter_rows(
min_row=2, max_row=worksheet.max_row, min_col=1, max_col=df.shape[1]
)
):
for cell in row:
if i % 2 == 0:
cell.fill = PatternFill(
start_color=body_fill_color,
end_color=body_fill_color,
fill_type=fill_type,
)
cell.font = Font(color=body_font_color)
cell.border = thin_border_body
except Exception as e:
message = f"엑셀 서식 지정 중에 예외 발생: '\n{e}"
logger = await get_logger()
logger.error(message)
print(message)
raise e
# 본 강의 drf 엑셀 생성 파트를 참고해서 만든 엑셀 생성 함수
async def create_xlsx_file(
data: Union[Dict, List],
file_name: str = DEFAULT_DIR_NAME,
sheet_name: str = DEFAULT_DIR_NAME,
) -> BytesIO:
df = pd.json_normalize(data)
io = BytesIO()
io.name = file_name
try:
writer = pd.ExcelWriter(io, engine="openpyxl") # noqa
df.to_excel(
writer,
index=False,
engine="openpyxl",
sheet_name=sheet_name,
)
workbook = writer.book
worksheet = workbook.active
tasks = [
calculate_dimension(worksheet),
cell_pattern_fill(df, worksheet),
]
await tqdm.gather(*tasks, desc=f" 엑셀 파일 생성중")
writer._save() # noqa
except Exception as e:
message = f"엑셀 생성 중에 예외 발생: '\n{e}"
logger = await get_logger()
logger.error(message)
print(message)
raise e
io.seek(0)
return io
# 엑셀 저장 함수
async def save_to_xlsx(
xlsx_file: BytesIO,
dirname: str = DEFAULT_DIR_NAME,
):
output_path = BASE_DIR / "스크랩_결과" / "엑셀" / dirname
output_path.mkdir(parents=True, exist_ok=True)
now = datetime.datetime.now()
timestamp = now.strftime("%Y-%m-%d_%H_%M")
filename = f"{xlsx_file.name}_{timestamp}"
extension = ".xlsx"
file_path = output_path / (filename + extension)
try:
async with aiofiles.open(file_path, "wb") as f:
await f.write(xlsx_file.getvalue())
except Exception as e:
message = f"엑셀 파일 저장 중에 예외 발생: '{filename}'\n{e}"
logger = await get_logger()
logger.error(message)
print(message)
raise e
전체적인 함수는 위와 같으며 엑셀 생성 중에 에러가 발생하였으니 create_xlsx_file 함수 부분에서 해결을 해보아야 할것 같습니다.
아니면 혹시 엑셀의 행을 생성 중에 에러가 발생하였을 때 해당 행은 스킵하고 이어서 진행하게 하는 방법이 있을까요?? "raise e"을 발생 시키지 않아도 엑셀 생성 작업 스킵이 되지않고 작업 자체에 문제가 생기네요
답변 1
0
안녕하세요.
async 처리 상의 이슈가 아닐까 추측이 됩니다. 일단 async를 제거하시고 동기적으로 먼저 구현하신 후에, async 적용을 검토해보시면 어떨까요?
0
ILLEGAL_CHAR_PATTERN = r"[\000-\031]" # 제어 문자 정규식
def clean_data(data):
if isinstance(data, str):
return re.sub(ILLEGAL_CHAR_PATTERN, "", data)
elif isinstance(data, dict):
return {key: clean_data(value) for key, value in data.items()}
elif isinstance(data, list):
return [clean_data(item) for item in data]
else:
return data
async def create_xlsx_file(
data: Union[Dict, List],
file_name: str = DEFAULT_DIR_NAME,
sheet_name: str = DEFAULT_DIR_NAME,
) -> BytesIO:
# 데이터에서 불법 문자를 제거
cleaned_data = clean_data(data)
df = pd.json_normalize(cleaned_data)
# 데이터프레임에서 불법 문자를 제거
df = df.replace(ILLEGAL_CHAR_PATTERN, "", regex=True)
...
제어 문자가 문제인가 싶어서 제거해봤는데 이 경우 제대로 작동합니다.
그런데 해당 텍스트에서는 그럴만한 요소가 없었는데 왜인지 찝찝하네요...
Django의 View나 URL의 네이밍 컨벤션
0
60
1
08-14 FormView 관련 질문
0
72
1
07-01 IPv4AddressIntegerField 질문
0
82
1
14-08 수업 확인 요청 드립니다.
0
91
2
nextjs git 관리?
0
75
1
14-07에서 SESSION_COOKIE_DOMAIN = None 처리 필요.
0
75
2
고민
0
219
3
django-component==0.139 실행 시 오류
0
167
2
django-csp 4.0 migration 관련
0
122
2
01 윈도우 개발환경 설치 문의
0
99
1
강의 자료 문의
0
129
2
선생님 학습 방법 질문이 있습니다.
0
154
2
bulk_update에서 updated_at 필드
0
124
1
정규표현식
0
107
2
선생님 질문 있습니다.
0
80
1
공유자님 이 강의 공부 방법에 대한 질문입니다.
0
181
2
mydjango.py 질문 있습니다.
0
147
3
Django-Components의 0.128 세팅
0
226
3
질문 아님.
0
127
1
mydjango.py 실습 질문있습니다.
0
87
2
pycharm 개발환경 설정 오류
0
182
2
강의 듣다가 유료pycharm에 비해 vscode지원기능이 아쉬워서 확장프로그램 만들었는데 여기 공유해도 될까요?
0
167
1
중단점에 대한 질문 있습니다.
0
133
2
todo / react 붙이는 깃주소를 받고 싶습니다.
0
179
6





