| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 피파온라인 API
- 조축회
- 트위치
- 구글 스토어 리뷰
- geocoding
- CTM
- KeyBert
- 데벨챌
- Optimizer
- 문맥을 반영한 토픽모델링
- BERTopic
- 자연어 모델
- NLP
- 데이터넥스트레벨챌린지
- 다항분포
- Tableu
- LDA
- 데이터리안
- 토픽 모델링
- Roberta
- 개체명 인식
- 붕괴 스타레일
- 코사인 유사도
- 원신
- 옵티마이저
- 클래스 분류
- 블루아카이브 토픽모델링
- 블루 아카이브
- SBERT
- 포아송분포
- Today
- Total
분석하고싶은코코
BeautifulSoup을 이용한 네이버 뉴스 크롤링(Multiprocessing) 본문
Python의 가장 쉬운 크롤링 툴인 BeautifulSoup 모듈입니다.
이 모듈을 사용해 크롤링하면 정말 간편하게 크롤링이 가능합니다! 그런데 많은양의 크롤링을 하게 되면 대기시간이 오래걸리게 됩니다.
이를 해결하기 위해서 Python의 내장 모듈인 Multiprocessing을 사용해 어느정도 해결이 가능하다고 합니다.
그 방법을 스터디하며 공유를 위한 문서를 작성하였습니다. 아래는 그 문서를 첨부합니다.
MultiProcessing in Python¶
간단한 예제¶
import time
start_time = time.time()
def sleepFunction(t):
print(f"{t} sec Sleep....")
time.sleep(t)
print(f'Sleep Fin {t}sec')
num_list = [1,2,3,4]
for num in num_list:
sleepFunction(num)
print('Total Running Time : ', time.time() - start_time)
1 sec Sleep.... Sleep Fin 1sec 2 sec Sleep.... Sleep Fin 2sec 3 sec Sleep.... Sleep Fin 3sec 4 sec Sleep.... Sleep Fin 4sec Total Running Time : 10.013209819793701
위 예시에서 보시다시피 리스트를 단순 반복문을 돌리면 앞에 작업이 끝나기 전에는 뒷 작업을 진행 할 수 없어 총 대기시간의 합인 10초가 걸렸습니다.
# 멀티프로세싱
from multiprocessing import Pool
import time
start_time = time.time()
def sleepFunction(t):
print(f"{t} sec Sleep....")
time.sleep(t)
print(f'Sleep Fin {t}sec')
num_list = [1,2,3,4]
pool = Pool(processes=4)
pool.map(sleepFunction, num_list)
print('Total Running Time : ', time.time() - start_time)
1 sec Sleep....3 sec Sleep....2 sec Sleep.... 4 sec Sleep.... Sleep Fin 1sec Sleep Fin 2sec Sleep Fin 3sec Sleep Fin 4sec Total Running Time : 4.073814630508423
멀티프로세싱 진행 후에는 4개의 작업대에서 각각 진행하니까 가장 길었던 4초의 시간만 걸린 것을 확인 할 수 있습니다. 이 멀티프로세싱에는 pool과 process 두 가지 방법이 있다고 합니다. pool은 작업 전체를 던져주고 알아서 처리해! 라는 방식이고 process는 직접 넌 이거 넌 저거 이런식으로 지정해주는 것이라고합니다. 아래는 위 작업을 process로 구현한 코드입니다.
import multiprocessing as redfoxtistory
import time
start_time = time.time()
def sleepFunction(t):
sec = int(t)
print(f"{sec} sec Sleep....")
time.sleep(sec)
print(f'Sleep Fin {sec}sec')
p1 = redfoxtistory.Process(target = sleepFunction, args = ('1'))
p2 = redfoxtistory.Process(target = sleepFunction, args = ('2'))
p3 = redfoxtistory.Process(target = sleepFunction, args = ('3'))
p4 = redfoxtistory.Process(target = sleepFunction, args = ('4'))
#작업대에서 해야할 일을 설정해주고 실행시켜줍니다.
p1.start()
p2.start()
p3.start()
p4.start()
# join은 저희가 기존에 사용하던 str.join()과 완전 별개의 함수입니다.
# multiprocessing안에 있는 함수로 이 함수는 내가 할당한 작업대의 작업이 끝날때까지 기다려라 라는 뜻입니다.
# 즉, 내가 할당한 작업대의 작업이 끝날때까지 기다린다는 의미입니다.
# 모두 활성화 해도 합이 아닌 가장 시간이 오래걸리는 작업대 시간만큼만 기다리는거보면 큰 작업장 개념이 따로 있나봅니다.
# 좀 더 쉽게 이해하고 싶으시면 p3만 활성화 하고 실행해보시면 p4는 작업을 완료하지 못했는데 그냥 넘어가버립니다.
p1.join() # 1초 대기
p2.join() # 2초 대기
p3.join() # 3초 대기
p4.join() # 4초 대기
print('Total Running Time : ', time.time() - start_time)
2 sec Sleep.... 1 sec Sleep.... 3 sec Sleep....4 sec Sleep.... Sleep Fin 1sec Sleep Fin 2sec Sleep Fin 3sec Sleep Fin 4sec Total Running Time : 4.109197378158569
크롤링에 적용해보기¶
우선 기본적인 뷰티풀숩으로 0201일의 경제-금융의 기사들을 크롤링해보겠습니다.
# 저는 useragent를 페이크로 사용했습니다. 직접 입력해주시면 이부분은 실행 안하셔도 됩니다.
!pip install fake_useragent
from bs4 import BeautifulSoup as bs
import requests
#useragent를 직접 입력하신다면 아래부분은 지우시고 headers부분만 직접 입력해주시면 됩니다.
from fake_useragent import UserAgent
ua = UserAgent(verify_ssl=False)
fake_ua = ua.random
headers = {
'user-agent' : fake_ua
}
import time
start_time = time.time()
i=1
total_urls = []
urls = []
while True:
test_url = f'https://news.naver.com/main/list.naver?mode=LS2D&mid=shm&sid2=259&sid1=101&date=20230201&page={i}'
soup = bs(requests.get(test_url, headers=headers).text, "html.parser")
now_url =[]
# url만 가져오기
for row in soup.select('#main_content > div.list_body.newsflash_body > ul > li'):
row = row.select_one('a')
now_url.append(row['href'])
if urls == now_url:
break
urls = now_url
total_urls += now_url
print(f"{i} PAGE FIN URLS CNT : ", len(urls))
i += 1
print("-"*10)
print('END PAGE : ', i-1)
print('Total Urls : ', len(total_urls))
print('Check Running Time : ', time.time() - start_time)
1 PAGE FIN URLS CNT : 20 2 PAGE FIN URLS CNT : 20 3 PAGE FIN URLS CNT : 20 4 PAGE FIN URLS CNT : 20 5 PAGE FIN URLS CNT : 20 6 PAGE FIN URLS CNT : 20 7 PAGE FIN URLS CNT : 20 8 PAGE FIN URLS CNT : 20 9 PAGE FIN URLS CNT : 20 10 PAGE FIN URLS CNT : 20 11 PAGE FIN URLS CNT : 20 12 PAGE FIN URLS CNT : 20 13 PAGE FIN URLS CNT : 20 14 PAGE FIN URLS CNT : 20 15 PAGE FIN URLS CNT : 20 16 PAGE FIN URLS CNT : 20 17 PAGE FIN URLS CNT : 20 18 PAGE FIN URLS CNT : 20 19 PAGE FIN URLS CNT : 20 20 PAGE FIN URLS CNT : 20 21 PAGE FIN URLS CNT : 20 22 PAGE FIN URLS CNT : 20 23 PAGE FIN URLS CNT : 20 24 PAGE FIN URLS CNT : 20 25 PAGE FIN URLS CNT : 20 26 PAGE FIN URLS CNT : 20 27 PAGE FIN URLS CNT : 18 ---------- END PAGE : 27 Total Urls : 538 Check Running Time : 14.20963191986084
멀티프로세싱을 통한 크롤링¶
이번에는 멀티프로세싱으로 크롤링 해보겠습니다.
from multiprocessing import Pool
import time
start_time = time.time()
# 페이지 탐색을 자동으로 종료하는 방법은 떠오르지 않아 마지막 페이지 정보를 먼저 가져오도록 했습니다.
# 위 방법처럼 url들을 비교하는 방법이 있을 것 같긴한데 저도 좀 더 알아봐야할 것 같습니다. 혹시 발견하신 분은 공유해주시면 좋을 것 같아요!
def getEndPage():
url = f'https://news.naver.com/main/list.naver?mode=LS2D&mid=shm&sid2=259&sid1=101&date=20230201&page=999'
soup = bs(requests.get(url, headers=headers).text, "html.parser")
return soup.select_one('#main_content > div.paging > strong').text
def getUrls(page):
test_url = f'https://news.naver.com/main/list.naver?mode=LS2D&mid=shm&sid2=259&sid1=101&date=20230201&page={page}'
res = requests.get(test_url, headers=headers)
if res.status_code != 200:
print(i, "page Request Error")
return
soup = bs(res.text, "html.parser")
now_urls =[]
for row in soup.select('#main_content > div.list_body.newsflash_body > ul > li'):
row = row.select_one('a')
now_urls.append(row['href'])
print(f"Crawing Fin {page} URLS CNT : ", len(now_urls))
return now_urls
pages = [i for i in range(1, int(getEndPage())+1)]
pool = Pool(processes=4)
result = pool.map(getUrls, pages)
urls = []
for url in result:
urls += url
print("-"*10)
print('URL CNT : ', len(urls))
print('Check Running Time : ', time.time() - start_time)
Crawing Fin 3 URLS CNT : 20 Crawing Fin 7 URLS CNT : 20 Crawing Fin 1 URLS CNT : 20 Crawing Fin 5 URLS CNT : 20 Crawing Fin 4 URLS CNT : 20 Crawing Fin 8 URLS CNT : 20 Crawing Fin 6 URLS CNT : 20 Crawing Fin 2 URLS CNT : 20 Crawing Fin 9 URLS CNT : 20 Crawing Fin 11 URLS CNT : 20 Crawing Fin 13 URLS CNT : 20 Crawing Fin 15 URLS CNT : 20 Crawing Fin 10 URLS CNT : 20 Crawing Fin 12 URLS CNT : 20 Crawing Fin 17 URLS CNT : 20 Crawing Fin 19 URLS CNT : 20 Crawing Fin 14 URLS CNT : 20 Crawing Fin 18 URLS CNT : 20 Crawing Fin 16 URLS CNT : 20 Crawing Fin 20 URLS CNT : 20 Crawing Fin 21 URLS CNT : 20 Crawing Fin 23 URLS CNT : 20 Crawing Fin 27 URLS CNT : Crawing Fin 25 URLS CNT : 18 20 Crawing Fin 22 URLS CNT : 20 Crawing Fin 24 URLS CNT : 20 Crawing Fin 26 URLS CNT : 20 ---------- URL CNT : 538 Check Running Time : 6.2536797523498535
핵심인 시간이 6초로 크게 줄어든게 눈에 보입니다. 그렇다면 작업대(processes)를 2배로 늘려보겠습니다.
# 위에서 작업했던 크롤링을 가져와서 그대로 사용해 보겠습니다.
from multiprocessing import Pool
import time
start_time = time.time()
def getEndPage():
url = f'https://news.naver.com/main/list.naver?mode=LS2D&mid=shm&sid2=259&sid1=101&date=20230201&page=999'
soup = bs(requests.get(url, headers=headers).text, "html.parser")
return soup.select_one('#main_content > div.paging > strong').text
def getUrls(page):
test_url = f'https://news.naver.com/main/list.naver?mode=LS2D&mid=shm&sid2=259&sid1=101&date=20230201&page={page}'
res = requests.get(test_url, headers=headers)
if res.status_code != 200:
print(i, "page Request Error")
return
soup = bs(res.text, "html.parser")
now_urls =[]
for row in soup.select('#main_content > div.list_body.newsflash_body > ul > li'):
row = row.select_one('a')
now_urls.append(row['href'])
print(f"Crawing Fin {page} URLS CNT : ", len(now_urls))
return now_urls
pages = [i for i in range(1, int(getEndPage())+1)]
pool = Pool(processes=8)
result = pool.map(getUrls, pages)
urls = []
for url in result:
urls += url
print("-"*10)
print('URL CNT : ', len(urls))
print('Check Running Time : ', time.time() - start_time)
Crawing Fin 2 URLS CNT : Crawing Fin 8 URLS CNT : Crawing Fin 7 URLS CNT : Crawing Fin 1 URLS CNT : 2020 Crawing Fin 6 URLS CNT : 20 20 20Crawing Fin 3 URLS CNT : Crawing Fin 4 URLS CNT : 2020 Crawing Fin 5 URLS CNT : 20 Crawing Fin 10 URLS CNT : 20 Crawing Fin 11 URLS CNT : Crawing Fin 9 URLS CNT : 2020 Crawing Fin 16 URLS CNT : Crawing Fin 12 URLS CNT : 2020 Crawing Fin 14 URLS CNT : 20 Crawing Fin 15 URLS CNT : Crawing Fin 13 URLS CNT : 20 20 Crawing Fin 17 URLS CNT : 20 Crawing Fin 18 URLS CNT : 20 Crawing Fin 21 URLS CNT : Crawing Fin 19 URLS CNT : 20 20 Crawing Fin 24 URLS CNT : Crawing Fin 22 URLS CNT : Crawing Fin 20 URLS CNT : 20Crawing Fin 23 URLS CNT : 2020 20 Crawing Fin 25 URLS CNT : 20 Crawing Fin 26 URLS CNT : 20 Crawing Fin 27 URLS CNT : 18 ---------- URL CNT : 538 Check Running Time : 4.952283620834351
더 줄었네요! 그런데 여기서 중요한 점은 'processes'옵션의 숫자는 사용자의 컴퓨터의 성능에 따라 달라진다고 합니다. 컴퓨터 하드웨어 중 CPU를 평가할때 8코어 16코어 이렇게 이야기하는건 광고에서 강조하는 부분이라 한 번쯤은 들어보셨을거라 생각합니다. 이게 바로 프로세스를 처리할 수 있는 작업대 수를 결정하는 거라고 합니다. 따라서 작업대를 너무 많이 설정하면 오히려 속도 저하의 원인이 된다고 합니다.
멀티프로세스에 관련한 내용이 정리된 링크 공유로 마무리하겠습니다!
'Python > 크롤링' 카테고리의 다른 글
| Python Free Proxy 사용하기 (0) | 2023.05.09 |
|---|---|
| 새로운 뉴스 기사 알람 (0) | 2023.04.17 |
| Riot API 사용 간단 정리 (0) | 2023.02.16 |
| Geocoding - 네이버 API (0) | 2023.02.12 |