중고나라를 기준으로 잡아, 네이버 카페 크롤러를 만들어 보았습니다.
원하는 카테고리를 정한 뒤 게시물 제목, 번호, 작성자, URL, 가격, 내용까지 크롤링합니다.
큰 흐름은 다음과 같습니다.
왜인지 send_keys를 보내는 일반적인 로그인 시도는 실패하므로, pyperclip을 이용하여 ID와 PW를 복붙함.
원하는 카페의 원하는 메뉴를 크롤링하기 위해서는 카페 ID와 메뉴 ID를 알아야합니다.
크롬을 사용중이라면, 크롤링을 원하는 카페에 들어가 F12를 누르고 빨간버튼(단축키 Ctrl+Shift+C)를 누른 뒤
원하는 게시판을 클릭하면 아래와 같이 정보를 얻을 수 있습니다.
저는 clubid를 joonggonara_id, menuid를 menu_id로 설정했습니다.
네이버 카페 내부는 iframe으로 꾸며져있기 때문에 크롬 드라이버의 모드를 iframe으로 전환해야 합니다.
driver.switch_to_frame("cafe_main")을 이용하여 cafe_main의 iframe으로 전환합니다.
가장 첫번째 글(최신글)부터 크롤링을 시작하여, 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부분은 사용자의 설계에 맞게 변경한 뒤 사용하는 것을 추천합니다.
Puppeteer의 networkidle0과 networkidle2 차이점 (0) | 2023.01.17 |
---|---|
퍼펫티어(Puppeteer)와 셀레니움(Selenium)의 장단점 (0) | 2023.01.07 |
웹 크롤링과 웹 스크래핑의 차이 (0) | 2023.01.03 |
[Python] 파이썬(Python)과 셀레니움(Selenium)을 이용한 크롤링 (0) | 2020.08.30 |
[Python][Selenium] 디씨 클리너 만들기 (11) | 2020.08.30 |