노트

[Python] Selenium으로 중고나라 크롤링 후 MySQL에 저장 본문

코딩/Python

[Python] Selenium으로 중고나라 크롤링 후 MySQL에 저장

_Myway 2020. 1. 11. 17:22

안녕하세요!

이번엔 중고나라를 크롤링했습니다.

예전에 과제로 했던거라 기억은 잘 안나지만 최대한 써볼게요!

제가 크롤링한 부분은 중고나라의 미개봉 새상품 목록입니다.

크롤링할 데이터는 아래 내용입니다.

.

Selenium을 사용하기 위해서는 chrome web-driver가 필요한데

이전 게시물에서 설명했으니까 참고해 주세요.

참고로 저는 주피터 노트북으로 작업했습니다.



.

.

.

ln[1]:

#중고나라에서 크롤링으로 데이터 뽑기
from selenium import webdriver
import time

driver = webdriver.Chrome('C:/Users/samsung/Downloads/chromedriver')
driver.get('https://cafe.naver.com/joonggonara?iframe_url=/ArticleList.nhn%3Fsearch.clubid=10050146%26search.menuid=1900%26search.boardtype=L')
bb2 = []
dataset = []
time.sleep(2)

driver.switch_to.frame("cafe_main")
for l in range(1,71):
    tag = driver.find_elements_by_xpath('//div[@class="article-board m-tcol-c"]//table/tbody/tr')
    bb2 = tag
    time.sleep(2)
    
    for i in range(len(bb2)):
        dataset.append(bb2[i].text)

    if l % 10 == 0:
        c10 = driver.find_element_by_link_text('다음')
        c10.click()
    else:
        a = str(l+1)
        c = driver.find_element_by_link_text(a)
        c.click()
    time.sleep(2)

driver.quit()

driver로 웹드라이버를 정의해주고, 크롤링하고싶은 웹페이지의 url을 넣어줍니다.

그리고 제가 크롤링하고 싶은 부분의 html 코드를 보면

이렇게 div태그가 iframe 안에 있는 것을 볼 수 있습니다.

코드에 driver.switch_to.frame("cafe_main") 을 넣어주면 iframe 안의 내용을 크롤링 할 수 있습니다.

그리고 for문에서 범위를 1~71이라고 한 이유는

한페이지당 목록이 15개정도 있어서 70페이지쯤 크롤링하면 1000개정도 되기 때문입니다.



.

tag = driver.find_elements_by_xpath('//div[@class="article-board m-tcol-c"]//table/tbody/tr')

html 태그로 찾는 것을 find_elements_by_xpath라고 하는데

iframe안의 '//' : 많은 태그들을 지나서

'div[@class="article-board m-tcol-c"]' : div안의 class이름이 article-board m-tcol-c 인 곳 아래의

'//' : 많은 태그들을 지나서

'table/tbody/tr' : table안의 tbody안의 tr 태그 안에

크롤링하려고 하는 모든 데이터들이 있습니다.

F12 키를 누르면 html소스를 볼 수 있으니까 직접 확인해보세요.

.

.

그리고 이렇게 태그 안의 검은색 글씨들을 text라고 하는데

.

dataset.append(bb2[i].text)

그래서 이 코드는 태그 안의 text들을 가져온다는 뜻입니다.



.

c10 = driver.find_element_by_link_text('다음')

이 부분은 중고나라에서 다음페이지로 표시하는 부분이 10페이지까지 표시되어서

10번 페이지를 넘기면 selenium으로 다음 버튼을 누르도록 했고

c = driver.find_element_by_link_text(a)

이 부분은 페이지번호 1, 2, 3, .. 을 각각 selenium으로 눌러주는 코드입니다.

find_element_by_link_text 라는 부분은

중고나라의 html코드를 확인해보시면 무슨 뜻인지 알 수 있을 것입니다.

.

.

중간중간에 sleep을 줘서 사이트가 충분히 로딩되도록 해주세요.

저같은 경우에는 크롤링하다가 자꾸 max try error였었나 

암튼 저런게 떠서 sleep을 줬던것 같네요.

.

.

.

이렇게 해서 크롤링한 정보를 print해보면

이런 식으로 나오게 되는데

별로 보기에 예쁘지가 않죠?

그리고 DB에 저장하려면 규격을 맞춰야하므로 전처리를 해줍니다.



.

da = dataset
#중복된 이름 attribute 제거
d = len(da)-1
while d >= 0:
    if d % 2 == 1:
        del da[d]
    d-=1
print(da[:27])

먼저 위에서 출력한 부분의 짝수번째 줄에 중복된 이름 데이터를 지워줍니다.

맨 밑의 코드에서 굳이 27을 출력한 이유는 제가 예전에 크롤링했을 때

1페이지에는 공지사항까지 같이 떠서 공지개수+게시물개수가 아마 27개라서 그랬던것 같습니다.

아무튼 이렇게 하면

이렇게 중복된 데이터가 사라집니다.

.

이제 공지사항을 삭제해줍니다.

위랑 비슷하게 코드를 짜면

dda = da
#공지사항 삭제
d = len(dda)-1
while d >= 0:
    if '공지' in dda[d]:
        del dda[d]
    d -= 1
print(dda[:15])

지금 보니까 저 '필독'이란 부분도 지웠어야했는데 안지웠네요.



.

.

#new와 사진이 없는 경우, 둘 다 있는 경우, 둘 중 하나만 있는 경우
#\n으로 구분된 attribute들 나눠주고 마지막 값은 
#'날짜(또는 시간) 조회수' 로 되어있으므로 공백으로 나눠준 후 append
data = []
for r in range(len(dda)):
    a = dda[r].split('\n')
    if len(a) == 4:
        b = a[3].split(' ')
        del a[3]
        for i in b:
            a.append(i)
        data.append(a)
    elif len(a) == 6:
        b = a[5].split(' ')
        del a[5]
        for i in b:
            a.append(i)
        data.append(a)
    else:
        b = a[4].split(' ')
        del a[4]
        for i in b:
            a.append(i)
        data.append(a)
data

이부분은 그냥 mysql에 넣을 수 있도록 틀을 잡아주는 부분입니다.

#db에 저장하기위해 같은 길이의 리스트로 만들기
#사진이 없는 곳에 빈 공간 만들기
datas = data
dataas = []
for r in range(len(datas)):
    tmp = []
    tm = datas[r]
    for t in range(len(tm)):
        if t == 2 and tm[t] != '사진':
            tmp.append('')
        tmp.append(tm[t])
        
    dataas.append(tmp)
dataas
#new가 없는 곳에 빈 공간 만들기
datas2 = dataas
real_dataset = []
for r in range(len(datas2)):
    tmp = []
    tm = datas2[r]
    for t in range(len(tm)):
        if t == 3 and tm[t] != 'new':
            tmp.append('')
        tmp.append(tm[t])
        
    real_dataset.append(tmp)
real_dataset

이것도 틀잡는거

.

결과가 대충 이런식으로 나오는데 이러면 mysql에 한번에 집어넣기 편합니다.



.

그리고 데이터 중에 제목 데이터 뒤에 대부분 댓글수가 같이 붙어서 나오는데

몇개는 따로 떨어져서 나오더라구요. 이런걸 정리해줍니다.

저는 이런게 몇개 없어서 그냥 인덱스를 찾아서 그것만 지우는 식으로 했습니다.

#댓글 데이터가 제목이랑 붙어 나오는게있고 이렇게 나오는게 있어서 
#그냥 이렇게 나오는걸 제거(2개밖에 없다)
for i in range(len(real_dataset)):
    if len(real_dataset[i])>7:
        print(i)
        del real_dataset[i]

.

.

아 MySQL에는 작은따옴표(')는 안들어가니까 주의해주세요.

#mysql에는 작은따옴표를 넣을 수 없으므로 '를 찾아서 없애준다
for r in real_dataset:
    if "'" in r[1]:
        print(real_dataset.index(r))

이것도 하나밖에 없어서 그냥 하드코딩으로 지워줬었네요.

#216 하나밖에 없어서 그냥 없애줌
real_dataset[216][1] = real_dataset[216][1].replace("'",'')
real_dataset[216][1]

.

.

마지막으로 Mysql에 저장하는 부분입니다.



#DB에 저장
import pymysql

connect = pymysql.connect(host='localhost', user='유저명', password='비밀번호', db='디비이름', charset='utf8mb4')
cursor = connect.cursor()

for r in real_dataset:
    boardnum = int(r[0])
    title = str(r[1])
    photo = str(r[2])
    newornot = str(r[3])
    uname = str(r[4])
    utime = str(r[5])
    readnum = int(r[6])
    
    sql = """insert into contents 
    (boardnum, title, photo, newornot, uname, utime, readnum) 
    values (%d, '%s', '%s', '%s', '%s', '%s', '%d')
    """ % (boardnum, title, photo, newornot, uname, utime, readnum)

    cursor.execute(sql)
    connect.commit()
connect.close()

mysql에 디비를 만든 후 이렇게 넣어주면 끝

스키마는 

이렇게 했습니다.

.

.

<결과>

.

.

.

.

.

Comments