네이버 부동산 사이트처럼 API를 사용하는 경우 이를 이용해 응용 프로그램을 만들 수 있다.
지역명을 입력하면 그 주변 맛집을 찾아주는 서비스를 만들어 보자. 개발 방식은 지난 포스팅한 네이버 부동산 정보 추출과 동일한다. 처음엔 복잡해 보이는데 한 두 번만 해보면 돌아가는 메커니즘이 보이고 응용 방안도 다양하다는 걸 알게 된다.
1. 다이닝코드에서 광화문 검색
여기에서 제공하는 맛집 정보를 가져오는 코드를 작성하자.
F12 개발자 모드 - Network - Fetch/XHR 결과에서 맛집 정보를 담은 항목을 카피한다. cURL 형태로 복사해야 한다. (마우스 오른쪽 copy-cURL(bash) 이용
2. curl conver 사이트 접소해 파이썬 코드로 변환한다.
3. LLM 인공지능에 코드 작성 요청한다. (DeepSeek, Claude, ChatGpt 아무거나) 개인적으로 Claude가 만족도 높았음.
단, Output에 대한 정보를 인공지능이 모르기 때문에 형태를 알려줘야 한다. response부분을 일부 카피해서 인공지능 요청 시 포함해 물어본다.
import requests
headers = {
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': 'https://www.diningcode.com',
'Referer': 'https://www.diningcode.com/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36',
'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Google Chrome";v="132"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
data = {
'query': '행당동',
'addr': '',
'keyword': '',
'order': 'r_score',
'distance': '',
'rn_search_flag': 'on',
'search_type': 'poi_search',
'lat': '',
'lng': '',
'rect': '',
's_type': '',
'token': '',
'mode': 'poi',
'dc_flag': '1',
'page': '2',
'size': '20',
}
response = requests.post('https://im.diningcode.com/API/isearch/', headers=headers, data=data)
===========
{
"data": "{\"query\":\"행당동\",\"from\":20,\"size\":20,\"order\":\"r_score\",\"rn_search_flag\":\"on\"}",
"result_code": "100",
"result_data": {
"params": {
"query": "행당동",
"from": 20,
"size": 20,
"order": "r_score",
"rn_search_flag": "on"
},
"search_filter": [
{
"group": "이용자층",
"list": [
{
"display": "20대",
"query": "20대"
},
{
"display": "30대",
"query": "30대"
},
{
"display": "40대",
"query": "40대"
},
{
"display": "50대",
"query": "50대"
},
{
"display": "60대이상",
"query": "60대이상"
},
{
"display": "여성",
"query": "여성"
},
{
"display": "남성",
"query": "남성"
}
]
},
{
"group": "카테고리",
"list": [
{
"display": "밥집",
"query": "밥집"
},
{
"display": "술집",
"query": "술집"
},
{
"display": "고깃집",
"query": "고깃집"
},
{
"display": "한식",
"query": "한식"
},
{
"display": "카페",
"query": "카페"
},
{
"display": "일식",
"query": "일식"
},
{
"display": "횟집",
"query": "횟집"
},
{
"display": "양식",
"query": "양식"
},
{
"display": "중식",
"query": "중식"
},
{
"display": "국물요리",
"query": "국물요리"
},
{
"display": "해산물",
"query": "해산물"
},
{
"display": "면요리",
"query": "면요리"
},
{
"display": "이탈리안",
"query": "이탈리안"
},
{
"display": "브런치",
"query": "브런치"
},
{
"display": "뷔페",
"query": "뷔페"
},
{
"display": "분식",
"query": "분식"
},
{
"display": "태국음식",
"query": "태국음식"
},
{
"display": "베트남음식",
"query": "베트남음식"
},
{
"display": "패스트푸드",
"query": "패스트푸드"
},
{
"display": "멕시칸",
"query": "멕시칸"
},
{
"display": "프렌치",
"query": "프렌치"
}
]
},
{
"group": "방문목적",
"list": [
{
"display": "점심식사",
"query": "점심식사"
},
{
"display": "아침식사",
"query": "아침식사"
},
{
"display": "저녁식사",
"query": "저녁식사"
},
{
"display": "가족외식",
"query": "가족외식"
},
{
"display": "데이트",
"query": "데이트"
},
{
"display": "회식",
"query": "회식"
},
{
"display": "혼밥",
"query": "혼밥"
},
{
"display": "식사모임",
"query": "식사모임"
},
{
"display": "술모임",
"query": "술모임"
},
{
"display": "접대",
"query": "접대"
},
{
"display": "혼술",
"query": "혼술"
},
{
"display": "아이동반",
"query": "아이동반"
},
{
"display": "건강식",
"query": "건강식"
},
{
"display": "간식",
"query": "간식"
},
{
"display": "혼카페",
"query": "혼카페"
},
{
"display": "다이어트",
"query": "다이어트"
},
{
"display": "차모임",
"query": "차모임"
},
{
"display": "실버푸드",
"query": "실버푸드"
}
]
},
{
"group": "편의기능",
"list": [
{
"display": "주차",
"query": "주차"
},
{
"display": "개별룸",
"query": "개별룸"
},
{
"display": "24시영업",
"query": "24시영업"
},
{
"display": "반려동물동반",
"query": "반려동물동반"
},
{
"display": "콜키지무료",
"query": "콜키지무료"
},
{
"display": "야외좌석(테라스)",
"query": "야외좌석(테라스)"
},
{
"display": "대형룸",
"query": "대형룸"
},
{
"display": "놀이방",
"query": "놀이방"
},
{
"display": "발렛가능",
"query": "발렛가능"
},
{
"display": "드라이브스루",
"query": "드라이브스루"
}
]
},
{
"group": "분위기",
"list": [
{
"display": "가성비좋은",
"query": "가성비좋은"
},
{
"display": "분위기좋은",
"query": "분위기좋은"
},
{
"display": "고급스러운",
"query": "고급스러운"
},
{
"display": "조용한",
"query": "조용한"
},
{
"display": "예쁜",
"query": "예쁜"
},
{
"display": "깔끔한",
"query": "깔끔한"
},
{
"display": "뷰가좋은",
"query": "뷰가좋은"
},
{
"display": "격식있는",
"query": "격식있는"
},
{
"display": "이색적인",
"query": "이색적인"
},
{
"display": "푸짐한",
"query": "푸짐한"
},
{
"display": "지역주민이찾는",
"query": "지역주민이찾는"
},
{
"display": "서민적인",
"query": "서민적인"
},
{
"display": "시끌벅적한",
"query": "시끌벅적한"
}
]
},
{
"group": "인증맛집",
"list": [
{
"display": "다코숨은맛집",
"query": "다코숨은맛집"
},
{
"display": "식객허영만의백반기행",
"query": "식객허영만의백반기행"
},
{
"display": "수요미식회",
"query": "수요미식회"
},
{
"display": "생활의달인",
"query": "생활의달인"
},
{
"display": "성시경의먹을텐데",
"query": "성시경의먹을텐데"
},
{
"display": "맛있는녀석들",
"query": "맛있는녀석들"
},
{
"display": "백종원의3대천왕",
"query": "백종원의3대천왕"
},
{
"display": "백년가게",
"query": "백년가게"
},
{
"display": "미쉐린",
"query": "미쉐린"
},
{
"display": "흑백요리사",
"query": "흑백요리사"
},
{
"display": "최자로드",
"query": "최자로드"
},
{
"display": "전지적참견시점",
"query": "전지적참견시점"
},
{
"display": "한국인의밥상",
"query": "한국인의밥상"
},
{
"display": "백종원의골목식당",
"query": "백종원의골목식당"
}
]
}
],
"search_order": [
{
"display": "연관순",
"order": "r_score"
},
{
"display": "평점순",
"order": "r_rate"
},
{
"display": "리뷰많은순",
"order": "r_count"
},
{
"display": "좋아요많은순",
"order": "l_count"
},
{
"display": "거리순",
"order": "dist"
}
],
"poi_section": {
"type": "area",
"search_type": "ranking_search",
"distance": null,
"lat": "",
"lng": "",
"total_cnt": 613,
"list": [
{
"v_rid": "jDskmsq3ZXZT",
"nm": "행운돈까스",
"branch": null,
"addr": "서울특별시 성동구 행당동 158-44",
"road_addr": "서울특별시 성동구 마조로1길 2",
"phone": "02-2296-3406",
"distance": "",
"category": "돈까스",
"keyword": [
{
"term": "혼밥",
"mark": 0
},
{
"term": "서민적인",
"mark": 0
},
{
"term": "포장",
"mark": 0
}
],
"area": [
"한양대"
],
"lat": 37.5584167,
"lng": 127.0403375,
"open_status": "영업 중",
"score": 75,
"image": "https:\/\/d12zq4w4guyljn.cloudfront.net\/300_300_20240612022948_photo1_87b24f44814c.jpg",
"user_score": 3.9,
"my_favorites": {
"favorites": "N",
"dt": ""
},
"favorites_cnt": 103,
"review_cnt": 25,
"recommend_cnt": 18,
"display_review": {
"user_nm": null,
"review_cont": "1. 가성비 좋은 돈까스식당 2. 무난하게 먹을수 있는 돈까스 3. 현금 만원이상 결제시 콜라1병 써비스 한양대앞에 꾀 오래된 돈까스 전문점 돈가스 7,000원 정식(돈까스,함박,",
"review_reg_dt": "55년 전",
"review_reg_dt2": "55년 전",
"user_level": {
"cd": "NORMAL_TASTER",
"text": "일반회원",
"icon": ""
}
},
"review_total_cnt": 18,
"review_list": [
{
"user_nm": null,
"review_cont": "1. 가성비 좋은 돈까스식당 2. 무난하게 먹을수 있는 돈까스 3. 현금 만원이상 결제시 콜라1병 써비스 한양대앞에 꾀 오래된 돈까스 전문점 돈가스 7,000원 정식(돈까스,함박,",
"review_reg_dt": "55년 전",
"review_reg_dt2": "55년 전",
"user_level": {
"cd": "NORMAL_TASTER",
"text": "일반회원",
"icon": ""
}
}
]
},
===========
맛집 검색 프로그램과 응답값을 참고하여 프로그램을 완성해줘.
인공지능은 놀라울 정도로 수준높은 코드를 작성한다. 물론 일부 보완이나 수정사항이 있을 거다. 그럴 때마다 재차 물어서 보완하자.
검색결과값을 csv파일로 저장하는 코드를 추가해 줘.
GUI 형태로 보기 편하게 코드를 수정해줘.
위 코드에서 한글 인코딩을 추가해 줘. csv파일 열면 한글이 깨지지 않도록 하려고해. 그리고 결과 창에서 항목별 sorting을 추가해줘.
이 코드에 streamlit을 이용한 홈페이지 기능을 추가해 줄 있어?
출력결과 순번은 1번부터 시작해 줘. 그리고 csv파일 저장 시 한글이 깨져. 다음과 같이 해결해 줘.
1. encoding='cp949' 사용 - 한국어 Windows의 기본 인코딩
2. 인코딩 실패 시 utf-8-sig로 폴백 하는 예외처리 추가
3. data 부분을 encode('cp949')로 바이트 변환
import requests
import pandas as pd
import streamlit as st
from io import StringIO, BytesIO
# Streamlit 페이지 설정
st.set_page_config(page_title="맛집 검색 프로그램", layout="wide")
# 검색 함수
def search_restaurants(query, page=1, size=20):
headers = {
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': 'https://www.diningcode.com',
'Referer': 'https://www.diningcode.com/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36',
'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Google Chrome";v="132"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
data = {
'query': query,
'addr': '',
'keyword': '',
'order': 'r_score',
'distance': '',
'rn_search_flag': 'on',
'search_type': 'poi_search',
'lat': '',
'lng': '',
'rect': '',
's_type': '',
'token': '',
'mode': 'poi',
'dc_flag': '1',
'page': str(page),
'size': str(size),
}
response = requests.post('https://im.diningcode.com/API/isearch/', headers=headers, data=data)
if response.status_code == 200:
return response.json()
else:
st.error(f"API 요청 실패: {response.status_code}")
return None
# 결과를 DataFrame으로 변환
def convert_to_dataframe(results):
if not results or 'result_data' not in results or 'poi_section' not in results['result_data']:
return None
restaurants = results['result_data']['poi_section']['list']
data = []
for idx, restaurant in enumerate(restaurants, start=1): # 순번을 1부터 시작
name = restaurant['nm']
address = restaurant['addr']
phone = restaurant['phone']
score = restaurant['user_score']
review_count = restaurant['review_cnt']
keywords = ', '.join([keyword['term'] for keyword in restaurant['keyword']])
data.append([idx, name, address, phone, score, review_count, keywords]) # 순번 추가
df = pd.DataFrame(data, columns=["순번", "이름", "주소", "전화번호", "평점", "리뷰 수", "키워드"])
return df
# CSV 파일로 저장 (cp949 우선 시도, 실패 시 utf-8-sig로 폴백)
def save_to_csv(df):
if df is None or df.empty:
st.warning("저장할 데이터가 없습니다.")
return
try:
# CSV 파일 생성을 위한 메모리 버퍼
csv_buffer = StringIO()
# BOM 추가 및 CP949 인코딩으로 변환
df.to_csv(csv_buffer, index=False, encoding='cp949')
csv_str = csv_buffer.getvalue()
# 다운로드 버튼 생성
st.download_button(
label="CSV 파일로 저장",
data=csv_str.encode('cp949'), # 바이트로 인코딩
file_name="restaurants.csv",
mime="text/csv",
)
except UnicodeEncodeError:
# CP949로 변환이 안 되는 문자가 있는 경우 UTF-8로 폴백
csv_buffer = StringIO()
df.to_csv(csv_buffer, index=False, encoding='utf-8-sig')
csv_str = csv_buffer.getvalue()
st.download_button(
label="CSV 파일로 저장",
data=csv_str,
file_name="restaurants.csv",
mime="text/csv",
)
# Streamlit UI
st.title("맛집 검색 프로그램")
# 검색어 입력
query = st.text_input("검색할 지역 또는 음식점 이름을 입력하세요:", "")
# 페이지 및 결과 수 입력
col1, col2 = st.columns(2)
with col1:
page = st.number_input("페이지 번호 (기본값: 1):", min_value=1, value=1)
with col2:
size = st.number_input("결과 수 (기본값: 20):", min_value=1, value=20)
# 검색 버튼
if st.button("검색"):
if not query:
st.warning("검색어를 입력하세요.")
else:
results = search_restaurants(query, page, size)
if results:
df = convert_to_dataframe(results)
if df is not None:
st.dataframe(df) # 결과를 테이블로 표시
save_to_csv(df) # CSV 저장 버튼 표시
4. 맛집 검색 프로그램 실행하기
streamlit run .\find_rest.py
csv파일 출력
5. 마치며
이 정도면 유료 서비스와 견주어도 전혀 밀리 않는 거 같다. 10~30만원 주고 정보 수집하는 비용을 간단한 코딩과 인공지능 문의로 만들 수 있다. 파이썬과 인공지능에 대한 약간의 이해와 노력이 필요하긴 하다만, 이정도 관심이면 누가나 만들 수 있는 수준이다.
프로그램 개발 영역은 당연히 프로그래머의 몫이라 생각했었는데, 이젠 그 경계가 완전히 무서진 것 같다. 크몽이나 클래스 101에서 사용자 프로그램을 판매하는 개발자의 미래가 암울하다. 단순 개발이 아닌 좀 더 창의적인 프로그램을 제공하거나 사용자 교육을 통한 수익 창출을 노려봐야 하지 않을까...
'코드리뷰 > chatGPT(Python)코드' 카테고리의 다른 글
AI 인공지능 프리젠테이션 만들기 자동화 (0) | 2025.02.07 |
---|---|
네이버 부동산 매물정보 수집 프로그램 만들기 10분 컷 (1) | 2025.02.01 |
파이썬 코드로 네이버 뉴스기사 요약하기 -DeepSeek ChatGPT Claude (0) | 2025.01.30 |
댓글