본문 바로가기
text/Python

네이버 기사 댓글 가져오기

by hoonzii 2021. 3. 29.
반응형

네이버 기사 댓글 가져오기

들어가기 전 네이버의 robots.txt 에 대해 먼저 숙지하자.

네이버의 robots.txt

 

 

사용 언어 및 모듈
    - python 3.7
    - request = request 요청을 보내 html 값을 가져오기
    - bs4 (BeautifulSoup) = 받은 html 값을 요소별로 구분하기
    - pandas = 구분한 값을 보기 편하게
    - tqdm = 얼만큼 진행되었는지 보기 위해
    - random = 요청보내는 시간을 random하게 조절
    - time = 한번 요청을 보내고 잠시 대기 하기 위해

 

네이버 영화 평에 이어 네이버 댓글을 가져오고 싶어졌다.

이유는 문장생성 때문. 영화평으로 문장을 생성하면 잘 만들어진 결과가

input : "이" => output : "이 영화 너무 재밌어요!" 정도.

한마디로 성에 차지 않는다. (조금 더 자극적이고, 헛소리 였으면 좋겠다...)

 

그래서 조금 더 과격하고, 문장이 다채로우면서 날 것의 데이터가 필요해졌다.

떨어질 때마다 '풀매수'…삼성전자만 2조 담은 개미들 기사 댓글 예시

 

아주 맘에 드는 데이터를 발견했다.

다른 사이트의 글을 가져오는 것 처럼 가져오기 위해 살펴보던 중 난관이 발견됐다.

더보기 버튼의 존재

더 보기를 눌러야 다음 댓글을 조회할 수 있다는 점...

 

구글 검색 결과 다들 selenium + BeautifulSoup 조합을 통해 매크로 형식으로 가져오고 있었다.

물론 좋은 방법이다. 나는 하기 싫을 뿐...드라이버ㅓ다운받기 귀찮아...

방법이 없을까?

 

더보기를 눌렀을때 나타는 현상을 보자

크롬을 기준으로 F12를 눌러 개발자 도구를 연 뒤, network 탭으로 가준다.

네트워크 탭

 

더보기 버튼을 눌러보면

더보기를 누른 뒤 나타난 새로운 값들

무언가 알수 없는 것들이 network를 통해 넘어왔다. 조금 더 살펴보자.

 

넘어온 값에 댓글이 있다

넘겨받은 값 들중 댓글에 대한 정보가 들어있는 것을 reponse 값으로 확인할 수 있다.

 

그럼 코드 상으로 request에 해당하는 값을 알맞은 url에 보내준다면 response 값 역시 받아볼 수 있지 않을까?

header 탭을 통해 살펴보자

 

headers 부분

어떤 url로 보내야하는지, 보낸 방법은 무엇인지(get) 등의 정보가 있다.

 

parameter 부분

코드로 한번 테스트 해보자

import requests

url = "https://apis.naver.com/commentBox/cbox/web_naver_list_jsonp.json?ticket=news&templateId=default_society&pool=cbox5&lang=ko&country=KR&objectId=news119%2C0002479801&categoryId=&pageSize=200&indexSize=10&groupId=&listType=OBJECT&pageType=all&page=2&initialize=true&userType=&useAltSort=true&replyPageSize=20&sort=old&includeAllStatus=true"

header = {
    'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36',
    'accept' : "*/*",
    'accept-encoding' : 'gzip, deflate, br',
    'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
}

html = requests.get(url, headers = header)

<Response [200]> 으로 잘왔고... 내용을 보자

 

엥? 이게 무슨일이지,,, 하고 열심히 구글링을 했다.

 

ref) m.blog.naver.com/PostView.nhn?blogId=codingspecialist&logNo=221336552535&categoryNo=100&proxyReferer=https:%2F%2Fwww.google.com%2F

 

네이버 크롤링

아래는 자바로 네이버 댓글을 크롤링하는 방식이다. R이나 파이썬으로 하면 아래를 참고하자. https://beo...

blog.naver.com

해당 게시물에서 답을 찾았다.  이전 페이지에 대한 참조가 없어 접근이 잘못된다는 점이 였다.

header에 referer를 추가해주자.

referer 값은 역시 header 탭에 나와있다.

 

url = "https://apis.naver.com/commentBox/cbox/web_naver_list_jsonp.json?ticket=news&templateId=default_society&pool=cbox5&lang=ko&country=KR&objectId=news119%2C0002479801&categoryId=&pageSize=200&indexSize=10&groupId=&listType=OBJECT&pageType=all&page=2&initialize=true&userType=&useAltSort=true&replyPageSize=20&sort=old&includeAllStatus=true"

header = {
    'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36',
    'accept' : "*/*",
    'accept-encoding' : 'gzip, deflate, br',
    'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
    'referer' : 'https://news.naver.com/main/read.nhn?m_view=1&includeAllCount=true&mode=LSD&mid=shm&sid1=102&oid=119&aid=0002479801'
}

html = requests.get(url, headers = header)

결과를 살펴보자.

넘어온 댓글 데이터

request url을 통해 결과를 받았는데 그럼 url에 붙은 parameter를 좀더 살펴보자

from urllib.parse import urlparse, parse_qs
url_param = parse_qs(urlparse(url).query)
url_param

앞서 살펴본 parameter 값들이 들어있는것을 확인 할수 있다. 그렇다면 해당 값들을 변경시켜 돌아오는 response 값을 바꿀수 있을 것이다. 

(하지만 매우 귀찮기 때문에... 몇가지만 바꿔서 날려봤다.)

바꾼 값으로는

pageSize =10 -> 200, (돌아오는 댓글 개수가 변함, 최대 100개... 100개 이상의 값으로 조정)

pageType = more -> all, (변한건 없다)

templateId = default_society -> X (없더라도 동작한다)

sort = favorite -> old (공감순 -> 오래된순으로 변함)

 

두번째 난관이 있다. 가져온 데이터를 보자

넘어온 댓글 데이터

_callback()으로 쌓여서 돌아왔다. 원하는 부분만 뽑으려면 key-value 접근이 편한 json값으로 변환해야 하는데 저부분이 골치아프다. 

 

저부분만 날리고 json형태로 바꿔보자.

test_text = html.text
test_text = test_text.replace("_callback(","")[:-2] #앞 _callback( 과 뒤 ); 부분을 자르기 위해
comment_text = json.loads(test_text)
comment_text

의도 대로 추출한 것을 확인할 수 있다. 이제 댓글만 가져와보자.

page_num = 1
url = "https://apis.naver.com/commentBox/cbox/web_naver_list_jsonp.json?ticket=news&pool=cbox5&lang=ko&country=KR&objectId=news119%2C0002479801&categoryId=&pageSize=100&indexSize=10&groupId=&listType=OBJECT&pageType=more&page={}"
header = {
    'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36',
    'accept' : "*/*",
    'accept-encoding' : 'gzip, deflate, br',
    'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
    'referer' : 'https://news.naver.com/main/read.nhn?m_view=1&includeAllCount=true&mode=LSD&mid=shm&sid1=102&oid=119&aid=0002479801'
}
comment_all_list = []
while(True):
    req_url = url.format(page_num)
    html = requests.get(req_url, headers = header)
    
    test_text = html.text
    test_text = test_text.replace("_callback(","")
    comment_text = json.loads(test_text[:-2])
    
    temp_comment_list = [comment_info['contents'] for comment_info in comment_text['result']['commentList']]
    comment_all_list.extend(temp_comment_list)
    
    if(len(temp_comment_list) < 100):
        break
    page_num+=1
comment_all_list

 

 

 

코드 실행 결과
해당 기사 댓글

작성자가 삭제한 댓글 제외한 나머지 댓글에 대해 잘 가져온 것을 확인할 수 있었다.

 

그렇다면 이것 코드를 반복시켜 댓글을 가져오게 만들어야 하는데....

 

원하는 로직을 정리해보자

1. 기사 url을 수집

2. 수집된 기사 url 에 들어가 댓글만 가져오기

 

1번 로직을 수행해보자.

네이버 뉴스 정치 일반

정치 뉴스가 댓글이 제일 쎄다. 여기서 쎄다의 기준은 순전히 본인의 판단이고, 무엇이 쎈지에 대한 기준은 노코멘트 하겠다. (무튼 졸라 쎄다. 투명드래곤급)

url을 살펴보자

news.naver.com/main/list.nhn?mode=LS2D&mid=shm&sid2=269&sid1=100&date=20210329&page=2

date 부분과 page 부분을 변경시키면 날짜와 page를 변하게 해 정치 뉴스를 가져올수 있을 것이다.

해당 부분을 변경시키며 기사 목록을 가져오자

date = "20210328"
article_page_num = 1
# date 부분과 page 부분을 뚫어놓고 바꾸기
category_url = 'https://news.naver.com/main/list.nhn?mode=LS2D&mid=shm&sid2=269&sid1=100&date={}&page={}'
# header 가 없으면 값을 보내주지 않는다...
header = {
    'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36',
}
req_url = category_url.format(date, article_page_num)
html = requests.get(req_url, headers = header)
soup = BeautifulSoup(html.content, 'html.parser')
# url 부분만 추출해 url_list로 만드는 부분인데 나보다 더 잘 설명하시는 분들이 많기 때문에 생략!
temp_article_list = list(set([url_info.find('a').get('href') for url_info in soup.find_all('dt')]))

한 page를 잘 가져오는지 파악한 뒤 반복문으로 동작시키자.

 

date = "20210328" # 날짜는 임의로 정했다. 날짜 값도 반복문으로 처리할 경우, 해당 기간동안 발생한 댓글 전부를 가져올수 있을듯!
article_page_num = 1
category_url = 'https://news.naver.com/main/list.nhn?mode=LS2D&mid=shm&sid2=269&sid1=100&date={}&page={}'
header = {
    'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36',
}

total_article_list = []
prev_article_list = []
while(True):
    req_url = category_url.format(date, article_page_num)
    html = requests.get(req_url, headers = header)
    soup = BeautifulSoup(html.content, 'html.parser')
    temp_article_list = list(set([url_info.find('a').get('href') for url_info in soup.find_all('dt')]))
    
    #page 마지막에 도달할 경우 가져온 값이 이전값과 동일, 해당 경우를 체크하기 위해
    if(temp_article_list == prev_article_list):
        break
    else:
        prev_article_list = list(temp_article_list)
        total_article_list.extend(temp_article_list)
        article_page_num+=1
    print(len(total_article_list)) # 가져오는 기사의 수가 몇개인지

내 경우 1248개의 기사를 가져왔다. 정확히는 1248개의 기사 url.

 

가져온 url로 기사의 댓글을 가져와 보자.

comment_all_list = []
for article_url in tqdm(total_article_list):
    url_param = parse_qs(urlparse(article_url).query)
    oid = url_param['oid'][0] # parameter objectid newsXXX 의 XXX부분을 채우기 위함
    aid = url_param['aid'][0] # parameter objectid newsXXX,oooooooo의 부분을 채우기 위함
    # news+oid,+aid 의 형태로 넣어야됌
    # 잘모르겠으면 밑에 코드 보기~
    
    page_num = 1
    url = "https://apis.naver.com/commentBox/cbox/web_naver_list_jsonp.json?ticket=news&pool=cbox5&lang=ko&country=KR&objectId=news{}%2C{}&categoryId=&pageSize=100&indexSize=10&groupId=&listType=OBJECT&pageType=more&page={}"
    header = {
        'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36',
        'accept' : "*/*",
        'accept-encoding' : 'gzip, deflate, br',
        'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
        'referer' : article_url # referer 부분에 article_url 을 넣어서 가져오기!
    }
    
    comment_list = []
    while(True):
        req_url = url.format(oid, aid, page_num)
        html = requests.get(req_url, headers = header)

        test_text = html.text
        test_text = test_text.replace("_callback(","")[:-2]
        comment_text = json.loads(test_text)

        temp_comment_list = [comment_info['contents'] for comment_info in comment_text['result']['commentList']]
        comment_list.extend(temp_comment_list)

        if(len(temp_comment_list) < 100):
            break
        page_num+=1
    comment_all_list.extend(comment_list)
    time.sleep(random.uniform(0.5, 1))

tqdm 을 사용해 진행상황을 모니터링 가능. 나의 경우 18:35초가 걸렸음

결과

저렇게 모인 2021-03-28/ 정치 일반 뉴스 댓글을 봐보자

comment_df = pd.DataFrame(comment_all_list , columns = ['comment'])
comment_df.head()

놀랍지 않은 쫘빨들의 행태와 그걸 지켜보는 나
수집한 댓글 총 개수

 

이렇게 댓글 수집을 해보았다. 

 

아쉬운 점

- 날짜 하루치 하는데 18분이나 걸린다. 일주일치를 한다면 1시간 반~ 2시간 까지도 걸린다는 소리인데...

좀더 빠르게 할 방법이 없을까

 

- 정치 면 뿐아니라 경제, 사회 쪽도 댓글 가져오면 좀더 다채로운 걸 볼수 있을텐데... 그럼 걸리는시간*n 이 될것이다...

 

소감

- 이걸로 문장 생성 모델을 만들어 보고 싶다 얼른...

 

댓글환영

반응형

댓글