이번 글에는 이전 글에서 작성했던
"시간차 분석 (뉴스 ↔ SNS 전파 패턴)"
에 대해서 완성하고 어떤식으로 돌아가는지 어떤 로직을 통해 동작하는지에 대해서 간단하게 정리하면서
나도 더 기억할 수 있게끔 작성해보도록 할 예정이다.
먼저 우리가 만든 프로젝트는 News의 RSS와 SNS(DCinside, Reddit)의 RSS,Scraping으로 수집된 article,post들을 분석 후 대시보드에 내보내서 결과적으로는 수집된 글과 함께 분석이 완료되어 News,SNS의 관계도 함께 볼 수 있는 웹 페이지를 만들고 있다.
이전 글에서는 키워드 추출과 플랫폼별 비교 분석을 완성했고, 이번에는 뉴스와 SNS 간의 키워드 전파 패턴을 분석하는 시간차 분석 기능을 구현했다.
시간차 분석이란 특정 키워드가 뉴스에서 먼저 등장했는지, SNS에서 먼저 등장했는지를 파악하고, 두 플랫폼 간의 전파 속도와 방향을 분석하는 기능이다. 이를 통해 어떤 플랫폼이 트렌드를 먼저 포착하는지, 또는 뉴스가 SNS로 전파되는 속도는 어느 정도인지 등을 파악할 수 있다.
전체적인 실행 흐름은 아래와 같다.
> [1단계] 키워드 리스트 준비 (공통 키워드 추출 또는 직접 지정)
> ↓
> [2단계] 뉴스/SNS 데이터 수집 및 QuerySet 준비
> ↓
> [3단계] 형태소 분석 및 키워드 추출 (최적화: 한 번만 분석하고 결과 재사용)
> ↓
> [4단계] 각 키워드별 최초 등장 시간 찾기
> ↓
> [5단계] 시간차 계산 및 전파 방향 판단
> ↓
> [6단계] 통계 집계 및 결과 반환
> ↓
> 결과 출력
services.py의 내용을 살펴보겠다.
[1단계] 키워드 리스트 준비
def analyze_time_lag(
keywords: Optional[List[str]] = None,
days: int = 7,
min_frequency: float = 0.001,
top_n: Optional[int] = None,
news_queryset=None,
sns_queryset=None
) -> Dict[str, any]:
위의 함수로 시간차 분석을 진행한다.
키워드 리스트를 준비하는 방법은 두 가지가 있다.
1. 직접 키워드 리스트를 전달하는 방법
- `keywords=['달러', '환율', '주식']` 처럼 분석하고 싶은 키워드를 직접 지정
2. 공통 키워드를 자동으로 추출하는 방법
- `keywords=None`으로 설정하면 `compare_platforms` 함수를 호출하여 뉴스와 SNS 모두에서 등장하는 공통 키워드를 자동으로 추출
- `min_frequency`와 `top_n` 파라미터로 필터링
# 키워드 리스트 준비
if keywords is None:
# compare_platforms로 공통 키워드 추출
comparison_result = compare_platforms(
news_queryset=news_queryset,
sns_queryset=sns_queryset,
days=days,
min_frequency=min_frequency,
top_n=top_n
)
keywords = [item['keyword'] for item in comparison_result['common_keywords']]
[2단계] 뉴스/SNS 데이터 수집 및 QuerySet 준비
분석할 기간을 설정하고, 해당 기간 내의 뉴스 기사와 SNS 게시물을 조회한다.
# QuerySet 준비
if news_queryset is None:
start_date = timezone.now() - timedelta(days=days)
news_queryset = NewsArticle.objects.filter(
published_at__gte=start_date
).exclude(published_at__isnull=True).order_by('published_at')
if sns_queryset is None:
start_date = timezone.now() - timedelta(days=days)
sns_queryset = SocialMediaPost.objects.filter(
published_at__gte=start_date
).exclude(published_at__isnull=True).order_by('published_at')
기본적으로 7일간의 데이터를 분석하지만 'days'로 기간을 조정할 수 있다.
[3단계] 형태소 분석 및 키워드 추출 (최적화: 한 번만 분석하고 결과 재사용)
이전에는 각 키워드마다 모든 게시물을 다시 형태소 분석하는 비효율적인 방식이었지만, 최적화를 통해 모든 게시물을 한 번만 형태소 분석하고 결과를 메모리에 저장한 후 재사용하도록 개선했다.(다음 글에 트러블 슈팅형식으로 효율적인 방식으로 변경한 내용 기재예정)
# 분석기 가져오기
analyzer = get_analyzer()
# 1단계: 모든 뉴스 기사를 한 번만 형태소 분석
logger.info(f"뉴스 기사 형태소 분석 시작: {news_queryset.count()}개")
news_keywords_map = {} # {article_id: [keyword1, keyword2, ...]}
for article in news_queryset:
text = extract_text_from_news_article(article)
if text:
keywords_extracted = analyzer.extract_keywords(text, min_length=2, exclude_stopwords=True)
news_keywords_map[article.id] = keywords_extracted
# 2단계: 모든 SNS 게시물을 한 번만 형태소 분석
logger.info(f"SNS 게시물 형태소 분석 시작: {sns_queryset.count()}개")
sns_keywords_map = {} # {post_id: [keyword1, keyword2, ...]}
for post in sns_queryset:
text = extract_text_from_sns_post(post)
if text:
keywords_extracted = analyzer.extract_keywords(text, min_length=2, exclude_stopwords=True)
sns_keywords_map[post.id] = keywords_extracted
logger.info("형태소 분석 완료. 키워드별 등장 시간 계산 시작")
이렇게 하면 형태소 분석을 게시물 수만큼만 수행하고, 이후 키워드별 등장 시간 계산은 메모리에서 빠르게 수행할 수 있다.
[4단계] 각 키워드별 최초 등장 시간 찾기
각 키워드에 대해 뉴스와 SNS에서 등장한 모든 시간을 찾고, 그 중 최초 등장 시간을 계산한다.
keyword_results = []
news_to_sns_lags = []
sns_to_news_lags = []
for keyword in keywords:
# 뉴스에서 등장한 게시물 찾기
news_occurrences = []
for article in news_queryset:
if article.id in news_keywords_map and keyword in news_keywords_map[article.id]:
if article.published_at:
news_occurrences.append(article.published_at)
# SNS에서 등장한 게시물 찾기
sns_occurrences = []
for post in sns_queryset:
if post.id in sns_keywords_map and keyword in sns_keywords_map[post.id]:
if post.published_at:
sns_occurrences.append(post.published_at)
# 최초 등장 시간 계산
news_first = min(news_occurrences) if news_occurrences else None
sns_first = min(sns_occurrences) if sns_occurrences else None
`news_keywords_map`과 `sns_keywords_map`을 사용하여 이미 형태소 분석이 완료된 결과를 빠르게 조회한다.
[5단계] 시간차 계산 및 전파 방향 판단
최초 등장 시간을 비교하여 전파 방향을 판단하고 시간차를 계산한다.
# 시간차 계산
result = {
'keyword': keyword,
'news_first_occurrence': news_first,
'sns_first_occurrence': sns_first,
'news_occurrence_count': len(news_occurrences),
'sns_occurrence_count': len(sns_occurrences),
'direction': None,
'time_lag_hours': None,
'time_lag_timedelta': None
}
if news_first and sns_first:
# 두 플랫폼 모두에서 등장
if news_first < sns_first:
# 뉴스가 먼저
time_lag = sns_first - news_first
result['direction'] = 'news_to_sns'
result['time_lag_timedelta'] = time_lag
result['time_lag_hours'] = time_lag.total_seconds() / 3600
news_to_sns_lags.append(time_lag.total_seconds() / 3600)
elif sns_first < news_first:
# SNS가 먼저
time_lag = news_first - sns_first
result['direction'] = 'sns_to_news'
result['time_lag_timedelta'] = time_lag
result['time_lag_hours'] = time_lag.total_seconds() / 3600
sns_to_news_lags.append(time_lag.total_seconds() / 3600)
else:
# 동시 등장 (거의 불가능하지만)
result['direction'] = 'simultaneous'
result['time_lag_hours'] = 0
elif news_first and not sns_first:
result['direction'] = 'news_only'
elif sns_first and not news_first:
result['direction'] = 'sns_only'
else:
result['direction'] = 'not_found'
keyword_results.append(result)
전파 방향은 다음과 같이 분류된다
- `news_to_sns`: 뉴스에서 먼저 등장한 후 SNS로 전파
- `sns_to_news`: SNS에서 먼저 등장한 후 뉴스로 전파
- `simultaneous`: 동시에 등장
- `news_only`: 뉴스에만 등장
- `sns_only`: SNS에만 등장
- `not_found`: 둘 다 등장하지 않음
[6단계] 통계 집계 및 결과 반환
모든 키워드에 대한 분석이 완료되면 통계를 집계한다.
# 통계 집계
news_to_sns_count = sum(1 for r in keyword_results if r['direction'] == 'news_to_sns')
sns_to_news_count = sum(1 for r in keyword_results if r['direction'] == 'sns_to_news')
simultaneous_count = sum(1 for r in keyword_results if r['direction'] == 'simultaneous')
news_only_count = sum(1 for r in keyword_results if r['direction'] == 'news_only')
sns_only_count = sum(1 for r in keyword_results if r['direction'] == 'sns_only')
# 평균 시간차 계산
avg_news_to_sns = sum(news_to_sns_lags) / len(news_to_sns_lags) if news_to_sns_lags else None
avg_sns_to_news = sum(sns_to_news_lags) / len(sns_to_news_lags) if sns_to_news_lags else None
statistics = {
'total_keywords': len(keyword_results),
'news_to_sns': news_to_sns_count,
'sns_to_news': sns_to_news_count,
'simultaneous': simultaneous_count,
'news_only': news_only_count,
'sns_only': sns_only_count,
'avg_time_lag_news_to_sns_hours': avg_news_to_sns,
'avg_time_lag_sns_to_news_hours': avg_sns_to_news,
'news_to_sns_percentage': (news_to_sns_count / len(keyword_results) * 100) if keyword_results else 0,
'sns_to_news_percentage': (sns_to_news_count / len(keyword_results) * 100) if keyword_results else 0
}
return {
'keywords': keyword_results,
'statistics': statistics
}
결과 예시는 아래와 같다.
다음 글에는 이 분석하는 로직을 최적화 했던 방향을 트러블 슈팅으로 작성할 예정이다
================================================================================
시간차 분석 시작
================================================================================
분석 기간: 최근 30일
[INFO] 2026-01-09 14:20:28,383 services PyKOMORAN 형태소 분석기 초기화 완료 (모델: STABLE)
[INFO] 2026-01-09 14:20:28,436 services 불용어 파일 로드 완료: 884개 단어 (C:\Users\vhtmx\OneDrive\바탕 화면\trend\analyzer\data\stopwords_ko.txt)
[INFO] 2026-01-09 14:24:28,685 services 뉴스 기사 분석 완료: 19369개 기사, 37849개 키워드
[INFO] 2026-01-09 14:24:32,968 services SNS 게시물 분석 완료: 1010개 게시물, 5388개 키워드
[INFO] 2026-01-09 14:24:32,973 services 플랫폼 비교 분석 완료: 뉴스 19369개, SNS 1010개, 공통 키워드 50개
[INFO] 2026-01-09 14:24:32,982 services 뉴스 기사 형태소 분석 시작: 19367개
[INFO] 2026-01-09 14:28:31,196 services SNS 게시물 형태소 분석 시작: 1010개
[INFO] 2026-01-09 14:28:35,633 services 형태소 분석 완료. 키워드별 등장 시간 계산 시작
[INFO] 2026-01-09 14:28:35,810 services 시간차 분석 완료: 4개 키워드, 뉴스→SNS 4개, SNS→뉴스 0개
[실행 시간]
--------------------------------------------------------------------------------
⏱️ 총 실행 시간: 490.14초 (8.17분)
[시간차 분석 통계]
--------------------------------------------------------------------------------
✅ 분석 완료: 4개 키워드
- 뉴스 → SNS: 4개 (100.0%)
- SNS → 뉴스: 0개 (0.0%)
- 동시 등장: 0개
- 뉴스 전용: 0개
- SNS 전용: 0개
- 뉴스 → SNS 평균 시간차: 309.36시간
[키워드별 분석 결과]
--------------------------------------------------------------------------------
📰 → 📱 뉴스 → SNS 전파:
1. 안성기 시간차: 180.87시간 (뉴스: 2025-12-30 18:17, SNS: 2026-01-07 07:10)
2. 배우 시간차: 195.00시간 (뉴스: 2025-12-10 05:39, SNS: 2025-12-18 08:39)
3. 지구촌 시간차: 199.05시간 (뉴스: 2025-12-10 17:56, SNS: 2025-12-19 01:00)
4. 일화 시간차: 662.50시간 (뉴스: 2025-12-10 16:04, SNS: 2026-01-07 06:35)
================================================================================
분석 완료
================================================================================
'AI_RSS_트래픽 프로젝트' 카테고리의 다른 글
| 모르는 상태로 하는 RSS&분석&RAG 프로젝트(19) 트렌드 분석 (0) | 2026.01.14 |
|---|---|
| 모르는 상태로 하는 RSS&분석&RAG 프로젝트(18) 트러블슈팅2 analyze_time_lag (0) | 2026.01.09 |
| 모르는 상태로 하는 RSS&분석&RAG 프로젝트(16) 크로스 플랫폼 키워드 추출 및 빈도 분석 (0) | 2026.01.02 |
| 모르는 상태로 하는 RSS&분석&RAG 프로젝트(15) 크로스 플랫폼 키워드 추출 및 빈도 분석 (0) | 2025.12.30 |
| 모르는 상태로 하는 RSS&분석&RAG 프로젝트(14) 데이터 분석(계획 및 공부) (0) | 2025.12.30 |