9/10 웹 크롤링

레롤롤레로·2026년 9월 10일

랭체인AI

목록 보기
1/17

오늘은 국비교육에서 웹 크롤링 수업을 듣고, 배운 내용을 바탕으로 알라딘 베스트셀러 정보를 수집하는 과제를 했다. 수업에서는 Selenium으로 브라우저를 조작하고 BeautifulSoup으로 데이터를 추출하는 방법을 다뤘다.

수업 예제를 따라갈 때는 비슷해 보였던 두 도구의 역할이 과제를 하면서 헷갈렸다. 특히 페이지를 넘긴 뒤 어떤 HTML을 가져와야 하는지 다시 정리할 필요가 있었다.

오늘 한 작업

수업에서는 스타벅스 매장 찾기, 무신사 스냅, 네이버 영화 관람평을 예제로 버튼 클릭과 스크롤, HTML 분석을 실습했다.

이후 알라딘 과제에서는 책 제목, 원가, 할인가, 별점을 추출하고 CSV로 저장하는 코드를 작성했다. 여러 페이지를 순회하는 부분도 작성했지만, 페이지별 데이터를 제대로 가져오는지는 추가 확인이 필요한 상태다.

수업에서 배운 내용

Selenium으로 화면을 조작하고 BeautifulSoup으로 추출하기

스타벅스 실습에서는 Selenium으로 지역 버튼을 클릭하고 기다린 뒤 다음 코드를 사용했다.

starbucks_soup = bs(chrome.page_source, "html.parser")

이때 chrome.page_source로 가져온 HTML에서 매장 목록을 찾았다.

starbucks_list = starbucks_soup.select(".quickResultLstCon")

starbucks_address_list = [
    (
        i.select_one("strong").text.strip(),
        i.select_one(".result_details").text.replace("1522-3232", "")
    )
    for i in starbucks_list
]

목록 전체에서 반복되는 매장 요소를 찾고, 각 요소 안에서 이름과 주소를 꺼내는 방식이다. 수업 코드에서는 주소에 함께 들어 있는 전화번호 문자열도 제거했다.

무신사 예제에서는 계절 필터를 선택한 뒤 페이지 끝까지 스크롤하는 코드를 다뤘다. 이후 HTML에서 이미지 주소를 모으고 requests로 파일을 내려받는 흐름이었다.

네이버 관람평은 직접 풀어보기

네이버 관람평은 먼저 직접 풀어봤다. 후기 요소를 하나씩 순회하면서 내용, 작성자, 별점을 가져오고, 5점은 제외한 뒤 5점보다 높으면 추천, 낮으면 비추천으로 분류했다.

if 별점 == 5:
    continue

추천여부 = "추천" if 별점 > 5 else "비추천"
관람_후기.append((추천여부, 별점, 내용, 작성자))

후기 문장의 의미를 분석한 것은 아니고, 별점을 기준으로 조건문을 적용했다.

노트북 아래쪽의 naver_soup부터는 강사님 풀이를 복사해둔 부분이다. 강사님 풀이는 별점과 후기 목록을 각각 만든 뒤 zip()으로 묶는 방식이었다. 이 부분은 내 풀이와 비교하면서 다시 복습할 예정이다.

두 도구를 연결하는 부분

수업에서 사용한 흐름은 Selenium으로 클릭이나 스크롤을 한 뒤, chrome.page_source를 BeautifulSoup에 전달하는 방식이었다.

Selenium에도 텍스트를 가져오는 기능은 있다. 이번 수업에서는 브라우저 조작과 HTML 분석을 나눠서 다뤘다.

BeautifulSoup 객체는 생성할 때 전달한 HTML을 분석한다. 이후 브라우저에서 다른 페이지로 이동해도 기존 soup 내용이 자동으로 바뀌지는 않는다. 바뀐 화면의 데이터를 추출하려면 HTML을 다시 가져와야 한다.

알라딘 과제에 적용하기

책 정보에서 필요한 값 추출하기

과제는 알라딘 베스트셀러에서 책 제목, 가격, 별점을 수집하는 것이었다. 수업에서 목록 안의 반복 요소를 찾았던 것처럼, #Myform 안에서 .ss_book_box를 찾아 책 한 권씩 처리하도록 작성했다.

book_area = aladin_soup.select_one("#Myform")
books = book_area.select(".ss_book_box")

for book in books:
    title = book.select_one(".bo3").get_text()

    li = book.select("li")
    price_text = li[3].get_text().split(" → ")
    price = price_text[0].strip().replace("원", "")
    discount_price = price_text[1].split("원")[0]

    star_tag = book.select_one("span.star_score")
    if star_tag:
        star = star_tag.get_text(strip=True)
    else:
        star = ""

    book_infos.append((title, price, discount_price, star))

가격은 원가와 할인가 사이의 →를 기준으로 나누고 원을 제거했다. 아직 숫자로 변환하지는 않았기 때문에 쉼표가 포함된 문자열로 남는다.

별점은 태그가 있을 때만 텍스트를 꺼내고, 없으면 빈 문자열을 넣었다. 반면 가격은 네 번째 li에 있다는 전제로 작성해서 다른 형태의 책 정보에서도 동작하는지 확인이 필요하다.

마지막에는 book_infos를 CSV로 쓰는 코드를 추가했다.

with open(file_path, "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.writer(f)
    writer.writerow(["책 제목", "원가", "할인가", "별점"])
    writer.writerows(book_infos)

문제 또는 시행착오

과제에서 페이지 이동과 HTML 요청을 섞어 쓴 부분

알라딘 코드에는 페이지 버튼을 클릭하는 반복문이 있는데, 그 안에서 HTML을 가져오는 부분은 다음과 같았다.

response = requests.get(url)
aladin_soup = bs(response.text, "html.parser")

여기서 url은 처음 접속한 베스트셀러 주소다. Selenium에서 페이지 버튼을 눌러도 이 변수는 바뀌지 않는다. requests의 요청 역시 브라우저에서 클릭한 상태를 그대로 이어받는 것이 아니다.

결국 현재 코드는 페이지를 넘겨도 같은 주소로 다시 요청하고 있어, 페이지별 데이터를 수집하려던 의도와 맞지 않는다. 브라우저에서 이동한 화면을 분석하려면 해당 화면이 로드된 뒤 다음처럼 가져오도록 수정해야 한다.

# 수정할 부분
aladin_soup = bs(chrome.page_source, "html.parser")

이 부분은 정리하면서 확인한 수정 사항이다. 아직 수정 후 전체 페이지를 다시 수집해 중복 여부까지 검증한 것은 아니다. 마지막 출력문도 print(book_info)로 되어 있어 실제 목록 이름인 book_infos로 맞춰야 한다.

알게 된 점

수업에서는 브라우저 조작부터 데이터 추출까지의 흐름을 따라가 봤고, 알라딘 과제에서는 그 흐름을 다른 페이지에 적용해봤다. 그 과정에서 선택자뿐 아니라 어느 시점의 HTML을 읽는지도 확인해야 한다는 걸 알게 됐다. 브라우저에서 화면이 바뀌어도 수집 코드가 이전 주소를 요청하거나 예전에 만든 soup을 사용하면 원하는 결과를 얻을 수 없다.

다음 작업

우선 알라딘 코드에서 현재 브라우저의 HTML을 가져오도록 수정하고, 페이지를 넘기기 전후의 책 제목을 비교해볼 예정이다. CSV 저장 코드를 작성하는 것과 의도한 데이터가 저장됐는지 확인하는 것은 따로 봐야겠다.

네이버 관람평은 직접 작성한 풀이와 복사해둔 강사님 풀이를 비교해볼 생각이다. 후기 하나에서 필요한 값을 함께 꺼내는 방식과, 별점·후기 목록을 따로 만들어 zip()으로 묶는 방식의 차이를 확인하면서 복습하려고 한다.

0개의 댓글