[python] 원하는 검색어로 네이버 뉴스 기사 제목 및 내용만 크롤링하기
안녕하세요!
크롤링 포스팅을 오랜만에 진행하네요~
이번에는 네이버 뉴스 검색 결과중 네이버 뉴스에 기사가 있는 링크들만 가져와 크롤링을 진행해 보도록 하겠습니다.
지난 크롤러에서 아쉬웠던점은
언론마다 홈페이지 html 구조가 달라 내용을 제대로 가져오기 어려웠는데요
이번에는 이러한 문제점을 극복하고자
검색 결과로 나오는 기사 중 '네이버 뉴스(news.naver.com)'에 해당 기사가 존재하는 url만 가져와서
내용도 깔끔하게 크롤링할 수 있도록 만들어 보았습니다.
그럼 시작하겠습니다!
1. 필요한 라이브러리들 불러오기
#크롤링시 필요한 라이브러리 불러오기
from bs4 import BeautifulSoup
import requests
import re
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
#웹드라이버 설정
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
2. 크롤링시 필요한 함수 만들기
# 페이지 url 형식에 맞게 바꾸어 주는 함수 만들기
#입력된 수를 1, 11, 21, 31 ...만들어 주는 함수
def makePgNum(num):
if num == 1:
return num
elif num == 0:
return num+1
else:
return num+9*(num-1)
# 크롤링할 url 생성하는 함수 만들기(검색어, 크롤링 시작 페이지, 크롤링 종료 페이지)
def makeUrl(search,start_pg,end_pg):
if start_pg == end_pg:
start_page = makePgNum(start_pg)
url = "https://search.naver.com/search.naver?where=news&sm=tab_pge&query=" + search + "&start=" + str(start_page)
print("생성url: ",url)
return url
else:
urls= []
for i in range(start_pg,end_pg+1):
page = makePgNum(i)
url = "https://search.naver.com/search.naver?where=news&sm=tab_pge&query=" + search + "&start=" + str(page)
urls.append(url)
print("생성url: ",urls)
return urls
3. 검색어를 받아서 네이버 검색 결과 페이지 url생성하기
##########뉴스크롤링 시작###################
#검색어 입력
search = input("검색할 키워드를 입력해주세요:")
#검색 시작할 페이지 입력
page = int(input("\n크롤링할 시작 페이지를 입력해주세요. ex)1(숫자만입력):")) # ex)1 =1페이지,2=2페이지...
print("\n크롤링할 시작 페이지: ",page,"페이지")
#검색 종료할 페이지 입력
page2 = int(input("\n크롤링할 종료 페이지를 입력해주세요. ex)1(숫자만입력):")) # ex)1 =1페이지,2=2페이지...
print("\n크롤링할 종료 페이지: ",page2,"페이지")
# naver url 생성
search_urls = makeUrl(search,page,page2)
여기까지는 그전 크롤러와 같습니다.
이제 본격적으로 네이버 뉴스의 기사들만 가져와 보겠습니다.
4. Selenium 사용하여 네이버 기사 url 추출하기
그 전에는 네이버 검색 결과 페이지 html에 필요한 url, 기사제목 등이 다 나와있어 바로 가져올 수 있었는데요,
네이버 뉴스만 가져오기 위해서는 다른 방법으로 접근해야 합니다.
예를 들어, '코로나' 관련 하여 검색을 진행했을 때, 하이라이트가 있는 '네이버 뉴스'를 클릭해야
아래처럼 네이버 뉴스 페이지의 해당 기사로 접속할 수 있습니다.
그래서 해당 url을 가져오기 위해
개발자 도구를 사용해 (F12)
검색 결과 페이지에 있는 네이버 뉴스 링크를 바로 가져와 파싱하려고
위의 사진 url인
('/p/crd/rd?m=1&px=405&py=267&sx=405&sy=167&p=hmkWFlprvTVssTYb7QKssssstb0-460233&q=%EC%BD%94%EB%A1%9C%EB%82%98&ie=utf8&rev=1&ssc=tab.news.all&f=news&w=news&s=rswJojqRF8DwzkNAwDOPrg%3D%3D&time=1645669880103&abt=%5B%7B%22eid%22%3A%2210%22%2C%22vid%22%3A%2218%22%7D%5D&a=nws*h.nav&r=1&i=88000127_000000000000000011024241&u=https%3A%2F%2Fnews.naver.com%2Fmain%2Fread.naver%3Fmode%3DLSD%26mid%3Dsec%26sid1%3D102%26oid%3D003%26aid%3D0011024241')
위 링크를 가져와 바로 주소창에 넣어보았으나....
제대로 열 수 없었습니다.
그 이유는
onclick, 즉 클릭을 하여야 링크가 올바른 링크로 바뀌어서 불러오게 되는 구조이기 때문입니다.
그래서 셀레니움을 사용하여 클릭 동작을 주고 해당 기사의 올바른 url 을 가져오는 방법을 선택하였습니다.
그래서 구글 웹드라이버도 크롬 버전에 맞게 설치하여 준 후,
웹드라이버로 해당 요소들을 가져와 클릭동작을 넣고 창 전환 후 현재 url을 가져왔습니다.
그리고 네이버 기사들만 naver_url 리스트에 추가해 주었습니다.
## selenium으로 navernews만 뽑아오기##
# 버전에 상관 없이 os에 설치된 크롬 브라우저 사용
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.implicitly_wait(3)
# selenium으로 검색 페이지 불러오기 #
naver_urls=[]
for i in search_urls:
driver.get(i)
time.sleep(1) #대기시간 변경 가능
# 네이버 기사 눌러서 제목 및 본문 가져오기#
# 네이버 기사가 있는 기사 css selector 모아오기
a = driver.find_elements(By.CSS_SELECTOR,'a.info')
# 위에서 생성한 css selector list 하나씩 클릭하여 본문 url얻기
for i in a:
i.click()
# 현재탭에 접근
driver.switch_to.window(driver.window_handles[1])
time.sleep(3) #대기시간 변경 가능
# 네이버 뉴스 url만 가져오기
url = driver.current_url
print(url)
if "news.naver.com" in url:
naver_urls.append(url)
else:
pass
# 현재 탭 닫기
driver.close()
# 다시처음 탭으로 돌아가기(매우 중요!!!)
driver.switch_to_window(driver.window_handles[0])
print(naver_urls)
selenium을 사용할 때는 동작 후 로딩 시간을 고려해 time.sleep()을 넣어 주는 것이 좋습니다.
(때문에 이 부분에서 크롤링 할 내용이 많으면 많을수록 시간이 걸립니다.)
여차저차 해서 드디어 우리에게 필요한 네이버 뉴스 기사들을 얻을 수 있었습니다!
5. 기사 제목 및 본문 크롤링하기
그 다음에는 네이버뉴스의 html구조만 파악하여 제목과 본문을 가져오면 됩니다.
네이버뉴스의 경우, 기사 제목은
div#ct > div.media_end_head.go_trans > div.media_end_head_title > h2
여기에 들어가 있고,
기사 내용은
div#dic_area
이곳에 들어가 있습니다.
위의 경로를 확인하려면 웹페이지에서 F12를 누른 후, 해당 요소를 클릭 후 우클릭 > Copy > Copy selector로 보다 편하게 확인할 수 있습니다.
그리고 내용을 가져올때, html태그도 다 가져오기 때문에
html 태그를 정규식 패턴을 이용하여 제거 후 각각 저장해 주도록 하겠습니다.
###naver 기사 본문 및 제목 가져오기###
# ConnectionError방지
headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4758.102" }
titles = []
contents=[]
for i in naver_urls:
original_html = requests.get(i,headers=headers)
html = BeautifulSoup(original_html.text, "html.parser")
# 검색결과확인시
#print(html)
#뉴스 제목 가져오기
title = html.select("div.content > div.article_header > div.article_info > h3")
# list합치기
title = ''.join(str(title))
# html태그제거
pattern1 = '<[^>]*>'
title = re.sub(pattern=pattern1,repl='',string=title)
titles.append(title)
#뉴스 본문 가져오기
content = html.select("div.content > div#articleBody > div#articleBodyContents")
# 기사 텍스트만 가져오기
# list합치기
content = ''.join(str(content))
#html태그제거 및 텍스트 다듬기
content = re.sub(pattern=pattern1,repl='',string=content)
pattern2 = """[\n\n\n\n\n// flash 오류를 우회하기 위한 함수 추가\nfunction _flash_removeCallback() {}"""
content = content.replace(pattern2,'')
contents.append(content)
print(titles)
print(contents)
확실히 전과 비교하여 깔끔하게 출력이 됩니다.
나머지 필요한 전처리들은 분석시 진행하면 됩니다.
6. DataFrame으로 만들기
그 후 결과물을 데이터프레임으로 정리하여 보기 좋게 해 줍니다.
#데이터프레임으로 정리(titles,url,contents)
import pandas as pd
news_df = pd.DataFrame({'title':titles,'link':naver_urls,'content':contents})
news_df.to_csv('NaverNews_%s.csv'%search,index=False,encoding='utf-8-sig')
보통 데이터 프레임을 csv 파일로 저장할 때,
encoding 설정을 따로 해 주지 않으면 기본으로 utf-8로 저장이 되는데
이 파일을 그냥 열면 한글이 다 깨져 보입니다.
그래서 이를 방지하기 위해 encoding='utf-8-sig'로 설정을 해 줍니다.
그러면
이렇게 csv파일을 열어도 한글이 깨지지 않습니다 ㅎㅎ
전체 내용을 보고 싶으시다면 아래 파일을 확인해 주세요:)
저는 코로나 검색어로 2페이지만 크롤링 하였습니다.
전체 코드
# 크롤링시 필요한 라이브러리 불러오기
from bs4 import BeautifulSoup
import requests
import re
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
# 웹드라이버 설정
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
# 페이지 url 형식에 맞게 바꾸어 주는 함수 만들기
# 입력된 수를 1, 11, 21, 31 ...만들어 주는 함수
def makePgNum(num):
if num == 1:
return num
elif num == 0:
return num + 1
else:
return num + 9 * (num - 1)
# 크롤링할 url 생성하는 함수 만들기(검색어, 크롤링 시작 페이지, 크롤링 종료 페이지)
def makeUrl(search, start_pg, end_pg):
if start_pg == end_pg:
start_page = makePgNum(start_pg)
url = "https://search.naver.com/search.naver?where=news&sm=tab_pge&query=" + search + "&start=" + str(
start_page)
print("생성url: ", url)
return url
else:
urls = []
for i in range(start_pg, end_pg + 1):
page = makePgNum(i)
url = "https://search.naver.com/search.naver?where=news&sm=tab_pge&query=" + search + "&start=" + str(page)
urls.append(url)
print("생성url: ", urls)
return urls
##########뉴스크롤링 시작###################
# 검색어 입력
search = input("검색할 키워드를 입력해주세요:")
# 검색 시작할 페이지 입력
page = int(input("\n크롤링할 시작 페이지를 입력해주세요. ex)1(숫자만입력):")) # ex)1 =1페이지,2=2페이지...
print("\n크롤링할 시작 페이지: ", page, "페이지")
# 검색 종료할 페이지 입력
page2 = int(input("\n크롤링할 종료 페이지를 입력해주세요. ex)1(숫자만입력):")) # ex)1 =1페이지,2=2페이지...
print("\n크롤링할 종료 페이지: ", page2, "페이지")
# naver url 생성
search_urls = makeUrl(search, page, page2)
## selenium으로 navernews만 뽑아오기##
# 버전에 상관 없이 os에 설치된 크롬 브라우저 사용
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.implicitly_wait(3)
# selenium으로 검색 페이지 불러오기 #
naver_urls = []
for i in search_urls:
driver.get(i)
time.sleep(1) # 대기시간 변경 가능
# 네이버 기사 눌러서 제목 및 본문 가져오기#
# 네이버 기사가 있는 기사 css selector 모아오기
a = driver.find_elements(By.CSS_SELECTOR, 'a.info')
# 위에서 생성한 css selector list 하나씩 클릭하여 본문 url얻기
for i in a:
i.click()
# 현재탭에 접근
driver.switch_to.window(driver.window_handles[1])
time.sleep(3) # 대기시간 변경 가능
# 네이버 뉴스 url만 가져오기
url = driver.current_url
print(url)
if "news.naver.com" in url:
naver_urls.append(url)
else:
pass
# 현재 탭 닫기
driver.close()
# 다시처음 탭으로 돌아가기(매우 중요!!!)
driver.switch_to.window(driver.window_handles[0])
print(naver_urls)
###naver 기사 본문 및 제목 가져오기###
# ConnectionError방지
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4758.102"}
titles = []
contents = []
for i in naver_urls:
original_html = requests.get(i, headers=headers)
html = BeautifulSoup(original_html.text, "html.parser")
# 검색결과확인시
# print(html)
# 뉴스 제목 가져오기
title = html.select("div#ct > div.media_end_head.go_trans > div.media_end_head_title > h2")
# list합치기
title = ''.join(str(title))
# html태그제거
pattern1 = '<[^>]*>'
title = re.sub(pattern=pattern1, repl='', string=title)
titles.append(title)
# 뉴스 본문 가져오기
content = html.select("div#dic_area")
# 기사 텍스트만 가져오기
# list합치기
content = ''.join(str(content))
# html태그제거 및 텍스트 다듬기
content = re.sub(pattern=pattern1, repl='', string=content)
pattern2 = """[\n\n\n\n\n// flash 오류를 우회하기 위한 함수 추가\nfunction _flash_removeCallback() {}"""
content = content.replace(pattern2, '')
contents.append(content)
print(titles)
print(contents)
# 데이터프레임으로 정리(titles,url,contents)
import pandas as pd
news_df = pd.DataFrame({'title': titles, 'link': naver_urls, 'content': contents})
news_df.to_csv('NaverNews_%s.csv' % search, index=False, encoding='utf-8-sig')
코드 파일
마무리
이번 포스팅도 금방 작성할 거라 생각했으나...
항상 예상을 벗어나는 법이죠 ㅠㅠ 네.. 오래걸렸어요...
그래도 저번보다 훨씬 좋은 크롤러로 업그레드한 것 같아 좋네요ㅎㅎ
앞으로도 지속적으로 리뷰 및 업그레이드 하겠습니다.
좋은 아이디어나 질문이 있다면 댓글 남겨주세요:)
------------------------------------------------------------------------------------
+04/05 셀레니움 오류 수정하였습니다.
+04/15 driver 오류 수정 완료.
+05/03 bs4 select 수정 완료.
+05/17 driver 및 창닫기 추가.
+ 07/14 최신버전을 확인하고 싶다면...?
https://wonhwa.tistory.com/m/52