작성
·
229
·
수정됨
0
안녕하세요.
강의와는 무관한 질문이지만 본 강의 수강 완료 후 혼자서 프로젝트를 하고 있습니다.
현재 구글 리뷰 크롤링 & 스크랩중인데요. 해당 에러가 발생하는 이유를 도무지 찾을 수 가 없어서 질문 드립니다...
여러 사이트를 크롤링 해보고 엑셀을 생성 해 보았지만 왜 이런 에러가 발생하는지 로그를 봐도 제대로 표시가 안되니깐 찾기가 힘드네요.
구글 리뷰 사이트만 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
제어 문자가 문제인가 싶어서 제거해봤는데 이 경우 제대로 작동합니다.
그런데 해당 텍스트에서는 그럴만한 요소가 없었는데 왜인지 찝찝하네요...