2023 주니어 SRE 엔지니어 후레임

반응형

 

 

중고나라를 기준으로 잡아, 네이버 카페 크롤러를 만들어 보았습니다.

 

원하는 카테고리를 정한 뒤 게시물 제목, 번호, 작성자, URL, 가격, 내용까지 크롤링합니다.

 

큰 흐름은 다음과 같습니다.

 

 

 

 

 

1. 로그인

왜인지 send_keys를 보내는 일반적인 로그인 시도는 실패하므로, pyperclip을 이용하여 ID와 PW를 복붙함.

 

 

 

2. 네이버 카페의 메뉴 열기

원하는 카페의 원하는 메뉴를 크롤링하기 위해서는 카페 ID메뉴 ID를 알아야합니다.

크롬을 사용중이라면, 크롤링을 원하는 카페에 들어가 F12를 누르고 빨간버튼(단축키 Ctrl+Shift+C)를 누른 뒤

원하는 게시판을 클릭하면 아래와 같이 정보를 얻을 수 있습니다.

 

저는 clubid를 joonggonara_id, menuid를 menu_id로 설정했습니다.

 

 

 

3. iframe으로 전환

네이버 카페 내부는 iframe으로 꾸며져있기 때문에 크롬 드라이버의 모드를 iframe으로 전환해야 합니다.

driver.switch_to_frame("cafe_main")을 이용하여 cafe_main의 iframe으로 전환합니다.

 

 

 

4. 최신 글부터 크롤링 시작.

가장 첫번째 글(최신글)부터 크롤링을 시작하여, pages로 정한 개수만큼 크롤링을 시도합니다.

(과도한 트래픽으로 인한 계정 블럭을 방지하기 위해, delay는 3초 이상으로 설정하기 바랍니다.)

한 페이지의 크롤링이 끝나면, '다음글'을 자동으로 클릭하여 그 다음글도 크롤링하는 식입니다.

 

 

 

** 본 소스를 사용함에 따라 발생하는 책임은 사용자 본인에게 있음을 밝힙니다. **

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

import time
import pyperclip
import pymysql

from selenium.common.exceptions import NoSuchElementException

driver = webdriver.Chrome('C:/chromedriver.exe')
driver.implicitly_wait(3)
ENTER='/ue007'

# 에러시 부여할 딜레이(단위[초])
delay = 3

# db 정보(mysql을 쓰는 경우)
conn = pymysql.connect(host='IP주소', user='계정명', password='패스워드', db='DB명')
cont_explanation_len = 8191
cursor = conn.cursor(pymysql.cursors.DictCursor)

login=1

if login==1 :
    # 로그인
    driver.get('https://nid.naver.com/nidlogin.login')
    time.sleep(1)

    pyperclip.copy('네이버 ID') # 네이버 ID를 복붙하여 넣음
    driver.find_element_by_id('id').send_keys(Keys.CONTROL, 'v')
    time.sleep(1)

    pyperclip.copy('네이버 패스워드') # 네이버 PW를 복붙하여 넣음
    driver.find_element_by_id('pw').send_keys(Keys.CONTROL, 'v')
    time.sleep(1)

    driver.find_element_by_id('log.login').click()
    time.sleep(1)

# 카페 정보
url = 'https://cafe.naver.com/joonggonara' # 크롤링 할 카페 주소
joonggonara_id = 10050146 # 크롤링 할 카페id

pages = 1000 # 크롤링할 페이지 수
menu_id = '카테고리 ID'

# 카테고리 열기
driver.get(f'{url}?iframe_url=/ArticleList.nhn?search.clubid={joonggonara_id}%26search.menuid={menu_id}%26')
time.sleep(2)

driver.switch_to.frame("cafe_main") # iframe으로 전환

# 카테고리 첫번째 글 클릭
#driver.get('http://cafe.naver.com/joonggonara/')
driver.find_element_by_xpath('//*[@id="main-area"]/div[4]/table/tbody/tr[1]/td[1]/div[2]/div/a').click()
time.sleep(1)

for i in range (1, pages):
    # 크롤링 시작
    print('[',i+1,'/',pages,']')

    try:
        # 게시물 URL
        cont_url = driver.find_element_by_xpath('//*[@id="spiButton"]').get_attribute('data-url')

        # 게시물 번호
        cont_id = cont_url.split('/')[-1]

        # 게시물 작성일
        cont_date = driver.find_element_by_class_name('date').text
        print(cont_id,' ',cont_date)

        # 게시물 작성자
        cont_author = driver.find_element_by_class_name('nick_box').text
    except Exception as error: # 로딩 실패시 재시도
        print(error)
        time.sleep(delay)
        pass

    # 게시물 가격
    try:
        cont_price = driver.find_element_by_class_name('cost').text
        cont_price = cont_price.replace('원','')
        cont_price = cont_price.replace(',','')
        
    except: # 가격이 없는 글이면 크롤링 부적절(이 경우 DB에 저장하지 않고 넘어감)
        print(cont_id, 'is invalid article')
        cont_id=None
        pass
    
    if cont_id != None:
        # 게시물 제목
        cont_title = driver.find_element_by_class_name('title_text').text

        # 게시물 내용
        try: # 본문이 se-main-container 타입인 경우
            cont_explanation = driver.find_element_by_class_name('se-main-container').text
        except NoSuchElementException: # 본문이 ContentRenderer 타입인 경우
            cont_explanation = driver.find_element_by_class_name('ContentRenderer').text
        except Exception as error: # 로딩 실패시 재시도
            print(error)
            pass

		# 본문이 너무 긴 경우 잘라넣기
        cont_explanation = cont_explanation[0:8191]

		sql_insert = (f'INSERT INTO JG_{cate_name} (cont_id, cont_title, cont_url, cont_author, cont_date, cont_price, cont_explanation) VALUES (%s, %s, %s, %s, %s, %s, %s)')
		val = (cont_id, cont_title, cont_url, cont_author, cont_date, cont_price, cont_explanation)
		cursor.execute(sql_insert,val)
		conn.commit()

    try:
        # 다음 페이지로
        driver.find_element_by_link_text('다음글').click()
        time.sleep(delay)
    except Exception as arror: # 로딩 실패시 재시도
        print(error)
        pass

conn.close()

 

DB부분은 사용자의 설계에 맞게 변경한 뒤 사용하는 것을 추천합니다.

 

반응형

이 글을 공유합시다

facebook twitter googleplus kakaoTalk kakaostory naver band