Python 좀 다뤄봤다 하는 친구들은 다 크롤링을 해본적 있는 것같다.
(또 나만 이렇게 뒤쳐졌어)
그래서 나도 한번 해보자 크롤링
나의 크롤링 재료가 될 사이트는 멜론차트(https://www.melon.com/chart/index.htm)
requerts와 BeautifulSoup을 이용해 봤지만,
차트를 js로 뿌려서 그런지 emtpy만 뱉어버리고 나오지않는다
그래서 이용한다 selenium
일단 selenium은 웹 사이트를 제어할 수 있는 라이브러리로,
js로 작동하는 사이트들은 이걸 쓰는 것이 좋다고들 한다.
옵션부터 손질을 하고 드라이버를 할것인데
난 크롬으로 사용할 것이고, --> webdriver.Chrome..
사이트는 별도로 띄우지 않을것이며, -0-add_argument('headless'))
한국어의 사이트를 크롤링하는 것이니 한글로 해준다. (add_argument('lang=ko_KR'))
webdriver.Chrome 할때 보통 드라이버 다운 받아서 경로를 지정해주지만
그냥 난 별도의 라이브러리를 사용하여 설치해서 사용하도록하겠다.
options = webdriver.ChromeOptions()
options.add_argument('headless')
options.add_argument('disable-gpu')
options.add_argument('lang=ko_KR')
driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)
자 브라우저를 설정했으면 이제 페이지를 전달받은 url로 이동시키는 함수를 만들어보자
webdriver의 get함수가 해당 url을 인자로 받아서 페이지로 이동한다
def page_move(url):
# str 타입이 아니면 str로 처리
if isinstance(url, str):
driver.get(url)
else:
driver.get(str(url))
해당 url의 데이터를 빼낼 구역을 분석한다.
이 부분을 개발자 모드를 켜서 확인을 해보면 아래와 같이 나오게 되는데
class namedl lst50 인게 차트 리스트이고 하위 td가 각각 입력되어있는 데이터라고 보여진다.
본격적으로 크롤링 작업을 하는 함수를 만든다
def get_chart_list():
try:
WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.CLASS_NAME, 'lst50')))
# 페이지 로드후 3초 지나서 class 이름이 lst50인 것을 찾아본다
chart_list = []
# 차트를 리스트 형식으로 저장하기위해 할당
data = driver.find_elements_by_class_name('lst50')
for k in data:
chart_list.append(k.text.split('\n'))
print(chart_list)
# 일단 여기까지만 적고 프린트해본다
except TimeoutException:
# class 이름이 lst50인게 없으면 아래와 같이 에러를 뱉는다
print('해당 페이지에 차트정보가 없습니다.')
finally:
# 성공을 하든 예외처리가 되든 완료한 드라이버는 닫아준다
driver.quit()
그렇게 정상적으로 처리가되면 잘 프린트가 되는데 문제는 여기서 부터다.
먼저 설명하기 앞서, 난 리스트에 넣어주고 원하는 정보만 빼서 추후에 bulk_create를 사용하려고한다.(이건 추후에 업로드예정)
그런데 출력된 결과를 보면
차트에서 증감이 0인 것은 html에서 빈칸으로 처리되니 저렇게 출력이 되어버린다. 저 상태로 list에 넣은다 한들 어떤 데이터는 index 1이 증감이고 어떤 데이터는 노래제목이 되고 데이터를 하나하나 따져서 넣지않는 한 힘들다.
그래서 중간에 find_elements_by_class_name를 이렇게 수정하고 받아와본다.
data = driver.find_elements_by_class_name('lst50 > td')
그렇게 되면 td안에 있는 모든 값들을 가져오게 되는데 index는 맞지만 한 list에 제목과 가수명이 둘다 있는 경우가 생겨버린다.
그래서 나는 일단 리스트를 다시 해체하고 td 갯수만큼 묶어버리는 방법을 택했다.
일단 상단에 itertools의 chain을 import 시킨다
(https://docs.python.org/3/library/itertools.html)
from itertools import chain
하나하나 list로 묶여져 있는것들을 다 풀어주기 위해 chain을 이용한다
chain_list = list(chain(*chart_list))
이걸 프린트하면 아래와 같이 나오게 된다.
묶어주는 함수를 만들도록한다.
이건 전에 30초 알고리즘에서 다뤄본적이 있으므로, 아래 링크를 참고할것
(https://velog.io/@rimi/Python-30초-파이썬-chunk)
현재 td는 12개인데 각각 한 td에 데이터가 2개씩 들어간게 2개이므로 14개로 묶어준다.
chuck_data = chunk(chain_list, 14)
나는 엑셀로 결과를 편하게 보고싶다. 그래서 설치한다 pandas.
설치가 되었다면 상단에 import는 잊지말고 해준다.
import pandas as pd
그리고 pandas를 이용해 csv로 저장하는 함수를 만든다.
def processing_chart_list(chart_list):
df = pd.DataFrame(chart_list, columns=['', '순위', '증감', '', '', '제목',
'가수', '앨범', '좋아요', '좋아요수', '', '', '', ''])
# DataFrame에 잘라놓은 리스트들을 담아준다. columns는 반드시 갯수에 맞게 만들어준다.
df.index = df.index + 1
# index 시작을 1부터 한다
df.to_csv(f'chart_df.csv', mode='w', encoding='utf-8-sig', header=True, index=True)
# csv로 저장해준다
print(df)
생성된 csv를 열어서 확인해준다
이제 원하는 데이터만 빼서 db에 저장만 하면됨 ^*^... 그건 나중에 할것이다.
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from webdriver_manager.chrome import ChromeDriverManager
from math import ceil
from itertools import chain
import pandas as pd
import time
options = webdriver.ChromeOptions()
options.add_argument('headless')
options.add_argument('disable-gpu')
options.add_argument('lang=ko_KR')
driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)
def page_move(url):
if isinstance(url, str):
driver.get(url)
else:
driver.get(str(url))
def get_chart_list():
try:
WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.CLASS_NAME, 'lst50')))
chart_list = []
data = driver.find_elements_by_class_name('lst50 > td')
for k in data:
chart_list.append(k.text.split('\n'))
chain_list = list(chain(*chart_list))
chuck_data = chunk(chain_list, 14)
processing_chart_list(chuck_data)
except TimeoutException:
print('해당 페이지에 차트정보가 없습니다.')
finally:
driver.quit()
def processing_chart_list(chart_list):
df = pd.DataFrame(chart_list, columns=['', '순위', '증감', '', '', '제목', '가수', '앨범', '좋아요', '좋아요수', '', '', '', ''])
df.index = df.index + 1
df.to_csv(f'chart_df.csv', mode='w', encoding='utf-8-sig', header=True, index=True)
print(df)
def chunk(lst, size):
return list(map(lambda x: lst[x * size:x * size + size], list(range(0, ceil(len(lst) / size)))))
if __name__ == '__main__':
page_move('https://www.melon.com/chart/index.htm')
get_chart_list()