1. 프로젝트 소개
제가 맡은 작업은 뉴스 데이터를 수집하여 이를 DB에 저장하는 배치 프로그램을 제작하는 것입니다. 1시간마다 배치 작업으로 데이터를 자동 수집 및 갱신하며, 수집된 데이터는 자사 앱의 뉴스화면에 활용됩니다.
2. 구현시 고려사항
1) 기능 세분화하기
프로그램의 기능을 세분화하면 다음과 같습니다.
- 배치하는 시점에서 수집할 url 담당
- html 파싱/ 추출 담당
- DB API 담당
배치시간마다 프로그램을 실행하면 위의 과정을 순차적으로 처리합니다.
2) 설정한 시간대에 데이터 수집하기
이 프로그램의 목적은 지정된 시간 구간 동안 생성된 뉴스 사이트의 URL을 수집하고 이를 추출하여 저장하는 것입니다.
- 인수값 입력:
- start_date: 마지막으로 배치가 실행된 시점의 시간.
- end_date: 현재 배치를 실행하는 시점의 시간.
- 시간 구간 설정 예시:
- 마지막 배치 실행 시점: 2020-01-01 18:00
- 현재 시점: 2020-01-01 19:00
- 프로그램은 2020-01-01 18:01부터 2020-01-01 19:00 사이에 생성된 데이터를 처리합니다.
- 뉴스 사이트의 작성 시간 기록은 분 단위로 이루어지기 때문에 분 단위로 관리 합니다.
# 예시코드
start_date = "2020-01-01 18:01" # start는 최근에 배치를 했던 시간에 +1분
end_date = "2020-01-01 19:00" # end는 배치시작하는 시작 즉 현재 시간
collector = NaverNewsCollector()
urls = collector.collect(start_date, end_date)
- 중복 적재 방지
위에서 수집된 데이터는 중복 적재가 될 수도 있습니다. 따라서 수집된 뉴스들을 식별할 키가 필요합니다.
"/news/news_read.naver?article_id=0005415420&office_id=009&
mode=LSS3D&type=0§ion_id=101§ion_id2=258§ion_id3=401&date=20241217&page=1"
네이버 뉴스 사이트의 url의 길이가 깁니다. 따라서 특정할 수 있는 정보를 따로 뽑을 필요가 있습니다. 여기서는 article_id와 office_id를 사용합니다. article_id는 기사 식별번호, office_id는 언론사식별번호이므로 이 둘을 조합하면 유일성을 보장할 수 있습니다.
데이터 저장 시, 이 두 값을 조합한 키를 기준으로 데이터베이스에 이미 저장된 데이터인지 확인합니다.이를 효율적으로 처리하기 위해 데이터베이스 테이블에 article_id와 office_id를 묶은 Unique Index를 설정합니다.
- 데이터 누락 방지
설정된 시간대에 데이터를 안정적으로 누락 없이 수집하기 위해 해당 날짜의 1페이지부터 시작하여 stat_date(마지막 수집 배치 시간)까지 수집합니다.
이를 검증하기 위해 아래와 같이 테스트 코드를 작성합니다.
start_date ~ end_date 기간동안 수집한 url의 개수를 기대값과 비교하여 검증합니다.
# 예시코드
@pytest.mark.parametrize(
"start_date, end_date, expected",
[
("20220312 00:00", "20220312 23:30", 28),
("20230312 17:33", "20230312 23:30", 9),
("20230306 15:59", "20230307 09:53", 79),
("20231016 20:00", "20231017 21:34", 193)
]
)
def test_naver_url(start_date, end_date, expected):
#시작 날짜
start = datetime.strptime(start_date, format_string)
start = tz.localize(start)
# 끝날짜 설정
end = datetime.strptime(end_date, format_string)
end = tz.localize(end)
collector = NaverNewsCollector(start=start, end=end)
#given
urls = collector.collect()
#then
assert len(urls) == expected
3) 예외 사항 처리하기
크롤링을 하다 보면 일시적인 네트워크 오류로 인해 요청이 실패할 수 있습니다. 따라서 이에 대한 예외 사항을 적절하게 처리해야 합니다. 파이썬의 데코레이터 문법을 활용하여 요청 실패 시 재시도 횟수와 요청 간격을 조절하도록 구현했습니다.
def retry(max_retries=3, delay=5, exceptions=(requests.RequestException,)):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except exceptions as e:
time.sleep(delay)
return None
return wrapper
return decorator
#예시 적용법
@retry(max_retries=3, delay=5)
def fetch_url(url):
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
return response.text
3. 한계점
이 방식에서는 확장성과 성능 측면에서 몇 가지 개선할 점이 있습니다.
- 코드 복잡도 증가
- 순수 파이썬 코드로만 작성되어 있어 요구사항이 추가될수록 코드가 복잡해집니다.
- 따라서 워크플로우 도구나 크롤링 프레임워크를 적용하면 유지보수성과 확장성이 향상될 수 있습니다.
- 리팩토링 필요
- 현재 네이버에 특화된 코드 구조로 작성되어 있어, 다른 사이트를 추가할 경우 재사용성이 제한됩니다.
- 따라서 다양한 사이트에 대응할 수 있도록 범용적인 구조로 개선 필요가 있습니다.
- 비동기 처리 고려
- 수집하는 데이터의 개수가 많지 않아서 현재 동기 방식으로도 몇 분 이내에 처리가 완료됩니다.
- 하지만 사이트 추가 및 요청할 URL이 많아질 경우, 성능을 위해 비동기 처리 방식을 고려해야 합니다. 단, 비동기 처리 시 과도한 요청으로 인해 접속 차단 가능성이 있으므로, 적절한 속도 조절이 필요합니다.
'컴퓨터 > 프로젝트' 카테고리의 다른 글
단기간 모의 테스트 (0) | 2024.12.29 |
---|---|
(프로젝트) SAM, Github action으로 서버리스 아키텍처 CI/CD구현 (0) | 2024.09.11 |
(프로젝트) 만개의 레시피 데이터 수집 자동화 (0) | 2024.08.31 |
(프로젝트) AWS를 활용한 간단한 최신 정보 알림 만들기 (0) | 2024.08.14 |
인턴생활 정리 - 이것 저것 (0) | 2024.03.21 |