"""
소상공인 진흥공단 OpenAPI
semas(Small Enterprise And Market Service)

1. StoreInfo 클래스: 소상공인 상가업소 정보 조회
    01.지정 상권조회
    02.반경내 상권조회
    03.사각형내 상권조회
    04.행정구역 단위 상권조회
    05.단일 상가업소 조회
    06.건물단위 상가업소 조회
    07.지번단위 상가업소 조회
    08.행정동 단위 상가업소 조회
    09.상권내 상가업소 조회
    10.반경내 상가업소 조회
    11.사각형내 상가업소 조회
    12.다각형내 상가업소 조회
    13.업종별 상가업소 조회
    14.수정일자기준 상가업소 조회
    15.상권정보 업종 대분류 조회
    16.상권정보 업종 중분류 조회
    17.상권정보 업종 소분류 조회
"""

import pandas as pd
import datetime
import logging
import requests
from bs4 import BeautifulSoup


class StoreInfo:
    """
    소상공인 상가업소 정보 조회 클래스
    """

    def __init__(self, serviceKey, debug=False):
        """
        공공 데이터 포털에서 발급받은 Service Key를 입력받아 초기화합니다.
        - serviceKey: 서비스 인증키 문자열
        - debug: True이면 모든 로깅 메시지 출력, False이면 에러 로깅 메시지만 출력
        """
        # 로거 설정
        self.logger = logging.getLogger("root")
        # 로깅 레벨 설정
        if debug == True:
            self.logger.setLevel(logging.INFO)
        else:
            self.logger.setLevel(logging.ERROR)
        # 출력 포매팅 설정 - 시간, 로거이름, 로깅레벨, 메세지
        formatter = logging.Formatter("[%(levelname)s] %(message)s")
        if len(self.logger.handlers) == 0:
            # 스트림 핸들러 설정 - 콘솔에 출력
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
            self.logger.addHandler(stream_handler)

        # OpenAPI 서비스 키 초기화
        self.serviceKey = serviceKey

        # ServiceKey 등록
        self.endpoint = f"http://apis.data.go.kr/B553077/api/open/sdsc/"

        # 오퍼레이션별 URL 및 컬럼 매핑 딕셔너리
        self.metaDict = {
            
            "지정상권": {
                "url": f"{self.endpoint}storeZoneOne?serviceKey={self.serviceKey}",
                "columns": ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords', 'stdrDt'],
            },
            
            "반경상권": {
                "url": f"{self.endpoint}storeZoneInRadius?serviceKey={self.serviceKey}",
                "columns": ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords', 'stdrDt'],
            },

            "사각형상권": {
                "url": f"{self.endpoint}storeZoneInRectangle?serviceKey={self.serviceKey}",
                "columns": ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords', 'stdrDt'],
            },

            "행정구역상권": {
                "url": f"{self.endpoint}storeZoneInAdmi?serviceKey={self.serviceKey}",
                "columns": ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords', 'stdrDt'],
            },

            "단일상가": {
                "url": f"{self.endpoint}storeOne?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "건물상가": {
                "url": f"{self.endpoint}storeListInBuilding?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "지번상가": {
                "url": f"{self.endpoint}storeListInPnu?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "행정동상가": {
                "url": f"{self.endpoint}storeListInDong?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "상권상가": {
                "url": f"{self.endpoint}storeListInArea?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "반경상가": {
                "url": f"{self.endpoint}storeListInRadius?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "사각형상가": {
                "url": f"{self.endpoint}storeListInRectangle?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "다각형상가": {
                "url": f"{self.endpoint}storeListInPolygon?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "업종별상가": {
                "url": f"{self.endpoint}storeListInUpjong?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "수정일자상가": {
                "url": f"{self.endpoint}storeListByDate?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "업종대분류": {
                "url": f"{self.endpoint}largeUpjongList?serviceKey={self.serviceKey}",
                "columns": ["indsLclsCd", "indsLclsNm", "stdrDt"],
            },

            "업종중분류": {
                "url": f"{self.endpoint}middleUpjongList?serviceKey={self.serviceKey}",
                "columns": ["indsLclsCd", "indsLclsNm", "indsMclsCd", "indsMclsNm", "stdrDt"],
            },

            "업종소분류": {
                "url": f"{self.endpoint}smallUpjongList?serviceKey={self.serviceKey}",
                "columns": ['indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'stdrDt'],
            },
            
        }


    def read_data(self, category, **kwargs):
        """
        category: 건축물대장 종류
        """

        # 엔드포인트, 파라미터 및 컬럼 목록 매핑
        try:
            endpoint = self.metaDict[category]['url']
            columns = self.metaDict[category]['columns']
        except:
            self.logger.error(f"{category} 참조 오류")
            return

        try:
            params = ""
            for key, value in kwargs.items():
                params += f"&{key}={value}"
        except:
            self.logger.error(f"{category} 파라미터 파싱 오류")
            return

        try:
            # URL
            url=f"""{endpoint}{params}&numOfRows=99999"""

            # OpenAPI 호출
            result = requests.get(url, verify=False)
            xmlsoup = BeautifulSoup(result.text, "lxml-xml")
            header = xmlsoup.find("header")
            result_code = header.find("resultCode").text
            result_msg = header.find("resultMsg").text
            items = xmlsoup.findAll("item")

        except:
            self.logger.error(f"OpenAPI 호출 오류")
            return

        if result_code == "00":
            """
            결과 정상
            """
            # 데이터프레임 생성
            try:
                df = pd.DataFrame()
                for item in items:
                    row = {}
                    for col in columns:
                        try:
                            tag = item.find(col)
                            row[col] = tag.text.strip()
                        except:
                            row[col] = ""
                    df_ = pd.DataFrame([row])
                    df = df.append(df_)

                if len(df) != 0:
                    df = df[columns]
                    df = self.ChangeCols(df)
                    df.index = range(len(df))

                else:
                    self.logger.info(f"조회 결과 없음")
                    df = pd.DataFrame(columns=columns)
                    df = self.ChangeCols(df)
                    return df

            except:
                self.logger.error(f"조회 로직 오류")
                return

        else:
            """
            결과 에러
            """
            self.logger.error(f"({result_code}) {result_msg}")
            return

        return df

    def ChangeCols(self, df):
        """
        영문 컬럼명을 국문 컬럼명으로 변경
        """
        
        self.colDict = {
            'adongCd': '행정동코드',
            'adongNm': '행정동명',
            'bizesId': '상가업소번호',
            'bizesNm': '상호명',
            'bldMngNo': '건물관리번호',
            'bldMnno': '건물본번지',
            'bldNm': '건물명',
            'bldSlno': '건물부번지',
            'brchNm': '지점명',
            'coordNum': '좌표개수',
            'coords': '좌표값',
            'ctprvnCd': '시도코드',
            'ctprvnNm': '시도명',
            'dongNo': '동정보',
            'flrNo': '층정보',
            'hoNo': '호정보',
            'indsLclsCd': '상권업종대분류코드',
            'indsLclsNm': '상권업종대분류명',
            'indsMclsCd': '상권업종중분류코드',
            'indsMclsNm': '상권업종중분류명',
            'indsSclsCd': '상권업종소분류코드',
            'indsSclsNm': '상권업종소분류명',
            'ksicCd': '표준산업분류코드',
            'ksicNm': '표준산업분류명',
            'lat': '위도',
            'ldongCd': '법정동코드',
            'ldongNm': '법정동명',
            'lnoAdr': '지번주소',
            'lnoCd': 'PNU코드',
            'lnoMnno': '지번본번지',
            'lnoSlno': '지번부번지',
            'lon': '경도',
            'mainTrarNm': '상권명',
            'newZipcd': '신우편번호',
            'oldZipcd': '구우편번호',
            'plotSctCd': '대지구분코드',
            'plotSctNm': '대지구분명',
            'rdnm': '도로명',
            'rdnmAdr': '도로명주소',
            'rdnmCd': '도로명코드',
            'signguCd': '시군구코드',
            'signguNm': '시군구명',
            'stdrDt': '데이터기준일자',
            'trarArea': '면적',
            'trarNo': '상권번호'
        }
        
        df = df.rename(columns=self.colDict)
        return df
a95d1554bf322399967588b94402cb33aaa51852
<mask token>


class StoreInfo:
    <mask token>

    def __init__(self, serviceKey, debug=False):
        """
        공공 데이터 포털에서 발급받은 Service Key를 입력받아 초기화합니다.
        - serviceKey: 서비스 인증키 문자열
        - debug: True이면 모든 로깅 메시지 출력, False이면 에러 로깅 메시지만 출력
        """
        self.logger = logging.getLogger('root')
        if debug == True:
            self.logger.setLevel(logging.INFO)
        else:
            self.logger.setLevel(logging.ERROR)
        formatter = logging.Formatter('[%(levelname)s] %(message)s')
        if len(self.logger.handlers) == 0:
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
            self.logger.addHandler(stream_handler)
        self.serviceKey = serviceKey
        self.endpoint = f'http://apis.data.go.kr/B553077/api/open/sdsc/'
        self.metaDict = {'지정상권': {'url':
            f'{self.endpoint}storeZoneOne?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '반경상권': {'url':
            f'{self.endpoint}storeZoneInRadius?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '사각형상권': {'url':
            f'{self.endpoint}storeZoneInRectangle?serviceKey={self.serviceKey}'
            , 'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '행정구역상권': {'url':
            f'{self.endpoint}storeZoneInAdmi?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '단일상가': {'url':
            f'{self.endpoint}storeOne?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '건물상가': {'url':
            f'{self.endpoint}storeListInBuilding?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '지번상가': {'url':
            f'{self.endpoint}storeListInPnu?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '행정동상가': {'url':
            f'{self.endpoint}storeListInDong?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '상권상가': {'url':
            f'{self.endpoint}storeListInArea?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '반경상가': {'url':
            f'{self.endpoint}storeListInRadius?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '사각형상가': {'url':
            f'{self.endpoint}storeListInRectangle?serviceKey={self.serviceKey}'
            , 'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '다각형상가': {'url':
            f'{self.endpoint}storeListInPolygon?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '업종별상가': {'url':
            f'{self.endpoint}storeListInUpjong?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '수정일자상가': {'url':
            f'{self.endpoint}storeListByDate?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '업종대분류': {'url':
            f'{self.endpoint}largeUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'stdrDt']}, '업종중분류': {
            'url':
            f'{self.endpoint}middleUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'indsMclsCd',
            'indsMclsNm', 'stdrDt']}, '업종소분류': {'url':
            f'{self.endpoint}smallUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'indsMclsCd',
            'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'stdrDt']}}
    <mask token>
    <mask token>

<mask token>


class StoreInfo:
    <mask token>

    def __init__(self, serviceKey, debug=False):
        """
        공공 데이터 포털에서 발급받은 Service Key를 입력받아 초기화합니다.
        - serviceKey: 서비스 인증키 문자열
        - debug: True이면 모든 로깅 메시지 출력, False이면 에러 로깅 메시지만 출력
        """
        self.logger = logging.getLogger('root')
        if debug == True:
            self.logger.setLevel(logging.INFO)
        else:
            self.logger.setLevel(logging.ERROR)
        formatter = logging.Formatter('[%(levelname)s] %(message)s')
        if len(self.logger.handlers) == 0:
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
            self.logger.addHandler(stream_handler)
        self.serviceKey = serviceKey
        self.endpoint = f'http://apis.data.go.kr/B553077/api/open/sdsc/'
        self.metaDict = {'지정상권': {'url':
            f'{self.endpoint}storeZoneOne?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '반경상권': {'url':
            f'{self.endpoint}storeZoneInRadius?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '사각형상권': {'url':
            f'{self.endpoint}storeZoneInRectangle?serviceKey={self.serviceKey}'
            , 'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '행정구역상권': {'url':
            f'{self.endpoint}storeZoneInAdmi?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '단일상가': {'url':
            f'{self.endpoint}storeOne?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '건물상가': {'url':
            f'{self.endpoint}storeListInBuilding?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '지번상가': {'url':
            f'{self.endpoint}storeListInPnu?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '행정동상가': {'url':
            f'{self.endpoint}storeListInDong?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '상권상가': {'url':
            f'{self.endpoint}storeListInArea?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '반경상가': {'url':
            f'{self.endpoint}storeListInRadius?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '사각형상가': {'url':
            f'{self.endpoint}storeListInRectangle?serviceKey={self.serviceKey}'
            , 'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '다각형상가': {'url':
            f'{self.endpoint}storeListInPolygon?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '업종별상가': {'url':
            f'{self.endpoint}storeListInUpjong?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '수정일자상가': {'url':
            f'{self.endpoint}storeListByDate?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '업종대분류': {'url':
            f'{self.endpoint}largeUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'stdrDt']}, '업종중분류': {
            'url':
            f'{self.endpoint}middleUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'indsMclsCd',
            'indsMclsNm', 'stdrDt']}, '업종소분류': {'url':
            f'{self.endpoint}smallUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'indsMclsCd',
            'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'stdrDt']}}

    def read_data(self, category, **kwargs):
        """
        category: 건축물대장 종류
        """
        try:
            endpoint = self.metaDict[category]['url']
            columns = self.metaDict[category]['columns']
        except:
            self.logger.error(f'{category} 참조 오류')
            return
        try:
            params = ''
            for key, value in kwargs.items():
                params += f'&{key}={value}'
        except:
            self.logger.error(f'{category} 파라미터 파싱 오류')
            return
        try:
            url = f'{endpoint}{params}&numOfRows=99999'
            result = requests.get(url, verify=False)
            xmlsoup = BeautifulSoup(result.text, 'lxml-xml')
            header = xmlsoup.find('header')
            result_code = header.find('resultCode').text
            result_msg = header.find('resultMsg').text
            items = xmlsoup.findAll('item')
        except:
            self.logger.error(f'OpenAPI 호출 오류')
            return
        if result_code == '00':
            """
            결과 정상
            """
            try:
                df = pd.DataFrame()
                for item in items:
                    row = {}
                    for col in columns:
                        try:
                            tag = item.find(col)
                            row[col] = tag.text.strip()
                        except:
                            row[col] = ''
                    df_ = pd.DataFrame([row])
                    df = df.append(df_)
                if len(df) != 0:
                    df = df[columns]
                    df = self.ChangeCols(df)
                    df.index = range(len(df))
                else:
                    self.logger.info(f'조회 결과 없음')
                    df = pd.DataFrame(columns=columns)
                    df = self.ChangeCols(df)
                    return df
            except:
                self.logger.error(f'조회 로직 오류')
                return
        else:
            """
            결과 에러
            """
            self.logger.error(f'({result_code}) {result_msg}')
            return
        return df

    def ChangeCols(self, df):
        """
        영문 컬럼명을 국문 컬럼명으로 변경
        """
        self.colDict = {'adongCd': '행정동코드', 'adongNm': '행정동명', 'bizesId':
            '상가업소번호', 'bizesNm': '상호명', 'bldMngNo': '건물관리번호', 'bldMnno':
            '건물본번지', 'bldNm': '건물명', 'bldSlno': '건물부번지', 'brchNm': '지점명',
            'coordNum': '좌표개수', 'coords': '좌표값', 'ctprvnCd': '시도코드',
            'ctprvnNm': '시도명', 'dongNo': '동정보', 'flrNo': '층정보', 'hoNo':
            '호정보', 'indsLclsCd': '상권업종대분류코드', 'indsLclsNm': '상권업종대분류명',
            'indsMclsCd': '상권업종중분류코드', 'indsMclsNm': '상권업종중분류명',
            'indsSclsCd': '상권업종소분류코드', 'indsSclsNm': '상권업종소분류명', 'ksicCd':
            '표준산업분류코드', 'ksicNm': '표준산업분류명', 'lat': '위도', 'ldongCd':
            '법정동코드', 'ldongNm': '법정동명', 'lnoAdr': '지번주소', 'lnoCd': 'PNU코드',
            'lnoMnno': '지번본번지', 'lnoSlno': '지번부번지', 'lon': '경도',
            'mainTrarNm': '상권명', 'newZipcd': '신우편번호', 'oldZipcd': '구우편번호',
            'plotSctCd': '대지구분코드', 'plotSctNm': '대지구분명', 'rdnm': '도로명',
            'rdnmAdr': '도로명주소', 'rdnmCd': '도로명코드', 'signguCd': '시군구코드',
            'signguNm': '시군구명', 'stdrDt': '데이터기준일자', 'trarArea': '면적',
            'trarNo': '상권번호'}
        df = df.rename(columns=self.colDict)
        return df

<mask token>


class StoreInfo:
    """
    소상공인 상가업소 정보 조회 클래스
    """

    def __init__(self, serviceKey, debug=False):
        """
        공공 데이터 포털에서 발급받은 Service Key를 입력받아 초기화합니다.
        - serviceKey: 서비스 인증키 문자열
        - debug: True이면 모든 로깅 메시지 출력, False이면 에러 로깅 메시지만 출력
        """
        self.logger = logging.getLogger('root')
        if debug == True:
            self.logger.setLevel(logging.INFO)
        else:
            self.logger.setLevel(logging.ERROR)
        formatter = logging.Formatter('[%(levelname)s] %(message)s')
        if len(self.logger.handlers) == 0:
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
            self.logger.addHandler(stream_handler)
        self.serviceKey = serviceKey
        self.endpoint = f'http://apis.data.go.kr/B553077/api/open/sdsc/'
        self.metaDict = {'지정상권': {'url':
            f'{self.endpoint}storeZoneOne?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '반경상권': {'url':
            f'{self.endpoint}storeZoneInRadius?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '사각형상권': {'url':
            f'{self.endpoint}storeZoneInRectangle?serviceKey={self.serviceKey}'
            , 'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '행정구역상권': {'url':
            f'{self.endpoint}storeZoneInAdmi?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '단일상가': {'url':
            f'{self.endpoint}storeOne?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '건물상가': {'url':
            f'{self.endpoint}storeListInBuilding?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '지번상가': {'url':
            f'{self.endpoint}storeListInPnu?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '행정동상가': {'url':
            f'{self.endpoint}storeListInDong?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '상권상가': {'url':
            f'{self.endpoint}storeListInArea?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '반경상가': {'url':
            f'{self.endpoint}storeListInRadius?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '사각형상가': {'url':
            f'{self.endpoint}storeListInRectangle?serviceKey={self.serviceKey}'
            , 'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '다각형상가': {'url':
            f'{self.endpoint}storeListInPolygon?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '업종별상가': {'url':
            f'{self.endpoint}storeListInUpjong?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '수정일자상가': {'url':
            f'{self.endpoint}storeListByDate?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '업종대분류': {'url':
            f'{self.endpoint}largeUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'stdrDt']}, '업종중분류': {
            'url':
            f'{self.endpoint}middleUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'indsMclsCd',
            'indsMclsNm', 'stdrDt']}, '업종소분류': {'url':
            f'{self.endpoint}smallUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'indsMclsCd',
            'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'stdrDt']}}

    def read_data(self, category, **kwargs):
        """
        category: 건축물대장 종류
        """
        try:
            endpoint = self.metaDict[category]['url']
            columns = self.metaDict[category]['columns']
        except:
            self.logger.error(f'{category} 참조 오류')
            return
        try:
            params = ''
            for key, value in kwargs.items():
                params += f'&{key}={value}'
        except:
            self.logger.error(f'{category} 파라미터 파싱 오류')
            return
        try:
            url = f'{endpoint}{params}&numOfRows=99999'
            result = requests.get(url, verify=False)
            xmlsoup = BeautifulSoup(result.text, 'lxml-xml')
            header = xmlsoup.find('header')
            result_code = header.find('resultCode').text
            result_msg = header.find('resultMsg').text
            items = xmlsoup.findAll('item')
        except:
            self.logger.error(f'OpenAPI 호출 오류')
            return
        if result_code == '00':
            """
            결과 정상
            """
            try:
                df = pd.DataFrame()
                for item in items:
                    row = {}
                    for col in columns:
                        try:
                            tag = item.find(col)
                            row[col] = tag.text.strip()
                        except:
                            row[col] = ''
                    df_ = pd.DataFrame([row])
                    df = df.append(df_)
                if len(df) != 0:
                    df = df[columns]
                    df = self.ChangeCols(df)
                    df.index = range(len(df))
                else:
                    self.logger.info(f'조회 결과 없음')
                    df = pd.DataFrame(columns=columns)
                    df = self.ChangeCols(df)
                    return df
            except:
                self.logger.error(f'조회 로직 오류')
                return
        else:
            """
            결과 에러
            """
            self.logger.error(f'({result_code}) {result_msg}')
            return
        return df

    def ChangeCols(self, df):
        """
        영문 컬럼명을 국문 컬럼명으로 변경
        """
        self.colDict = {'adongCd': '행정동코드', 'adongNm': '행정동명', 'bizesId':
            '상가업소번호', 'bizesNm': '상호명', 'bldMngNo': '건물관리번호', 'bldMnno':
            '건물본번지', 'bldNm': '건물명', 'bldSlno': '건물부번지', 'brchNm': '지점명',
            'coordNum': '좌표개수', 'coords': '좌표값', 'ctprvnCd': '시도코드',
            'ctprvnNm': '시도명', 'dongNo': '동정보', 'flrNo': '층정보', 'hoNo':
            '호정보', 'indsLclsCd': '상권업종대분류코드', 'indsLclsNm': '상권업종대분류명',
            'indsMclsCd': '상권업종중분류코드', 'indsMclsNm': '상권업종중분류명',
            'indsSclsCd': '상권업종소분류코드', 'indsSclsNm': '상권업종소분류명', 'ksicCd':
            '표준산업분류코드', 'ksicNm': '표준산업분류명', 'lat': '위도', 'ldongCd':
            '법정동코드', 'ldongNm': '법정동명', 'lnoAdr': '지번주소', 'lnoCd': 'PNU코드',
            'lnoMnno': '지번본번지', 'lnoSlno': '지번부번지', 'lon': '경도',
            'mainTrarNm': '상권명', 'newZipcd': '신우편번호', 'oldZipcd': '구우편번호',
            'plotSctCd': '대지구분코드', 'plotSctNm': '대지구분명', 'rdnm': '도로명',
            'rdnmAdr': '도로명주소', 'rdnmCd': '도로명코드', 'signguCd': '시군구코드',
            'signguNm': '시군구명', 'stdrDt': '데이터기준일자', 'trarArea': '면적',
            'trarNo': '상권번호'}
        df = df.rename(columns=self.colDict)
        return df

<mask token>
import pandas as pd
import datetime
import logging
import requests
from bs4 import BeautifulSoup


class StoreInfo:
    """
    소상공인 상가업소 정보 조회 클래스
    """

    def __init__(self, serviceKey, debug=False):
        """
        공공 데이터 포털에서 발급받은 Service Key를 입력받아 초기화합니다.
        - serviceKey: 서비스 인증키 문자열
        - debug: True이면 모든 로깅 메시지 출력, False이면 에러 로깅 메시지만 출력
        """
        self.logger = logging.getLogger('root')
        if debug == True:
            self.logger.setLevel(logging.INFO)
        else:
            self.logger.setLevel(logging.ERROR)
        formatter = logging.Formatter('[%(levelname)s] %(message)s')
        if len(self.logger.handlers) == 0:
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
            self.logger.addHandler(stream_handler)
        self.serviceKey = serviceKey
        self.endpoint = f'http://apis.data.go.kr/B553077/api/open/sdsc/'
        self.metaDict = {'지정상권': {'url':
            f'{self.endpoint}storeZoneOne?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '반경상권': {'url':
            f'{self.endpoint}storeZoneInRadius?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '사각형상권': {'url':
            f'{self.endpoint}storeZoneInRectangle?serviceKey={self.serviceKey}'
            , 'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '행정구역상권': {'url':
            f'{self.endpoint}storeZoneInAdmi?serviceKey={self.serviceKey}',
            'columns': ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords',
            'stdrDt']}, '단일상가': {'url':
            f'{self.endpoint}storeOne?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '건물상가': {'url':
            f'{self.endpoint}storeListInBuilding?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '지번상가': {'url':
            f'{self.endpoint}storeListInPnu?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '행정동상가': {'url':
            f'{self.endpoint}storeListInDong?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '상권상가': {'url':
            f'{self.endpoint}storeListInArea?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '반경상가': {'url':
            f'{self.endpoint}storeListInRadius?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '사각형상가': {'url':
            f'{self.endpoint}storeListInRectangle?serviceKey={self.serviceKey}'
            , 'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '다각형상가': {'url':
            f'{self.endpoint}storeListInPolygon?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '업종별상가': {'url':
            f'{self.endpoint}storeListInUpjong?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '수정일자상가': {'url':
            f'{self.endpoint}storeListByDate?serviceKey={self.serviceKey}',
            'columns': ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd',
            'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd',
            'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm',
            'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd',
            'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno',
            'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno',
            'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd',
            'dongNo', 'flrNo', 'hoNo', 'lon', 'lat']}, '업종대분류': {'url':
            f'{self.endpoint}largeUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'stdrDt']}, '업종중분류': {
            'url':
            f'{self.endpoint}middleUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'indsMclsCd',
            'indsMclsNm', 'stdrDt']}, '업종소분류': {'url':
            f'{self.endpoint}smallUpjongList?serviceKey={self.serviceKey}',
            'columns': ['indsLclsCd', 'indsLclsNm', 'indsMclsCd',
            'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'stdrDt']}}

    def read_data(self, category, **kwargs):
        """
        category: 건축물대장 종류
        """
        try:
            endpoint = self.metaDict[category]['url']
            columns = self.metaDict[category]['columns']
        except:
            self.logger.error(f'{category} 참조 오류')
            return
        try:
            params = ''
            for key, value in kwargs.items():
                params += f'&{key}={value}'
        except:
            self.logger.error(f'{category} 파라미터 파싱 오류')
            return
        try:
            url = f'{endpoint}{params}&numOfRows=99999'
            result = requests.get(url, verify=False)
            xmlsoup = BeautifulSoup(result.text, 'lxml-xml')
            header = xmlsoup.find('header')
            result_code = header.find('resultCode').text
            result_msg = header.find('resultMsg').text
            items = xmlsoup.findAll('item')
        except:
            self.logger.error(f'OpenAPI 호출 오류')
            return
        if result_code == '00':
            """
            결과 정상
            """
            try:
                df = pd.DataFrame()
                for item in items:
                    row = {}
                    for col in columns:
                        try:
                            tag = item.find(col)
                            row[col] = tag.text.strip()
                        except:
                            row[col] = ''
                    df_ = pd.DataFrame([row])
                    df = df.append(df_)
                if len(df) != 0:
                    df = df[columns]
                    df = self.ChangeCols(df)
                    df.index = range(len(df))
                else:
                    self.logger.info(f'조회 결과 없음')
                    df = pd.DataFrame(columns=columns)
                    df = self.ChangeCols(df)
                    return df
            except:
                self.logger.error(f'조회 로직 오류')
                return
        else:
            """
            결과 에러
            """
            self.logger.error(f'({result_code}) {result_msg}')
            return
        return df

    def ChangeCols(self, df):
        """
        영문 컬럼명을 국문 컬럼명으로 변경
        """
        self.colDict = {'adongCd': '행정동코드', 'adongNm': '행정동명', 'bizesId':
            '상가업소번호', 'bizesNm': '상호명', 'bldMngNo': '건물관리번호', 'bldMnno':
            '건물본번지', 'bldNm': '건물명', 'bldSlno': '건물부번지', 'brchNm': '지점명',
            'coordNum': '좌표개수', 'coords': '좌표값', 'ctprvnCd': '시도코드',
            'ctprvnNm': '시도명', 'dongNo': '동정보', 'flrNo': '층정보', 'hoNo':
            '호정보', 'indsLclsCd': '상권업종대분류코드', 'indsLclsNm': '상권업종대분류명',
            'indsMclsCd': '상권업종중분류코드', 'indsMclsNm': '상권업종중분류명',
            'indsSclsCd': '상권업종소분류코드', 'indsSclsNm': '상권업종소분류명', 'ksicCd':
            '표준산업분류코드', 'ksicNm': '표준산업분류명', 'lat': '위도', 'ldongCd':
            '법정동코드', 'ldongNm': '법정동명', 'lnoAdr': '지번주소', 'lnoCd': 'PNU코드',
            'lnoMnno': '지번본번지', 'lnoSlno': '지번부번지', 'lon': '경도',
            'mainTrarNm': '상권명', 'newZipcd': '신우편번호', 'oldZipcd': '구우편번호',
            'plotSctCd': '대지구분코드', 'plotSctNm': '대지구분명', 'rdnm': '도로명',
            'rdnmAdr': '도로명주소', 'rdnmCd': '도로명코드', 'signguCd': '시군구코드',
            'signguNm': '시군구명', 'stdrDt': '데이터기준일자', 'trarArea': '면적',
            'trarNo': '상권번호'}
        df = df.rename(columns=self.colDict)
        return df

"""
소상공인 진흥공단 OpenAPI
semas(Small Enterprise And Market Service)

1. StoreInfo 클래스: 소상공인 상가업소 정보 조회
    01.지정 상권조회
    02.반경내 상권조회
    03.사각형내 상권조회
    04.행정구역 단위 상권조회
    05.단일 상가업소 조회
    06.건물단위 상가업소 조회
    07.지번단위 상가업소 조회
    08.행정동 단위 상가업소 조회
    09.상권내 상가업소 조회
    10.반경내 상가업소 조회
    11.사각형내 상가업소 조회
    12.다각형내 상가업소 조회
    13.업종별 상가업소 조회
    14.수정일자기준 상가업소 조회
    15.상권정보 업종 대분류 조회
    16.상권정보 업종 중분류 조회
    17.상권정보 업종 소분류 조회
"""

import pandas as pd
import datetime
import logging
import requests
from bs4 import BeautifulSoup


class StoreInfo:
    """
    소상공인 상가업소 정보 조회 클래스
    """

    def __init__(self, serviceKey, debug=False):
        """
        공공 데이터 포털에서 발급받은 Service Key를 입력받아 초기화합니다.
        - serviceKey: 서비스 인증키 문자열
        - debug: True이면 모든 로깅 메시지 출력, False이면 에러 로깅 메시지만 출력
        """
        # 로거 설정
        self.logger = logging.getLogger("root")
        # 로깅 레벨 설정
        if debug == True:
            self.logger.setLevel(logging.INFO)
        else:
            self.logger.setLevel(logging.ERROR)
        # 출력 포매팅 설정 - 시간, 로거이름, 로깅레벨, 메세지
        formatter = logging.Formatter("[%(levelname)s] %(message)s")
        if len(self.logger.handlers) == 0:
            # 스트림 핸들러 설정 - 콘솔에 출력
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
            self.logger.addHandler(stream_handler)

        # OpenAPI 서비스 키 초기화
        self.serviceKey = serviceKey

        # ServiceKey 등록
        self.endpoint = f"http://apis.data.go.kr/B553077/api/open/sdsc/"

        # 오퍼레이션별 URL 및 컬럼 매핑 딕셔너리
        self.metaDict = {
            
            "지정상권": {
                "url": f"{self.endpoint}storeZoneOne?serviceKey={self.serviceKey}",
                "columns": ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords', 'stdrDt'],
            },
            
            "반경상권": {
                "url": f"{self.endpoint}storeZoneInRadius?serviceKey={self.serviceKey}",
                "columns": ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords', 'stdrDt'],
            },

            "사각형상권": {
                "url": f"{self.endpoint}storeZoneInRectangle?serviceKey={self.serviceKey}",
                "columns": ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords', 'stdrDt'],
            },

            "행정구역상권": {
                "url": f"{self.endpoint}storeZoneInAdmi?serviceKey={self.serviceKey}",
                "columns": ['trarNo', 'mainTrarNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'trarArea', 'coordNum', 'coords', 'stdrDt'],
            },

            "단일상가": {
                "url": f"{self.endpoint}storeOne?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "건물상가": {
                "url": f"{self.endpoint}storeListInBuilding?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "지번상가": {
                "url": f"{self.endpoint}storeListInPnu?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "행정동상가": {
                "url": f"{self.endpoint}storeListInDong?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "상권상가": {
                "url": f"{self.endpoint}storeListInArea?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "반경상가": {
                "url": f"{self.endpoint}storeListInRadius?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "사각형상가": {
                "url": f"{self.endpoint}storeListInRectangle?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "다각형상가": {
                "url": f"{self.endpoint}storeListInPolygon?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "업종별상가": {
                "url": f"{self.endpoint}storeListInUpjong?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "수정일자상가": {
                "url": f"{self.endpoint}storeListByDate?serviceKey={self.serviceKey}",
                "columns": ['bizesId', 'bizesNm', 'brchNm', 'indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'ksicCd', 'ksicNm', 'ctprvnCd', 'ctprvnNm', 'signguCd', 'signguNm', 'adongCd', 'adongNm', 'ldongCd', 'ldongNm', 'lnoCd', 'plotSctCd', 'plotSctNm', 'lnoMnno', 'lnoSlno', 'lnoAdr', 'rdnmCd', 'rdnm', 'bldMnno', 'bldSlno', 'bldMngNo', 'bldNm', 'rdnmAdr', 'oldZipcd', 'newZipcd', 'dongNo', 'flrNo', 'hoNo', 'lon', 'lat'],
            },

            "업종대분류": {
                "url": f"{self.endpoint}largeUpjongList?serviceKey={self.serviceKey}",
                "columns": ["indsLclsCd", "indsLclsNm", "stdrDt"],
            },

            "업종중분류": {
                "url": f"{self.endpoint}middleUpjongList?serviceKey={self.serviceKey}",
                "columns": ["indsLclsCd", "indsLclsNm", "indsMclsCd", "indsMclsNm", "stdrDt"],
            },

            "업종소분류": {
                "url": f"{self.endpoint}smallUpjongList?serviceKey={self.serviceKey}",
                "columns": ['indsLclsCd', 'indsLclsNm', 'indsMclsCd', 'indsMclsNm', 'indsSclsCd', 'indsSclsNm', 'stdrDt'],
            },
            
        }


    def read_data(self, category, **kwargs):
        """
        category: 건축물대장 종류
        """

        # 엔드포인트, 파라미터 및 컬럼 목록 매핑
        try:
            endpoint = self.metaDict[category]['url']
            columns = self.metaDict[category]['columns']
        except:
            self.logger.error(f"{category} 참조 오류")
            return

        try:
            params = ""
            for key, value in kwargs.items():
                params += f"&{key}={value}"
        except:
            self.logger.error(f"{category} 파라미터 파싱 오류")
            return

        try:
            # URL
            url=f"""{endpoint}{params}&numOfRows=99999"""

            # OpenAPI 호출
            result = requests.get(url, verify=False)
            xmlsoup = BeautifulSoup(result.text, "lxml-xml")
            header = xmlsoup.find("header")
            result_code = header.find("resultCode").text
            result_msg = header.find("resultMsg").text
            items = xmlsoup.findAll("item")

        except:
            self.logger.error(f"OpenAPI 호출 오류")
            return

        if result_code == "00":
            """
            결과 정상
            """
            # 데이터프레임 생성
            try:
                df = pd.DataFrame()
                for item in items:
                    row = {}
                    for col in columns:
                        try:
                            tag = item.find(col)
                            row[col] = tag.text.strip()
                        except:
                            row[col] = ""
                    df_ = pd.DataFrame([row])
                    df = df.append(df_)

                if len(df) != 0:
                    df = df[columns]
                    df = self.ChangeCols(df)
                    df.index = range(len(df))

                else:
                    self.logger.info(f"조회 결과 없음")
                    df = pd.DataFrame(columns=columns)
                    df = self.ChangeCols(df)
                    return df

            except:
                self.logger.error(f"조회 로직 오류")
                return

        else:
            """
            결과 에러
            """
            self.logger.error(f"({result_code}) {result_msg}")
            return

        return df

    def ChangeCols(self, df):
        """
        영문 컬럼명을 국문 컬럼명으로 변경
        """
        
        self.colDict = {
            'adongCd': '행정동코드',
            'adongNm': '행정동명',
            'bizesId': '상가업소번호',
            'bizesNm': '상호명',
            'bldMngNo': '건물관리번호',
            'bldMnno': '건물본번지',
            'bldNm': '건물명',
            'bldSlno': '건물부번지',
            'brchNm': '지점명',
            'coordNum': '좌표개수',
            'coords': '좌표값',
            'ctprvnCd': '시도코드',
            'ctprvnNm': '시도명',
            'dongNo': '동정보',
            'flrNo': '층정보',
            'hoNo': '호정보',
            'indsLclsCd': '상권업종대분류코드',
            'indsLclsNm': '상권업종대분류명',
            'indsMclsCd': '상권업종중분류코드',
            'indsMclsNm': '상권업종중분류명',
            'indsSclsCd': '상권업종소분류코드',
            'indsSclsNm': '상권업종소분류명',
            'ksicCd': '표준산업분류코드',
            'ksicNm': '표준산업분류명',
            'lat': '위도',
            'ldongCd': '법정동코드',
            'ldongNm': '법정동명',
            'lnoAdr': '지번주소',
            'lnoCd': 'PNU코드',
            'lnoMnno': '지번본번지',
            'lnoSlno': '지번부번지',
            'lon': '경도',
            'mainTrarNm': '상권명',
            'newZipcd': '신우편번호',
            'oldZipcd': '구우편번호',
            'plotSctCd': '대지구분코드',
            'plotSctNm': '대지구분명',
            'rdnm': '도로명',
            'rdnmAdr': '도로명주소',
            'rdnmCd': '도로명코드',
            'signguCd': '시군구코드',
            'signguNm': '시군구명',
            'stdrDt': '데이터기준일자',
            'trarArea': '면적',
            'trarNo': '상권번호'
        }
        
        df = df.rename(columns=self.colDict)
        return df