[Python] 다들 하는 크롤링 나도 한번 해보자 -1(selenium부터 pandas까지)

리미·2020년 4월 28일
0

Python Crawling

목록 보기
1/1
post-thumbnail

요즘 크롤링 안해보면 문찐이라며?

Python 좀 다뤄봤다 하는 친구들은 다 크롤링을 해본적 있는 것같다.
(또 나만 이렇게 뒤쳐졌어)
그래서 나도 한번 해보자 크롤링

시작해볼과

나의 크롤링 재료가 될 사이트는 멜론차트(https://www.melon.com/chart/index.htm)
requerts와 BeautifulSoup을 이용해 봤지만,
차트를 js로 뿌려서 그런지 emtpy만 뱉어버리고 나오지않는다
그래서 이용한다 selenium

일단 selenium은 웹 사이트를 제어할 수 있는 라이브러리로,
js로 작동하는 사이트들은 이걸 쓰는 것이 좋다고들 한다.

1) 브라우저 설정

옵션부터 손질을 하고 드라이버를 할것인데
난 크롬으로 사용할 것이고, --> 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)

2) 페이지이동

자 브라우저를 설정했으면 이제 페이지를 전달받은 url로 이동시키는 함수를 만들어보자
webdriver의 get함수가 해당 url을 인자로 받아서 페이지로 이동한다

def page_move(url):
    # str 타입이 아니면 str로 처리
    if isinstance(url, str):
        driver.get(url)
    else:
        driver.get(str(url))

3) 페이지 분석

해당 url의 데이터를 빼낼 구역을 분석한다.

이 부분을 개발자 모드를 켜서 확인을 해보면 아래와 같이 나오게 되는데

class namedl lst50 인게 차트 리스트이고 하위 td가 각각 입력되어있는 데이터라고 보여진다.

4) 본격적인 크롤링

본격적으로 크롤링 작업을 하는 함수를 만든다

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)

5) 사실 해도되고 안해도됨 부록정도?

나는 엑셀로 결과를 편하게 보고싶다. 그래서 설치한다 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)

6) 확인작업

생성된 csv를 열어서 확인해준다

이제 원하는 데이터만 빼서 db에 저장만 하면됨 ^*^... 그건 나중에 할것이다.

진짜진짜_최종.py

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()
profile
Python이 하고싶은데 자꾸 Flutter 시켜서 빡쳐서 만든 블로그

0개의 댓글