import os
import threading
from pykrx import stock
from datetime import datetime, timedelta
import pandas as pd
from tqdm import tqdm
import requests
from bs4 import BeautifulSoup
import time
from multiprocessing import Pool, Manager
import parmap
from concurrent import futures
from threading import Thread
# 국내 주식 모든 종목들의 날짜별 네이버 뉴스 개수 크롤링하기
def count_news(search_words):
global count_dict
global day_list
global dc
# url making
day = day_list[dc]
for search_word in tqdm(search_words, position=0, leave=True):
url = "https://search.naver.com/search.naver?where=news&query={}&pd=3&ds={}&de={}&start=40001".format(search_word,day, day)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/100.0.48496.75"}
req = requests.get(url, headers=headers)
# 정상적인 request 확인
if req.ok:
soup = BeautifulSoup(req.text, "html.parser")
# articles = soup.select("div.group_news > ul.list_news > li div.news_area > a") #
pages = soup.select("#main_pack > div.api_sc_page_wrap > div > div > a") # 마지막 페이지
if not pages: # 뉴스가 없을떄
last_page = 0
dff.loc[day, search_word] = last_page
continue
else:
last_page = pages[-1].text # 마지막 페이지 번호
url = "https://search.naver.com/search.naver?where=news&query={}&pd=3&ds={}&de={}&start={}".format(search_word,day, day,str(int(last_page) * 10 - 9))
# html불러오기
req = requests.get(url, headers=headers)
soup = BeautifulSoup(req.text, "html.parser")
# 검색결과
articles = soup.select("div.group_news > ul.list_news > li div.news_area > a")
news_count = (int(last_page) - 1) * 10 + len(articles)
dff.loc[day,search_word] = news_count
#직접 실행된 모듈인 경우 실행, import한 경우 실행 x 직접실행시 name은 main
#import된 모듈은 그 파일의 이름이 name이 된다.
if __name__ == '__main__':
#pool,parmap은 main 모듈에서만 실행된디
# manager은 멀티 프로세서에서 처리한 값을 리스트, 딕셔너리, 큐를 공유하여 저장함
#코랩에서는 __main__ 밖에서 manager을 만들어도 됬는데 맥 환경에서 오류가 나온다. 이유를 모르겠음
df = pd.read_csv("csvFile/stock2019.csv", index_col=0)
stockName = df["회사명"].drop_duplicates().values.tolist()
if os.path.isfile("csvFile/newsCount.csv")==True:
#update 이전에 스크랩핑해온 마지막날짜의 다음날부터 시작
start = pd.read_csv("csvFile/newsCount.csv", index_col=0).index[-1]
start_date = datetime.strptime(start, "%Y.%m.%d")+timedelta(days=1)
last_date=(datetime.today()-timedelta(days=1))
print(last_date)
else:
start_date = (datetime.today() - timedelta(days=31))
last_date = (datetime.today() - timedelta(days=26))
#코스피 코스닥 종목명 가져오기
dc = 0
day_list = [] # 날짜들
# #처음
# start = "2022-04-16"
# last = "2022-04-20"
# # 시작일, 종료일 datetime 으로 변환
# start_date = datetime.strptime(start, "%Y-%m-%d")
# last_date = datetime.strptime(last, "%Y-%m-%d")
# 종료일 까지 반복
while start_date <= last_date:
dates = start_date.strftime("%Y.%m.%d")
day_list.append(dates)
# 하루 더하기
start_date += timedelta(days=1)
dff = pd.DataFrame(columns=stockName, index=day_list)
start_time = time.time()
# pool = Pool(processes=4) #4개의 프로세스 동시에 작동
for day in tqdm(day_list, position=0, leave=True):
#주어지 기간의 날짜를 반복 매번 멀티 프로세서를 만듬
#프로세서들 크롤링한 값들은 count_dict에 저장되고 이를 df에 저징
print(day)
def list_chunk(lst, n):
return [lst[i:i + len(lst)//n] for i in range(0, len(lst), len(lst)//n)]
with futures.ThreadPoolExecutor() as executor:
sub_routine = list_chunk(stockName,10)
results = executor.map(count_news, sub_routine)
#parmap을 사용하여 멀티 프로세스 사용 pool과 다르게 진행 사항을 알 수 있고 사용하기 쉽다
# result = parmap.map(count_news, stockName, pm_pbar=True, pm_processes='8')
# pool.map(title_to_list,stockName) #title_to_list라는 함수에 1 ~ end까지 10씩늘려가며 인자로 적용
#결과값은 count_dict에 저장
# cd = count_dict.copy()
#이 부분에서 manager.dict를 바로 df 넣으면 value가 아닌 종목명만 들어간다
#그래서 manger.dict를 .copy()로 얕은복사하여 일반 dict로 만들어 처리
dc+=1
print("실행 시간 : %s초" % (time.time() - start_time))
print(dff)
if os.path.isfile("csvFile/newsCount.csv")==True:
tmp = pd.read_csv("csvFile/newsCount.csv",index_col=0)
df = pd.concat([tmp,dff])
df.to_csv("csvFile/newsCount.csv",mode='w')
else:
dff.to_csv("csvFile/newsCount.csv",mode='w')
결과
3S AJ네트웍스 SK렌터카 AK홀딩스 ... DL이앤씨2우(전환) 신영스팩7호 포바이포 상상인제3호스팩
2022.04.20 5 0 11 5 ... 0 0 20 2
2022.04.21 24 0 23 23 ... 0 0 39 0
2022.04.22 2 0 3 2 ... 0 3 5 0
2022.04.23 0 0 0 0 ... 0 0 0 0
2022.04.24 0 0 0 0 ... 0 3 6 2
2022.04.25 2 0 4 0 ... 0 4 12 2
2022.04.26 3 0 4 2 ... 0 0 6 0
2022.04.27 20 0 3 2 ... 0 0 15 0
2022.04.28 3 6 60 6 ... 0 0 75 2
2022.04.29 3 0 52 13 ... 0 2 29 0
2022.04.30 0 0 5 0 ... 0 0 3 0
2022.05.01 0 0 4 0 ... 0 0 11 0
2022.05.02 0 5 36 0 ... 0 0 5 0
2022.05.03 2 0 7 11 ... 0 0 6 0
2022.05.04 0 0 15 2 ... 0 0 4 0
2022.05.05 0 0 4 0 ... 0 0 0 0
2022.05.06 0 0 40 0 ... 0 0 10 0
2022.05.07 0 0 2 0 ... 0 0 0 0
2022.05.08 2 0 0 0 ... 0 0 4 0
2022.05.09 7 0 70 0 ... 0 0 22 0
2022.05.10 3 3 56 0 ... 0 0 8 0
2022.05.11 28 0 5 2 ... 0 0 6 0
2022.05.12 2 0 52 0 ... 0 0 12 0
2022.05.13 0 0 3 0 ... 0 0 2 0
2022.05.14 0 0 3 0 ... 0 0 2 0
2022.05.15 2 2 3 2 ... 0 0 0 0
2022.05.16 0 2 71 0 ... 0 0 8 0
2022.05.17 5 0 20 0 ... 0 0 0 0
2022.05.18 3 0 11 0 ... 0 0 3 0
2022.05.19 0 0 4 0 ... 0 0 7 0
2022.05.20 10 0 0 0 ... 0 0 6 0
2022.05.21 2 0 0 0 ... 0 0 2 0
2022.05.22 4 0 3 0 ... 0 0 5 0
2022.05.23 0 3 23 4 ... 0 0 3 0
2022.05.24 6 0 14 0 ... 0 0 6 0
2022.05.25 2 6 4 0 ... 0 0 7 0
2022.05.26 0 0 9 0 ... 0 0 10 0
2022.05.27 0 0 36 0 ... 0 0 3 0
2022.05.28 0 0 2 0 ... 0 0 2 0
2022.05.29 0 0 0 0 ... 0 0 2 0
2022.05.30 3 0 33 2 ... 0 0 13 0
2022.05.31 5 0 40 0 ... 0 0 6 0
주의할점
네이버 뉴스의 경우 4000 페이지가 최대이기 때문에 40001로 페이지 번호를 보내면 마지막 페이지로 이동한다.
거기서 마지막 페이지 번호를 추출한다.
40001 페이지 번호로 들어가면 나오는 뉴스의 갯수가 이상하기 때문에 (이유를 모르겟다)
전에 추출한 마지막 페이지 번호로 들어가 나머지 뉴스 갯수를 새야한다.
2756개의 종목명들의 하루 검색량을 가져오는데 꽤 긴 시간이 걸려 parmap(멀티 프로세서)를 이용해 보았는데 짧은 io작업 처리 속도보다 context switching 시간이 길어서 그런지 시간 단축 효과가 크게 나지 않았다.
그래서 멀티 쓰레드를 사용해봤는데 하루에 11분 걸리던 작업이 2분으로 단축 되었다.
복잡한 계산의 경우 멀티 프로세스를 사용하는게 좋고 주로 짧은 io처리가 필요한 경우 멀티 쓰레드가 좋은거 같다
'주식 데이터 분석' 카테고리의 다른 글
| 네이버 주식 관련 블로그 크롤링 (파이썬) (0) | 2024.06.16 |
|---|---|
| [Python] Dataframe을 [컬럼명,인덱스,값] 2차원 리스트로 만들기 (0) | 2022.11.13 |
| [Python] 국내 주식 상한 VI를 카운팅해서 관련주 찾기 (0) | 2022.05.02 |
| [Python] 국내 주식 종목 간 상관관계 분석해서 관련주 찾기 (0) | 2022.05.02 |