ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [크롤링]에어코리아 대기정보 데이터 가져오기(1)
    Node.js/예제 2021. 4. 10. 11:13

     

    미세먼지 수치 데이터를 크롤링하기 위해 에어코리아 페이지를 접속한다.

    https://www.airkorea.or.kr/web

    에어코리아는 미세먼지 정보 뿐만 아니라 대기 관련 여러 데이터를 함께 제공한다. 접속했을 때의 초기 화면은 초미세먼지 정보 페이지인데 각각에 대한 정보를 확인하려면 좌측 메뉴에서 선택을 해야한다. 우선 이번 예제에서는 페이지 초기화면에서 곧바로 활인할 수 있는 초미세먼지(PM 2.5) 데이터를 가져올 것이다.

    에어코리아에서는 위와 같이 지도상에서 측정소를 표시해주고 특정 측정소에 마우스를 올렸을 때 툴팁 형태로 위치정보와 1시간 수치, 24시간 수치를 보여준다. 크롤링을 통해 위 데이터를 가져와볼 예정이다.

     

    const puppeteer = require('puppeteer');
    
    const crawler = async () => {
      try {
        const browser = await puppeteer.launch({
          headless: true,
        });
        const page = await browser.newPage();
        await page.goto('https://www.airkorea.or.kr/web');
    
        await page.evaluate(() => {
          
        });
    
        await page.close();
        await browser.close();
      } catch (e) {
        console.error(e);
      }
    };
    crawler();

    puppeteer 크롤링을 위한 대략적인 템플릿이라고 보면 된다. 브라우저를 열지 않고 곧바로 데이터만 추출할 것이므로 headless 옵션을 true로 설정했다. evaluate 함수 안에서 데이터가 들어있는 html 태그를 분석하여 데이터를 조합하여 return한다.

     

    우선 가져오고자 하는 데이터가 있는 컴포넌트가 무엇인지부터 태그 분석을 통해 파악을 해야 한다. f12를 눌러 Elements 탭에서 해당 컴포넌트의 위치를 찾는다. 마우스를 올려야 활성화되는 영역이기 때문에 hover 옵션을 활성화하거나 ctrl + f 를 통해 적당한 키워드를 입력하여 위치를 찾아낸다. 

     

    const puppeteer = require('puppeteer');
    
    const crawler = async () => {
      try {
        const browser = await puppeteer.launch({
          headless: false,
        });
        const page = await browser.newPage();
        await page.goto('https://www.airkorea.or.kr/web');
    
        const airData = await page.evaluate(() => {
          const elements = document.querySelectorAll("#tab1InfoArea10008 div");
          const result = [];
          
          return result;
        });
    
        console.log(airData);
    
        await page.close();
        await browser.close();
      } catch (e) {
        console.error(e);
      }
    };
    crawler();

    찾고자 하는 데이터는 tab1InfoArea10008라는 아이디를 가진 태그의 하위 div 안에 있으므로 querySelectorAll을 통해 이 div 태그를 모두 읽어들인다.

    evaluate에서 분석한 데이터를 저장하기 위해 변수를 선언하고(airData), 함수 안에서는 배열 안에 저장하여 return할 것이므로 이를 위한 변수(result)도 내부에 선언해둔다.

     

    const puppeteer = require('puppeteer');
    
    const crawler = async () => {
      try {
        const browser = await puppeteer.launch({
          headless: false,
        });
        const page = await browser.newPage();
        await page.goto('https://www.airkorea.or.kr/web');
    
        const airData = await page.evaluate(() => {
          const elements = document.querySelectorAll("#tab1InfoArea10008 div");
          const result = [];
          elements.forEach(el => {
            const obj = {};
            const str = el.textContent;
            const list = str.split("농도 : ");
            list[0] = list[0].replace("위치 : ", "");
    
            const pList = list[0].split(" ");
            const pObj = {};
            pList.forEach((el, i) => {
              pObj[`place${i + 1}`] = el;
            })
    
            const dList = list[1].split(", ")
            const dObj = {
              h1: dList[0].replace("㎍/㎥(1시간)", ""),
              h24: dList[1].replace("㎍/㎥(24시간)", ""),
            };
    
    
            obj["place"] = pObj;
            obj["density"] = dObj;
            obj["timestamp"] = Date.now();
    
            result.push(obj);
          });
          return result;
        });
    
        console.log(airData);
    
        await page.close();
        await browser.close();
      } catch (e) {
        console.error(e);
      }
    };
    crawler();

    queurySelectorAll을 통해 읽어들인 정보가 담긴 배열 변수(elements)를 for 반목문을 통해 순회하면서 데이터를 적절히 가공하여 result 배열에 push해준다. 각각의 측정소에 대한 데이터를 obj라는 이름으로 선언한 객체에 담을 건데, 이 객체는 위치(place), 초미세먼지 수치(density), 시간(timestamp) 정보를 포함한다. place는 카테고리별 분류가 가능하도록 시도, 도로명 별로 분리하여 저장하였다.

     

    위 코드를 실행해보면 콘솔창에 다음과 같이 출력된다.

     

Designed by Tistory.