웹 크롤링에서 파일 저장 문제 해결하기: 크롤러 개선 사례

문제 상황: 크롤링은 되지만 파일이 저장되지 않는 현상

많은 크롤링 프로젝트에서 흔히 발생하는 문제 중 하나는 웹페이지 탐색과 데이터 추출은 성공적으로 이루어지지만, 추출한 데이터(JSON)나 이미지 파일이 실제로 저장되지 않는 현상입니다.

특히 다음과 같은 증상이 나타날 수 있습니다:

  • 코드 실행 중 오류 메시지는 없지만 파일이 생성되지 않음
  • 디렉토리 경로 문제로 인한 파일 저장 실패
  • 권한 문제로 인한 파일 쓰기 실패
  • 상대 경로와 절대 경로의 혼용으로 인한 문제

문제 해결을 위한 핵심 접근 방법

1. 디버깅 코드 추가

파일 저장 문제를 해결하기 위한 첫 번째 단계는 상세한 디버깅 정보를 추가하는 것입니다. 다음과 같은 정보를 로깅하면 문제 진단에 도움이 됩니다:

# 디렉토리 경로 확인
print(f"DEBUG - 이미지 저장 디렉토리: {os.path.dirname(filepath)}")
print(f"DEBUG - 디렉토리 존재 여부: {os.path.exists(os.path.dirname(filepath))}")

# 파일 저장 시도 전후 상태 확인
print(f"DEBUG - 파일 쓰기 시작: {filepath}")
# 파일 쓰기 작업
print(f"DEBUG - 파일 쓰기 완료")

# 파일 생성 확인
if os.path.exists(filepath):
    print(f"파일 저장 성공: {filepath} (크기: {os.path.getsize(filepath)} 바이트)")
else:
    print(f"파일이 생성되지 않음: {filepath}")

2. 다중 저장 경로 전략

단일 경로에만 의존하지 않고, 여러 단계의 대체 경로를 준비하는 것이 좋습니다:

try:
    # 기본 경로에 저장 시도
    with open(filepath, 'wb') as file:
        file.write(response.content)
    print(f"파일 쓰기 완료: {filepath}")
except Exception as file_err:
    print(f"파일 쓰기 오류: {file_err}")
    # 대체 경로 시도
    alt_filepath = os.path.join('./images', os.path.basename(filepath))
    try:
        os.makedirs('./images', exist_ok=True)
        with open(alt_filepath, 'wb') as file:
            file.write(response.content)
        print(f"대체 경로에 파일 쓰기 완료: {alt_filepath}")
        filepath = alt_filepath  # 경로 업데이트
    except Exception as alt_err:
        print(f"대체 경로 파일 쓰기 오류: {alt_err}")

3. 디렉토리 생성 확인 강화

파일을 저장하기 전에 디렉토리가 확실히 존재하는지 확인하고, 권한 테스트를 수행합니다:

dir_path = os.path.dirname(filepath)
if not os.path.exists(dir_path):
    print(f"디렉토리 생성 시도: {dir_path}")
    os.makedirs(dir_path, exist_ok=True)
    
    # 폴더 권한 테스트
    test_file = os.path.join(dir_path, 'test_write.txt')
    try:
        with open(test_file, 'w') as f:
            f.write('test')
        os.remove(test_file)
        print(f"쓰기 권한 확인 완료")
    except Exception as e:
        print(f"쓰기 권한 문제 발생: {e}")

실제 적용 사례: 크롤러 개선

위의 방법론을 적용하여 크롤러의 파일 저장 문제를 해결한 코드 중 핵심 부분을 살펴보겠습니다.

이미지 다운로드 함수 개선

def download_image(url, filepath, max_retries=3):
    """이미지 다운로드 함수 - 재시도 로직 추가 및 디버깅 개선"""
    if not url:
        print("이미지 URL이 없습니다.")
        return False
        
    # URL 유효성 검사
    try:
        parsed_url = urlparse(url)
        if not parsed_url.scheme or not parsed_url.netloc:
            print(f"잘못된 URL 형식: {url}")
            return False
    except Exception as e:
        print(f"URL 파싱 오류: {url}, 에러: {e}")
        return False
        
    # 파일 저장 전 디렉토리 명시적 확인
    try:
        dir_path = os.path.dirname(filepath)
        if not os.path.exists(dir_path):
            print(f"디렉토리 생성 시도: {dir_path}")
            os.makedirs(dir_path, exist_ok=True)
    except Exception as dir_err:
        print(f"디렉토리 생성 오류: {dir_err}")
        
    # 재시도 로직
    for attempt in range(max_retries):
        # 다운로드 시도 코드
        # ...
        
        # 파일이 실제로 생성되었는지 확인
        if os.path.exists(filepath) and os.path.getsize(filepath) > 0:
            print(f"이미지 다운로드 성공: {filepath} (크기: {os.path.getsize(filepath)} 바이트)")
            return True

JSON 파일 저장 함수 개선

def save_json(data, filepath):
    """JSON 파일 저장 함수 - 예외 처리 및 디버깅 개선"""
    try:
        # 디렉토리 확인 및 생성
        dir_path = os.path.dirname(filepath)
        if not os.path.exists(dir_path):
            os.makedirs(dir_path, exist_ok=True)
            
        # 파일 쓰기
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
            
        # 파일 생성 확인
        if os.path.exists(filepath):
            print(f"JSON 파일 저장 성공: {filepath} (크기: {os.path.getsize(filepath)} 바이트)")
            return True
        else:
            print(f"JSON 파일이 생성되지 않음: {filepath}")
            return False
    except Exception as e:
        print(f"JSON 파일 저장 중 오류: {filepath}, 에러: {e}")
        
        # 대체 경로 시도
        alt_filepath = f"./backup_{os.path.basename(filepath)}"
        try:
            with open(alt_filepath, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            return True
        except:
            return False

추가 개선 사항: 크롤링 안정성 향상

파일 저장 외에도 크롤링의 전반적인 안정성을 높이기 위한 몇 가지 추가 개선 사항을 적용했습니다:

1. 다양한 선택자 시도

웹사이트 구조가 변경될 수 있으므로, 여러 가능한 선택자를 순차적으로 시도합니다:

product_selectors = [
    'div.c_listing > ul > li',  # 기본 리스트 뷰
    'div.c_prd_list > ul > li',  # 대체 리스트 뷰
    'div.search_product_wrap li',  # 다른 가능한 선택자
    # 더 넓은 선택자들...
]

for selector in product_selectors:
    products = page.query_selector_all(selector)
    if products:
        used_selector = selector
        print(f"제품 선택자 발견: {selector}, {len(products)}개")
        break

2. 예외 처리 강화

각 상품 처리 과정을 개별적인 try-except 블록으로 감싸 한 상품의 처리 실패가 전체 프로세스에 영향을 미치지 않도록 합니다:

for i, product in enumerate(products):
    try:
        # 상품 처리 코드
        # ...
    except Exception as e:
        print(f"상품 #{i + 1} 처리 중 에러: {e}")
        continue

3. 진행 상태 모니터링

정기적으로 중간 결과를 저장하고 진행 상황을 출력합니다:

print(f"{page_num} 페이지 수집 완료, 이 페이지에서 {len(page_results)}개 수집됨, 현재까지 총 {len(all_results)}개 수집됨.")

# 중간 저장
if page_results:
    if save_json(all_results, json_path):
        print(f"중간 저장 완료: {json_path}")
    else:
        print(f"중간 저장 실패. 대체 경로에 저장 시도...")

결론: 견고한 크롤러 설계 원칙

웹 크롤링 프로젝트에서 파일 저장 문제를 해결하고 안정성을 높이기 위한 핵심 원칙을 정리하면 다음과 같습니다:

  1. 상세한 디버깅 정보 – 각 단계마다 충분한 로그를 남깁니다
  2. 다중 저장 전략 – 여러 경로에 저장을 시도합니다
  3. 명시적 경로 확인 – 모든 파일 작업 전에 경로와 권한을 확인합니다
  4. 다양한 선택자 대응 – 웹사이트 구조 변경에 대비합니다
  5. 강력한 예외 처리 – 부분적 실패가 전체 프로세스를 중단시키지 않도록 합니다
  6. 정기적 중간 저장 – 진행 중인 작업 결과를 안전하게 보존합니다

이러한 원칙을 적용하면 더 견고하고 신뢰할 수 있는 웹 크롤러를 개발할 수 있습니다. 특히 대규모 데이터 수집 프로젝트에서는 이러한 안정성 확보가 프로젝트 성공의 핵심 요소가 됩니다.

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다