thumbnail image

사업자등록상태 조회 프로그램 (휴/폐업, 간이/일반 여부)

Taedi

·

2021. 2. 6. 02:00

썸네일


다운로드

사업자등록 상태 조회.zip
5.37MB
사업자등록 상태 조회.z01
10.00MB
사업자등록 상태 조회.z02
10.00MB
사업자등록 상태 조회.z03
10.00MB
사업자등록 상태 조회.z04
10.00MB
사업자등록 상태 조회.z05
10.00MB

- 코딩 초보가 제작한 것으로 다소 느리거나 오류가 발생할 수 있습니다.

- 프로그램에 문제가 있다면 바로 삭제하겠습니다.


동기

 

이전 회사에서 거래처가 휴업 또는 폐업 처리한 상황인지 여부와 간이과세와 일반과세 여부를 주기적으로 확인하는 업무가 있었습니다. 일부 ERP 프로그램에서 사업자 조회가 가능한 경우가 있는 것으로 알고 있지만 아쉽게도 제가 근무하는 환경에서는 그렇지 않았습니다.

 

 

그럴 때마다 홈택스 조회 화면에서 사업자번호를 입력해야 했는데, 단 건은 상관없지만 수백 개의 사업자번호를 모두 조회하는 것은 대단한 근성이 필요했습니다.

 

홈택스 사업자등록상태조회 페이지

 

그래서 사업자등록상태를 대량으로 조회할 수 있는 프로그램을 제작해 보았습니다.

 

제작

웹 사이트를 다루는 방법은 잘 몰라서 인터넷을 찾아보며 끙끙댔는데

위의 링크에서 사업자번호를 입력하고 조회할 때 '개발자 도구' 부분을 확인해보면

'요청 URL'을 'POST' 방식으로 호출하는 것을 확인할 수 있었고

프로그램에서 이것을 대신해주면 되지 않을까 생각해 보았습니다.

 

엣지 브라우저 개발자 도구

 

그래서 requests 라이브러리를 활용하여 POST 방식으로 요청한 것을 ElementTree로 필요한 부분만 쏙 파싱 했더니 다행히도 원하는 값을 얻을 수 있었습니다.

 

- 코드

import requests
import xml.etree.ElementTree as ET

Biznum = "1234567890"   # 검색할 사업자번호

url = "https://teht.hometax.go.kr/wqAction.do?actionId=ATTABZAA001R08&screenId=UTEABAAA13&popupYn=false&realScreenId="
post = "<map id=\"ATTABZAA001R08\"><pubcUserNo/><mobYn>N</mobYn><inqrTrgtClCd>1</inqrTrgtClCd><txprDscmNo>Biznum</txprDscmNo><dongCode>81</dongCode><psbSearch>Y</psbSearch><map id=\"userReqInfoVO\"/></map><nts<nts>nts>58cKuokaDhrUdtF8gFLDQZU6XMel7xRdgvDvT322quE47"

res = requests.post(url, data=post.replace("Biznum",Biznum))
k = ET.fromstring(res.text).findtext("trtCntn")

print(k)

- 결과

국세청에 등록되지 않은 사업자등록번호입니다.

 

여기에 UI를 입히고 조회 결과값을 엑셀로 저장할 수 있도록 이것저것 추가하여 최종적으로 프로그램을 완성해 보았습니다. 

완성품

- 프로그램 화면

프로그램 작동 화면

- 엑셀 

엑셀 전환 결과

 

 

최종코드

꾸역꾸역 만들긴 했지만 pyqt5, pandas, re 같은 부분은 여전히 너무 어려워서 아직 많이 공부해야 할 것 같습니다..

더보기
# -*- coding: utf-8 -*-

import requests, re, datetime, icon
import xml.etree.ElementTree as ET
from PyQt5 import QtCore, QtGui, QtWidgets
import pandas as pd

url = "https://teht.hometax.go.kr/wqAction.do?actionId=ATTABZAA001R08&screenId=" \
    + "UTEABAAA13&popupYn=false&realScreenId="
post = "<map id=\"ATTABZAA001R08\"><pubcUserNo/><mobYn>N</mobYn>" \
    + "<inqrTrgtClCd>1</inqrTrgtClCd><txprDscmNo>Biznum</txprDscmNo>" \
    + "<dongCode>81</dongCode><psbSearch>Y</psbSearch>" \
    + "<map id=\"userReqInfoVO\"/></map><nts<nts>nts>58cKuokaDhrUdtF8gFLDQZU6XMel7xRdgvDvT322quE47"


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        #MainWindow.resize(733, 537)
        MainWindow.setFixedWidth(733)    # 사이즈 고정
        MainWindow.setFixedHeight(537)        
        MainWindow.setWindowIcon(QtGui.QIcon(':/icon.png'))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
        self.centralwidget.setSizePolicy(sizePolicy)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.gridLayoutWidget.setGeometry(QtCore.QRect(0, 10, 731, 521))
        self.gridLayoutWidget.setObjectName("gridLayoutWidget")
        self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
        self.gridLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setObjectName("gridLayout")
        self.tableWidget = QtWidgets.QTableWidget(self.gridLayoutWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.tableWidget.sizePolicy().hasHeightForWidth())
        self.tableWidget.setSizePolicy(sizePolicy)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        self.gridLayout.addWidget(self.tableWidget, 1, 1, 1, 1)
        self.btnSearch = QtWidgets.QPushButton(self.gridLayoutWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.btnSearch.sizePolicy().hasHeightForWidth())
        self.btnSearch.setSizePolicy(sizePolicy)
        self.btnSearch.setObjectName("btnSearch")
        self.gridLayout.addWidget(self.btnSearch, 2, 0, 1, 1)
        self.btnSave = QtWidgets.QPushButton(self.gridLayoutWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.btnSave.sizePolicy().hasHeightForWidth())
        self.btnSave.setSizePolicy(sizePolicy)
        self.btnSave.setObjectName("btnSave")
        self.gridLayout.addWidget(self.btnSave, 2, 1, 1, 1)
        self.tedtBiznum = QtWidgets.QPlainTextEdit(self.gridLayoutWidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.tedtBiznum.sizePolicy().hasHeightForWidth())
        self.tedtBiznum.setSizePolicy(sizePolicy)
        self.tedtBiznum.setMinimumSize(QtCore.QSize(0, 0))
        self.tedtBiznum.setMaximumSize(QtCore.QSize(200, 16777215))
        self.tedtBiznum.setObjectName("tedtBiznum")
        self.gridLayout.addWidget(self.tedtBiznum, 1, 0, 1, 1)
        self.label = QtWidgets.QLabel(self.gridLayoutWidget)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
        self.label_2 = QtWidgets.QLabel(self.gridLayoutWidget)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1)
        self.progressBar = QtWidgets.QProgressBar(self.gridLayoutWidget)
        self.progressBar.setProperty("value", 0)
        self.progressBar.setObjectName("progressBar")
        self.gridLayout.addWidget(self.progressBar, 3, 0, 1, 2)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)


        # ------------------------------------------------
        # UI 이벤트 설정
        # ------------------------------------------------
        self.btnSave.clicked.connect(self.btnSave_clicked)
        self.btnSearch.clicked.connect(self.btnSearch_clicked)


    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "사업자등록상태 조회"))
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "사업자번호"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "결과"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "조회일시"))
        self.tableWidget.setColumnWidth(0, 100)
        self.tableWidget.setColumnWidth(1, 300)
        self.tableWidget.setColumnWidth(2, 100)
        self.btnSearch.setText(_translate("MainWindow", "검색"))
        self.btnSave.setText(_translate("MainWindow", "결과 저장"))
        self.label.setText(_translate("MainWindow", "사업자번호(엔터로 구분)"))
        self.label_2.setText(_translate("MainWindow", "조회결과"))

    def btnSave_clicked(self):
        options = QtWidgets.QFileDialog.Options()
        options |= QtWidgets.QFileDialog.DontUseNativeDialog
        fname = QtWidgets.QFileDialog.getSaveFileName(
            MainWindow, "결과내역 다운로드", "", "xlsx(*.xlsx)", options=options)
        
        if fname[0]:
            df = pd.DataFrame()
            rows = self.tableWidget.rowCount()
            columns = self.tableWidget.columnCount()        

            for i in range(rows):            
                for j in range(columns):  
                    try:
                        df.loc[i , j] = str(self.tableWidget.item(i, j).text()) 
                    except AttributeError:
                        df.loc[i , j] = ""
            
            df.columns = ["사업자번호", "결과", "조회일시"]
            
            writer = pd.ExcelWriter(fname[0] + ".xlsx", engine='xlsxwriter')

            df.to_excel(writer,
                    sheet_name = '조회결과', 
                    na_rep = 'NaN', 
                    float_format = "%.2f", 
                    header = True, 
                    index = True, 
                    startrow = 0, 
                    startcol = 0, 
                    engine = 'xlsxwriter', 
                    freeze_panes = (1, 0)
                    )
            
            worksheet = writer.sheets["조회결과"]
            worksheet.set_column('A:A', 5)
            worksheet.set_column('B:B', 20)
            worksheet.set_column('C:C', 80)
            worksheet.set_column('D:D', 20)
            writer.save()
    
    def btnSearch_clicked(self):
        # 결과창 비우기
        # self.tableWidget.setRowCount(0)

        count = self.tableWidget.rowCount()

        # 데이터 가공
        inputdata = re.sub('[^0-9\n]', '',self.tedtBiznum.toPlainText()).strip()
        biznums = inputdata.split("\n") 
        biznums = [x for x in biznums if len(x)>=1]


        t = 1

        # 하나씩 읽어오기
        for i in biznums:
            total = len(biznums)
            max_retries = 40 # 시도횟수
            for retries in range(max_retries):
                try:
                    res = requests.post(url, data=post.replace("Biznum",i))
                    result = ET.fromstring(res.text).findtext("trtCntn")
                # 오류 발생 시
                except:
                    pass
                # 정상 완료 시
                else:
                    self.tableWidget.setRowCount(count + 1)
                    self.tableWidget.setItem(count, 0, QtWidgets.QTableWidgetItem(i))
                    self.tableWidget.setItem(count, 1, QtWidgets.QTableWidgetItem(result))
                    date = datetime.datetime.now().strftime('%Y-%m-%d')
                    self.tableWidget.setItem(count, 2, QtWidgets.QTableWidgetItem(date))
                    self.tableWidget.repaint()
                    count += 1
                    break
                
                # 실패 했을 경우
                if retries + 1 >= max_retries:
                    count += 1

            prog = t / total * 100
            self.progressBar.setValue(prog)
            self.progressBar.repaint()
            t += 1

        # 검색창 비우기
        self.tedtBiznum.clear()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
반응형

티스토리 아이디로 코멘트를 남기려면

여기를 눌러주세요!

3 Comments

  • 아바타 이미지

    감사합니다

    2021.05.18 11:09

    감사합니다. 덕분에 6명의 팀원의 일을 덜었습니다.

    • 아바타 이미지

      BlogIcon Taedi

      2021.05.18 12:38 신고

      별거아닌 실력이 도움이 되었다니 뿌듯하네요 ㅎㅎ 좋은 하루 되시기 바랍니다!

  • 아바타 이미지

    브라우니

    2021.11.01 09:20

    선생님 오늘 월초라서 사업자번호 400개정도 조회하다가.. 현타가와서.. ㅋㅋㅋㅋ
    몇년째 하는일이긴하지만.. 매크로로 돌리고 있었거든요. 근데 그마저도 오류가 심해서
    국세청 api 공지보고 어느 능력자분께서 만들어주시지 않았을까하고 (예전부터 찾아보기만..ㅎㅎ 능력이없어서)
    찾아봤는데 이렇게 보물이 있었네요. 아침마다 30~40분 걸리던걸 1-2분만에 조질 수 있으니 너무 감사합니다 ㅠ.ㅠ

닫기 아이콘
사이드 프로필 배경이미지
아바타 이미지

Taedi's Log

#태디 #코딩린이

자습한 내용을 기록하는 공간이라 다소 먼 길로 돌아가는 방법들이 존재할 수 있습니다🐹 Python, Web에 관심을 갖기 시작했습니다🐶