Project/[Python] 도서 정보 입력 프로그램

[도서 입력 프로그램] 2. BeautifulSoup 알라딘 도서 정보 수집

CodeNook; 2024. 5. 3. 10:30

[도서 정보 자동 입력 프로그램] 2. BeautifulSoup 알라딘 도서 정보 수집

이제 알라딘에서 필요한 도서정보를 수집 해보자.

Yes24 등록을 위해 수집해야하는 정보는 아래와 같다.

  • 도서정보
  • 판매가격
  • 상품 상태(상/중/하)
  • 도서 구분(국내도서/외국도서/음반/DVD)
  • 도서 대표이미지(이미지 주소로 추출)
  • 도서 주제분류(중분류/소분류)
  • 저자명
  • 출판사명
  • 출판일자
  • ISBN (없는 도서도 있음)
  • 책 주의사항(html 태그로 수집)
  • 책 사진(이미지 주소로 수집)

이렇게 12가지이다. 이외에 서점별로 책 분류 및 색인을 위해 표기하는 자체 상품코드도 있는데 알라딘 판매자 로그인 자동화가 불가하므로 그것은 다른 방법을 통해 입력하도록 하겠다.

 

도서 페이지에서 이제 정보를 추출하기 위해서 파이썬 BeautifulSoup4requests라이브러리를 사용하겠다. BeautifulSoup4와 requests는 페이지 변화가 없어 그 페이지 내에서 원하는 모든 정보를 추출하는 정적 크롤링에 적합한 라이브러리이다.

 

1. requets를 통해 웹페이지의 html 코드를 읽어온다.

2. BeautifulSoup4를 사용해 html코드를 보기 편하도록 정리(Parsing) 해준다.

3. BeautifulSoup4 메서드로 내가 원하는 요소를 찾거나(find, find_all) 또는 선택(select, select_one)하여 정보를 추출

 

위 순서로 정보를 추출하는데 이 과정에서 제일 골치아픈 것이 바로 3번 부분. 원하는 요소를 정확히 식별하는 부분이다.

<태그>로 이루어진 html문서에서 내가 원하는 위치의 일종의 주소를 명확히 식별하는 기준은 여러가지가 있는데 보통 CSS Selector나 XPATH를 사용한다.

 

크롬에서 F12버튼을 누르면 html 코드를 볼 수 있는데 원하는 위치를 클릭하여 CSS Selector나 XPATH 주소를 복사할 수 있다. 문제는 크롬에서 복사한 주소를 그대로 붙여넣었을때 잘 되는 경우도 많지만 에러가 나는 경우도 많다는 것이다. 그리고 사이트의 구조가 바뀌게 되면 잘되던 코드가 안되는 경우가 생긴다. 그러니 무작정 복붙하기보다는 html 구조를 보고 보다 확실히 특정할 수 있는 id나 class 정보를 추가하여 두는 것이 좋다.

 

CSS Selector 문법으로 예를 들면, 위 스크린샷에서 판매가 12,000원의 CSS Selector를 그대로 복사하면 아래와 같다.

#Ere_prod_allwrap > div.Ere_prod_bookwrap > div.Ere_prod_Binfowrap > div > div:nth-child(1) > ul > li:nth-child(2) > div.Ritem.Ere_ht11 > span.Ere_fs24

 

전체 html태그 구조를 다 가져와서 길이도 굉장히 길뿐 아니라, 'nth-child(1)'과 같은 부분은 '첫번째 div 태그'란 뜻인데 사이트 업데이트로 html 구조가 조금이라도 바뀌게 되면 바로 먹통이 되어버린다. 따라서 저렇게 모든 주소를 적기보다는 확실하게 구별할 수 있는 몇가지 태그에 id나 class 정보를 추가해서 구별하는 것이 좋다.

아래는 내가 넣은 주소다.

div.Ere_prod_Binfowrap div.Ritem > strong

태그 사이에 '>'는 앞의 태그 바로 아래에 위치한 태그라는 뜻이다. '>' 대신에 공백 ' '을 넣으면 앞의 태그 아래에 있는 모든 태그가 다 해당한다는 뜻으로 중간에 여러가지 의미 없는 태그는 생략해서 길이를 줄일 수 있다.

div.Ritem의 뜻은 class 이름이 'Ritem'인 div 태그란 뜻으로 class이름같은 속성이 특이한 경우에 태그를 확실히 구별할 수 있게 해준다.

그리하여 완성한 코드는 아래와 같다.

import requests
from bs4 import BeautifulSoup

#책 정보 페이지 접속
url = '알라딘 도서 주소'
response = requests.get(url) # url 접속
soup = BeautifulSoup(response.text, 'html.parser') # html문서 parsing

#책 정보 추출
bookname = soup.select_one('div.tlist span.Ere_bo_title').text[5:] #[중고] 제외 제목 추출
price = int(soup.select_one('div.Ere_prod_Binfowrap div.Ritem.Ere_ht11 > span.Ere_fs24').text.replace(',','')) # 판매가 추출
level = soup.select_one('div.Ere_prod_Binfowrap div.Ritem > strong').text #상품 상태 추출
bookorigin = soup.select_one('#ulCategory > li > a:nth-child(2)').text #국내/외국/음반/DVD 종류
main_img = soup.select_one('#CoverMainImage').attrs['src'] # 대표이미지 추출
category = soup.select_one('#ulCategory > li > a:nth-child(3)').text #주제분류 추출
subcategory = soup.select_one('#ulCategory > li > a:nth-child(4)').text #주제소분류 추출

 

필요한 12개 정보중 6개를 추출했다. 이제 저자명, 출판사명, 출판일, ISBN, 책 주의사항, 책 사진 6개가 남았다.

그런데 이게 앞에 6개랑 다르게 좀 복잡하다.

 

명확히 위치가 구분지어져서 Selector를 구별하기 쉬운 위 6개와 다르게 알라딘 사이트 특성상 태그 구분이 모호하거나 여러개가 섞여있어 원하는 부분만 긁어모으기가 쉽지 않았다.

특히, 아래 그림에서 도서 제목 아랫줄에 지은이, 출판사, 출판일이 연달아 나오는데 이 부분이 골치아프다.

어떤 책은 a 태그로 링크가 걸려있고 어떤 책은 링크가 없이 span 태그로 텍스트만 나와있다. 그리고 더 골치아프게 출판일은 특정 태그로 묶여있기 보다 li태그로 여러개 태그 묶음 중 곁가지로 들어가있어 따로 뽑아내는 것이 보통일이 아니다...😓

아래 김동근, 김병모 (지은이) 진원사 2014-10-13 이부분을 추출하는 것이 골치아팠다.

 

이렇게 저렇게 온갖 방법은 궁리해보다가 결국 내가 찾은 방법은 저 부분을 아예 Box형태로 통째로 추출한다음 슬라이싱하는 것이었다. 도서 한권에 대한 정보만 추출하면 세부적으로 들어갈 수 있겠지만, 알라딘 모든 페이지에서 동일하게 정보를 추출하려면 공통으로 적용할 수 있는 방법을 선택해야만했기에 부득이한 선택이었다.

통째로 태그를 긁어오면 아래와 같다.

<li class="Ere_sub2_title"><a class="Ere_sub2_title" href="링크">김동근</a>,<span class="Ere_PR10"></span><a class="Ere_sub2_title" href="링크">김병모</a> (지은이)<span class="Ere_PR10"></span><a class="Ere_sub2_title" href="링크">진원
</a><span class="Ere_PR10"></span>2014-10-13</li>

 

위 String에서 붉은색 부분만 따로 추려내야한다. 원래라면 .text 메서드를 사용해서 바로 태그를 제외한 텍스트를 추려낼 수 있지만, 저 붉은 글자들이 하나의 String으로 합쳐져서 슬라이싱이 더 곤란해진다.

그래서 태그를 지우기 전에 미리 split 메서드로 구분이 되도록 나누기로 했다. 사이마다 공통적으로 들어가는 부분은 "<span"이라 "<span"을 기준으로 split한 결과,

 

['<li class="Ere_sub2_title"><a class="Ere_sub2_title" href="링크">김동근</a>,',
 ' class="Ere_PR10"></span><a class="Ere_sub2_title" href="링크">김병모</a>\xa0(지은이)', 
' class="Ere_PR10"></span><a class="링크"> 
진원사</a>', 
' class="Ere_PR10"></span>2014-10-13</li>']

이렇게 길이 4의 리스트로 쪼갰다.

이제 여기서 tag를 없애고 텍스트만 추려야하는데 text메서드를 쓰니 앞에서 "<span" 부분만 사라져서 정확하게 태그가 지워지지 않는다. 

정리를 위해선

1. "<"와 ">" 사이의 모든 문자와 숫자를 제거

2. 혹시 있을지 모를 ',' 삭제

3. (지은이) 또는 (옮긴이)앞의 '\xa0' 제거

 

3개를 거쳐 깨끗히 하는 clean_tag 함수를 만들어보았다. 

정규식을 써야하는데 정규식은 항상 외워도 까먹는터라 그냥 ChatGPT에 물어보는게 가장 정확하다.

 

Chat GPT의 도움으로 만든 함수는 아래와 같다.

import re

#<>태그 제거 함수
def cleantag(raw_html):
   cleanr = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});') #<>사이 문자/숫자 제거
   cleantext = re.sub(cleanr, '', raw_html)
   cleantext = cleantext.replace(',', '')  # ',' 삭제
   if '">' in cleantext:
      cleantext2 = cleantext.split('">')[1]
      if '\xa0' in cleantext2:
         result = cleantext2.split('\xa0')[0]
      else:
         result = cleantext2
   elif '\xa0' in cleantext:
      result = cleantext.split('\xa0')[0]
   else:
      result = cleantext
   return result

 

이제 깔끔하게 [지은이, 출판사, 출판일]로 정리된 리스트를 슬라이싱해서 각각의 변수로 저장하면 끝.

 

그다음 ISBN의 경우,

책에 따라 ISBN이 있는 경우, 없는 경우가 있어서 구분지어서 추출해야한다.

또 골때리는게 ISBN이 있다고해서 중고서점 페이지에서는 바로 보이지 않고 새책 소개 페이지로 링크를 타고 들어가야만 ISBN을 확인할 수 있다.

 

ISBN이 있는 경우 붉은색 박스로 표기된 새책 부분 링크에 접속해야한다.

 

그래서 try문으로 ISBN 링크가 있는 경우 그 링크로 html 문서를 별도 parsing해서 ISBN을 가져오도록 코드를 짰다.

책 주의사항과 책 이미지의 경우도 역시 책에 따라 적지 않은 경우가 있어 try문을 사용해 있는 경우만 가져오도록 코드를 짰다.

그 결과는 아래와 같다.

# ISBN 및 도서 정가 수집
try: # ISBN있는 경우
    isbn_url = soup.select_one('td.Tit_tableTF a.pdp_black').attrs['href']
    isbn_response = requests.get(isbn_url)
    isbn_soup = BeautifulSoup(isbn_response.text, 'html.parser')
    isbn = isbn_soup.select_one("li:contains('ISBN :')").get_text()[7:]
    origin_price = isbn_soup.select_one("div.Ere_prod_Binfowrap div.info_list > ul > li:nth-child(1) > div.Ritem").get_text()[:-2]
except: # ISBN없는 경우
    isbn = None
    origin_price = None

#책 주의사항 수집
try: #주의사항 입력된 경우
   description_tag = soup.select_one('#usedDecription')
   description_img = [img["src"] for img in soup.select('#usedDecription img')]
except:
   description_tag = None
   description_img = None

 

위 코드에서 origin_price는 도서 정가인데, 나중에 Yes24 입력할때 정가보다 판매가를 높이 설정하려면 '소장용 도서'로 선택을 해야하는데 그것을 구분하기 위해서 따로 추출해두었다.

 

이제 기본적인 12가지 도서 정보는 다 추출했다.

Yes24로 입력할때 약간의 전처리가 필요하겠지만 어쨌든 도서 정보를 입력할 기본준비는 끝난 것이다.

 

이번 포스팅은 여기까지 😎

 

다음은 드디어 Yes24로 입력하는 과정을 코드로 짜보겠다.

이게 생각보다 복잡해서 한개 포스트로는 어렵고, 여러개로 나누어 가야할것 같다.

 

이전글: [도서 입력 프로그램] 1. Selenium 자동 로그인 (feat. 소스코드 민감정보 보안)

 

[도서 입력 프로그램] 1. Selenium 자동 로그인 (feat. 소스코드 민감정보 보안)

[도서 정보 자동 입력 프로그램] 1. Seleinium 자동 로그인자동화가 필요한 대략적인 작업 구조는 이러하다.1. 알라딘 판매자 로그인2. Yes24로 입력할 도서 정보(ISBN, 도서명, 가격, 상태 등) 추출3. Yes2

codenook.site

 

다음글: [도서 입력 프로그램] 3-1. Selenium 입력 구조 구상

 

[도서 입력 프로그램] 3-1. Selenium 입력 구조 구상

[도서 정보 자동 입력 프로그램] 3-1. Selenium 입력 구조 구상 이번 포스팅부터 본격적으로 Yes24로 도서 정보 입력을 시작한다.제일 처음으로 입력하는 구조를 구상해야 한다. 도서 정보를 알라딘

codenook.site