본문 바로가기

DB엔지니어가 공부하는 python

[파이썬]네이버 카페 게시판 크롤링 웹 스크래핑 2021년 버전

[파이썬]네이버 카페 게시판 제목, 본문, 작성자, 글번호 웹 스크래핑 2021년 버전

[파이썬]네이버 카페 게시판 제목, 본문, 작성자, 글번호 웹 스크래핑 2021년 버전

안녕하세요.
한 2년여전에 네이버 카페 스크래핑을 할 일이 있어서 했었고, 이번에 다시 하는데, 안되더라구요.
역시.. 네이버..ㅎㅎ
많이 바꿨더군요.

그래서 아예 새롭게 개발을 했습니다.
다른 여러분들께도 조언도 구하고, 구글링도 해가면서 여차저차 완성을 하여 이렇게 공개를 합니다.
바로 시작 해보겠습니다.

database 테이블 생성

우선, 저는 스크래핑한 데이터를 DB에 저장 할겁니다. mysql을 기반으로 사용했습니다.
저장 하기 위해선 테이블이 필요하겠죠.
테이블 생성 DDL은 다음과 같습니다.
mysql이 있으시면 접속하셔서 생성 하시면 됩니다.

create table clubrav4.jau_2021
(
	seq varchar(100) null,
	title varchar(2000) null,
	contents text null,
	writer varchar(200) null,
	reg_date varchar(100) null,
	chk varchar(1) null
);

mysql에 자신이 없으신 분은 아래 링크로 가셔서 설치 하는 방법을 잘 참고 하시길 바랍니다.

2020.08.13 - [Database/mariaDB administrator] - 윈도우10 MySQL Community 8.0 버전 다운로드 및 설치 방법 가이드

 

윈도우10 MySQL Community 8.0 버전 다운로드 및 설치 방법 가이드

윈도 10 MySQL Community 8.0 버전 다운로드 및 설치 방법 가이드 안녕하세요. 요즘 SQLD, SQLP 취득을 목표로 하시거나, 아니면 업무적으로 SQL 공부를 하기 위해서 MySQL을 본인의 윈도 PC에 설치를 하시려

stricky.tistory.com

import library

import time
import pandas as pd
import os
import pymysql
import re
from selenium import webdriver # pip install selenium
from bs4 import BeautifulSoup as bs # pip install bs4

뭐 여차저차 이렇게 많은 library가 필요 하더라구요.
음.. 전 전문 파이썬 개발자는 아니라.. 흔히들 이야기 하는 파이써니컬한 코드는 제 코드에서 보기 어려울것 입니다.
그저 동작이 가능한 코드일 뿐이에요.

간혹 좋은 조언들, 그러닌깐 이건 이런식으로~ 저건 저런식으로 작성 방법을 댓글로 남겨주시는 분들 계신데 정말 감사합니다. 그렇게 남겨주시고, "뭔 파이썬 코드를 이따위로 짰냐" 라고 생각이 드실법 하지만, 그런 말은 속으로~ 해주시면 정말 감사하겠습니다.

아무튼, 저런 라이브러리들을 불러다 쓰겠습니다.

 

 

 

database 연동

다음은 database 연동 부분 입니다.
저는 웹 스크래핑 결과를 DB에 저장을 하는 방식을 택했습니다. 그러니 DB 연동을 해줘야 겠죠.

# db connect 
conn = pymysql.connect(host='111.222.333.444', user = 'DB계정', password='DB비밀번호', db = 'keyword',charset = 'utf8') 
curs = conn.cursor(pymysql.cursors.DictCursor)

위와 같이 DB 연결을 설정 해두겠습니다.

크롬 드라이버 및 네이버 로그인

# chrome 드라이버
driver = webdriver.Chrome("c:/chromedriver.exe")
driver.get('https://nid.naver.com/nidlogin.login?svctype=262144&url=http://naver.com/')
# 카페로 이동
driver.get('https://nid.naver.com/nidlogin.login?mode=form&url=https%3A%2F%2Fwww.naver.com')
driver.find_element_by_xpath('//*[@id="id"]').send_keys("네이버계정")
time.sleep(35)

# 카페로 이동
driver.get('https://cafe.naver.com/xxxxxx')   #(스크래핑 할 카페주소)

크롬 드라이버를 미리 다운받으시고, 설정 하시면 됩니다.
크롬 드라이버는 아래 링크로 가셔서 다운로드 하세요.

https://chromedriver.chromium.org/downloads

 

ChromeDriver - WebDriver for Chrome - Downloads

Current Releases If you are using Chrome version 92, please download ChromeDriver 92.0.4515.43 If you are using Chrome version 91, please download ChromeDriver 91.0.4472.101 If you are using Chrome version 90, please download ChromeDriver 90.0.4430.24 If y

chromedriver.chromium.org

아마 selenium 버전이랑 맞아야 할겁니다.
자신의 PC타입에 맞게 다운로드하세요.

그리고, 네이버 계정을 미리 입력해두고 진행하시면 됩니다. 음, 저는 아무리 해도 비밀번호까지 입력을 해도 자동으로 로그인이 안되서 그냥 뺐습니다.

마지막에 sleep(35)는 로그인을 수동으로 하는데 걸리는 시간을 미리 잡아둔것 입니다. 손빠르신분들은 좀 줄여도 됩니다. 그럼 그 시간이 흐른 후 스크래핑 할 카페로 이동을 짜잔 하게 됩니다.

자유게시판 스크래핑

이젠 본격적으로 네이버 카페 스크래핑을 시작 합니다.
여러가지 방법이 있겠지만, 간략하게 제가 한 방식을 소개 하겠습니다.

우선, 특정 게시판별로 스크래핑을 진행하는데, 웹에서 네이버 카페의 특정 게시판으로 들어가면 우측 상단에 목록을 50개씩 불러오는 옵션이 있습니다.

리스트 50개씩 보기

해당 옵션의 목록 링크를 사용 할 것 입니다.
그래고 50개를 불러온 페이지에서 글번호, 제목, 작성자, 작성일을 스크래핑하여 DB에 저장을 쭉 합니다.

 

 

 

목록

그리고 list 자료형에 해당 글번호와 제목, 작성자, 작성일을 쭉 저장을 합니다.
insert문을 만들어 써야 하닌깐요.

그리고 다시 해당 자료형을 참조하여 insert 문을 만들어 DB에 저장을 하게 됩니다.
이때, 아래 소스코드를 보시면 아시겠지만, 첫페이지 게시판 목록에는 공지사항이 있습니다. 해당 공지사항의 글번호는 목록에 나타나지 않기때문에, for문에서 리스트를 불러올때 변수로 제어를 해주게 됩니다.

그렇게 목록에서 필요한 정보를 다 스크래핑하고, 이젠 게시글 하나 하나 들어가서 본문에서 텍스트만 추출을 하게 됩니다. 그리고 해당 글 번호를 이용하여 update 해주게 되죠.

그리고 게시판 링크나, 게시글 링크에 보면 menuid가 있습니다. 이 menuid는 웹 스크래핑 하시려는 clubid와 함께 원하시는 카페와 게시판 번호를 알아서 넣어주시면 됩니다. 알아내는 방법은 아래 그림과 같은 위치에서 해당 버튼을 클릭하여 메모장 같은데 붙여 넣으시면 확인이 가능 합니다.

 

 

 

clubid 와 menuid 확인하기

그럼 코드를 공개 하겠습니다.
다음과 같습니다.

# 자유게시판 리스트 50개 짜리로 이동
for i in range(60):  #스크래핑 할 페이지수
    
    #강제#  중간에 끊겼을때 해당 페이수보다 -1 한 값을 입력하고 주석 제거
    #i = i + 15
    #강제#

    pg = str(i+1)
    addr = 'https://cafe.naver.com/ArticleList.nhn?search.clubid=네이버카페ID&search.menuid=44&userDisplay=50&search.boardtype=L&search.specialmenutype=&search.totalCount=501&search.page='+pg  
            
    
    driver.get(addr)
    driver.switch_to.frame('cafe_main')
    
    html = driver.page_source

    soup = bs(html, 'html.parser')
    
    a_num_list = soup.findAll("div",{"class":"inner_number"})
    a_title_list = soup.findAll("a",{"class":"article"})
    a_writer_list = soup.findAll("a",{"class":"m-tcol-c"})
    a_regdate_list = soup.findAll("td",{"class":"td_date"})

    total_list = []
    article_link_list = []  #글 링크

    if i == 0:
          for a, b, c, d in zip(a_num_list, a_title_list[7:], a_writer_list[7:], a_regdate_list[7:]):  # 나눔해요게시판, 목격담, 5세대 시공 장착 정비 수리
               list = []
               list.append(a.text)
               list.append(b.text.strip())
               list.append(c.text)
               list.append(d.text)
               total_list.append(list)
               article_link_list.append("https://cafe.naver.com/ArticleRead.nhn?clubid=19553263&page=" + pg + "&userDisplay=50&menuid=44&boardtype=L&articleid=" + a.text + "&referrerAllArticles=false") 
    else:
         for a, b, c, d in zip(a_num_list, a_title_list, a_writer_list, a_regdate_list):
               list = []
               list.append(a.text)
               list.append(b.text.strip())
               list.append(c.text)
               list.append(d.text)
               total_list.append(list)
               article_link_list.append("https://cafe.naver.com/ArticleRead.nhn?clubid=네이버카페ID&page=" + pg + "&userDisplay=50&menuid=44&boardtype=L&articleid=" + a.text + "&referrerAllArticles=false") 
    
    # DB 저장
    for x in total_list:

        print("insert into cl.jau_2021(seq, title, writer, reg_date, chk) values ('" + x[0]+ "','" + re.sub('[^A-Za-z0-9가-힣\s,.,?,!]', "", x[1]) + "','" + re.sub('[^A-Za-z0-9가-힣\s,.,?,!]', "", x[2]) + "','" + x[3] + "', '4')")
        sql = "insert into cl.jau_2021(seq, title, writer, reg_date, chk) values ('" + x[0]+ "','" + re.sub('[^A-Za-z0-9가-힣\s,.,?,!]', "", x[1]) + "','" + re.sub('[^A-Za-z0-9가-힣\s,.,?,!]', "", x[2]) + "','" + x[3] + "', '4')" 

        curs.execute(sql)
        conn.commit()

    # 글 스크랩핑
    for x in total_list:

         adrs = "https://cafe.naver.com/ArticleRead.nhn?clubid=19553263&page=" + str(pg) + "&userDisplay=50&menuid44&boardtype=L&articleid=" + x[0] +"&referrerAllArticles=false"
         print(adrs)

         driver.get(adrs)

         time.sleep(2)
         driver.switch_to.frame('cafe_main')

         html = driver.page_source

         soup = bs(html, 'html.parser')

         list = soup.find_all("div", {"class":"article_viewer"})

         for xx in list:
             
             cont = ''
             cont += re.sub('[^A-Za-z0-9가-힣\s,.,?,!]', "", xx.text.strip()).replace('\n','')

         #driver.close()

         print(cont)

         sql = "update cl.jau_2021 set contents = '" + str(cont).replace("'", "") + "' where seq = '" + x[0] + "'"
         print(sql)
         curs.execute(sql)
         conn.commit()

    # 리스트 변수 초기화
    a_num_list = []
    a_title_list = []
    a_writer_list = []
    a_regdate_list = []

    print("#############################################    " + pg + "  페이지 완료 #############################################")

driver.close()

 

 

 

자, 이렇게 네이버 카페 스크래핑을 할 수 있습니다. 
필요 하신 분들은 해보시고, 잘 안되면 아래 댓글 남겨 주세요!
아는 내용 한해서는 답글을 달아 드리도록 하겠습니다.
생각보다 어렵지 않으니 필요하신 분들은 도전하세요!!

감사합니다.

by.sTricky

  • 2021.07.07 16:19

    비밀댓글입니다

    • Favicon of https://stricky.tistory.com nice sTricky 2021.07.07 16:44 신고

      아하..
      음.. 댓글 불러오는건..
      저기.. 본문 내용을 보시면.. 개발자도구에서 소스를 보시다 보면..
      댓글 부분에 div명을 가지고 본문에서처럼 게시글 본문 가지고 오는 부분과 똑같이 처리를 하시면 될텐데..
      저도 그 부분을 하려다가 끝까지 못한거거든요...ㅠㅠ 제가 어떻게 해드리긴 어렵지 않을까 생각합니다.. 죄송합니다.

  • 2021.08.11 16:26

    비밀댓글입니다

  • 크롤링초보 2021.09.02 23:49

    체크박스를 클릭해서 체크표시하도록 하려고 하는데, 'element not interactable' 오류가 납니다.
    element는 아래와 같고, 첫번째 체크박스만 체크하려고
    find_element_by_id("id_filter_simsearch1.fha").click() 또는
    find_element_by_xpath("//*[@id='id_filter_simsearch1.fha']").click()
    를 시도해봤으나 모두 안되네요. 혹시 방법이 있을까요?
    -----------------------------------------------------------------------------------------------------------------------------------
    <ul class="items">
    <li>
    <input aria-checked="false" type="checkbox" name="filter" id="id_filter_simsearch1.fha" value="simsearch1.fha" data-is-custom="false">
    <label for="id_filter_simsearch1.fha">
    Abstract
    </label>
    </li>
    <li>
    <input aria-checked="false" type="checkbox" name="filter" id="id_filter_simsearch2.ffrft" value="simsearch2.ffrft" data-is-custom="false">
    <label for="id_filter_simsearch2.ffrft">
    Free full text
    </label>
    </li>
    <li>
    <input aria-checked="false" type="checkbox" name="filter" id="id_filter_simsearch3.fft" value="simsearch3.fft" data-is-custom="false">
    <label for="id_filter_simsearch3.fft">
    Full text
    </label>
    </li>
    </ul>
    -------------------------------------------------------------------------------------------------------------------------------------