Web_Crawling

BLAKE KIM·2020년 7월 27일
0

정적인 페이지 크롤링하기

사전 스터디 때 진행한 내용으로 아래 링크 참조

TIL No.13: Python - 네이버 뉴스에서 헤드라인 뉴스 목록 크롤링해서 저장하기

https://velog.io/@blakekim93/TIL-No.13-Python-네이버-뉴스에서-헤드라인-뉴스-목록-크롤링해서-저장하기

3주차 Python 크롤링 코드 마감. 전체 코드 및 실행 화면

https://velog.io/@blakekim93/3주차-Python-크롤링-코드-마감.-전체-코드-및-실행-화면

Selenium

Selenium은 주로 웹앱을 테스트하는데 이용하는 프레임워크다. webdriver라는 API를 통해 운영체제에 설치된 Chrome등의 브라우저를 제어하게 된다. 브라우저를 직접 동작시킨다는 것은 JavaScript를 이용해 비동기적으로 혹은 뒤늦게 불러와지는 컨텐츠들을 가져올 수 있다는 것이다. 즉, ‘눈에 보이는’ 컨텐츠라면 모두 가져올 수 있다는 뜻이다. 우리가 requests에서 사용했던 .text의 경우 브라우저에서 ‘소스보기’를 한 것과 같이 동작하여, JS등을 통해 동적으로 DOM이 변화한 이후의 HTML을 보여주지 않는다. 반면 Selenium은 실제 웹 브라우저가 동작하기 때문에 JS로 렌더링이 완료된 후의 DOM결과물에 접근이 가능하다.

driver.page_source VS requests.text

driver.page_source: 브라우저에 보이는 그대로의 HTML, 크롬 개발자 도구의 Element 탭 내용과 동일.

requests 통해 가져온 req.text: HTTP요청 결과로 받아온 HTML, 크롬 개발자 도구의 페이지 소스 내용과 동일.

  • driver.page_source는 JavaScript로 늦게 랜더링된 것까지 가져온다.
  • 그러나 req.text는 JavaScript가 적용되기 전의 html문서 자체를 말한다.
  • JavaScript에 의해 동적으로 작용하는 페이지의 경우 두 결과가 다르다. 그러나 정적인 페이지의 경우 동일하다.

다음 내용은 작성한 코드를 기반으로 작성할 예정.
빌보드 차트 100위에 있는 노래와 가수 그리고 이미지의 링크까지 크롤링하는 코드

빌보드 차트 100 노래, 가수, 이미지 링크

import requests
from bs4 import BeautifulSoup
from selenium import webdriver

# 스크롤 다운을 위해 import함
from selenium.webdriver.common.keys import Keys
import pandas,time

# options = webdriver.ChromeOptions()
# options.add_argument('headless')	# 브라우저 창을 열지 않고 실행할 수 있는 옵션

# driver = webdriver.Chrome('/Applications/chromedriver', chrome_options=options) options 사용시 이렇게 작성해야함
driver = webdriver.Chrome('/Applications/chromedriver')

driver.implicitly_wait(10)

url = 'https://www.billboard.com/charts/hot-100'
driver.get(url)

body = driver.find_element_by_css_selector('body')
for i in range(30):
    body.send_keys(Keys.PAGE_DOWN)
    time.sleep(0.1)

html = driver.page_source
bsObject = BeautifulSoup(html, 'html.parser')
driver.close()

musics = bsObject.select(
    'div#charts > div > div.chart-list.container > ol > li > button > span.chart-element__information > span.chart-element__information__song.text--truncate.color--primary'
)
artists = bsObject.select(
    'div#charts > div > div.chart-list.container > ol > li > button > span.chart-element__information > span.chart-element__information__artist.text--truncate.color--secondary'
)
imgs = bsObject.select(
    'div#charts > div > div.chart-list.container > ol > li > button > span.chart-element__image'
)

hotMusic = []
hotArtist = []
images = []
for music in musics:
    hotMusic.append(music.text)  # text를 붙이지 않으면 태그 정보까지 함께 출력

for artist in artists:
    hotArtist.append(artist.text)

for img in imgs:
    img = img['style']	# style = "여기" 에 해당하는 값을 가져온다.
    img_url = img[img.find('http'):img.rfind('\"')]	# "여기"에서 http로 시작하고 \까지 가져온다.
    images.append(img_url)

hot100 = []
for i in range(len(hotMusic)):
    hot100.append([i+1, hotMusic[i], hotArtist[i], images[i]])

#time = datetime.today().strftime("%Y/%m/%d %H:%M:%S")

data = pandas.DataFrame(hot100)
data.columns = ['RANK', 'MUSIC', 'ARTIST', 'IMG_LINK']
print(data)
data.to_csv('billoboard_hot100.csv', mode = 'w', encoding='euc-kr', index=False)

Keys.PAGE_DOWN을 하는 이유는 html문서에는 해당 공간에 style="inline-block"이라고만 입력되어 있고 이미지 url이 담겨 있지 않다. 즉 JavaScript에 의해 구현된 부분으로 이미지 링크를 받아오기 위해서는 Selenium을 통해 해당 이미지를 랜더링한 후에 받아와야 이미지 url이 담겨 있게 된다.

또한 for img in imgs:부분에서 img['style']style에 지정된 값을 가져올 수 있던 이유는 imgbs4.element.Tag type으로 그 안의 내용을 가져오기 위해서는 dictionarykey값을 통해 가져오는 것처럼 가져올 수 있다.

img['style']로 가져온 값은 str이기 때문에 slicing이 가능하다.

스타벅스 전체 음료 이름과 이미지 링크

import requests
from bs4 import BeautifulSoup
from selenium import webdriver
import pandas

driver = webdriver.Chrome('/Applications/chromedriver') # chromedriver 경로 지정
url = 'https://www.starbucks.co.kr/menu/drink_list.do'  # 불러올 주소
driver.get(url) # driver에 전달

html = driver.page_source
bsObject = BeautifulSoup(html, 'html.parser')
menu_name = bsObject.select(
    'div#container > div.content > div.product_result_wrap.product_result_wrap01 > div > dl > dd:nth-child(2) > div.product_list > dl > dd > ul > li > dl > dt > a > img'
)
name = []
img = []
menu = []
for i in menu_name:
    name.append(i.get("alt"))
    driver.implicitly_wait(2)
    img.append(i.get("src"))
    driver.implicitly_wait(2)

for i in range(len(name)):
    menu.append([ name[i], img[i] ])

data = pandas.DataFrame(menu)
data.columns = ['NAME', 'IMG_LINK']
print(data)
data.to_csv('starbucks_menu.csv', mode = 'w', encoding='euc-kr', index=False)

스타벅스 코리아 페이지 또한 메뉴에 대해서는 JavaScript로 구현이 되어 있기에 Selenium을 사용해야 한다. (무거워서 CPU가 뜨거워지지만 어쩔 수 없다.ㅠㅠ)

profile
BackEnd

0개의 댓글