이전 포스트들에서 requests
라이브러리와 urllib
라이브러리를 이용해 웹에 접근하는 방법에 대해 정리했습니다. 오늘은 BeautifulSoup을 이용해 'yes24.com' 사이트에서 책 정보를 웹 스크래핑 하는 방법에 대해 정리하도록 하겠습니다.
quote
의 사용웹스크래핑을 할 때, 한글이 URL에 포함되어야 하는 경우(예: 한글 검색어에 대한 결과 출력화면)에는 한글 부분을 인코딩 해주어야 합니다. 파이썬의 urllib.parse
모듈에 있는 quote
함수를 이용해 처리할 수 있습니다.
quote
함수quote
함수는 비-ASCII 문자를 포함한 문자열을 받아 ASCII 문자셋만을 사용하는 URL 인코딩으로 변환합니다. 이렇게 변환된 문자열은 안전하게 URL에 포함될 수 있으며, 웹 서버는 이를 다시 원래의 문자열로 해석하여 적절한 응답을 반환할 수 있습니다.
'yes24.com'에서 '데이터 분석'으로 검색해 나오는 책 정보를 스크랩 하려고 합니다. 검색결과 화면에서 URL 주소를 확인해보면 다음과 같습니다.
URL 주소에 한글이 포함되어 있습니다. urllib.parse.quote
를 이용해 검색어 '데이터 분석'을 인코딩 해줍니다.
import requests
from bs4 import BeautifulSoup
from urllib.parse import quote
url = 'https://www.yes24.com/Product/Search?domain=ALL&query={search_word}'
res = requests.get(url.format(search_word=quote('데이터 분석')))
soup = BeautifulSoup(res.content, 'html.parser')
# 이후 BeautifulSoup을 사용하여 웹 페이지에서 필요한 데이터를 추출합니다.
quote
를 이용해 한글을 인코딩하고, 문자열 포매팅을 이용해 인코딩값을 URL에 추가해주었습니다.
find
와 find_all
메서드 사용법quote
를 사용해 한글이 포함된 웹 주소를 인코딩해 웹 서버로 요청을 보내고 응답받은 HTML을 BeautifulSoup
을 이용해 파싱했습니다. 파싱한 HTML로부터 find
와 find_all
메소드를 사용해 책 제목, 저자, 가격 등의 정보를 수집하도록 하겠습니다.
find
find
메서드는 HTML 문서에서 조건에 맞는 첫 번째 요소를 찾습니다. 예를 들어, 특정 클래스를 가진 태그를 찾고 싶다면 다음과 같이 작성할 수 있습니다:
title = soup.find('h1', class_='title')
print(title.text)
이 코드는 class
가 'title'인 첫 번째 h1
태그를 찾아 그 내용을 출력합니다. 만약 id
나 다른 특성을 기준으로 검색하고 싶다면, 해당 특성을 find
메서드에 전달하면 됩니다.
find_all
find_all
메서드는 조건에 맞는 모든 요소를 리스트 형태로 반환합니다. 예를 들어, 모든 a
태그를 찾고 싶다면 다음과 같이 사용합니다:
links = soup.find_all('a')
for link in links:
print(link.get('href'))
이 코드는 페이지에 있는 모든 링크를 찾아 그 href
속성을 출력합니다.
Yes24에서 '데이터 분석'으로 검색한 화면입니다.
개발자 도구에서 해당 페이지를 분석하면, 책 정보들은 'li' 태그에 담겨 있습니다. 그리고 24개의 'li'태그가 'ul'태그에 속해 있습니다.
search_list = soup.find(id='yesSchList').find_all('li')
고유한 값을 갖는 'id' 특성을 이용해 'ul' 태그를 find
메소드를 선택해 찾았습니다. 그 아래 있는 24개의 'li' 태그를 find_all
메소드를 사용해 찾았습니다. 이제 저 'li' 태그를 분석해 책 제목, 저자, 출판사, 가격 정보를 가져오도록 하겠습니다.
# 책 정보를 담기 위한 빈 리스트
book_list = []
author_list = []
pub_list = []
price_list = []
# 책 정보를 담고 있는 ul 태그 선택
search_list = soup.find(id='yesSchList').find_all('li')
# 반복문을 활용한 정보 추출
for idx, each_item in enumerate(search_list):
authors = [] # 여러명의 저자를 갖는 경우
book_name = each_item.find(class_='gd_name').text
book_list.append(book_name)
author_elements = each_item.find(class_='authPub info_auth').find_all('a')
authors = [author_elem.text for author_elem in author_elements]
author_list.append(", ".join(authors))
publisher = each_item.find(class_='authPub info_pub').find('a').text
pub_list.append(publisher)
price = each_item.find(class_='info_row info_price').find(class_='yes_b').text
price_list.append(price)
print(idx)
각 단계를 세세하게 설명하면 다음과 같습니다:
빈 리스트 생성:
book_list
, author_list
, pub_list
, price_list
라는 네 개의 빈 리스트를 만듭니다. 이 리스트들은 각각 책 제목, 저자, 출판사, 가격 정보를 저장하는 데 사용됩니다.
find
메소드를 이용한 'ul' 태그 선택:
soup.find(id='yesSchList')
는 id
가 'yesSchList'인 'ul' 태그를 찾아내는 코드입니다. 이 태그는 웹페이지에서 책 목록을 포함하는 컨테이너 역할을 합니다.
find_all
메소드를 이용한 'li' 태그 선택:
find_all('li')
는 방금 찾은 'ul' 태그의 모든 'li' 자식 태그를 찾습니다. 각 'li' 태그는 개별 책 항목의 정보를 담고 있습니다.
책 제목 추출:
each_item.find(class_='gd_name').text
코드는 각 'li' 태그 내에 있는 책 제목을 나타내는 태그를 찾아 그 텍스트를 추출합니다.
저자 정보 추출:
each_item.find(class_='authPub info_auth').find_all('a')
는 각 'li' 태그 내에 있는 저자 정보를 포함하는 모든 'a' 태그를 찾습니다. 여기서 저자 이름이 하이퍼링크로 되어 있기 때문에 'a' 태그를 찾는 것입니다. 리스트 컴프리헨션을 사용해 각 저자의 텍스트를 추출하고, ", ".join(authors)
를 통해 여러 저자가 있을 경우 쉼표로 구분된 문자열을 만듭니다.
출판사 정보 추출:
each_item.find(class_='authPub info_pub').find('a').text
는 출판사 이름을 추출합니다. 출판사도 하이퍼링크로 연결된 'a' 태그 내에 위치합니다.
가격 정보 추출:
each_item.find(class_='info_row info_price').find(class_='yes_b').text
는 가격 정보가 포함된 태그에서 가격 텍스트를 추출합니다.
인덱스 출력:
print(idx)
는 현재 처리 중인 'li' 태그의 인덱스를 출력합니다. 이는 디버깅 과정에서 어디까지 데이터 추출이 진행되었는지 추적하기 위해 유용합니다.
데이터를 수집한 후에는 이후 분석과정에서 활용하기 위해 파일로 저장합니다. 여기선 수집한 데이터를 엑셀로 저장하도록 하겠습니다.
판다스의 데이터 프레임은 표 형식의 데이터를 저장하고 조작할 수 있는 2차원 구조입니다. 웹 스크래핑으로 추출한 데이터를 데이터 프레임으로 변환합니다.
import pandas as pd
data = {
'Book': book_list,
'Author': author_list,
'Publisher': pub_list,
'Price': price_list
}
df = pd.DataFrame(data)
위 코드는 책의 이름, 저자, 출판사, 가격 리스트를 딕셔너리로 구성하고, 이를 데이터 프레임으로 변환합니다. 이렇게 만들어진 데이터 프레임은 엑셀, CSV 등 다양한 형식으로 쉽게 내보낼 수 있습니다.
데이터 프레임을 엑셀 파일로 저장하는 것은 판다스의 to_excel
메서드를 사용하여 간단하게 수행할 수 있습니다:
df.to_excel('./yes24_books.xlsx', index=False)
to_excel
메서드는 데이터 프레임을 엑셀 파일로 변환하여 저장합니다. index=False
옵션은 판다스가 기본적으로 데이터 프레임의 인덱스를 저장하는 것을 방지합니다. 이는 더 깔끔한 엑셀 파일을 생성하게 해줍니다.
위에서 만든 스크래핑 코드를 통해 추출한 책 정보를 엑셀 파일로 저장하는 과정은 매우 간단합니다. 데이터를 수집한 후에 데이터 프레임을 만들고, 다음과 같이 엑셀 파일로 저장합니다:
# 데이터 프레임 생성
data = {
'Book': book_list,
'Author': author_list,
'Publisher': pub_list,
'Price': price_list
}
df = pd.DataFrame(data)
# 엑셀 파일로 저장
df.to_excel('./yes24_books.xlsx', index=False)
이 코드는 각각의 리스트를 데이터 프레임의 열로 변환하고, 이 데이터 프레임을 'yes24_books.xlsx'라는 파일로 저장합니다. 이 파일은 후에 데이터 분석이나 다른 목적으로 사용될 수 있습니다.
저장한 파일을 확인하면 다음과 같습니다.
웹스크래핑에 있어서 BeautifulSoup은 강력한 도구이지만, 모든 웹페이지가 정적인 HTML로만 이루어져 있는 것은 아닙니다. 많은 부분은 사용자와의 상호작용에 따라 동적으로 내용이 변화하며, 이는 주로 자바스크립트를 통해 구현됩니다. 따라서 BeautifulSoup만으로는 이러한 동적인 웹페이지의 데이터를 수집하는 데 한계가 있습니다. 웹스크래핑을 위해 웹페이지들을 분석하다보면, 많은 곳들이 동적으로 내용이 변화하기 때문에 BeautifulSoup을 이용해 웹스크래핑을 할 수 없었습니다.
이때 필요한 것이 'Selenium'입니다. Selenium은 웹 브라우저를 자동화하기 위한 도구로, 웹 드라이버를 통해 실제 브라우저를 조작할 수 있게 해줍니다. 이를 통해 자바스크립트가 실행된 후의 페이지 상태를 캡처하고, 동적으로 로드되는 데이터에 접근할 수 있습니다. 예를 들어, 스크롤을 할 때마다 새로운 콘텐츠가 로드되거나, 클릭을 통해 새로운 데이터가 표시되는 경우 등에 Selenium을 사용하면 효과적입니다.
다음 글에서는 Selenium을 사용한 동적 데이터 수집 방법에 대해 정리하고, 실제 코드 예시를 통해 이를 실습해보도록 하겠습니다.