여러 사이트에서 크롤링한 데이터를 하나로 합치고 싶으신가요? 딕셔너리 병합은 데이터 통합의 핵심 기술입니다. 이 글에서는 5가지 딕셔너리 병합 방법과 실전 활용법을 코드와 함께 알려드립니다.
딕셔너리 병합이 필요한 이유
웹 크롤링으로 여러 소스에서 수집한 키워드 데이터를 하나의 딕셔너리로 통합하면 중복 제거, 순위 비교, 통합 분석이 가능해집니다. 특히 트렌드 키워드 분석, SEO 키워드 연구, 경쟁사 모니터링에서 필수적입니다.
기본 병합 방법 5가지
1. update() 메서드 (가장 기본)
keyword_google = {'파이썬': 1, '크롤링': 2}
keyword_keyzard = {'데이터베이스': 1, '파이썬': 3}
# keyzard 데이터를 google에 병합
keyword_google.update(keyword_keyzard)
print(keyword_google)
# 출력: {'파이썬': 3, '크롤링': 2, '데이터베이스': 1}
핵심 특징: 중복 키가 있으면 나중에 추가되는 값으로 덮어씁니다. 원본 딕셔너리가 수정됩니다.
2. 언패킹 연산자 ** (Python 3.5+)
keyword_google = {'파이썬': 1, '크롤링': 2}
keyword_keyzard = {'데이터베이스': 1, '파이썬': 3}
# 새로운 딕셔너리 생성 (원본 유지)
keyword = {**keyword_google, **keyword_keyzard}
print(keyword)
# 출력: {'파이썬': 3, '크롤링': 2, '데이터베이스': 1}
실무 팁: 원본 데이터를 보존해야 할 때 사용합니다. 가독성이 좋아 최근 가장 많이 사용되는 방법입니다.
3. 파이프 연산자 | (Python 3.9+)
keyword_google = {'파이썬': 1, '크롤링': 2}
keyword_keyzard = {'데이터베이스': 1, '파이썬': 3}
# 가장 직관적인 병합
keyword = keyword_google | keyword_keyzard
print(keyword)
# 출력: {'파이썬': 3, '크롤링': 2, '데이터베이스': 1}
최신 방법: Python 3.9 이상에서 사용 가능하며, 코드가 매우 간결합니다.
4. dict() 생성자와 체이닝
from itertools import chain
keyword_google = {'파이썬': 1, '크롤링': 2}
keyword_keyzard = {'데이터베이스': 1}
keyword = dict(chain(keyword_google.items(), keyword_keyzard.items()))
print(keyword)
# 출력: {'파이썬': 1, '크롤링': 2, '데이터베이스': 1}
활용 시나리오: 3개 이상의 딕셔너리를 병합할 때 효율적입니다.
5. ChainMap 사용 (조회 전용)
from collections import ChainMap
keyword_google = {'파이썬': 1, '크롤링': 2}
keyword_keyzard = {'데이터베이스': 1, '파이썬': 3}
# 여러 딕셔너리를 논리적으로 연결
keyword = ChainMap(keyword_google, keyword_keyzard)
print(keyword['파이썬']) # 1 (첫 번째 딕셔너리 값)
print(dict(keyword)) # 일반 딕셔너리로 변환
특징: 실제 병합하지 않고 논리적으로만 연결합니다. 메모리 효율이 좋습니다.
크롤링 데이터 병합 실전 예제
기본 구조 개선
from bs4 import BeautifulSoup as bs
import requests
keyword_keyzard = {}
keyword_google = {}
keyword_zum = {}
def keyzard():
try:
URL = '<https://keyzard.org/realtimekeyword>'
html = requests.get(URL, timeout=10)
soup = bs(html.text, 'html.parser')
req = soup.select('td.ellipsis100')
for i, k in enumerate(req, 1):
keyword_keyzard[k.text.strip()] = i
print(f"✅ Keyzard: {len(keyword_keyzard)}개 수집")
except Exception as e:
print(f"❌ Keyzard 오류: {e}")
def google():
try:
URL = '<https://trends.google.com/trends/trendingsearches/daily/rss?geo=KR>'
html = requests.get(URL, timeout=10)
soup = bs(html.text, 'html.parser')
req = soup.select('item > title')
for i, g in enumerate(req[:20], 1):
keyword_google[g.text.strip()] = i
print(f"✅ Google: {len(keyword_google)}개 수집")
except Exception as e:
print(f"❌ Google 오류: {e}")
def zum():
try:
URL = '<https://issue.zum.com/>'
html = requests.get(URL, timeout=10)
soup = bs(html.text, 'html.parser')
req = soup.select('div.cont > span.word')
for i, z in enumerate(req[:10], 1):
keyword_zum[z.text.strip()] = i
print(f"✅ ZUM: {len(keyword_zum)}개 수집")
except Exception as e:
print(f"❌ ZUM 오류: {e}")
# 데이터 수집
keyzard()
google()
zum()
개선 포인트: enumerate()를 사용해 코드가 간결해지고, strip()으로 공백을 제거하며, 예외 처리를 추가했습니다.
방법 1: update() 사용
# 모든 딕셔너리 병합
keyword_all = {}
keyword_all.update(keyword_google)
keyword_all.update(keyword_keyzard)
keyword_all.update(keyword_zum)
print(f"📊 총 {len(keyword_all)}개 키워드 병합 완료")
print(keyword_all)
장점: 간단하고 직관적입니다. 단점: 중복 키가 있으면 마지막 값으로 덮어씁니다.
방법 2: 언패킹 연산자 (추천)
# 원본 보존하며 병합
keyword_all = {
**keyword_google,
**keyword_keyzard,
**keyword_zum
}
print(f"📊 총 {len(keyword_all)}개 키워드")
권장 이유: 원본 데이터가 보존되고 가독성이 좋습니다.
방법 3: 파이프 연산자 (Python 3.9+)
# 최신 방법
keyword_all = keyword_google | keyword_keyzard | keyword_zum
print(f"📊 병합 완료: {len(keyword_all)}개")
최고의 가독성: 수학 연산처럼 직관적입니다.
중복 키 처리 전략
전략 1: 소스 정보 포함
def merge_with_source():
keyword_merged = {}
# 각 소스별로 딕셔너리 순회
for kw, rank in keyword_google.items():
keyword_merged[kw] = {
'google_rank': rank,
'keyzard_rank': None,
'zum_rank': None
}
for kw, rank in keyword_keyzard.items():
if kw in keyword_merged:
keyword_merged[kw]['keyzard_rank'] = rank
else:
keyword_merged[kw] = {
'google_rank': None,
'keyzard_rank': rank,
'zum_rank': None
}
for kw, rank in keyword_zum.items():
if kw in keyword_merged:
keyword_merged[kw]['zum_rank'] = rank
else:
keyword_merged[kw] = {
'google_rank': None,
'keyzard_rank': None,
'zum_rank': rank
}
return keyword_merged
result = merge_with_source()
print(result)
# 출력 예시: {'파이썬': {'google_rank': 1, 'keyzard_rank': 3, 'zum_rank': None}}
실무 활용: 어떤 플랫폼에서 어느 순위로 등장했는지 추적할 수 있습니다.
전략 2: 최소값 선택
def merge_min_rank():
keyword_merged = {}
sources = [keyword_google, keyword_keyzard, keyword_zum]
for source in sources:
for kw, rank in source.items():
if kw in keyword_merged:
# 더 높은 순위(낮은 숫자) 선택
keyword_merged[kw] = min(keyword_merged[kw], rank)
else:
keyword_merged[kw] = rank
return keyword_merged
result = merge_min_rank()
print(result)
활용 예: 여러 플랫폼 중 가장 높은 순위를 기준으로 삼을 때 사용합니다.
전략 3: 리스트로 누적
def merge_with_list():
keyword_merged = {}
for kw, rank in keyword_google.items():
keyword_merged.setdefault(kw, []).append(('google', rank))
for kw, rank in keyword_keyzard.items():
keyword_merged.setdefault(kw, []).append(('keyzard', rank))
for kw, rank in keyword_zum.items():
keyword_merged.setdefault(kw, []).append(('zum', rank))
return keyword_merged
result = merge_with_list()
print(result)
# 출력: {'파이썬': [('google', 1), ('keyzard', 3)]}
데이터 분석: 모든 출처 정보를 유지해야 할 때 최적입니다.
고급 병합 기법
defaultdict 활용
from collections import defaultdict
def merge_with_defaultdict():
keyword_merged = defaultdict(list)
sources = {
'google': keyword_google,
'keyzard': keyword_keyzard,
'zum': keyword_zum
}
for source_name, keywords in sources.items():
for kw, rank in keywords.items():
keyword_merged[kw].append({
'source': source_name,
'rank': rank
})
return dict(keyword_merged)
result = merge_with_defaultdict()
for kw, data in list(result.items())[:3]:
print(f"{kw}: {data}")
코드 간결화: 키 존재 여부를 체크하지 않아도 됩니다.
Counter로 빈도 계산
from collections import Counter
def merge_with_frequency():
all_keywords = []
all_keywords.extend(keyword_google.keys())
all_keywords.extend(keyword_keyzard.keys())
all_keywords.extend(keyword_zum.keys())
keyword_count = Counter(all_keywords)
# 가장 많이 등장한 키워드 순으로 정렬
return keyword_count.most_common()
result = merge_with_frequency()
print("가장 핫한 키워드:")
for kw, count in result[:5]:
print(f"{kw}: {count}개 플랫폼에서 등장")
트렌드 분석: 여러 플랫폼에서 공통으로 등장하는 키워드를 찾을 수 있습니다.
데이터베이스 저장 예제
병합된 데이터 DB 저장
import pymysql
def save_merged_keywords():
# 데이터 병합
keyword_all = {**keyword_google, **keyword_keyzard, **keyword_zum}
# DB 연결
conn = pymysql.connect(
host='localhost',
user='root',
password='your_password',
database='keyword_db',
charset='utf8mb4'
)
cursor = conn.cursor()
# 테이블 생성 (없으면)
cursor.execute("""
CREATE TABLE IF NOT EXISTS merged_keywords (
id INT AUTO_INCREMENT PRIMARY KEY,
keyword VARCHAR(255) UNIQUE,
rank_avg FLOAT,
collected_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
# 데이터 삽입
sql = """
INSERT INTO merged_keywords (keyword, rank_avg)
VALUES (%s, %s)
ON DUPLICATE KEY UPDATE
rank_avg = VALUES(rank_avg),
collected_at = CURRENT_TIMESTAMP
"""
val = [(kw, rank) for kw, rank in keyword_all.items()]
try:
cursor.executemany(sql, val)
conn.commit()
print(f"✅ {len(val)}개 키워드 저장 완료")
except pymysql.Error as e:
print(f"❌ DB 오류: {e}")
conn.rollback()
finally:
cursor.close()
conn.close()
save_merged_keywords()
실무 활용: 시간대별 트렌드 변화를 추적할 수 있습니다.
소스별 저장
def save_with_source():
conn = pymysql.connect(host='localhost', user='root', password='pw', database='db')
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS keywords_by_source (
id INT AUTO_INCREMENT PRIMARY KEY,
keyword VARCHAR(255),
source VARCHAR(50),
rank INT,
collected_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_keyword_source (keyword, source)
)
""")
sql = """
INSERT INTO keywords_by_source (keyword, source, rank)
VALUES (%s, %s, %s)
ON DUPLICATE KEY UPDATE rank = VALUES(rank)
"""
val = []
for kw, rank in keyword_google.items():
val.append((kw, 'google', rank))
for kw, rank in keyword_keyzard.items():
val.append((kw, 'keyzard', rank))
for kw, rank in keyword_zum.items():
val.append((kw, 'zum', rank))
cursor.executemany(sql, val)
conn.commit()
print(f"✅ {len(val)}개 레코드 저장")
cursor.close()
conn.close()
save_with_source()
분석 가능: 플랫폼별 트렌드 차이를 비교할 수 있습니다.
성능 비교 및 선택 가이드
병합 방법별 성능 테스트
import time
def performance_test():
dict1 = {f'key{i}': i for i in range(10000)}
dict2 = {f'key{i}': i*2 for i in range(5000, 15000)}
# update() 방식
start = time.time()
result1 = dict1.copy()
result1.update(dict2)
print(f"update(): {time.time() - start:.4f}초")
# 언패킹 방식
start = time.time()
result2 = {**dict1, **dict2}
print(f"언패킹: {time.time() - start:.4f}초")
# 파이프 연산자 (Python 3.9+)
start = time.time()
result3 = dict1 | dict2
print(f"파이프: {time.time() - start:.4f}초")
performance_test()
결과 요약: 소규모 데이터에서는 차이가 미미하며, 가독성을 우선하세요.
상황별 추천 방법
원본 보존 필요: {**dict1, **dict2} 또는 dict1 | dict2
원본 수정 가능: dict1.update(dict2)
3개 이상 병합: 파이프 연산자 dict1 | dict2 | dict3
조회만 필요: ChainMap(dict1, dict2)
중복 키 특별 처리: 커스텀 함수 작성
실전 활용 시나리오
트렌드 점수 계산
def calculate_trend_score():
# 각 플랫폼의 가중치 설정
weights = {'google': 3, 'keyzard': 2, 'zum': 1}
trend_scores = {}
sources = {
'google': keyword_google,
'keyzard': keyword_keyzard,
'zum': keyword_zum
}
for source_name, keywords in sources.items():
weight = weights[source_name]
for kw, rank in keywords.items():
# 순위가 높을수록(숫자 낮을수록) 점수 높게
score = (100 - rank) * weight
trend_scores[kw] = trend_scores.get(kw, 0) + score
# 점수 순으로 정렬
sorted_trends = sorted(trend_scores.items(), key=lambda x: x[1], reverse=True)
print("🔥 종합 트렌드 스코어 TOP 10:")
for kw, score in sorted_trends[:10]:
print(f"{kw}: {score}점")
return dict(sorted_trends)
trend_result = calculate_trend_score()
마케팅 활용: 여러 플랫폼 데이터를 종합해 진짜 핫한 키워드를 찾습니다.
JSON 파일로 내보내기
import json
from datetime import datetime
def export_to_json():
merged_data = {
'collected_at': datetime.now().isoformat(),
'sources': {
'google': keyword_google,
'keyzard': keyword_keyzard,
'zum': keyword_zum
},
'merged': {**keyword_google, **keyword_keyzard, **keyword_zum}
}
with open('keywords.json', 'w', encoding='utf-8') as f:
json.dump(merged_data, f, ensure_ascii=False, indent=2)
print("✅ JSON 파일 저장 완료")
export_to_json()
데이터 공유: API 응답이나 데이터 교환 형식으로 활용합니다.
자주 하는 실수와 해결법
실수 1: 원본 수정 문제
# 잘못된 예
dict1 = {'a': 1}
dict2 = dict1 # 복사가 아닌 참조!
dict2['b'] = 2
print(dict1) # {'a': 1, 'b': 2} - 원본도 변경됨!
# 올바른 예
dict2 = dict1.copy() # 얕은 복사
dict2['b'] = 2
print(dict1) # {'a': 1} - 원본 유지
해결책: .copy() 또는 dict() 생성자를 사용하세요.
실수 2: 중첩 딕셔너리 복사
import copy
# 얕은 복사 문제
dict1 = {'a': {'nested': 1}}
dict2 = dict1.copy()
dict2['a']['nested'] = 2
print(dict1) # {'a': {'nested': 2}} - 중첩값 변경됨!
# 올바른 깊은 복사
dict2 = copy.deepcopy(dict1)
dict2['a']['nested'] = 3
print(dict1) # {'a': {'nested': 2}} - 원본 유지
중요: 중첩 구조는 deepcopy()를 사용해야 합니다.
마치며
파이썬 딕셔너리 병합은 update(), 언패킹 **, 파이프 | 연산자 등 다양한 방법이 있으며, 상황에 따라 적절히 선택해야 합니다. 웹 크롤링 데이터를 병합할 때는 중복 키 처리 전략을 명확히 하고, 소스 정보를 유지하며, 데이터베이스 저장까지 고려해야 실전에서 활용 가능합니다.
이 가이드의 코드를 프로젝트에 바로 적용하고, 여러 플랫폼의 트렌드 데이터를 통합 분석해보세요.
참고 자료
- Python 공식 문서 – dict: https://docs.python.org/3/library/stdtypes.html#dict
- PEP 584 – 딕셔너리 병합 연산자: https://peps.python.org/pep-0584/
- Real Python 딕셔너리 가이드: https://realpython.com/python-dicts/