작성자 주저리주저리
지난주 1주차 동영상 강의를 듣고 복습 차원에서 구글링하면서 네이버 뉴스 기사 크롤링을 해보았다.
하지만, 웹스크래핑과 HTML 구조와 태그를 잘 이해하지 못하고 진행하여서인지 왠지 모를 찜찜함이 남아있었는데 오늘 강의 들으면서 완전히 해결되었다 ㅎㅎ
사실 당장 반년 전에 학교 팀프로젝트로 네이버 뉴스, 블로그 크롤링하고, Okt로 분석하고, 연결망 분석까지 진행했는데 이제야 알게 된다는 게 참 부끄러우면서도 한편으로는 다행이다.
멋사 AI SCHOOL 수업 벌써 2주차가 끝났다. 이번주부터 시작된 7시간 실시간 수업은 물론 고되다,,
그치만 강사님이 너무 좋으셔서 강의도 너무 재밌고 더 일찍이 신청할걸 생각이 들게 해주신다.
항상 코드를 짜면서도 코드 이해를 제대로 하지 못한 채 짠 적이 많다. 그리고 이게 내가 이번 휴학 기간에 꼭 개선하고자 한 문제였다. 이 문제를 멋사를 통해 너무 잘 해결하고, 나 스스로 더 다지고, 단단해지는 기회가 되는 것 같아 멋사 지원하길 너무 잘했다는 생각이 든다ㅎㅎㅎ
이 블로그를 읽는 분이시라면 강력히 지원 추천드립니다!!!
네이버 뉴스 기사 스크래핑
0. 라이브러리 설치
import requests # == from urllib.request import urlopen
# URL에 한글이 포함되어 있으면 requests 라이브러리로
from bs4 import BeautifulSoup # HTTP Response -> HTML
import pandas as pd
from datetime import datetime
import time # time.sleep() : 네이버 서버 컴퓨터에 무리가지 않도록
import re
1. URL 분석 & 2. URL 구성
# https://search.naver.com/search.naver?where=news&query=데이터분석
query = '데이터분석'
url = "https://search.naver.com/search.naver?where=news&query=" + query
3. HTTPS Response 얻기 & 4. HTML source 얻기
web = requests.get(url).content
# request.get(url) 대신 urlopen(url)을 사용한다면 :
# 에러:'ascii' codec can't encode characters in position XX-XX: ordinal not in range()
source = BeautifulSoup(web, 'html.parser')
print(source)
5. HTML Tag 꺼내기 & 6. Tag로부터 Text / Attrs 꺼내기
Ctrl+Shift+I를 눌러 크롬의 개발자 도구를 활용한다.
개발자 도구의 Inspector를 활성화하고 크롤링하고 싶은 내용을 클릭하면 관련 Tag를 확인할 수 있다.
Tag 중에 target="_blank" 또는 onclick=" " 는 각각 새 창을 띄우는 명령, 자바스크립트 함수를 수행하는 태그로 사실상 우리가 find함수를 통해 꺼낼 수 없는 태그이다. 다시 말해, 우리가 잘 활용할 수 없는, 필요가 없는 함수이다. 만일, 내가 출력하고 싶은 무언가의 HTML 태그에 target, onclick 태그만 있다면, 부모 태그를 활용하여 뽑아내는 것이 더 좋은 방법이다.
더불어, 404 에러가 나면 우리가 요청을 잘못한 것이니 try, except 함수로 예외처리 코드를 사용하는 것도 하나의 해결방안이 될 수 있다. (400s 에러는 우리가 잘못 요청한 것, 500s는 서버가 잘못한 것으로 생각하면 된다)
5.1) 기사 제목들 출력
news_subjects = source.find_all('a', {'class' : 'news_tit'})
# find_all()에 .get_text() 사용할 수 없으므로 for문으로 태그 하나씩 돌려서 출력해야 함
subject_list = []
for subject in news_subjects:
subject_list.append(subject.get_text()) # subject.text도 가능
print(subject_list)
5.2) 기사 URL 출력
urls_list = []
for urls in source.find_all('a', {'class' : "info"}):
if urls.attrs["href"].startswith("https://news.naver.com"): # starts with ~~~
urls_list.append(urls.attrs["href"])
urls_list
5.3) 단일 뉴스 페이지 분석
- 기사의 본문만 스크래핑하는 경우, 언론사별로 본문의 HTML 구조가 다르다.
- 네이버 기사의 경우에는 '네이버기사'를 통해 통일된 형식의 HTML 구조를 담고 있으므로 쉽게 스크래핑이 가능하다.
- 그렇지 않을 경우, 주요 언론사를 지정한 후 언론사별로 함수를 만들고 적용해주거나,
- div로 가장 많은 text가 있는 tag를 선택하거나
- 머신러닝, 딥러닝 모델로 본문을 뽑아내는 등의 모델을 학습시킬 수 있다.
- 단일 뉴스 페이지를 분석할 때는 네이버가 Bot으로 인식해 접속을 차단시키므로 headers를 반드시 지정해서 브라우저처럼 실행해야 한다.
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
web_news = requests.get(urls_list[0], headers=headers).content
source_news = BeautifulSoup(web_news, 'html.parser')
'''제목'''
title = source_news.find('h3', {'id' : 'articleTitle'}).get_text()
print(title)
'''날짜'''
date = source_news.find('span', {'class' : 't11'}).get_text()
date = date.replace(" ","")
date1 = date[:11] # 날짜까지 파싱
date2 = date[13:] # 시간부분 파싱
# lambda를 임시함수로 사용, lambda로 if문을 쓸 때는 else가 필수
date3 = (lambda x : 'am' if x == '오전' else 'pm')(date[11:13])
'''기사 본문'''
article = source_news.find('div', {'id' : 'articleBodyContents'}).get_text()
article
'''기사 발행 언론사'''
press_company = source_news.find('address', {'class' : 'address_cp'}).find('a').get_text()
print(press_company)
+ 출력값에서 불필요한 공백, 줄바꿈, 단어 등 삭제하기
article = article.replace("\n", "") # 교체하다
article = article.replace("// flash 오류를 우회하기 위한 함수 추가function _flash_removeCallback() {}", "")
article = article.replace("동영상 뉴스 ", "")
article = article.replace("동영상 뉴스", "")
article = article.strip() # 불필요한 공백 잘라내기
article
+ 출력값에서 불필요한 기호와 기자 이메일 주소 등 삭제하기 (정규표현식)
import re
'''compile() : 정규표현식 패턴 만드는 함수'''
pattern = re.compile(r'[\s\Wa-zA-Z0-9]*@')
# [white-space, anti-alphanumeric, lower/upper, numeric] 중에서 0 or more + "@"
# r : default로 앞에 붙여서 \ 대신 씀
# \s: 모든 여백
# \w: alpha,numeric <-> \W: anti-alpha,numeric (특수문자만)
'''pattern.search(찾을문서)'''
email_address = pattern.search(news_contents)
print(email_address) # span=(774, 781)
print(email_address.start()) # 774
news_contents[:email_address.start()]
# 1) "\'" 기호 삭제하기
pattern = re.compile(r'\'')
'''교체대상패턴.sub(교체될문자열, 본문)'''
news_contents = pattern.sub('', news_contents) # substitute
# 2) 기자 이메일 주소부터 그 이후 전체 삭제하기
pattern = re.compile(r'[\s\Wa-zA-Z0-9]*@')
email_address = pattern.search(news_contents)
news_contents = news_contents[:email_address.start()]
print(news_contents)
'멋쟁이 사자처럼 AI SCHOOL 5기 > Today I Learned' 카테고리의 다른 글
[3주차 총정리] Okt 텍스트 분석 후에 Word Cloud 제작하기 (0) | 2022.03.29 |
---|---|
[3주차 총정리] Web Scrapping (네이버 여러 뉴스 기사) (0) | 2022.03.28 |
[2주차 총정리] 자연어처리(NLP) _ 2. Text Similarity Analysis (TF-IDF, Cosine Similarity 이론 및 실습) (0) | 2022.03.26 |
[2주차 총정리] 자연어처리(NLP) _ 1. Preprocessing Text Data & Text Data Exploration (0) | 2022.03.25 |
[2주차 총정리] Web Scrapping 실전 기초 (0) | 2022.03.25 |