본문 바로가기
인공지능/LLM 서비스

예스 24, 교보문고 크롤링(mongoDB,Excel 저장)

by hyunji00pj 2025. 1. 12.

지금까지 배운 크롤링을 사용하여 Yes24교보문고 웹사이트에서 책 데이터를 크롤링한 뒤, MongoDB에 저장하고 Excel 파일로 저장하는 과정을 수행하는 코드를 작성해 보았다

 

필요한 라이브러리

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
from pymongo import MongoClient

 

  • Selenium: 동적으로 생성되는 웹 콘텐츠를 탐색하고 조작.
  • BeautifulSoup: HTML 파싱 및 데이터 추출.
  • MongoDB: 크롤링 데이터를 저장.
  • openpyxl: Excel 파일 생성 및 이미지 삽입.
  • requests: 이미지 다운로드.
  • time: 크롤링 속도 제어를 위해 딜레이 추가.

함수 save_to_excel_with_images 설명

def save_to_excel_with_images(yes24_data, kyobo_data, keyword):
    from openpyxl import Workbook
    from openpyxl.drawing.image import Image as ExcelImage
    import os
    import requests

    # 엑셀 파일 생성
    workbook = Workbook()

    # Yes24 시트
    yes24_sheet = workbook.active
    yes24_sheet.title = "Yes24"
    headers = ["검색어", "책제목", "저자", "가격", "출판사", "출판일", "이미지"]
    yes24_sheet.append(headers)

    # 이미지 저장 경로 생성
    yes24_image_dir = f"images_yes24_{keyword}"
    if not os.path.exists(yes24_image_dir):
        os.makedirs(yes24_image_dir)

    # Yes24 데이터 추가
    for idx, item in enumerate(yes24_data, start=2):  # 헤더가 1행이므로 2부터 시작
        # 텍스트 데이터 추가
        row = [
            keyword,
            item.get("title"),
            item.get("writer"),
            item.get("price"),
            item.get("company"),
            item.get("day"),
        ]
        yes24_sheet.append(row)

        # 이미지 다운로드 및 삽입
        image_url = item.get("image_link")
        if image_url:
            try:
                # 이미지 다운로드
                image_path = os.path.join(yes24_image_dir, f"image_{idx}.jpg")
                response = requests.get(image_url, stream=True)
                if response.status_code == 200:
                    with open(image_path, "wb") as f:
                        f.write(response.content)

                    # 이미지 삽입
                    img = ExcelImage(image_path)
                    img.width, img.height = 50, 50  # 이미지 크기 조정
                    yes24_sheet.add_image(img, f"G{idx}")  # G열에 이미지 삽입
            except Exception as e:
                print(f"YES24 이미지 다운로드 실패: {image_url}, 오류: {e}")

    # Kyobo 시트
    kyobo_sheet = workbook.create_sheet(title="Kyobo")
    headers = ["검색어", "책제목", "저자", "가격", "출판사", "출판일", "이미지"]
    kyobo_sheet.append(headers)

    # 이미지 저장 경로 생성
    kyobo_image_dir = f"images_kyobo_{keyword}"
    if not os.path.exists(kyobo_image_dir):
        os.makedirs(kyobo_image_dir)

    # Kyobo 데이터 추가
    for idx, item in enumerate(kyobo_data, start=2):  # 헤더가 1행이므로 2부터 시작
        # 텍스트 데이터 추가
        row = [
            keyword,
            item.get("title"),
            item.get("writer"),
            item.get("price"),
            item.get("company"),
            item.get("day"),
        ]
        kyobo_sheet.append(row)

        # 이미지 다운로드 및 삽입
        image_url = item.get("image_link")
        if image_url:
            try:
                # 이미지 다운로드
                image_path = os.path.join(kyobo_image_dir, f"image_{idx}.jpg")
                response = requests.get(image_url, stream=True)
                if response.status_code == 200:
                    with open(image_path, "wb") as f:
                        f.write(response.content)

                    # 이미지 삽입
                    img = ExcelImage(image_path)
                    img.width, img.height = 50, 50  # 이미지 크기 조정
                    kyobo_sheet.add_image(img, f"G{idx}")  # G열에 이미지 삽입
            except Exception as e:
                print(f"Kyobo 이미지 다운로드 실패: {image_url}, 오류: {e}")

    # 파일 저장
    file_name = f"books_{keyword}.xlsx"
    workbook.save(file_name)
    print(f"엑셀 파일로 저장 완료: {file_name}")

기능: 크롤링한 데이터를 Excel 파일로 저장하며, 이미지도 포함.

 

Excel 파일 생성:

workbook = Workbook()
yes24_sheet = workbook.active
yes24_sheet.title = "Yes24"
headers = ["검색어", "책제목", "저자", "가격", "출판사", "출판일", "이미지"]
yes24_sheet.append(headers)

 

 

  • Workbook을 생성하고 Yes24 데이터를 저장할 첫 번째 시트를 만듦.
  • Excel 파일의 첫 번째 행에 헤더(열 이름)를 추가.

이미지 저장 경로 생성:

yes24_image_dir = f"images_yes24_{keyword}"
if not os.path.exists(yes24_image_dir):
    os.makedirs(yes24_image_dir)

 

  • Yes24 이미지를 저장할 폴더를 생성.

Yes24 데이터 저장 및 이미지 삽입:

for idx, item in enumerate(yes24_data, start=2):
    row = [...]
    yes24_sheet.append(row)

    image_url = item.get("image_link")
    if image_url:
        response = requests.get(image_url, stream=True)
        if response.status_code == 200:
            with open(image_path, "wb") as f:
                f.write(response.content)
            img = ExcelImage(image_path)
            img.width, img.height = 50, 50
            yes24_sheet.add_image(img, f"G{idx}")
  • 데이터를 각 행에 추가하며, 이미지도 다운로드하여 Excel 파일의 G열에 삽입.

교보문고 데이터도 동일하게 처리:

kyobo_sheet = workbook.create_sheet(title="Kyobo")
...

Excel 파일 저장:

file_name = f"books_{keyword}.xlsx"
workbook.save(file_name)
print(f"엑셀 파일로 저장 완료: {file_name}")

 

함수 crawling 설명

def crawling(keyword, pages1, pages2):
    # MongoDB 연결 설정
    url = "mongodb+srv://tlaguswl3461:2cIsbVnReMrUAylP@crawtest.sqi3g.mongodb.net/crawtest?retryWrites=true&w=majority&appName=crawtest"
    mongodb = MongoClient(url)
    database = mongodb['crawling']
    collection = database['booklist']

    driver = webdriver.Chrome()

    # YES24 크롤링
    def crawl_yes24():
        BASE_URL = 'https://www.yes24.com/Main/default.aspx'
        driver.get(BASE_URL)

        search_box = WebDriverWait(driver, 5).until(
            EC.visibility_of_element_located((By.XPATH, '//*[@id="query"]'))
        )
        search_box.send_keys(keyword)

        search_btn = WebDriverWait(driver, 5).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="yesSForm"]/fieldset/span[2]/button'))
        )
        search_btn.click()
        time.sleep(2)

        all_data = []

        for page in range(1, pages1 + 1):
            print(f"YES24 - 현재 페이지: {page}페이지")
            try:
                if page > 1:
                    next_page_xpath = f'//*[@id="goodsListWrap"]/div[4]/div/a[{page - 1}]'
                    next_page_button = WebDriverWait(driver, 10).until(
                        EC.presence_of_element_located((By.XPATH, next_page_xpath))
                    )
                    next_page_button.click()
                    time.sleep(2)

                soup = BeautifulSoup(driver.page_source, 'html.parser')
                book_elements = soup.select('#yesSchList > li')
                for book in book_elements:
                    category = book.select_one('div.item_info > div.info_row.info_name > span.gd_res')
                    if category and category.get_text(strip=True) in ['[도서]', '[eBook]']:
                        title = book.select_one('div.item_info > div.info_row.info_name > a.gd_name')
                        writer = book.select_one('div.item_info > div.info_row.info_pubGrp > span.authPub.info_auth')
                        price = book.select_one('div.item_info > div.info_row.info_price > strong')
                        company = book.select_one('div.item_info > div.info_row.info_pubGrp > span.authPub.info_pub > a')
                        day = book.select_one('div.item_info > div.info_row.info_pubGrp > span.authPub.info_date')
                        image = book.select_one('div.item_img > div.img_canvas > span > span > a > em > img')

                        book_data = {
                            "title": title.get_text(strip=True) if title else None,
                            "writer": writer.get_text(strip=True) if writer else None,
                            "price": price.get_text(strip=True) if price else None,
                            "company": company.get_text(strip=True) if company else None,
                            "day": day.get_text(strip=True) if day else None,
                            "image_link": image['src'] if image else None,
                            "keyword": keyword,
                            "site": "yes24",
                        }
                        all_data.append(book_data)

            except Exception as e:
                print(f"YES24 - {page}페이지에서 오류 발생: {e}")
                break

        return all_data

    # 교보문고 크롤링
    def crawl_kyobo():
        BASE_URL = 'https://www.kyobobook.co.kr/'
        driver.get(BASE_URL)

        search_box = WebDriverWait(driver, 5).until(
            EC.visibility_of_element_located((By.XPATH, '//*[@id="searchKeyword"]'))
        )
        search_box.send_keys(keyword)

        search_btn = WebDriverWait(driver, 5).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="welcome_header_wrap"]/div[3]/div/div[2]/a'))
        )
        search_btn.click()
        time.sleep(2)

        all_data = []

        for page in range(1, pages2 + 1):
            print(f"교보문고 - 현재 페이지: {page}페이지")
            try:
                if page > 1:
                    next_page_xpath = f'//*[@id="pagi"]/div/a[{page}]'
                    next_page_button = WebDriverWait(driver, 10).until(
                        EC.element_to_be_clickable((By.XPATH, next_page_xpath))
                    )
                    next_page_button.click()
                    time.sleep(2)

                soup = BeautifulSoup(driver.page_source, 'html.parser')
                book_elements = soup.select('#shopData_list > ul > li')
                for book in book_elements:
                    title = book.select_one('div.prod_area.horizontal > div.prod_info_box > div.auto_overflow_wrap.prod_name_group > div.auto_overflow_contents > div > a > span[id^="cmdtName_"]')
                    writer = book.select_one('div.prod_area.horizontal > div.prod_info_box > div.prod_author_info > div.auto_overflow_wrap.prod_author_group > div.auto_overflow_contents > div.auto_overflow_inner')
                    price = book.select_one('div.prod_price > span.price > span.val')
                    company = book.select_one('div.prod_author_info > div.prod_publish > a')
                    day = book.select_one('div.prod_author_info > div.prod_publish > span.date')
                    image = book.select_one('div.prod_area.horizontal > div.prod_thumb_box > a > span > img')

                    book_data = {
                        "title": title.get_text(strip=True) if title else None,
                        "writer": writer.get_text(strip=True) if writer else None,
                        "price": price.get_text(strip=True) if price else None,
                        "company": company.get_text(strip=True) if company else None,
                        "day": day.get_text(strip=True) if day else None,
                        "image_link": image['src'] if image else None,
                        "keyword": keyword,
                        "site": "kyobo",
                    }
                    all_data.append(book_data)

            except Exception as e:
                print(f"교보문고 - {page}페이지에서 오류 발생: {e}")
                break

        return all_data

    # 두 사이트 크롤링 실행
    yes24_data = crawl_yes24()
    kyobo_data = crawl_kyobo()

    # MongoDB에 저장
    all_data = yes24_data + kyobo_data
    if all_data:
        collection.insert_many(all_data)
        print(f"몽고DB에 {len(all_data)}개의 데이터를 저장했습니다.")
    else:
        print("저장할 데이터가 없습니다.")

    save_to_excel_with_images(yes24_data, kyobo_data, keyword)
    driver.quit()


# 실행
if __name__ == "__main__":
    crawling("파이썬", 3, 3)

기능: Yes24와 교보문고에서 데이터를 크롤링하고 MongoDB에 저장한 뒤 Excel 파일로 저장.

 

MongoDB 연결

url = "mongodb+srv://...@crawtest"
mongodb = MongoClient(url)
database = mongodb['crawling']
collection = database['booklist']
  • MongoDB 클러스터에 연결하여 booklist 컬렉션에 데이터를 저장.

Yes24 크롤링 (crawl_yes24)

기능: Yes24에서 책 데이터를 크롤링.

 

driver.get(BASE_URL)
search_box = WebDriverWait(driver, 5).until(...)
search_box.send_keys(keyword)
search_btn.click()
  • Selenium으로 Yes24 메인 페이지에 접속한 후, 검색창에 keyword를 입력하고 검색 버튼 클릭.

페이지 탐색 및 데이터 추출:

for page in range(1, pages1 + 1):
    soup = BeautifulSoup(driver.page_source, 'html.parser')
    book_elements = soup.select('#yesSchList > li')
    for book in book_elements:
        ...
        all_data.append(book_data)

 

  • BeautifulSoup으로 HTML 파싱 후 책 데이터를 추출.
  • book_data에는 제목, 저자, 가격, 출판사, 출판일, 이미지 링크 등이 저장됩니다.

교보문고 크롤링 (crawl_kyobo)

기능: 교보문고에서 책 데이터를 크롤링.

 

검색어 입력 및 검색 실행:

driver.get(BASE_URL)
search_box = WebDriverWait(driver, 5).until(...)
search_box.send_keys(keyword)
search_btn.click()

 

페이지 탐색 및 데이터 추출:

for page in range(1, pages2 + 1):
    soup = BeautifulSoup(driver.page_source, 'html.parser')
    book_elements = soup.select('#shopData_list > ul > li')
    ...
    all_data.append(book_data)

MongoDB에 저장

all_data = yes24_data + kyobo_data
if all_data:
    collection.insert_many(all_data)
    print(f"몽고DB에 {len(all_data)}개의 데이터를 저장했습니다.")
  • 두 사이트에서 수집한 데이터를 합쳐 MongoDB에 저장.

Excel 파일 저장

save_to_excel_with_images(yes24_data, kyobo_data, keyword)

 

 

실행

if __name__ == "__main__":
    crawling("파이썬", 3, 3)

 

if __name__ == "__main__":의 사용 이유는 Python 스크립트를 실행할 때 모듈로 사용되는 경우와 직접 실행되는 경우를 구분하기 위해서입니다.

 

1. if __name__ == "__main__":의 동작 원리

  • Python에서 모든 스크립트는 실행될 때 내부적으로 특별한 변수인 __name__이 생성됩니다.
  • 이 변수는 현재 파일의 실행 방법에 따라 다른 값을 가집니다:
    • 직접 실행: 스크립트를 직접 실행할 때, __name__의 값은 "__main__"입니다.
    • 모듈로 임포트: 다른 스크립트에서 해당 파일을 모듈로 임포트하면, __name__의 값은 파일의 이름(모듈 이름)이 됩니다.

2. 사용하는 이유

if __name__ == "__main__":는 코드의 직접 실행과 모듈로 임포트된 경우를 구분하여 특정 코드의 실행을 제어합니다.

 

직접 실행되는 경우

if __name__ == "__main__":
    crawling("파이썬", 3, 3)

이 코드는 스크립트를 직접 실행할 때만 crawling("파이썬", 3, 3) 함수가 호출되도록 합니다.

 

모듈로 임포트되는 경우

  • 이 스크립트를 다른 파일에서 임포트할 때는 crawling 함수가 자동으로 실행되지 않습니다.
  • 예:
import your_script

위와 같은 방식으로 임포트하면, if __name__ == "__main__": 아래의 코드는 실행되지 않음.

 

3. 장점

  1. 코드 재사용 가능:
    • 파일의 함수나 클래스를 다른 스크립트에서 임포트하여 사용할 수 있음.
    • 예: from your_script import crawling로 가져와 사용할 수 있음.
  2. 테스트 및 실행 코드 분리:
    • 직접 실행 시 동작하는 코드와, 모듈로 사용할 때 동작하지 않아야 하는 코드를 구분할 수 있음.
  3. 코드의 명확성:
    • 스크립트의 실행 진입점을 명확히 정의하여 유지보수성과 가독성을 높임.

5. 예제

직접 실행:

파일 이름: my_crawler.py

if __name__ == "__main__":
    crawling("파이썬", 3, 3)
    
# 실행 python my_crawler.py
  • crawling("파이썬", 3, 3) 함수가 실행됨.

 

모듈로 사용:

파일 이름: main_script.py

from my_crawler import crawling
crawling("데이터 분석", 5, 5)

# 실행 python main_script.py
  • my_crawler.py는 임포트되지만 crawling("파이썬", 3, 3)은 실행되지 않음.
  • 대신 crawling("데이터 분석", 5, 5)가 실행됨.

'인공지능 > LLM 서비스' 카테고리의 다른 글

Gradio  (0) 2025.01.12
프롬프트 엔지니어링  (0) 2025.01.12
생성형 AI에 대해서 - GPT API  (0) 2025.01.12
셀레니움 - 요기요 리뷰 크롤링  (0) 2025.01.12
크롤링  (0) 2025.01.12