공공데이터 포털 open API 이용하기

R Crawling

관세청 수출입실적 쉽게 수집하기

nackta true
10-30-2021

공공데이터 포털의 open API

공공데이터 포털에서 제공하는 open API가 7천여개 정도 있으며 해당 사이트에서는 이용자들이 API를 쉽게 이용할 수 있도록 샘플코드를 다음과 같이 제공하고 있습니다.

# install.packages("httr")
# 
# library(httr)
# GET('http://openapi.customs.go.kr/openapi/service/newTradestatistics/getNitemtradeList?
#     serviceKey=ServiceKey&searchBgnDe=201601&searchEndDe=201601&searchItemCd=0202&searchStatCd=US')

위 코드는 사실 이용하기 힘듭니다… 그래서 새로운 방법으로 API수집을 위한 코딩이 필요하죠. 이번 포스팅에서는 관세청 수출입실적 open API를 수집하는 함수를 만들어보겠습니다.

관세청 수출입실적

관세청 수출입실적은 아래 그림처럼 시작년도월, 끝년도월, 품목코드, 국가명을 요청하면 요청값에 맞는 수출입실적 데이터를 출력해주는 API입니다.

API 수집 코드의 흐름

url만들기

요청하는 url를 확인해보면 다음과 같습니다.


http://openapi.customs.go.kr/openapi/service/newTradestatistics/getNitemtradeList?

serviceKey=ServiceKey&

searchBgnDe=201601&

searchEndDe=201601&

searchItemCd=0202&

searchStatCd=US


굵은 글씨만 바꿔서 url를 만들 수 있도록 하는 것이 첫번째 목표입니다. 각 요청값을 인자로 받아 요청url을 만들어주는 함수를 만듭니다.

one_getNitemtradeList <- function(ServiceKey, startpoint, endpoint, hscode, country){
  
    url <- paste0("http://openapi.customs.go.kr/openapi/service/newTradestatistics/getNitemtradeList?ServiceKey=",
                  ServiceKey,
                  
                  "&searchBgnDe=", startpoint,
                  "&searchEndDe=", endpoint,
                  "&searchItemCd=", hscode,
                  "&searchStatCd=", country)
    return(url)
}

myurl <- one_getNitemtradeList('ServiceKey', '201601', '201612', '0202', 'US')
myurl
[1] "http://openapi.customs.go.kr/openapi/service/newTradestatistics/getNitemtradeList?ServiceKey=ServiceKey&searchBgnDe=201601&searchEndDe=201612&searchItemCd=0202&searchStatCd=US"

여기서 paste0() 함수는 각 문자열을 빈칸없이 이어서 출력해줍니다.

paste0('a', 'b', 'c')
[1] "abc"

url을 이용하여 요청값 받기

공공데이터포털에서 제공하는 API중 상당부분이 XML형식으로 제공하고 있습니다. 관세청 데이터도 마찬가지 입니다. 그러므로 xml파일을 읽기위한 “xml2”패키지를 사용해보겠습니다. 저는 블로그에 서비스키를 노출할 수 없으므로 xml파일로 저장하여 불러오겠습니다. 실제로는 read_xml(myurl)을 실행시키면 됩니다.

library(xml2)
# read_xml(myurl)
myxml <- read_xml('myurl.xml')
myxml
{xml_document}
<response>
[1] <header>\n  <resultCode>00</resultCode>\n  <resultMsg>NORMAL SE ...
[2] <body>\n  <items>\n    <item>\n      <balPayments>-93603291</ba ...

xml을 쉽게 읽을 수 있을때까지 하위 노드로 이동합니다.

{xml_nodeset (2)}
[1] <header>\n  <resultCode>00</resultCode>\n  <resultMsg>NORMAL SE ...
[2] <body>\n  <items>\n    <item>\n      <balPayments>-93603291</ba ...
{xml_nodeset (3)}
[1] <resultCode>00</resultCode>
[2] <resultMsg>NORMAL SERVICE.</resultMsg>
[3] <items>\n  <item>\n    <balPayments>-93603291</balPayments>\n   ...
xml_expimp <- myxml %>% xml_children() %>% xml_children() %>% xml_children
xml_expimp
{xml_nodeset (13)}
 [1] <item>\n  <balPayments>-93603291</balPayments>\n  <expDlr>1508 ...
 [2] <item>\n  <balPayments>-49505329</balPayments>\n  <expDlr>1691 ...
 [3] <item>\n  <balPayments>-42048571</balPayments>\n  <expDlr>0</e ...
 [4] <item>\n  <balPayments>-47107245</balPayments>\n  <expDlr>0</e ...
 [5] <item>\n  <balPayments>-63364870</balPayments>\n  <expDlr>0</e ...
 [6] <item>\n  <balPayments>-63149821</balPayments>\n  <expDlr>3069 ...
 [7] <item>\n  <balPayments>-57094516</balPayments>\n  <expDlr>0</e ...
 [8] <item>\n  <balPayments>-81466942</balPayments>\n  <expDlr>0</e ...
 [9] <item>\n  <balPayments>-70583966</balPayments>\n  <expDlr>0</e ...
[10] <item>\n  <balPayments>-75331115</balPayments>\n  <expDlr>0</e ...
[11] <item>\n  <balPayments>-62108095</balPayments>\n  <expDlr>0</e ...
[12] <item>\n  <balPayments>-44762813</balPayments>\n  <expDlr>0</e ...
[13] <item>\n  <balPayments>-750126574</balPayments>\n  <expDlr>626 ...

위처럼 3번째 하위 노드로 내려가면 <변수명>출력값 형태의 출력값에 접근할 수 있습니다. 이제 데이터 프레임 형태로 정리하는 일만 남았습니다.

데이터 프레임으로 정리

xml_expimp의 첫번째 노드를 살펴보면 각 출력값마다 ’/’이 포함되 있는 규칙을 발견할 수 있습니다.

xml_expimp[1]
{xml_nodeset (1)}
[1] <item>\n  <balPayments>-93603291</balPayments>\n  <expDlr>15082 ...

이를 xml_find_all()과 정규표현식을 이용해 다음과 같이 정리할 수 있습니다.

temp_row <- xml_find_all(xml_expimp[1], './*')
temp_row
{xml_nodeset (10)}
 [1] <balPayments>-93603291</balPayments>
 [2] <expDlr>150822</expDlr>
 [3] <expWgt>18003</expWgt>
 [4] <hsCd>0202</hsCd>
 [5] <impDlr>93754113</impDlr>
 [6] <impWgt>14809834</impWgt>
 [7] <statCd>US</statCd>
 [8] <statCdCntnKor1>미국</statCdCntnKor1>
 [9] <statKor>쇠고기(냉동한 것으로 한정한다)&#13;\n</statKor>
[10] <year>2016.01</year>

이제 출력명과 출력값을 나눌 차례입니다. xml_name()통해 <출력명>을 추출하고, xml_text()통해 출력값을 추출할 수 있습니다.

temp_row %>% xml_name()
 [1] "balPayments"    "expDlr"         "expWgt"        
 [4] "hsCd"           "impDlr"         "impWgt"        
 [7] "statCd"         "statCdCntnKor1" "statKor"       
[10] "year"          
temp_row %>% xml_text()
 [1] "-93603291"                         
 [2] "150822"                            
 [3] "18003"                             
 [4] "0202"                              
 [5] "93754113"                          
 [6] "14809834"                          
 [7] "US"                                
 [8] "미국"                              
 [9] "쇠고기(냉동한 것으로 한정한다)\r\n"
[10] "2016.01"                           

이를 tibble을 사용하여 데이터 프레임 형태로 정리하면,

library(tibble)
tibble(idx = 1, key = temp_row %>% xml_name(), value = temp_row %>% xml_text())
# A tibble: 10 x 3
     idx key            value                               
   <dbl> <chr>          <chr>                               
 1     1 balPayments    "-93603291"                         
 2     1 expDlr         "150822"                            
 3     1 expWgt         "18003"                             
 4     1 hsCd           "0202"                              
 5     1 impDlr         "93754113"                          
 6     1 impWgt         "14809834"                          
 7     1 statCd         "US"                                
 8     1 statCdCntnKor1 "미국"                              
 9     1 statKor        "쇠고기(냉동한 것으로 한정한다)\r\n"
10     1 year           "2016.01"                           

이를 모든 노드에 적용하여 tibble로 만들어 줍니다.

tmp1 <- lapply(seq_along(xml_expimp),
               function(x){
                 temp_row <- xml_find_all(xml_expimp[x], './*')
                 tibble(idx = x,
                        key = temp_row %>% xml_name(),
                        value = temp_row %>% xml_text()
                 ) %>% return()
                 }
               )
tmp1 %>% head(3)
[[1]]
# A tibble: 10 x 3
     idx key            value                               
   <int> <chr>          <chr>                               
 1     1 balPayments    "-93603291"                         
 2     1 expDlr         "150822"                            
 3     1 expWgt         "18003"                             
 4     1 hsCd           "0202"                              
 5     1 impDlr         "93754113"                          
 6     1 impWgt         "14809834"                          
 7     1 statCd         "US"                                
 8     1 statCdCntnKor1 "미국"                              
 9     1 statKor        "쇠고기(냉동한 것으로 한정한다)\r\n"
10     1 year           "2016.01"                           

[[2]]
# A tibble: 10 x 3
     idx key            value                               
   <int> <chr>          <chr>                               
 1     2 balPayments    "-49505329"                         
 2     2 expDlr         "169130"                            
 3     2 expWgt         "18051"                             
 4     2 hsCd           "0202"                              
 5     2 impDlr         "49674459"                          
 6     2 impWgt         "8592190"                           
 7     2 statCd         "US"                                
 8     2 statCdCntnKor1 "미국"                              
 9     2 statKor        "쇠고기(냉동한 것으로 한정한다)\r\n"
10     2 year           "2016.02"                           

[[3]]
# A tibble: 10 x 3
     idx key            value                               
   <int> <chr>          <chr>                               
 1     3 balPayments    "-42048571"                         
 2     3 expDlr         "0"                                 
 3     3 expWgt         "0"                                 
 4     3 hsCd           "0202"                              
 5     3 impDlr         "42048571"                          
 6     3 impWgt         "7274029"                           
 7     3 statCd         "US"                                
 8     3 statCdCntnKor1 "미국"                              
 9     3 statKor        "쇠고기(냉동한 것으로 한정한다)\r\n"
10     3 year           "2016.03"                           

각 tibble을 bind_rows()를 이용하여 하나의 tibble로 만들어줍니다.

tmp2 <- bind_rows(tmp1)
tmp2
# A tibble: 130 x 3
     idx key            value                               
   <int> <chr>          <chr>                               
 1     1 balPayments    "-93603291"                         
 2     1 expDlr         "150822"                            
 3     1 expWgt         "18003"                             
 4     1 hsCd           "0202"                              
 5     1 impDlr         "93754113"                          
 6     1 impWgt         "14809834"                          
 7     1 statCd         "US"                                
 8     1 statCdCntnKor1 "미국"                              
 9     1 statKor        "쇠고기(냉동한 것으로 한정한다)\r\n"
10     1 year           "2016.01"                           
# ... with 120 more rows

spread()이용하여 출력명을 기준으로 출력값을 정리해줍니다. 이렇게 하면 원하는 데이터 프레임 형태의 값을 얻을 수 있습니다.

library(tidyr)
spread(tmp2, key = 'key', value = 'value')
# A tibble: 13 x 11
     idx balPayments expDlr expWgt hsCd  impDlr    impWgt    statCd
   <int> <chr>       <chr>  <chr>  <chr> <chr>     <chr>     <chr> 
 1     1 -93603291   150822 18003  0202  93754113  14809834  US    
 2     2 -49505329   169130 18051  0202  49674459  8592190   US    
 3     3 -42048571   0      0      0202  42048571  7274029   US    
 4     4 -47107245   0      0      0202  47107245  8142437   US    
 5     5 -63364870   0      0      0202  63364870  10805844  US    
 6     6 -63149821   306916 21418  0202  63456737  11040639  US    
 7     7 -57094516   0      0      0202  57094516  9980159   US    
 8     8 -81466942   0      0      0202  81466942  14426608  US    
 9     9 -70583966   0      0      0202  70583966  13198366  US    
10    10 -75331115   0      0      0202  75331115  13838084  US    
11    11 -62108095   0      0      0202  62108095  11295626  US    
12    12 -44762813   0      0      0202  44762813  8155284   US    
13    13 -750126574  626868 57472  -     750753442 131559098 -     
# ... with 3 more variables: statCdCntnKor1 <chr>, statKor <chr>,
#   year <chr>

함수 만들기

지금까지 했던 과정을 종합하여 함수로 만듭니다. 공공데이터 포털에서 서비스키만 발급받는다면 사용할 수 있습니다!

one_getNitemtradeList <- function(ServiceKey, startpoint, endpoint, hscode, country){
     
    url <- paste0("http://openapi.customs.go.kr/openapi/service/newTradestatistics/
                  getNitemtradeList?ServiceKey=", ServiceKey,
                  "&searchBgnDe=", startpoint,
                  "&searchEndDe=", endpoint,
                  "&searchItemCd=", hscode,
                  "&searchStatCd=", country)
    
    xml_expimp <- read_xml(url) %>% xml_children() %>% xml_children() %>% xml_children()
    
    tmp1 <- lapply(seq_along(xml_expimp),
                   function(x){
                       temp_row <- xml_find_all(xml_expimp[x], './*')
                       tibble(idx = x,
                              key = temp_row %>% xml_name(),
                              value = temp_row %>% xml_text()
                       ) %>% return()
                   }
    )
    
    tmp2 <- bind_rows(tmp1)
  
    spread(tmp2, key = 'key', value = 'value') %>% return()
}

# one_getNitemtradeList(ServiceKey, "201001", "201012", "3304", "US")

Citation

For attribution, please cite this work as

nackta (2021, Oct. 30). nackta blog: 공공데이터 포털 open API 이용하기. Retrieved from https://nackta.github.io/posts/2021-10-30-use-gdata-api/

BibTeX citation

@misc{nackta2021공공데이터,
  author = {nackta, },
  title = {nackta blog: 공공데이터 포털 open API 이용하기},
  url = {https://nackta.github.io/posts/2021-10-30-use-gdata-api/},
  year = {2021}
}