[스파르타코딩클럽] 웹개발 종합반 - 3주차(6)

변시윤·2021년 7월 17일
0

이번주에 배운 내용을 응용해서 빌보드 핫백 차트를 크롤링 해보자.

🖥 완성 코드

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://www.billboard.com/charts/hot-100',headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

billboard = soup.select('ol > li')
for hot100 in billboard:
    rank = hot100.select_one('span.chart-element__rank.flex--column.flex--xy-center.flex--no-shrink > span.chart-element__rank__number').text
    song = hot100.select_one('span.chart-element__information > span.chart-element__information__song.text--truncate.color--primary').text
    artist = hot100.select_one('span.chart-element__information > span.chart-element__information__artist.text--truncate.color--secondary').text
    print(rank, song, artist)

출력결과

1 Butter BTS
2 Good 4 U Olivia Rodrigo
3 Levitating Dua Lipa Featuring DaBaby
4 Kiss Me More Doja Cat Featuring SZA
5 Montero (Call Me By Your Name) Lil Nas X
6 Bad Habits Ed Sheeran
7 Leave The Door Open Silk Sonic (Bruno Mars & Anderson .Paak)
8 Peaches Justin Bieber Featuring Daniel Caesar & Giveon
9 Save Your Tears The Weeknd & Ariana Grande
10 Deja Vu Olivia Rodrigo
.
.
Process finished with exit code 0

우선 완성코드부터. 이렇게 보면 아주 간단한 것 같지만 장장 3시간에 걸친 시행착오를 거쳤다.

😩 시행착오

billboard = soup.select('#charts > div > div.chart-list__wrapper > div > ol > li')


li list 맞잖아...왜 안돼?

문제는 동적 페이지 라는 것!

  • '정적페이지'란?
    고정된 정보로 웹페이지 정보를 요청시 추가적인 처리 과정 없이 그대로 클라이언트에게 전송함

  • '동적페이지'란?
    유동적인 정보로 클라이언트와의 상호작용을 통해 요청을 분석하여 클라이언트에게 전송함

    더 자세한 설명은 이곳을 참조
    https://ai-creator.tistory.com/210

  • 동적페이지
#charts > div > div.chart-list.container > ol > li:nth-child(1) 
  • 정적페이지
#charts > div > div.chart-list__wrapper > div > ol > li:nth-child(1)

li list를 각각 동적페이지와 정적페이지로 Copy selector로 서로 다른 코드가 나오는데, 빌보드 핫백 차트는 동적페이지이므로 soup.select값에 동적페이지 코드를 넣어야 한다.

  • 동적페이지 Copy selector 출력시
  • 정적페이지 Copy selector 출력시


그러나 자세히 비교해보면 동적페이지와 정적페이지의 코드 중 마지막 ol > li:nth-child(1) 부분이 일치하는 것을 알 수 있다.
바꿔 말하면 정적페이지에 해당되는 코드는 그 전, 즉, '#charts > div > div.chart-list__wrapper > div'까지라는 뜻이다. 그러므로 동적페이지 코드를 전부 쓰지 않고 'ol > li'만 적어도 출력이 가능하다.


for hot100 in billboard:
    rank = hot100.select_one('span.chart-element__rank.flex--column.flex--xy-center.flex--no-shrink > span.chart-element__rank__number').text
    song = hot100.select_one('span.chart-element__information > span.chart-element__information__song.text--truncate.color--primary').text
    artist = hot100.select_one('span.chart-element__information > span.chart-element__information__artist.text--truncate.color--secondary').text
    print(rank, song, artist)

이제 for문을 적어보자! 이번엔 select_one을 이용해 각각의 변수 값을 지정한다. .text도 잊지 않는다. 이렇게 적으면 성공적으로 출력된다.

🖥 번외

billboard = soup.select('li > button')
for hot100 in billboard:
    rank = hot100.select_one('span.chart-element__rank.flex--column.flex--xy-center.flex--no-shrink > span.chart-element__rank__number').text
    song = hot100.select_one('span.chart-element__information > span.chart-element__information__song.text--truncate.color--primary').text
    artist = hot100.select_one('span.chart-element__information > span.chart-element__information__artist.text--truncate.color--secondary').text
    print(rank, song, artist)

처음엔 정적/동적페이지가 문제인 것을 몰라서 Copy selector가 잘못 지정된 줄 알고 soup.select값에 'ol > li'가 아닌 'li > button'을 썼었다. 이렇게 쓰면 none 때문에 .text로 출력을 할 수가 없다.

rank = hot100.select_one('span.chart-element__rank.flex--column.flex--xy-center.flex--no-shrink > span.chart-element__rank__number').text
AttributeError: 'NoneType' object has no attribute 'text'

⚠️ 위 코드로 출력시 나오는 오류 메시지

for hot100 in billboard:
    r = hot100.select_one('span.chart-element__rank.flex--column.flex--xy-center.flex--no-shrink > span.chart-element__rank__number')
    s = hot100.select_one('span.chart-element__information > span.chart-element__information__song.text--truncate.color--primary')
    a = hot100.select_one('span.chart-element__information > span.chart-element__information__artist.text--truncate.color--secondary')
    if r and s and a is not None:
        rank = r.text
        song = s.text
        artist = a.text
        print(rank, song, artist)

이 경우엔 조건문 if을 이용해야 한다. .text를 출력하는 변수가 3가지이므로 and도 같이 사용한다. 여기에 다시 변수를 적용해 변수의 .text를 지정한다. 출력하면 처음과 같은 결과를 얻을 수 있다.


확실히 익히고 싶어서 연습삼아 했던 빌보드 차트에서 3시간 잡아먹다...^^ 이번주는 드디어 다음주 진도 먼저 빼나 했더니...^^ 그래도 뭐가 문제인지 확실히 파악해서 결과적으로 다행이라고 생각한다. 따로 연습 안했으면 원인도 모르고 정적/동적페이지 개념조차 모른채 헤맸을 것이다. 동적페이지 크롤링은 따로 공부를 해야할 듯 싶다. 어쨌든 3주차도 이제 진짜 끝!

큰 도움 주신 스파르타코딩클럽 내일배움단 박초롱님께 다시 한 번 감사의 말씀 전합니다😍

profile
개그우먼(개발을 그은성으로 하는 우먼)

0개의 댓글