라. VBA 코드 작성
(1) VBA 코드
Option Explicit
Sub Get_URL_XML()
'xml parsing을 위한 변수 설정
Dim xmlHttp As New MSXML2.ServerXMLHTTP60
Dim xDoc As MSXML2.DOMDocument60
Dim xNodes As MSXML2.IXMLDOMNodeList, yNodes As MSXML2.IXMLDOMNodeList, zNodes As MSXML2.IXMLDOMNodeList
Dim xNode As MSXML2.IXMLDOMNode, yNode As MSXML2.IXMLDOMNode, zNode As MSXML2.IXMLDOMNode
Dim i As Integer, j As Integer, k As Integer
Dim endRow As Integer, sanilban As Integer
Dim search_area As Range
Dim dongcode As String, bonbun As String, bubun As String
Dim authKey As String, search_URL As String
'A열에서 G열 지우기
Range("C4:f20000").Clear
' Call ClearImmediateWindow
'xml doc 문서 생성
Set xDoc = New MSXML2.DOMDocument60
'vlookup함수의 2번째 인수인 찾을 범위 지정
Sheets(1).Select
Set search_area = Range("a2").CurrentRegion
Sheets(2).Select
authKey = Range("b1") '인증키
endRow = Range("a" & Rows.Count).End(xlUp).Row '실행할 마지막행 설정
'4행부터 A열의 맨 아래행까지 반복
For i = 4 To endRow
' 행정구역명을 첫번째 시트에서 찾기
If IsError(WorksheetFunction.VLookup(Range("a" & i), search_area, 2, 0)) Then
MsgBox "행정구역명을 정확히 입력하세요."
Else
dongcode = WorksheetFunction.VLookup(Range("a" & i), search_area, 2, 0)
End If
' 산인지 일반인지에 따라 토지(임야)대장구분과 본번, 부번을 구함
If Left(Range("b" & i), 1) = "산" Then
sanilban = 2 '산 지번
'지번에 -이 있는 경우
If InStr("-", Range("b" & i)) > 0 Then
' 본번은 지번에서 두번째 부터 하이픈 전까지인데,
' 앞에 0을 붙인 후 오른쪽 4개를 가져옴
bonbun = Right("000" & Mid(Range("b" & i), 2, InStr("-", Range("b" & i)) - 2), 4)
' 부번은 하이픈 다음부터 끝까지임
bubun = Right("000" & Mid(Range("b" & i), InStr("-", Range("b" & i)) + 1), 4)
Else '지번에 하이픈이 없는 경우
'본번은 지번에서 두번째 부터이고, 앞에 0을 붙인 후 오른쪽 4개를 가져옴
bonbun = Right("000" & Mid(Range("b" & i), 2), 4)
' 부번은 0000임
bubun = "0000"
End If
Else
sanilban = 1 '일반 지번
'지번에 하이픈이 있는 경우
If InStr("-", Range("b" & i)) > 0 Then
' 본번은 지번에서 첫번째 부터 하이픈 전까지인데,
' 앞에 0을 붙인 후 오른쪽 4개를 가져옴
bonbun = Right("000" & Left(Range("b" & i), InStr("-", Range("b" & i)) - 1), 4)
' 부번은 하이픈 다음부터 끝까지임
bubun = Right("000" & Mid(Range("b" & i), InStr("-", Range("b" & i)) + 1), 4)
Else '지번에 하이픈이 없는 경우
' 본번은 지번에서 첫번째 부터 끝까지임
bonbun = Right("000" & Range("b" & i), 4)
' 부번은 0000
bubun = "0000"
End If
End If
search_URL = "http://apis.data.go.kr/1611000/nsdi/eios/LadfrlService/ladfrlList.xml?" & _
"pnu=" & dongcode & sanilban & bonbun & bubun & "&ServiceKey=" & authKey
' GET 방식 URL을 전송
xmlHttp.Open "GET", search_URL, False
xmlHttp.setRequestHeader "Content-Type", "text/xml"
xmlHttp.send
'검증 결과를 xDoc에 저장하고,노드(nodes)를 node 기준으로 parsing
xDoc.LoadXML (xmlHttp.responseText)
' xNodes를 xDoc에서 fileds인 노드를 선택한 후 인덱스 0인 item의 자식노드들을 저장
Set xNodes = xDoc.SelectNodes("fields").Item(0).ChildNodes '.Item(3).ChildNodes
For Each xNode In xNodes
' Debug.Print ("xNode의 노드명: " & xNode.nodeName)
'yNodes 설정
Set yNodes = xNode.ChildNodes
For Each yNode In yNodes
Select Case yNode.nodeName
Case "lndcgrCodeNm" '지목
Range("c" & i) = yNode.Text
Case "lndpclAr" '면적
Range("d" & i) = yNode.Text
Range("d" & i).NumberFormatLocal = "#,##0.0"
Case "posesnSeCodeNm" '소유구분
Range("e" & i) = yNode.Text
Case "ladFrtlScNm" '축적구분
Range("f" & i) = "'" & yNode.Text
Case "lastUpdtDt" '최종 갱신일
Range("g" & i) = yNode.Text
End Select
Next
Next
Next
Range("a3:f3").HorizontalAlignment = xlCenter
Columns("c:g").AutoFit
End Sub
(2) 코드의 구성
아래와 같이 크게 7개로 구분되어 있는데,
① XML 변수 및 일반 변수 선언
② 기존 내용 지우기
③ XML 문서 생성
④ 검색 URL 생성 : PNU 코드 만들기, 인증키를 받아 접속 URL과 연결
⑤ 검색 URL 실행하고, 검색 결과를 XML 문서에 저장
⑥ XML 문서에서 nodeName과 text 알아내서 엑셀에 입력하기
⑦ 가운데 정렬, 열 너비 자동 정렬
(3) 코드 설명
(가) 변수 선언
Dim xmlHttp As New MSXML2.ServerXMLHTTP60 Dim xDoc As MSXML2.DOMDocument60 Dim xNodes As MSXML2.IXMLDOMNodeList, yNodes As MSXML2.IXMLDOMNodeList Dim zNodes As MSXML2.IXMLDOMNodeList Dim xNode As MSXML2.IXMLDOMNode, yNode As MSXML2.IXMLDOMNode, zNode As MSXML2.IXMLDOMNode Dim i As Integer, j As Integer, k As Integer Dim endRow As Integer, sanilban As Integer Dim search_area As Range Dim dongcode As String, bonbun As String, bubun As String Dim authKey As String, search_URL As String |
- Dim xmlHttp As New MSXML2.ServerXMLHTTP60 : XML 파일을 읽을 때는 필요 없는 부분으로, HTTP 전송을 위한 변수 선언 부분입니다.
- Dim endRow As Integer, sanilban As Integer : endRow는 실행할 마지막 행을 저장하기 위한 변수이고, sanilban은 산과 일반지번을 저장하기 위한 변수로 1은 일반 지번, 2는 산 지번으로 하기 위해 정수 형식으로 선언
- Dim search_area As Range : Vlookup 함수의 두 번째 인수인 table_array를 저장하기 위한 변수로 범위 형식임
- Dim dongcode As String, bonbun As String, bubun As String : dongcode는 행정동에 해당하는 코드를 저장하고, bonbun은 본번, bubun은 부번을 저장하기 위한 변수입니다. 네 자리 수로 앞에 0이 있을 수 있기 때문에 문자 형식으로 정했습니다.
- Dim authKey As String, search_URL As String : authKey는 인증키를 저장하기 위한 변수, search_URL은 검색 URL을 저장하기 위한 변수입니다.
(나) 기존 내용 지우기
Range("C4:f20000").Clear |
지목, 면적 등 기존 내용을 지우기 위한 코드로 넉넉하게 F열 2만행까지로 정했으며, Clear는 홈 탭 편집 그룹의 모두 지우기에 해당합니다.
(다) XML 문서 생성
Set xDoc = New MSXML2.DOMDocument60 |
XML Document를 생성하는 구문으로 파일을 읽을 때와 동일합니다.
(라) 검색 URL 생성 : PNU 코드 만들기, 인증키를 받아 접속 URL과 연결
(a) 행정동 코드 찾기
Sheets(1).Select Set search_area = Range("a2").CurrentRegion |
- Sheets(1).Select : 첫 번째 시트, 다시 말해 행정동과 해당 코드가 있는 시트를 선택합니다.
- Set search_area = Range("a2").CurrentRegion : 찾을 범위를 A2셀을 기준으로 한 현재 영역, 다시 말해 연속된 범위로 지정합니다.
Sheets(2).Select authKey = Range("b1") '또는 여기에서 인증키를 입력하고 도구 - 프로젝트 속성에서 비밀번호 설정 endRow = Range("a" & Rows.Count).End(xlUp).Row '실행할 마지막행 설정 '4행부터 A열의 맨 아래행까지 반복 For i = 4 To endRow ' 행정구역명을 첫번째 시트에서 찾기 If IsError(WorksheetFunction.VLookup(Range("a" & i), search_area, 2, 0)) Then MsgBox "행정구역명을 정확히 입력하세요." Else dongcode = WorksheetFunction.VLookup(Range("a" & i), search_area, 2, 0) End If |
- Sheets(2).Select : 첫 번째 시트에서 두 번째 시트, 다시 말해 찾고자 하는 법정동과 지번이 있는 시트로 이동합니다.
- authKey = Range("b1") : B1셀에 있는 인증키를 authKey변수에 저장합니다. 또는 VB Editor 메뉴 도구 - 프로젝트 속성에서 비밀번호를 설정할 수도 있습니다.
- endRow = Range("a" & Rows.Count).End(xlUp).Row : A열의 총 행수, 다시 말해 A열 맨 아래에서 Ctrl + ↑키를 누르면 데이터가 있는 맨 아래 셀에 셀 포인터가 위치하는데, 그 셀의 행수를 endRow변수에 저장합니다.
- For i = 4 To endRow : 4행부터 데이터가 있는 마지막 행, endRow까지 반복 실행합니다.
- If IsError(WorksheetFunction.VLookup(Range("a" & i), search_area, 2, 0)) Then
MsgBox "행정구역명을 정확히 입력하세요."
Else
dongcode = WorksheetFunction.VLookup(Range("a" & i), search_area, 2, 0)
End If
입력된 법정동명을 첫번째 시트에서 찾는데 에러가 난 경우, 다시 말해 법정동이 없을 경우는 "행정구역명을 정확히 입력하세요."란 메시지를 표시하고,
아니면 법정동명에 해당하는 코드를 dongcode변수에 저장합니다.
(b) 토지(임야)대장 구분, 본번, 부번 구하기
㉮ 산 지번인 경우
If Left(Range("b" & i), 1) = "산" Then sanilban = 2 '산 지번 '지번에 -이 있는 경우 If InStr(Range("b" & i), "-") > 0 Then ' 본번은 지번에서 두번째 부터 하이픈 전까지인데, ' 앞에 0을 붙인 후 오른쪽 4개를 가져옴 bonbun = Right("000" & Mid(Range("b" & i), 2, InStr(Range("b" & i), "-") - 2), 4) ' 부번은 하이픈 다음부터 끝까지임 bubun = Right("000" & Mid(Range("b" & i), InStr(Range("b" & i), "-") + 1), 4) Else '지번에 하이픈이 없는 경우 '본번은 지번에서 두번째 부터이고, 앞에 0을 붙인 후 오른쪽 4개를 가져옴 bonbun = Right("000" & Mid(Range("b" & i), 2), 4) ' 부번은 0000임 bubun = "0000" End If |
- If Left(Range("b" & i), 1) = "산" Then : B열 i행 셀의 지번 맨 앞 글자가 "산"인 경우
- sanilban = 2 : 토지(임야) 대장 구분은 2가 되며 이 값을 sanilban 변수에 저장합니다.
- If InStr(Range("b" & i), "-") > 0 Then : B열 i행 셀에 -(하이픈)이 있는 경우
-bonbun = Right("000" & Mid(Range("b" & i), 2, InStr(Range("b" & i), "-") - 2), 4) : 산 지번이므로 두 번째부터 하이픈 이전까지 지번을 가져온 후 앞에 0 3개를 붙인 다음 오른쪽 4개를 가져와 bonbun 변수에 저장합니다. 다시 말해 산35-2번지라면 00035가 되는데 오른쪽 4개를 가져오므로 0035가 됩니다.
- bubun = Right("000" & Mid(Range("b" & i), InStr(Range("b" & i), "-") + 1), 4) : 하이픈이 있는 다음부터 끝까지 지번을 가져와서 앞에 000을 붙인 후 마찬가지로 오른쪽 4개를 가져옵니다. 워크시트 Mid함수의 경우는 Mid(범위, 시작위치, 개수) 형식으로 입력하는데, VBA의 Mid함수는 범위와 시작위치만 지정하고 개수는 지정하지 않아도 됩니다.
- Else : 지번에 하이픈이 없는 경우입니다.
- bonbun = Right("000" & Mid(Range("b" & i), 2), 4) : 산 지번이므로 B열 i행의 지번에서 두번째부터 숫자를 가져와 000을 붙인 후 오른쪽 4개를 가져옵니다.
- bubun = "0000" : 하이픈이 없으므로 부번이 없고, 따라서, 0000을 bubun 변수에 저장합니다. 다시 말해 산35라면 2와 0035, 그리고 0000이 결합되므로 200350000이 됩니다.
㉯ 일반 지번인 경우
Else sanilban = 1 '일반 지번 '지번에 하이픈이 있는 경우 If InStr(Range("b" & i), "-") > 0 Then ' 본번은 지번에서 첫번째 부터 하이픈 전까지인데, ' 앞에 0을 붙인 후 오른쪽 4개를 가져옴 bonbun = Right("000" & Left(Range("b" & i), InStr(Range("b" & i), "-") - 1), 4) ' 부번은 하이픈 다음부터 끝까지임 bubun = Right("000" & Mid(Range("b" & i), InStr(Range("b" & i), "-") + 1), 4) Else '지번에 하이픈이 없는 경우 ' 본번은 지번에서 첫번째 부터 끝까지임 bonbun = Right("000" & Range("b" & i), 4) ' 부번은 0000 bubun = "0000" End If End If |
일반 지번은 산이 없으므로 sanilban이 1이고, 산과는 달리 본번을 첫 번째부터 가져온다는 점만이 다릅니다.
(c) 검색 URL 만들기
search_URL = "http://apis.data.go.kr/1611000/nsdi/eios/LadfrlService/ladfrlList.xml?" & _ "pnu=" & dongcode & sanilban & bonbun & bubun & "&ServiceKey=" & authKey |
검색 URL은 접속 URL http://apis.data.go.kr/1611000/nsdi/eios/LadfrlService/ladfrlList.xml에 에 ?pnu=을 붙인 다음 pnu code를 붙이는데, pnu코드는 dongcode & sanilban & bonbun & bubun, 다시 말해 법정도코드에 토지(임야) 대장 구분, 본번과 부번을 결합한 것입니다.
그리고, &ServiceKey= 다음에 authKey(인증키)를 결합니다.
&가 큰 따옴표 안에 있는 것과 바깥에 있는 것이 있는데 큰 따옴표 안에 있는 것은 Get변수를 전달할 때 변수를 연결하기 위한 것이고, 바깥에 있는 것은 변수끼리 또는 문자와 변수를 결합하기 위한 결합 연산자로 다릅니다.
searh_URL은 http://apis.data.go.kr/1611000/nsdi/eios/LadfrlService/ladfrlList.xml?pnu=1114010300100310000&ServiceKey=R2... 식이 됩니다.
(마) 검색 URL 실행하고, 검색 결과를 XML 문서에 저장
' GET 방식 URL을 전송 xmlHttp.Open "GET", search_URL, False xmlHttp.setRequestHeader "Content-Type", "text/xml" xmlHttp.send '응답 결과를 xDoc에 저장 xDoc.LoadXML (xmlHttp.responseText) |
위 구문은 크게 URL을 전송하는 부분과 응답결과를 xDoc 문서에 저장하는 부분으로 나뉩니다.
파일을 읽어서 저장할 때는 xDoc.Load (S_filename)라고 xDoc.Load라고 쓰는데, URL응답결과를 저장할 때는 xDoc.LoadXML (xmlHttp.responseText)이라고 쓰는 점이 다릅니다.
(바) XML 문서에서 nodeName과 text 알아내서 엑셀에 입력하기
(첫 번째 방법)
' xNodes를 xDoc에서 fileds인 노드를 선택한 후 인덱스 0인 item의 자식노드들을 저장 Set xNodes = xDoc.SelectNodes("fields") For Each xNode In xNodes ' Debug.Print ("xNode의 노드명: " & xNode.nodeName) 'yNodes 설정 Set yNodes = xNode.ChildNodes For Each yNode In yNodes Set zNodes = yNode.ChildNodes For Each zNode In zNodes Select Case zNode.nodeName Case "lndcgrCodeNm" '지목 Range("c" & i) = zNode.Text Case "lndpclAr" '면적 Range("d" & i) = zNode.Text Range("d" & i).NumberFormatLocal = "#,##0.0" Case "posesnSeCodeNm" '소유구분 Range("e" & i) = zNode.Text Case "ladFrtlScNm" '축적구분 Range("f" & i) = "'" & zNode.Text Case "lastUpdtDt" '최종 갱신일 Range("g" & i) = zNode.Text End Select Next Next Next Next |
응답결과가 아래와 같이 fields, ladfrlVOList, 그 아래 pnu 등으로 3단계로 되어 있으므로 for each문도 3단계로 구성했습니다.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<fields>
<numOfRows>10</numOfRows>
<pageNo>1</pageNo>
<totalCount>1</totalCount>
<ladfrlVOList>
<pnu>1114010300100310000</pnu>
<ldCode>1114010300</ldCode>
<ldCodeNm>서울특별시 중구 태평로1가</ldCodeNm>
<mnnmSlno>31-0</mnnmSlno>
<regstrSeCode>1</regstrSeCode>
<regstrSeCodeNm>토지대장</regstrSeCodeNm>
<lndcgrCode>08</lndcgrCode>
<lndcgrCodeNm>대</lndcgrCodeNm>
<lndpclAr>12709.4</lndpclAr>
<posesnSeCode>04</posesnSeCode>
<posesnSeCodeNm>시, 도유지</posesnSeCodeNm>
<cnrsPsnCo>0</cnrsPsnCo>
<ladFrtlSc>06</ladFrtlSc>
<ladFrtlScNm>1:600</ladFrtlScNm>
<lastUpdtDt>2022-06-07</lastUpdtDt>
</ladfrlVOList>
</fields>
- Set xNodes = xDoc.SelectNodes("fields") : 첫번째 노드 네임 fields에 해당하는 노드 들을 xNodes 변수에 저장합니다. 범위 지정할 때처럼 Set문을 사용합니다.
- Set yNodes = xNode.ChildNodes : yNodes는 xNode의 자식 노드들로 설정합니다. numOfRows, pageNo 등이 해당됩니다.
- Set zNodes = yNode.ChildNodes : zNodes는 yNode의 자식 노드들로 설정합니다.
- Select Case zNode.nodeName
Case "lndcgrCodeNm" '지목
Range("c" & i) = zNode.Text
zNode의 노드 네임이 lndcgrCodeNm인 경우 지목이므로 C열 i행에 입력합니다.
- Range("d" & i) = zNode.Text
Range("d" & i).NumberFormatLocal = "#,##0.0"
D열 i행에 면적을 입력하는데 쉼표를 입력하고 소수점이하 첫째 자리까지 입력합니다.
- Range("f" & i) = "'" & zNode.Text : F열 i행에 축적을 입력하는데 1:600으로 입력하면 소수로 입력돼서 앞에 '를 입력해서 문자 형식으로 입력되도록 했습니다.
(두 번째 방법)
아래와 같이 yNode까지만 설정한 후 SelectSingleNode안에 nodeName을 직접 입력하여 셀에 값을 입력할 수도 있습니다.
Set xNodes = xDoc.SelectNodes("fields") For Each xNode In xNodes ' Debug.Print ("xNode의 노드명: " & xNode.nodeName) 'yNodes 설정 Set yNodes = xNode.SelectNodes("ladfrlVOList") For Each yNode In yNodes Range("c" & i) = yNode.SelectSingleNode("lndcgrCodeNm").Text Range("d" & i) = yNode.SelectSingleNode("lndpclAr").Text Range("e" & i) = yNode.SelectSingleNode("posesnSeCodeNm").Text Range("f" & i) = "'" & yNode.SelectSingleNode("ladFrtlScNm").Text Range("g" & i) = yNode.SelectSingleNode("lastUpdtDt").Text Range("d" & i).NumberFormatLocal = "#,##0.0" Next Next |
(세 번째 방법)
xNodes를 설정할 때 item과 selectNodes("ladfrlVOList")를 이용해 yNodes까지 설정해서 1단계로 끝낼 수 있습니다.
Set xNodes = xDoc.SelectNodes("fields").Item(0).SelectNodes("ladfrlVOList") For Each xNode In xNodes Range("c" & i) = xNode.SelectSingleNode("lndcgrCodeNm").Text Range("d" & i) = xNode.SelectSingleNode("lndpclAr").Text Range("e" & i) = xNode.SelectSingleNode("posesnSeCodeNm").Text Range("f" & i) = "'" & xNode.SelectSingleNode("ladFrtlScNm").Text Range("g" & i) = xNode.SelectSingleNode("lastUpdtDt").Text Range("d" & i).NumberFormatLocal = "#,##0.0" Next |
(네 번째 방법)
xNodes를 설정할 때 zNodes까지 설정해서 1단계로 끝내는데, 이 때는 xNode.SelectSingleNode("lndcgrCodeNm")가 아니라 xNode.nodeName에 따라 Select Case문으로 처리해야 합니다.
Set xNodes = xDoc.SelectNodes("fields").Item(0).ChildNodes.Item(3).ChildNodes For Each xNode In xNodes Select Case xNode.nodeName Case "lndcgrCodeNm" '지목 Range("c" & i) = xNode.Text Case "lndpclAr" '면적 Range("d" & i) = xNode.Text Range("d" & i).NumberFormatLocal = "#,##0.0" Case "posesnSeCodeNm" '소유구분 Range("e" & i) = xNode.Text Case "ladFrtlScNm" '축적구분 Range("f" & i) = "'" & xNode.Text Case "lastUpdtDt" '최종 갱신일 Range("g" & i) = xNode.Text End Select Next |
검색 결과는 아래와 같으며, 인증키는 지웠으므로 각자 발급받아 사용하기 바랍니다.
'EXCEL - VBA' 카테고리의 다른 글
날짜 변환 - DateValue 함수, 선택하여 붙여넣기 VBA(2) (0) | 2023.02.17 |
---|---|
날짜 변환 - DateValue 함수, 선택하여 붙여넣기 VBA(1) (0) | 2023.02.15 |
엑셀 VBA - XML Parsing(XML 6.0 기준) (3) (LoadXML - 1) (0) | 2023.02.13 |
엑셀 VBA - XML Parsing(XML 6.0 기준) (1) (0) | 2023.02.08 |
CurrentRegion 속성을 이용한 합계 등 계산(3) (0) | 2023.02.07 |