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

minsubrother님의 프로필 이미지
minsubrother

작성한 질문수

Airflow 마스터 클래스

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

섹션7: SimpleHttpOperator import error문제

해결된 질문

작성

·

681

·

수정됨

0

안녕하세요. 서울시 공공데이터 포털에 있는 실시간 데이터를 바탕으로, 진행해보고 싶어서 강사님께서 짜신 코드와 조금 다르게 구성을 했는데,

패키지 에러 문제가 나타났습니다. 에러 사항은 import xmltodict error문제 입니다.

(1) 데이터 수집

https://data.seoul.go.kr/dataList/OA-21285/A/1/datasetView.do

샘플 데이터 양식: http://openapi.seoul.go.kr:8088/{API_KEY}/xml/citydata/{startnum}/{endnum}/POI{num}'

POI{num}의 경우, 해당 num은 숫자가 아닌 str형태입니다. (001 ~ 113)

(저는 여기서, 전기차 충전소와 충전기 데이터 분석을 통해, 급속 충전기 최적 입지 데이터 분석을 해보고 싶어 크롤링과 MYSQL에 데이터를 수집은 해놓았습니다. )

(강사님께 airflow를 배우며, airflow를 통해 배치 기반으로 데이터 수집을 자동화하고 싶습니다.)


[충전소 데이터]

[충전기 데이터]

---

 

(2) 에러 발생!

(3) 코드 개요

 

from airflow import DAG
from airflow.operators.bash import BashOperator
from airflow.decorators import task
from airflow.providers.http.operators.http import SimpleHttpOperator
# import common.poi_func as poi_array# poi nums
import pendulum

with DAG(
    dag_id="dags_simple_http_operators",
    start_date=pendulum.datetime(2023, 8, 1, tz="Asia/Seoul"),
    catchup=False,
    schedule="0 18-20 * * *" #매일 오후 6시~8시 1시간 간격으로 실행
) as dag:
    
    """ 서울시 실시간 전기차 충전소/충전기 데이터 정보 """
    # http_conn_id = 'openapi.seoul.go.kr'
    
    #array = poi_array()
    #for poi_number in array:
    tb_electric_station_info = SimpleHttpOperator(
        task_id="tb_electric_station_info",
        http_conn_id='openapi.seoul.go.kr',
        endpoint= '{{var.value.apikey_openapi_seoul_go_kr}}/xml/citydata/1/1/광화문·덕수궁',
        method='GET',
        headers={"Content-Type": "application/xml"}
    )
        
    @task(task_id='python_1')
    def python_1(**kwargs):
        ti = kwargs['ti']
        rslt = ti.xcom_pull(task_ids='tb_electric_station_info')
        from pprint import pprint
        import xmltodict
        response_dict = xmltodict.parse(rslt)
        pprint(response_dict)
    
    tb_electric_station_info >> python_1() 

 

강사님께서는 json.load()를 사용했지만, 해당 실시간 데이터는 xml 형식으로만 받아올 수 있어서, 출력을 하기 위해 xmltodict 를 import 해와서, 잘 받아오는지 테스트를 해보고자 하였습니다. 하나의 페이지에 무수히 많은 태그들이 있기 때문에, 페이지는 1페이지로만 설정을 했습니다.

 


해당 에러를 해결하기 위해서, pip install xmltodict를 진행하였습니다.

그럼에도, 해결이 되지 않아서, git이 연동되어있는 쪽에,

pip install xmltodict로 패키지를 설치해주었습니다.

혹시 몰라서, docker compose down을 시킨 뒤,

다시 docker_compose.yaml 파일이 있는 곳에서 docker compose up을 실행하여 airflow 환경에 접속을 했는데,

ModuleNotFoundError: No module named 'xmltodict' [2023-08-15, 23:47:23 KST] {taskinstance.py:1350} INFO - Marking task as FAILED. dag_id=dags_simple_http_operators, task_id=python_1, execution_date=20230815T144715, start_date=20230815T144723, end_date=20230815T144723 [2023-08-15, 23:47:23 KST] {standard_task_runner.py:109} ERROR - Failed to execute job 206 for task python_1 (No module named 'xmltodict'; 77)

여전히 해당 모듈이 없다는 에러가 뜨네요 ..

어떻게 해결해야할까요?

 ---

[추가1]

xcom에는 그래도 데이터가 잘 받아와지는 걸 확인할 수 있었습니다.

 해당 데이터를 파싱하는 과정에서 에러가 난 것 같습니다.


[추가2]

나름대로 방법을 찾아봤는데,

https://stackoverflow.com/questions/67851351/cannot-install-additional-requirements-to-apache-airflow

docker_compose.yaml파일을 수정해봤는데도...

종료가 되네요..

 

 

 

 

답변 1

0

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

안녕하세요, minsubrother 님!

xmltodict 라이브러리가 인식되지 않는 것은 실습 환경상 airflow 가 도커 이미지로 올라갔기 때문입니다.

도커는 일종의 경량화된 가상 캄퓨터로 생각하실 수 있습니다.

따라서 airflow 도커는 독립적 환경이므로 wsl에 라이브러리를 설치했다고해서 airflow 도커에 라이브러리가 설치된 것은 아니지요.

결국 xmltodict 라이브러리를 airflow 도커에 설치해줘야합니다. 현재 실행중인 도커 컨테이너에 설치하는 것은 무의미하고 도커 이미지 자체에 심어줘야 하는데요.

섹션 8의 "더 많은 Provider 확인 & Connection Type 추가하기" 과정에서 airflow 이미지에 라이브러리 추가하는 작업을 진행하므로 미리 참고해보셔도 좋을 듯 합니다.

미리 설명드리자면 xmltodict 라이브러리를 도커 이미지에 추가하기 위해 Dockerfile이라는 파일을 하나 만들고 아래 내용을 입력합니다.

FROM apache/airflow:{버전}

USER root

RUN apt-get update \

    && apt-get autoremove -yqq --purge \

    && apt-get clean \

    && rm -rf /var/lib/apt/lists/*

USER airflow

RUN pip install xmltodict

RUN pip uninstall -y argparse

 

위 내용을 작성 후 빠져나와 Dockerfile이 있는 디렉토리 위치에서

#> sudo docker build -t {새로 만들 이미지 명} .

이렇게 명령하면 xmltodict 라이브러리를 가진 새로운 Airflow 도커 이미지를 만들게 됩니다.

예를 들어 이렇게 입력했다고 해볼꼐요.

#> sudo docker build -t new_airflow .

그럼 new_airflow 라는 이름의 새로운 도커 이미지가 만들어집니다.

#> sudo docker image ls 명령으로 만들어진 도커 이미지를 확인할 수 있어요 .

그 후 docker-compose.yaml 파일을 열어보면 49번 라인 쯤에 이미지를 지정하는 부분이 있습니다.

그 부분에 이미지를 새로 만든 이미지로 지정하시면 새 이미지로 airflow 시작할 수 있습니다.

version: '3'
x-airflow-common:
  &airflow-common
  # In order to add custom dependencies or upgrade provider packages you can use your extended image.
  # Comment the image line, place your Dockerfile in the directory where you placed the docker-compose.yaml
  # and uncomment the "build" line below, Then run `docker-compose build` to build the images.
  image: ${AIRFLOW_IMAGE_NAME:-apache/airflow:2.5.1}

 

version: '3'
x-airflow-common:
  &airflow-common
  # In order to add custom dependencies or upgrade provider packages you can use your extended image.
  # Comment the image line, place your Dockerfile in the directory where you placed the docker-compose.yaml
  # and uncomment the "build" line below, Then run `docker-compose build` to build the images.
  image: new_airflow

 

이제 sudo docker compose up 으로 서비스 올려주시면 되요.

 

 

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

감사합니다. 말씀 주신 부분과 섹션 8 마지막 강의를 들으면서, dockerfile를 재 작성할 수 있었습니다. 패키지 를 다른 걸 설치하려면 또 다른 docker image를 만들어야 하니, 프로젝트를 진행할 때 필요한 라이브러리들을 미리 생각해서 구성하는게 중요하겠구나.. 라는 생각이 들었습니다.

minsubrother님의 프로필 이미지
minsubrother

작성한 질문수

질문하기