인프런 커뮤니티 질문&답변

hpsm5187님의 프로필 이미지
hpsm5187

작성한 질문수

Airflow 마스터 클래스

SimpleHttp 오퍼레이터로 서울시 공공데이터 API 받아오기

공공데이터 받아오기 - XML형식 표시하기 질문

해결된 질문

작성

·

195

0

항상 강의 감사드립니다.

SimpleHttp오퍼레이터로 공공데이터 받아오기 실습부분에 질문이 있습니다. 강의에서는 Json형식의 데이터를 받아와 load함수를 사용하셨는데요, 제가 받아온 데이터는 xml형식이어서 코드를 수정해가며 실습을 진행했습니다. 구글링을 통해 필요한 라이브러리를 가져와 다음과 같이 코드를 수정했습니다.

import pendulum
from airflow import DAG
from airflow.operators.bash import BashOperator
from airflow.providers.http.operators.http import SimpleHttpOperator
from airflow.decorators import task

with DAG(
    dag_id='dags_simple_http_operator',
    start_date=pendulum.datetime(2024, 3, 1, tz='Asia/Seoul'),
    schedule=None,
    catchup=False
) as dag:
    
    '''서울시 공공데이터 정보'''
    # http://openapi.seoul.go.kr:8088/(인증키)/xml/TnJbhntBassOpen/1/5/
    get_hr_data = SimpleHttpOperator(
        task_id='get_hr_data',
        http_conn_id='openapi.seoul.go.kr',
        endpoint='{{var.value.apikey_openapi_seoul_go_kr}}/xml/TnJbhntBassOpen/1/10/',
        method='GET',
        headers={
            'Content-Type':'application/json',
            'charset':'utf-8',
            'Accept':'*/*'
        }
    )

    @task(task_id='python_2')
    def python_2(**kwargs):
        ti = kwargs['ti']
        rslt = ti.xcom_pull(task_ids='get_hr_data') # SimpleHttpOperator가 가진 데이터를 가져오기
        import xml.etree.ElementTree as ET
        from pprint import pprint

        
        root = ET.fromstring(rslt)
        print(f'root : {root}, root/tag : {root.tag}, root/attrib : {root.attrib}')
        # for child in root:
        #     print(child.tag, child.attrib)
        for child in root:
            print(f'Tag : {child.tag}, Content : {child.text}')
        

    get_hr_data >> python_2()

(pprint라이브러리는 오류가 생겨 사용하지 않았습니다)

 

python_2 태스크의 Logs결과입니다 :

[2024-03-25, 00:18:05 KST] {logging_mixin.py:188} INFO - Tag : list_total_count, Content : 827
[2024-03-25, 00:18:05 KST] {logging_mixin.py:188} INFO - Tag : RESULT, Content :
[2024-03-25, 00:18:05 KST] {logging_mixin.py:188} INFO - Tag : row, Content :
[2024-03-25, 00:18:05 KST] {logging_mixin.py:188} INFO - Tag : row, Content :

결론적으로 내용들이 표시가 되지 않는데, 이유가 무엇인가요?

(child.attrib함수도 시도해보았지만, 역시 담는 내용이 없었습니다)

문서는 다음 문서를 참고하였습니다 :

https://docs.python.org/ko/3/library/xml.etree.elementtree.html#xml-tree-and-elements

답변 2

0

김현진님의 프로필 이미지
김현진
지식공유자

안녕하세요 hpsm5187님

우선 새로운 접근과 시도 좋습니다 ! (이렇게 하시면 실력도 일취월장 하실거라 믿습니다)

확인해보니 hpsm님이 작성하신대로 잘 출력되어 나온게 맞습니다.

오퍼레이터나 DAG 에 문제는 없고 순전히 XML 파싱 과정 중 오해가 있어서 그렇습니다.

로그 확인해보시면 hpsm님이 작성하신 아래 문장이 잘 출력되었는데 한번 따라가 볼꼐요.

print(f'Tag : {child.tag}, Content : {child.text}')

 

  1. 첫 번째 task인 get_hr_data 의 xcom 결과를 이쁘게 정렬해서 보면 아래와 같습니다.

    image

XML에서 tag 란 "<" 바로 뒤에 나오는 부분입니다.

TnJbhntBassOpen 태그가 있고 그 다음 레벨에 아래 태그가 반복되고 있습니다.

list_total_count

RESULT

row

row

..

 

따라서 hpsm님이 출력하려고 한 child.tag 는 바로 태그를 출력하는 것이므로 list_total_count, RESULT, row, row ... 를 출력하게 됩니다. 따라서 결과 로그라고 올려주신 부분은 잘 나온겁니다 (빨간 네모)

image

  1. 그럼 Content: 뒤에는 왜 아무것도 없는지 확인할 차례입니다.


    Content: 뒤에는 child.text를 이용하여 Element의 텍스트를 꺼내려고 하고 있습니다.
    list_total_count는 827 이라는 text를 가지고 있습니다. 그래서 list_total_count는 Content: 827이 나온겁니다.

    반면 RESULT와 row 태그는 자기 자신은 텍스트를 가진게 없습니다. 자식 태그가 text를 가지고 있죠.


    그래서 RESULT와 row 태그 element에서 .text를 하면 안되고, 자식 태그에 접근해서 .text를 시도해야합니다. 아래 정리한건데 잘 보이시나요? ^^

image

 

  1. 아마도 hpsm님이 원하시는 것은 표 형태로 정리해서 나오길 원하실 겁니다.

그러면 이렇게 한번 짜보세요. (pandas 데이터프레임 아시나요? )

    @task(task_id='python_2')
    def python_2(**kwargs):
        ti = kwargs['ti']
        rslt = ti.xcom_pull(task_ids='get_hr_data') # SimpleHttpOperator가 가진 데이터를 가져오기
        import xml.etree.ElementTree as ET
        import pandas as pd
        from pprint import pprint


        root = ET.fromstring(rslt)
        col_lst = ['JBHNT_REQST_NO','SEX','AGE','ACDMCR_CMMN_CODE_SE']
        rslt_df = pd.DataFrame()

        for col in col_lst:
            tmp_lst = []
            for row in root.iter(col):
                tmp_lst.append(row.text)
            rslt_df[col] = tmp_lst
        print(rslt_df)

이렇게 작성하면 row 태그의 자식 태그에 접근해서 .text를 이용해 텍스트를 가져올 수 있습니다.

해보시겠어요?

hpsm5187님의 프로필 이미지
hpsm5187
질문자

답변 감사합니다. 수정해주신 코드 참고하여 댁 실행, 데이터 확인까지 완료하였습니다.

이번 문제로 XML파일 형식에 대해 조금 더 이해하게 된 것 같습니다. 감사합니다!

image

0

안녕하세요, 인프런 AI 인턴입니다.

hpsm5187님의 질문과 유사한 상황을 다룬 기존 질문이 있어 관련 내용을 공유드립니다. 하단의 내용 중 “섹션7: SimpleHttpOperator import error문제”와 관련된 자세한 해결 방법이 있으니 참고하실 수 있습니다.

질문자님의 경우 xml.etree.ElementTree을 사용하여 XML 데이터를 파싱하고 있으며, 태그 이름과 내용을 출력하는 과정에서 예상한 결과를 얻지 못하는 것 같습니다. 기본적으로, ElementTree.fromstring() 함수는 XML 데이터를 파싱하여 요소를 반환합니다. 반환된 요소(여기서는 root)의 태그 이름과 속성(attrib)은 정상적으로 접근할 수 있지만, 내용(text) 접근에서 문제가 발생하는 것으로 보입니다.

주의할 점은 XML 데이터 내 ‘row’ 태그와 같이 자신이 가지고 있는 텍스트가 아닌 하위 요소를 갖는 경우, child.text 직접 접근으로는 내용을 가져올 수 없습니다. 대신, 해당 요소의 하위 요소에 직접 접근하여 내용을 확인해야 합니다.

기존에 해결된 질문에서는 XML 데이터 파싱과 관련하여 xmltodict 라이브러리를 사용한 예시가 있었습니다. 이 라이브러리는 XML 데이터를 Python의 딕셔너리로 변환하여 보다 직관적으로 데이터에 접근할 수 있게 해줍니다. 이와 비슷한 문제 해결 방식이 질문자님의 상황에도 도움이 될 수 있습니다.

이론적으로는 for 루프 내에서 특정 태그에 대한 하위 요소를 순회하거나 특정 하위 요소의 값을 직접 지정하여 접근해보시는 것이 좋습니다. 예를 들면 child.find('some_tag').text 방식으로 특정 하위 태그의 텍스트에 접근할 수 있습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

hpsm5187님의 프로필 이미지
hpsm5187

작성한 질문수

질문하기