수업시간에 배운내용을 토대로 Pandas를 이용하여 네이버 증권사이트 내 종목토론실의 데이터를 수집한다
나는 LG에너지솔루션 종목의 종목토론실을 수집하겠다!
일단 필요한 모듈들을 import 한다.
import pandas as pd
from bs4 import BeautifulSoup as bs
import requests
import time
일단 첫페이지의 데이터 부터 수집해보자
item_code = 373220
page_no = 1
list_url = f'https://finance.naver.com/item/board.naver?code={item_code}&page={page_no}'
headers = {"user-agent": "Mozilla/5.0"}
# requests로 요청한다
response = requests.get(list_url, headers = headers)
table = pd.read_html(response.text)
table = table[1]
table
여기서 첫번째 난관
결측치를 없애주기 위해 바로 위의 데이터에 dropna를 해보았다
table.dropna()
모든내용이 삭제되었다..
왜냐하면 디폴트가 any 이기때문에 row 또는 Column에 NaN 값이 하나만 있어도 drop을 하게 되는데,
Unnamed :6 컬럼의 값들이 모두 NaN이기 때문에 발생한 일이였다!!
그래서 먼저 Unnamed : 6 컬럼을 먼저 삭제해주자
drop_cols = [col for col in table.columns if 'Unnamed' in col]
table.drop(columns=drop_cols, inplace=True)
후에 다시 결측치를 삭제한다
table = table.dropna()
그리고 내가 추가로 원했던 것
1. 제목이 길어도 ...로 요약되는것이 아닌 풀로 가져오기
2. 답글이면 답글이라고 표시하기
3. 제목뿐아니라 글 내용도 가져오기
개발자 모드로 확인해보니 다음과 같이 되어 있다.
1. 전체 제목은 a 태그의 title 속성을 가져오자
2. 답글은 img 태그의 alt 속성이 있는지를 판단하면 될것 같다
+ 오늘 날짜의 글에는 new 이미지때문에 무조건 img 태그가 있지만 예전 날짜의 글은 아니다 그러니 먼저 img 태그 유무부터 판단하여야 한다.
3. a 태그의 href 속성에 글번호가 포함되어 있다.
이 모든 정보를 가지고 있는 title 클래스를 copy select 해와서 제목, 답글, 글번호를 가져오자
# copy 해온 값은 아래와 같음
#content > div.section.inner_sub > table.type2 > tbody > tr:nth-child(23) > td.title
# 글 제목을 전체로 가져오고, 글 번호를 가져온다.
html = bs(response.text)
res = html.select("div.section.inner_sub > table.type2 > tbody > tr > td.title")
title_list =[]
content_no_list=[]
reply_yn = []
for i in range(20):
# 오늘날짜의 글이 아니고, 답글이 아니면 img 태그 자체가 없다
if res[i].find('img') is not None :
# 0번째 img 태그가 답글이고 1번째 img 태그가 최신을 의미하기때문에 0 만
if res[i].select("img")[0].has_attr('alt') :
reply_yn.append("YES")
else:
reply_yn.append("NO")
else:
reply_yn.append("NO")
title_list.append(res[i].select("a")[0]["title"])
content_no_list.append(res[i].select("a")[0]["href"].split("=")[2][:-3])
답글여부와, 제목은 가져왔지만 글 번호만 가져왔지 글 내용을 가져오지 못했다
글 목록에서 해당 글을 클릭하여 들어가 내용을 확인 해보니
글 내용을 가져오기 위해서는 또한번의 requests 로 요청이 필요해 보인다.
글 내용을 가지고 있는 태그를 확인하니 다행히 id 로 지정하여 쉽게 가져올 수 있을 것 같다.
content_url = f'https://finance.naver.com/item/board_read.naver?code={item_code}&nid={nid}&st=&sw=&page={page_no}'
headers = {"user-agent": "Mozilla/5.0"}
content = requests.get(content_url, headers = headers)
html_content = bs(content.text)
res_content = html_content.select_one("#body").text
지금까지 수집한 데이터들을 "제목", "내용", "답글여부" 컬럼에 넣어준다.
여기까지의 과정을 함수로 만들면
# 글내용 가져오는 함수
def get_content(item_code, page_no, content_no_list):
#body
content_list = []
for nid in content_no_list:
content_url = f'https://finance.naver.com/item/board_read.naver?code={item_code}&nid={nid}&st=&sw=&page={page_no}'
headers = {"user-agent": "Mozilla/5.0"}
content = requests.get(content_url, headers = headers)
html_content = bs(content.text)
res_content = html_content.select_one("#body").text
content_list.append(res_content)
time.sleep(0.2)
return content_list
# 페이지에 따라 데이터를 읽어오는 함수
def get_page_list(item_code, page_no):
"""
페이지별로 글 목록을 읽어온다
"""
# URL을 만든다
list_url = f'https://finance.naver.com/item/board.naver?code={item_code}&page={page_no}'
headers = {"user-agent": "Mozilla/5.0"}
# requests로 요청한다
response = requests.get(list_url, headers = headers)
table = pd.read_html(response.text)
table = table[1]
# ['Unnamed: 6'] 컬럼을 삭제한다
drop_cols = [col for col in table.columns if 'Unnamed' in col]
table.drop(columns=drop_cols, inplace=True)
# 결측치 제거한다
table = table.dropna()
# 글 제목을 전체로 가져오고, 글 번호를 가져온다.
html = bs(response.text)
res = html.select("div.section.inner_sub > table.type2 > tbody > tr > td.title")
title_list =[]
content_no_list=[]
reply_yn = []
for i in range(20):
if res[i].select("img")[0].has_attr('alt') :
reply_yn.append("YES")
else:
reply_yn.append("NO")
title_list.append(res[i].select("a")[0]["title"])
content_no_list.append(res[i].select("a")[0]["href"].split("=")[2][:-3])
# 글 별로 글 내용을 가져온다
cotent_list = get_content(item_code, page_no, content_no_list)
# df에 글 제목을 수정하고, 글 내용을 추가한다
table["제목"] = title_list
table["내용"] = cotent_list
table["답글여부"] = reply_yn
return table
나는 한 페이지의 데이터만 가져오는게 아니라 내가 원하는 날짜까지의 데이터를 모두 가져오고 가져온 데이터를 엑셀로 저장해주고 싶어 다음과 같이 함수를 작성하였다.
# 원하는 날짜까지 읽어오기
def get_list(item_code, item_name, target_date):
"""
원하는 날짜까지 글을 읽어오는 함수
"""
page_no = 1
result = []
# 오늘 ~ 특정날짜까지 수집하기
while True:
table = get_page_list(item_code, page_no)
result.append(table)
date = table.iloc[-1,0][:-6]
if date < target_date:
break
page_no += 1
time.sleep(0.1)
df = pd.concat(result)
df = df[df["날짜"] >= target_date]
# 종목명, 종목코드를 파생변수로 생성하기
df["종목명"] = item_name
df["종목코드"] = item_code
# 컬럼 순서 변경하기
cols = ['종목코드', '종목명', '날짜', '답글여부', '제목', '내용', '글쓴이', '조회', '공감', '비공감']
df = df[cols]
# 중복값 제거하기
df = df.drop_duplicates()
# 지정한 날짜로 파일명만들기
file_date = target_date.replace('.','')
file_name = f'{item_name}-{item_code}-{file_date}.xlsx'
# 파일 만들고 읽기
df.to_excel(file_name, index = False)
list_result = pd.read_excel(file_name)
return list_result
사실.. csv 로 저장하고 읽으려고 마지막 #파일 만들고 읽기 부분을 아래와 같이 작성하였는데, 아래와 같은 에러가 발생하였다.
# 파일 만들고 읽기
df.to_csv(file_name, index = False)
list_result = pd.read_csv(file_name)
구글링을 해보니 데이터의 첫번째나 마지막 컬럼에 \n 이나 \r 이 들어가 있어서 그렇다고 한다.
데이터를 확인해보니 그런거 같지 않았다 ㅠ.... 그치만 하란대로 아래와 같이 변경하여 실행하니 에러가 발생하지 않았다..
나는 이해가 안가서 그냥 엑셀로 저장 후 읽는 코드를 사용할거다~!!!
나중에 이해하면 수정예정!
# 파일 만들고 읽기
df.to_csv(file_name, index = False)
list_result = pd.read_csv(file_name, sep='\t', lineterminator='\n')
그리고 함수 호출시 날짜를 YYYY.MM.DD 형식으로 입력하여야한다.
사용자가 편하게 dateformat을 변경하여 비교할 수도 있겠지만,,, 그건 나중에 시도하려고 한다...
display(get_list(373220, 'LG에너지솔루션', '2023.01.12'))
데이터 수집은 된다.. 그치만 겨우 5페이지 약 100몇개의 데이터일 뿐인데 2분이 넘게 걸린다..
이게 맞는걸까?
'데이터분석' 카테고리의 다른 글
Correlation Coefficient : 상관계수 (0) | 2023.01.05 |
---|---|
Conditional Probability : 조건부 확률 (0) | 2023.01.03 |