네이버 기사 댓글 가져오기
들어가기 전 네이버의 robots.txt 에 대해 먼저 숙지하자.
사용 언어 및 모듈
- python 3.7
- request = request 요청을 보내 html 값을 가져오기
- bs4 (BeautifulSoup) = 받은 html 값을 요소별로 구분하기
- pandas = 구분한 값을 보기 편하게
- tqdm = 얼만큼 진행되었는지 보기 위해
- random = 요청보내는 시간을 random하게 조절
- time = 한번 요청을 보내고 잠시 대기 하기 위해
네이버 영화 평에 이어 네이버 댓글을 가져오고 싶어졌다.
이유는 문장생성 때문. 영화평으로 문장을 생성하면 잘 만들어진 결과가
input : "이" => output : "이 영화 너무 재밌어요!" 정도.
한마디로 성에 차지 않는다. (조금 더 자극적이고, 헛소리 였으면 좋겠다...)
그래서 조금 더 과격하고, 문장이 다채로우면서 날 것의 데이터가 필요해졌다.
아주 맘에 드는 데이터를 발견했다.
다른 사이트의 글을 가져오는 것 처럼 가져오기 위해 살펴보던 중 난관이 발견됐다.
더 보기를 눌러야 다음 댓글을 조회할 수 있다는 점...
구글 검색 결과 다들 selenium + BeautifulSoup 조합을 통해 매크로 형식으로 가져오고 있었다.
물론 좋은 방법이다. 나는 하기 싫을 뿐...드라이버ㅓ다운받기 귀찮아...
방법이 없을까?
더보기를 눌렀을때 나타는 현상을 보자
크롬을 기준으로 F12를 눌러 개발자 도구를 연 뒤, network 탭으로 가준다.
더보기 버튼을 눌러보면
무언가 알수 없는 것들이 network를 통해 넘어왔다. 조금 더 살펴보자.
넘겨받은 값 들중 댓글에 대한 정보가 들어있는 것을 reponse 값으로 확인할 수 있다.
그럼 코드 상으로 request에 해당하는 값을 알맞은 url에 보내준다면 response 값 역시 받아볼 수 있지 않을까?
header 탭을 통해 살펴보자
어떤 url로 보내야하는지, 보낸 방법은 무엇인지(get) 등의 정보가 있다.
코드로 한번 테스트 해보자
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]> 으로 잘왔고... 내용을 보자
엥? 이게 무슨일이지,,, 하고 열심히 구글링을 했다.
네이버 크롤링
아래는 자바로 네이버 댓글을 크롤링하는 방식이다. 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))
결과
저렇게 모인 2021-03-28/ 정치 일반 뉴스 댓글을 봐보자
comment_df = pd.DataFrame(comment_all_list , columns = ['comment'])
comment_df.head()
이렇게 댓글 수집을 해보았다.
아쉬운 점
- 날짜 하루치 하는데 18분이나 걸린다. 일주일치를 한다면 1시간 반~ 2시간 까지도 걸린다는 소리인데...
좀더 빠르게 할 방법이 없을까
- 정치 면 뿐아니라 경제, 사회 쪽도 댓글 가져오면 좀더 다채로운 걸 볼수 있을텐데... 그럼 걸리는시간*n 이 될것이다...
소감
- 이걸로 문장 생성 모델을 만들어 보고 싶다 얼른...
댓글환영
'text > Python' 카테고리의 다른 글
문장 생성 해보기 with. mini-GPT (feat. 네이버 기사 댓글) (0) | 2021.04.21 |
---|---|
문장 생성 해보기 (feat. 네이버 기사 댓글) (0) | 2021.04.02 |
DOM Based Content Extraction via Text Density 구현해보기 (3) | 2021.04.01 |
네이버 영화평 가져오기 (0) | 2021.03.29 |
Word2Vec parameter 정리 (0) | 2021.02.18 |
댓글