Skip to content

Repository files navigation

💳 WiseCard

WiseCard는 사용자가 보유한 카드의 혜택 정보를 기반으로, 현재 위치에서의 최적의 할인 매장과 사용자의 소비 계획에 따른 최적의 혜택 카드를 안내해주는 카드 혜택 추천 모바일 애플리케이션입니다.

📍 핵심 기능

  • 카드사 혜택 정보 크롤링 🔍
  • Naver Map 연동으로 주변 매장 시각화 및 매장 추천 🗺️
  • 사용자의 결제 상황에 맞춘 최적의 카드 자동 추천 💡

🚀 구현 기능

📌 카드 혜택 및 기간 한정 이벤트 정보 크롤링

카드사의 혜택 정보를 자동으로 수집하고 정리하는 크롤링 시스템을 구현하였습니다. 전체 과정은 다음과 같습니다:

  1. 카드 목록 수집

    • extract_card_info_from_url(list_url, card_url_prefix) 함수를 사용하여 카드 목록을 수집합니다.

    • 수집된 정보는 _creditcardInfos.csv 파일에 저장됩니다.

    • CSV 파일에는 다음 항목이 포함됩니다:

      • 카드 이름
      • 카드 이미지 URL
      • 카드 상세정보 URL
  2. 카드 상세 혜택 정보 크롤링

    • _creditcardInfos.csv에 저장된 각 카드의 상세 페이지 URL을 기반으로, 혜택 정보를 추가로 크롤링합니다.

    • 크롤링된 정보는 credit_benefit.csv에 저장되며, 아래와 같은 항목을 포함합니다:

      • 카드 ID
      • 카드 이름
      • 카드 이미지 URL
      • 혜택 정보
      • 크롤링 일자 (생성 일자)
      • 카드 종류 (신용/체크 여부)

🧩 카드 목록 추출 함수

✅ 기능 요약

  • 카드 이름 수집
  • 카드 코드 추출 → 이미지 URL 및 상세 페이지 URL 생성
  • 크롤링한 정보를 각각 card_names, card_imgs, card_urls 리스트에 저장

🧪 주요 로직 예시

# 카드 이름 추출name=li.find('span', class_='h4_b_lt')
ifname:
card_names.append(name.text.strip())
# 카드 코드 추출 → 이미지 및 상세 페이지 URL 생성a_tag=card_div.find('a')
ifa_tagand'onclick'ina_tag.attrs:
onclick_text=a_tag['onclick']
match=re.search(r"goCardDetail\('([^']+)'\)", onclick_text)
ifmatch:
card_code=match.group(1)
card_imgs.append(f'https://img.hyundaicard.com/img/com/card/card_{card_code}_h.png')
card_urls.append(f'{card_url_prefix}{card_code}&eventCode=00000')

🧩 카드 상세 혜택 정보 크롤링

✅ 기능 요약

  • 각 카드 상세 URL에 접속 카드 혜택 관련 텍스트만 선별 추출 (카드사마다 상이함)
  • 혜택 텍스트를 benefits 리스트에 저장

🧪 주요 로직 예시

# 웹 페이지 접속 및 파싱driver.get(card_urls[i])
html=driver.page_sourcesoup=BeautifulSoup(html, 'html.parser')
# 팝업 컨테이너 중 혜택 관련된 모달만 선별popup_containers=soup.find_all('div', class_='modal_pop')
exclude_ids= {'popup_call_reservation', 'cardApplyPop', ...}
forcontainerinpopup_containers:
ifcontainer.get('id') inexclude_ids:
continueforelincontainer.find_all(['h1', 'p', 'li']):
texts.append(el.get_text(strip=True))
benefits.append(texts)
  • selenium, BeautifulSoup, pandas를 활용하여 카드사 웹페이지를 자동으로 순회하며 상세 정보를 수집합니다.
  • 팝업 ID 필터링을 통해 혜택과 무관한 요소는 제외합니다.

📌 Gemini를 활용한 카드 혜택 정보 정제

  • Google AI - Gemini 2.0 Flash 모델

웹 크롤링을 통해 수집한 카드 혜택 문장을 입력값으로 활용하여, Gemini가 해당 문장을 이해한 후 카테고리, 할인율, 포인트, 캐시백, 해당 매장 등으로 자동 분류하고 JSON 형태로 구조화합니다.

for_, cardindf.iterrows():
benefit_text=card["benefits"]
prompt= (
f"{benefit_text}\n\n"''' JSON 예시를 포함한 프롬프트 '''
)
try:
response=model.generate_content(prompt)
json_response=response.text.strip().replace("```json","").replace("```","")
updated_benefits.append(json_response)
print(json_response)
exceptExceptionase:
print(f"Error processing card {card['name']}: {e}")
updated_benefits.append("")

📌 Kakao Map API - 장소 검색 API

🧩 혜택 적용 매장 탐색

사용자의 GPS 기반 위치를 기준으로, 크롤링을 통해 수집한 카드 혜택의 적용 매장이 주변에 존재하는지를 확인하는 용도로 사용하였습니다.

검색 키워드와 위치 좌표를 함께 전송하면, 결과로 해당 장소의 이름, 주소, 거리, 위도/경도 등의 정보를 반환합니다.

x_coord="126.94"y_coord="37.55"results= []
forcafeincafe_targets_list:
params= {
"query": cafe,
"x": x_coord,
"y": y_coord,
"radius": 1000,
"size": 5
}
response=requests.get(url, headers=headers, params=params)
ifresponse.status_code!=200:
print(f"{cafe} 검색 실패: {response.status_code}")
continueplaces=response.json().get("documents", [])
forplaceinplaces:
place_info= {
"place_name": place.get("place_name"),
"road_address_name": place.get("road_address_name"),
"x": place.get("x"),
"y": place.get("y"),
"카페명": cafe,
"거리": int(place.get("distance", "0")), # 문자열을 정수로 변환"카드별_혜택": {}
}
# 3. 이 place의 카페명으로 어떤 카드에서 혜택 있는지 확인 후 혜택 첨부forcard_name, infoincard_cafe_benefits.items():
ifcafeininfo["대상"]:
place_info["카드별_혜택"][card_name] =deepcopy(info["혜택"]) # 깊은 복사results.append(place_info)

🧩 카드 내역의 매장 카테고리 분류 및 활용

사용자 카드 내역 파일(카드내역.csv)을 불러와, 결제된 매장의 카테고리를 자동으로 추출하는 과정에서 Kakao Map API를 사용합니다.

추출한 카테고리 정보를 기반으로 해당 분야(예: 카페)에서의 결제 건별 평균 소비 금액을 도출할 수 있으며, 이는 이후 예상 할인 금액을 계산하고 카드 추천 시스템에 활용하는 과정에 적용할 수 있습니다.

1. 카드 내역 불러오기

card_history_df=pd.read_csv('카드내역2.csv')

2. 카카오 API 설정

headers= {"Authorization": "KakaoAK API-KEY"}
url="https://dapi.kakao.com/v2/local/search/keyword.json"

3. 카테고리 코드 매핑

category_map= {
"MT1": "대형마트", "CS2": "편의점", "PS3": "어린이집, 유치원", "SC4": "학교",
"AC5": "학원", "PK6": "주차장", "OL7": "주유소, 충전소", "SW8": "지하철역",
"BK9": "은행", "CT1": "문화시설", "AG2": "중개업소", "PO3": "공공기관",
"AT4": "관광명소", "AD5": "숙박", "FD6": "음식점", "CE7": "카페",
"HP8": "병원", "PM9": "약국"
}

4. 카테고리별 누적 정보 저장용 dict

category_stats= {}

5. 검색 및 수집

foridx, rowincard_history_df.iterrows():
store_name=row['가맹점명']
try:
amount=int(str(row['승인금액']).replace(",", "").replace("원", "").strip())
except:
amount=0store_name=store_name.replace("( 주 )", "").replace("주식회사", "")
params= {"query": store_name}
try:
response=requests.get(url, headers=headers, params=params)
data=response.json()
print(f"\n[{idx}] 가맹점명: {store_name} | 승인금액: {amount}원")
ifdata.get("documents"):
doc=data["documents"][0]
category_code=doc.get("category_group_code", "")
category_name=category_map.get(category_code, "기타")
print(f" • 이름: {doc['place_name']}")
print(f" 카테고리: {category_name} ({category_code})")
print(f" ----------------------")
ifcategory_namenotincategory_stats:
category_stats[category_name] = {"total_amount": 0, "count": 0}
category_stats[category_name]["total_amount"] +=amountcategory_stats[category_name]["count"] +=1else:
print(" → 검색 결과 없음")
time.sleep(0.3)
exceptExceptionase:
print(f" 오류 발생: {e}")

6. 카테고리별 총합과 평균 출력

print("\n카테고리별 총합 및 평균 금액:")
forcategory, statsincategory_stats.items():
total=stats["total_amount"]
count=stats["count"]
avg=total/countifcountelse0print(f" - {category}: 총 {total:,}원 / {count}회 / 평균 {avg:,.0f}원")

📌 혜택 적용 시스템

사용자가 받을 수 있는 카드 혜택과 앞으로 받을 수 있는 혜택 한도를 계산합니다.

사용자의 카드 결제 내역 하나를 기준으로, 해당 결제에 대해 혜택을 받을 수 있는 카드들을 식별한 뒤, 할인 > 적립 > 캐시백 순의 우선 순위에 따라 가장 먼저 적용된 하나만 사용합니다.

혜택이 적용될 수 있는 최소 조건이 있다면 해당 조건을 만족하는지 우선 판단하고, 조건을 만족했을 때 혜택 계산을 진행합니다.

importre# 혜택이 적용되는 최소 조건(결제 금액 등)을 만족하는 지 판단하는 함수defextract_minimum_amount(condition_str):
""" '5만원 이상', '1만 원 이상', '5천원 이상' 등의 조건에서 최소 금액을 원 단위로 추출 """ifnotcondition_str:
returnNonepattern=r'(\d+)(만|천)?\s*원?\s*이상'# 숫자 + (만|천) + 원 + 이상 패턴match=re.search(pattern, condition_str)
ifmatch:
num=int(match.group(1))
unit=match.group(2)
ifunit=='만':
returnnum*10000elifunit=='천':
returnnum*1000else:
returnnum# 원 단위returnNone
# 한 건의 결제내역에 대해 어떤 혜택이 적용되는지를 계산하는 함수defapply_benefit(row_data):
store_name=str(row_data["가맹점명"]) # 가맹점명을 문자열로 가져옴amount=row_data["이용금액"]
applied_amount=amountbenefit_detail=None# 어떤 혜택이 적용됐는지 (예: 할인, 적립, 캐시백)point_or_cashback=0.0# 적립/캐시백 금액 기본값 0원foridx, benefit_rowindf.iterrows():
benefits=benefit_row['benefits_dict']
forcategory, infoinbenefits.items():
ifinfoisnotNoneand"대상"ininfo:
targets=info["대상"] # 가맹점명이 혜택 대상에 포함되는지 확인iftargetsandany(targetinstore_namefortargetintargets):
# 현재 가맹점명이 혜택 대상에 포함되는 경우# ───── 할인 처리 시작 ─────할인_목록=info.get("혜택", {}).get("할인", [])
fordin할인_목록:
조건문=d.get("조건", "")
최소_금액=extract_minimum_amount(조건문) # 혜택이 적용되는 조건에서 최소금액 추출if최소_금액andamount<최소_금액: # 금액이 조건보다 작으면 건너뜀continueifd.get("할인율(%)"): # 할인율이 있는 경우rate=d["할인율(%)"]
ifrate:
applied_amount=amount* (1-rate/100)
benefit_detail="할인"returnapplied_amount, benefit_detail, 0.0elifd.get("금액(원)"): # 정액 할인 금액이 있는 경우discount_amt=d["금액(원)"]
ifdiscount_amt:
applied_amount=max(0, amount-discount_amt)
benefit_detail="할인"returnapplied_amount, benefit_detail, 0.0# ───── 적립 처리 시작 ─────적립_목록=info.get("혜택", {}).get("적립", [])
fordin적립_목록:
ifd.get("적립율(%)"): # 적립율이 있는 경우rate=d["적립율(%)"]
ifrate:
benefit_detail="적립"point_or_cashback=amount*rate/100returnamount, benefit_detail, point_or_cashback# 지불 금액은 변함없음# ───── 캐시백 처리 시작 ─────캐시백_목록=info.get("혜택", {}).get("캐시백", [])
fordin캐시백_목록:
ifd.get("캐시백 비율(%)"): # 비율 캐시백cashback_rate=d["캐시백 비율(%)"]
ifcashback_rate:
benefit_detail="캐시백"point_or_cashback=amount*cashback_rate/100returnamount, benefit_detail, point_or_cashbackelifd.get("금액(원)"): # 정액 캐시백cashback_amt=d["금액(원)"]
ifcashback_amt:
benefit_detail="캐시백"point_or_cashback=cashback_amtreturnamount, benefit_detail, point_or_cashback# 어떤 혜택도 적용되지 않은 경우returnamount, None, 0.0

About

[2025-EWHA-SPRING] 카드별 할인 및 혜택을 분석하여 사용자 위치 기반 최적의 할인 혜택 제공 매장을 안내해주는 모바일 어플리케이션

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors