크롤링 API 서버 개발 #2 (크롤링, Axios, Cherrio)

2022. 5. 15. 18:26Tech Note

728x90

해당 프로젝트 진행을 위해 샘플 데이터로 활용할 첫번째 타겟은 네이버 뉴스로 정했다.

카테고리는 경제, 사회, 정치 등의 각 분야에서 가져온다.

 

해당 작업을 위해 앞서 설정해놓은 express 서버에 크롤링 모듈을 설치하는 것.

파이썬의 경우 bs4, requests로 진행하면 되지만, node.js 서버의 경우 아래 플러그인을 활용하면 되겠다.

 

1. axios, cheerio 를 설치한다.

npm install --save axios cheerio

 

2. express 서버에 플러그인을 import 해준다.

const axios = require("axios")
const cheerio = require("cheerio")

 

3. 아래와 같이 입력, axios 호출을 통해 소스를 받아 온 후 cheerio를 통해 html eneity를 제어 할 수 있다.

const getAllNewsInfo = async function (req, res) {
    let url = "https://news.naver.com/main/main.naver?mode=LSD&mid=shm&sid1=100"
    getHtml(url)
    .then(html => {
        let ulList = []
        const $ = cheerio.load(html.data)
        const $bodyList = $("div.cluster").children("div.cluster_group")
        $bodyList.each(function(idx, val) {
            let title = $(this).find("div.cluster_text a").text()
            ulList[idx] = {
                "title": title,
                "url" : $(this).find("div.cluster_text a").attr("href")
            }
            console.log(ulList)
        })
        res.status(200).json(ulList);
    })
}

const getHtml = async(url) => {
    return await axios.get(url)
}

 

4. 결과

 

확인해보니 encoding 이슈가 있는 것 같다. 해당 이슈는 플러그인 설치로 간단히 해결한 사례가 있으니 그대로 진행

 

node-iconv, iconv 등이 있으나, python, c# 등의 의존성이 있고 성능 이슈가 있는 듯하다.

node 서버에서 가장 대중적으로 쓰이는 iconv-lite를 사용해준다.

npm install --save iconv-lite

 

설치가 완료되면 요청을 보낼 네이버 뉴스의 header의 content-type을 확인해본다.

EUC-KR이 확인되니 아래와 같이 소스를 수정해준다.

const getAllNewsInfo = async function (req, res) {
    let url = "https://news.naver.com/main/main.naver?mode=LSD&mid=shm&sid1=100"
    let result = getHtml(url)
    .then(html => {
        let ulList = []
        const contentType = html.headers["content-type"]
        const charset = contentType.includes("charset=")
        ? contentType.split("charset=")[1] : 'UTF-8'
        const $ = cheerio.load(html.data)
        const $bodyList = $("div.cluster").children("div.cluster_group")
        $bodyList.each(function(idx, val) {
            let title = Iconv.decode($(this).find("div.cluster_text a").text(), charset)
            ulList[idx] = {
                "title": title,
                "url" : $(this).find("div.cluster_text a").attr("href")
            }
            console.log(ulList)
        })
        res.status(200).json(ulList);
    })
}

 

수정이 완료되면 실행!

​값이 변경되긴 했으나 해결되진 않았다.

encoding 이슈가 아닌가?

 

5. 해결 및 결과 출력

encoding 문제가 맞다. 다만... 아래의 이유로 정상적인 진행이 되지 않았다.

1) euc-kr로 되어있는 페이지를 불러왔다. => 표현할 수 없는 값이라 한글 깨짐 발생

2) euc-kr을 decode해서 사용하기 전에 cheerio.load를 함으로써 변환 할 수 없는 객체가 되어버림.

3) 그 이후 decode를 한다. => 결국 decode 되지 않았다.

성공한 코드를 아래와 같다.

const getAllNewsInfo = async function (req, res) {
    let url = "https://news.naver.com/main/main.naver?mode=LSD&mid=shm&sid1=100"
    let result = getHtml(url)
    .then(html => {
        let ulList = []
        const contentType = html.headers["content-type"]
        const charset = contentType.includes("charset=")
        ? contentType.split("charset=")[1] : 'UTF-8'
        const content = Iconv.decode(html.data, charset).toString();
        const $ = cheerio.load(content)
        const $bodyList = $("div.cluster").children("div.cluster_group")
        $bodyList.each(function(idx, val) {
            let title = $(this).find("div.cluster_text a").text()
            ulList[idx] = {
                "title": title,
                "url" : $(this).find("div.cluster_text a").attr("href")
            }
            // console.log(ulList)
        })
        res.status(200).json(ulList);
    })
}

 

swagger UI를 통해 정상적으로 출력됨을 확인 할 수 있다!

별의별 해결방법을 다 고민하고 적용해봤지만 서순 문제였다. 개발은 늘 허를 찌르는 곳에서 날 힘들게 한다.

기본기의 중요성을 다시 한번 느낀다.

이번 문제를 해결하자마자 바로 async 이슈가 생겨서 작업이 한번 더 지체되었다.

다음 장엔 async/await 비동기처리 관련하여 정리해보고자 한다.

node.js를 하면서 날 가장 힘들게 하는 녀석이라. 한번 확실히 정리해두고 가야 될 듯하다.