[SAS-크롤링] Api로 지방재정365 데이터 수집하기

·

·

,

지방재정365에서 제공하는 데이터를 Open API를 이용하여 SAS를 통해 불러오는 방법입니다. 전체 코드는 하단에 있습니다. 코드는 크게 3가지 부분으로 나눌 수 있습니다.

  1. 매크로 지정
  2. 매크로 실행
  3. 원하는 변수로 수정

1. 매크로 지정

let dir=D:\\\\\\\\0008\\\\\\\\sas7bdat\\\\\\\\;
let lib=A0008;
let String01=ESJRV;/*apiKey*/
Code language: JavaScript (javascript)

%let something은 something에 매크로를 지정하는 것입니다. dir는 데이터를 저장할 공간, lib는 SAS의 라이브러리 이름, String01은 지방재정365에서 다운받은 apiKey입니다.

libname &lib "&dir";

Code language: JavaScript (javascript)

libname &lib “&dir”; 은 SAS의 라이브러리 지정 명령어입니다. 위의 매크로로 설정한 dir에 lib를 할당한다는 뜻입니다.

저는 매번 dir와 lib를 코드 작성시에 입력하는 것은 불편하고, 다른 프로젝트와 코드에서 dir와 lib를 일일이 바꿔주는 불편함을 피하기 위하여 항상 위의 4줄을 SAS 코드 상단에 추가합니다.

%macro json(String05, table, date_s, date_e);

String05, table, date_s, date_e를 변수로 가지는 json이라는 매크로를 만든다는 명령어로 %mend;까지의 내용이 매크로로 작성됩니다.

do date_want=&date_s %to &date_e;
  data L001_&date_want;run;

Code language: JavaScript (javascript)

%do는 %end까지 date_want에 대하여 지정된 &date_s부터 &date_e까지 값을 넣어서 실행시키라는 명령어입니다.

let string02=1;
do %until (&check=200);

Code language: JavaScript (javascript)

통계청, 지방재정365, 열린재정 등 대부분의 Open API를 제공하는 국가 통계포털 사이트들은 페이지 당 결과물 한도를 제한합니다. 가령 1,000개의 데이터가 있다면, 100개씩 10페이지로 나눠서 제공합니다. 그렇기 때문에 (&check=200)이라는 데이터가 없다는 오류가 나타날 때까지 string02(여기서 string02는 100개씩 보여주는 데이터의 페이지를 뜻합니다.)를 1부터 1씩 더해가며 %do에 포함된 코드를 실행시킵니다.

만약 이 과정을 제외한다면, 100개씩 몇 페이지가 있는지 모르는 데이터를 다 불러오기 위해서 임의로 n페이지를 지정하는 비효율적인 작업을 진행해야 합니다.

let url=&string05?key=&string01&type=json&pindex=&string02&psize=100&accnut_year=&date_want;
Code language: JavaScript (javascript)

json 포맷의 Open API를 불러오는 링크입니다. string02는 페이지, psize는 100개로 한 페이지당 100개씩 연도별로 불러오는 링크입니다. 위의 url은 지방재정365에서 제공하는 Open API url을 참고로 만들어진 것으로, 열린재정이나 통계청의 url은 다른 url을 사용하여야 합니다.

하단의 코드는 불러온 json 형태의 문서를 테이블 형태로 변형하고, 페이지와 연도별로 각각 불러온 파일을 합치는 작업입니다.

하단의 코드는 json 형태의 문서를 txt 파일로 불러와 변형하는 작업으로, SAS에서는 json이나 xml의 포맷을 해석할 수 있지만, SAS에서 제공하는 해석 방법을 사용하면 오류가 자주 발생해서, 저는 txt 파일을 이용하는 예전 방법을 사용하고 있습니다.

 filename out "&dir.SeriesDataOut.txt" recfm=v lrecl=999999999;
    proc http out=out url="&url" method="post" ct="application/json";
    run;

    data raw;
    infile "&dir.SeriesDataOut.txt" dsd lrecl=999999999 dlm='{}[]:,';
    input raw : $2000.@@;
    if _n_=5 then call symput('check', scan(raw,2));
    run;

    data temp;
    merge raw raw(firstobs=2 rename=(raw=_raw));
    if mod(_n_,2) eq 0;
    run;

    data temp;
    set temp;
    if raw='' then group+1;
    run;

    proc transpose data=temp out=data_one(drop=_:);
    by group;
    id raw;
    var _raw;
    run;

    data L001_&date_want;
    set L001_&date_want data_one;
    run;

    %let string02=%eval(&string02+1);
Code language: PHP (php)

하단의 %end는 %do %until (&check=200);의 끝을 의미합니다.

 %end;

하단의 %end는 %do date_want=&date_s %to &date_e;의 끝을 의미합니다.

 %end;

마지막으로 L0012011(예시)와 같은 형태로 만들어진 테이블들을 합치는 작업입니다. L001&dates부터 L001&date_e를 합쳐서 매크로로 지정한 &table 이름의 테이블을 만듭니다.

 data &table(drop=group RESULT CODE MESSAGE ARBGT list_total_count);
  set L001_&date_s-L001_&date_e;
  if accnut_year="" then delete;
  run;

%mend;
Code language: JavaScript (javascript)

%mend; 끝으로 %macro json(String05, table, date_s, date_e); 매크로 설정이 끝났습니다. 이제는 매크로를 실행하여 실제로 테이블을 불러오는 작업을 해야합니다.

2. 매크로 실행

%json(String05=http://lofin.mois.go.kr/HUB/CDDFA, table=table001, date_s=2010, date_e=2017);
%json(String05=http://lofin.mois.go.kr/HUB/GGNSE, table=table002, date_s=2010, date_e=2017);
%json(String05=http://lofin.mois.go.kr/HUB/FIACRV, table=table003, date_s=2010, date_e=2017);
%json(String05=http://lofin.mois.go.kr/HUB/HEDFC, table=table008, date_s=2010, date_e=2017);
%json(String05=http://lofin.mois.go.kr/HUB/JFIED, table=table009, date_s=2010, date_e=2017);
Code language: JavaScript (javascript)

앞서 만들어 놓은 json 매크로를 실행하는 코드입니다. 여기서 보여드리는 테이블의 정보는 하단에 있습니다.

table001: 회계별 단체별 세출결산; 범위: 2010-2017, 관측값: 1949개, 변수: 9개 table002: 구조별 기능별 세출결산; 범위: 2010-2017, 관측값: 25398개, 변수 13개 table003: 재원별 회계별 세입결산; 범위: 2010-2017, 관측값: 29661개, 변수 17개 table008: 예산대비채무비율; 범위: 2010-2017 table009: 재정자립도(최종); 범위: 2010-2017

3. 원하는 변수로 수정

불러온 테이블들은 SAS의 work에 table000의 형태로 저장되어 있습니다. 하지만, Open API로 불러온 파일들은 각각 변수가 무엇을 뜻하는지 해석하기가 쉽지 않습니다. 테이블에 라벨도 없고 변수명도 amt1과 같이 암호처럼 되어있기 때문입니다. 그래서 아래의 코드를 통해 변수명을 변환합니다.

proc sql;
create table &lib..table001 as
select input(accnut_year, best32.) as accnut_year label="회계연도"
, input(wdr_sfrnd_code, best32.) as wdr_sfrnd_code label="지역코드"
, wdr_sfrnd_code_nm as wdr_sfrnd_code_nm label="지역명"
, input(sfrnd_code, best32.) as sfrnd_code label="자치단체코드"
, sfrnd_nm_korean as sfrnd_nm_korean label="자치단체명"
, input(gnrl_anexptr_stacntam, best32.) as gnrl_anexptr_stacntam label="일반회계"
, input(etc_anexptr_stacntam, best32.) as etc_anexptr_stacntam label="기타특별회계"
, input(pblcorp_anexptr_stacntam, best32.) as pblcorp_anexptr_stacntam label="공기업특별회계"
from table001;
quit;
run;
Code language: JavaScript (javascript)

위의 코드는 회계별 단체별 세출결산인 table001을 변환하는 코드입니다. SAS의 work 라이브러리에 저장된 table001을 숫자 변수는 숫자로, 문자 변수는 문자로 수정하고 변수에 라벨을 추가한 뒤, 제가 앞서 설정한 A0008 라이브러리로 옮기는 작업니다. 코드는 proc sql;을 통해 SAS에서 sql문을 이용하였습니다.

input(accnut_year, best32.) as accnut_year label=“회계연도”은 accnut_year(회계연도)를 best32. 포맷(숫자형)으로 바꾸고 회계연도라는 라벨을 추가한 것입니다. json으로 가져올 때, txt에서 테이블로 변환하는 과정에서 문자형으로 가져오기 때문에 숫자변수는 숫자형으로 바꿔주는 작업이 필요합니다.

위의 변수명과 라벨은 지방재정365 테이블 정보에 가시면 볼 수 있습니다. 저는 변수와 설명을 그대로 복사하여 엑셀에서 명령어를 통해 위의 코드를 작성합니다.

이것으로 SAS에서 지방재정365의 테이블을 Open API를 이용해서 SAS의 테이블 형태로 변환하여 가져오는 코드를 알아보았습니다. 아래는 이번에 쓰인 전체 코드를 첨부합니다.

%let 부분을 수정하여, dir, lib, apiKey만 제대로 수정하면 작동합니다.

전체 코드

%let dir=D:\\\\\\\\0008\\\\\\\\sas7bdat\\\\\\\\;
%let lib=A0008;
%let String01=ESJRV;/*apiKey*/

libname &lib "&dir";

%macro json(String05, table, date_s, date_e);

    %do date_want=&date_s %to &date_e;
    data L001_&date_want;run;

        %let string02=1;
        %do %until (&check=200);

        %let url=&string05?key=&string01&type=json&pindex=&string02&psize=100&accnut_year=&date_want;

        filename out "&dir.SeriesDataOut.txt" recfm=v lrecl=999999999;
        proc http out=out url="&url" method="post" ct="application/json";
        run;

        data raw;
        infile "&dir.SeriesDataOut.txt" dsd lrecl=999999999 dlm='{}[]:,';
        input raw : $2000.@@;
        if _n_=5 then call symput('check', scan(raw,2));
        run;

        data temp;
        merge raw raw(firstobs=2 rename=(raw=_raw));
        if mod(_n_,2) eq 0;
        run;

        data temp;
        set temp;
        if raw='' then group+1;
        run;

        proc transpose data=temp out=data_one(drop=_:);
        by group;
        id raw;
        var _raw;
        run;

        data L001_&date_want;
        set L001_&date_want data_one;
        run;

        %let string02=%eval(&string02+1);

        %end;

    %end;

  data &table(drop=group RESULT CODE MESSAGE ARBGT list_total_count);
  set L001_&date_s-L001_&date_e;
  if accnut_year="" then delete;
  run;

%mend;

%json(String05=http://lofin.mois.go.kr/HUB/CDDFA, table=table001, date_s=2010, date_e=2017);
%json(String05=http://lofin.mois.go.kr/HUB/GGNSE, table=table002, date_s=2010, date_e=2017);
%json(String05=http://lofin.mois.go.kr/HUB/FIACRV, table=table003, date_s=2010, date_e=2017);
%json(String05=http://lofin.mois.go.kr/HUB/HEDFC, table=table008, date_s=2010, date_e=2017);
%json(String05=http://lofin.mois.go.kr/HUB/JFIED, table=table009, date_s=2010, date_e=2017);

/*아래는 변수 변환과 변수명 입력*/

proc sql;
create table &lib..table001 as
select input(accnut_year, best32.) as accnut_year label="회계연도"
, input(wdr_sfrnd_code, best32.) as wdr_sfrnd_code label="지역코드"
, wdr_sfrnd_code_nm as wdr_sfrnd_code_nm label="지역명"
, input(sfrnd_code, best32.) as sfrnd_code label="자치단체코드"
, sfrnd_nm_korean as sfrnd_nm_korean label="자치단체명"
, input(gnrl_anexptr_stacntam, best32.) as gnrl_anexptr_stacntam label="일반회계"
, input(etc_anexptr_stacntam, best32.) as etc_anexptr_stacntam label="기타특별회계"
, input(pblcorp_anexptr_stacntam, best32.) as pblcorp_anexptr_stacntam label="공기업특별회계"
from table001;
quit;
run;

proc sql;
create table &lib..table002 as
select input(accnut_year, best32.) as accnut_year label="회계연도"
, input(wdr_sfrnd_code, best32.) as wdr_sfrnd_code label="지역코드"
, wdr_sfrnd_code_nm as wdr_sfrnd_code_nm label="지역명"
, input(sfrnd_code, best32.) as sfrnd_code label="자치단체코드"
, sfrnd_nm_korean as sfrnd_nm_korean label="자치단체명"
, realm_code as realm_code label="분야코드"
, realm_nm as realm_nm label="분야명"
, input(p_amt, best32.) as p_amt label="정책사업"
, input(a_amt, best32.) as a_amt label="재무활동"
, input(f_amt, best32.) as f_amt label="행정운영경비"
, creat_dt as creat_dt label="생성날짜"
, crtr_id as crtr_id label="생성아이디"
from table002;
quit;
run;

proc sql;
create table &lib..table003 as
select input(accnut_year, best32.) as accnut_year label="회계연도"
, input(wdr_sfrnd_code, best32.) as wdr_sfrnd_code label="지역코드"
, wdr_sfrnd_nm as wdr_sfrnd_nm label="지역명"
, input(sfrnd_code, best32.) as sfrnd_code label="자치단체코드"
, sfrnd_nm_korean as sfrnd_nm_korean label="자치단체명"
, armok_code as armok_code label="세목코드"
, armok_code_nm_korean as armok_code_nm_korean label="세목명"
, input(level_no, best32.) as level_no label="레벨번호"
, input(amt1, best32.) as amt1 label="총계합계"
, input(amt2, best32.) as amt2 label="일반회계총계"
, input(amt3, best32.) as amt3 label="공기업특별회계총계"
, input(amt4, best32.) as amt4 label="기타특별회계총계"
, input(amt5, best32.) as amt5 label="순계합계"
, input(amt6, best32.) as amt6 label="일반회계순계"
, input(amt7, best32.) as amt7 label="공기업특별회계순계"
, input(amt8, best32.) as amt8 label="기타특별회계순계"
from table003;
quit;
run;

proc sql;
create table &lib..table008 as
select input(accnut_year, best32.) as accnut_year label="회계연도"
, input(wdr_sfrnd_code, best32.) as wdr_sfrnd_code label="지역코드"
, wdr_sfrnd_code_nm as wdr_sfrnd_code_nm label="지역명"
, input(sfrnd_code, best32.) as sfrnd_code label="자치단체코드"
, sfrnd_nm_korean as sfrnd_nm_korean label="자치단체명"
, input(amt1, best32.) as amt1 label="채무잔액"
, input(amt2, best32.) as amt2 label="최종예산액"
, input(rate1, best32.) as rate1 label="예산대비채무비율"
from table008;
quit;
run;

proc sql;
create table &lib..table009 as
select input(accnut_year, best32.) as accnut_year label="회계연도"
, input(wdr_sfrnd_code, best32.) as wdr_sfrnd_code label="지역코드"
, wdr_sfrnd_code_nm as wdr_sfrnd_code_nm label="지역명"
, input(sfrnd_code, best32.) as sfrnd_code label="자치단체코드"
, sfrnd_nm_korean as sfrnd_nm_korean label="자치단체명"
, input(amt1, best32.) as amt1 label="자체수입"
, input(amt2, best32.) as amt2 label="자치단체 예산규모"
, input(rate1, best32.) as rate1 label="재정자립도"
from table009;
quit;
run;
Code language: PHP (php)

댓글 남기기