[TIL] 이미지 소스 크롤링을 해보자 (with puppeteer)

·2024년 2월 6일
1

TIL

목록 보기
85/85
post-thumbnail

우리 프로젝트의 주요 서비스는, 장소 정보 공유 서비스 이다. 그래서 공공데이터포털에서 배리어프리 시설에 대한 정보를 가져오고 있지만, 이 API에는 장소 이미지에 대한 정보는 없다. 프로젝트 기획 단계에서 장소 이미지에 대한 정보를 어떻게 처리할 것인가에 대한 의견 공유가 있었고, 해당 장소의 사진이 있는 첫 리뷰의 첫번째 이미지를 장소 대표 이미지로 설정하고, 사진이 있는 리뷰가 없는 경우 defaultImage를 보여주기로 결정하였다. 그러나, 다음과 같은 문제가 발생했다.

트러블 슈팅

문제상황

문제 1.

12000여개의 장소데이터에서 사진이 있는 리뷰가 아직 극소수이다 보니, 대부분이 defaultImage 가 보이게 되어, 시각적으로 예쁘지 않았다. (다양하고 어려운 기술들을 적용하였지만 시각적으로 확 보여지는게 적다 보니, 프로젝트 완성도가 떨어져 보였다.)

문제 2.

장소카드의 이미지가 대부분 defaultImage 이다 보니, 이미지가 뜨지 않는 것 처럼 보인다는 유저 피드백이 꽤 있었다. (기존에 디폴트이미지가 회색계열이었는데, 그로 인해 스켈레톤 UI 처럼 보인다는 피드백도 있었다.)

해결방안

모든 장소에 이미지가 있는 리뷰를 달 수도 없는 노릇이고, 일일이 검색해서 넣을 수도 없다.
그렇다면?!
타 팀의 프로젝트에서 이미지 크롤링을 시도한 것을 보고, 우리도 장소 대표 이미지 소스를 크롤링해올 수 있지 않을까, 생각하여 puppeteer 라는 라이브러리를 사용하여 네이버에 시설명을 검색했을 때 나오는 플레이스 이미지 소스를 크롤링해오기로 했다.

결론부터 말하자면,
이미지 크롤링에 성공하였다!
물론 이미지 소스 크롤링을 해오는 과정에서 일부 장소와 관련 없는 이미지가 들어가기도 했지만,
대부분의 이미지들이 해당 장소명과 일치하는 이미지로 잘 들어갔다..!!!!!

어떻게 했는지 간략하게 살펴보겠다.

puppeteer

우선 puppeteer는 Headless Chrome을 제어할 수 있는 Node.js 라이브러리로, 웹 스크래핑 및 자동화를 위해 사용된다.

일단 설치 해보자.

npm i pupeteer

yarn add pupeteer

우선 puppeteer는 Node 환경에서 실행되는 라이브러리이므로,
기존 프로젝트와는 다른 프로젝트를 하나 생성하였다. (vanilla javascript로 작성하고 실행함)

puppeteer를 사용하여 필요한 웹 페이지를 열고, 데이터를 스크랩하는 코드를 작성한다.

const puppeteer = require("puppeteer");
const { supabase } = require("./supabase");
const { createClient, SupabaseClient } = require("@supabase/supabase-js");

(async () => {
  const supabaseUrl = // 생략
  const supabaseKey = // 생략
   
  const supabase = createClient(supabaseUrl, supabaseKey);

  const response = await fetch(
    `https://api.odcloud.kr/api/15111386/v1/uddi:c23c2f46-bd0d-4679-bb4f-818da7c5f160?page=1&perPage=2000&serviceKey=${apiKey}`
  );  // 공공데이터 포탈 Open Api (pageSize = 2000)
  const { data } = await response.json();
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  for (let i = 0; i < 2000; i++) {
    try {
      await page.goto(
        `https://search.naver.com/search.naver?where=image&sm=tab_jum&query=${data[i].시설명}`,
        { timeout: 10000 }
      );

      await page.waitForNavigation();
      const thumbnail = await page.$("._fe_image_tab_content_thumbnail_image");
      const result = await page.evaluate((thumbnail) => {
        // 여기에서 엘리먼트에 대한 조작 또는 정보 추출한다.
        return {
          src: thumbnail.src,
        };
      }, thumbnail);

		// places 테이블에 image_url 을 update 한다.
      const { data: placeData, error } = await supabase
        .from("places")
        .update({ image_url: result.src })
        .eq("place_name", data[i].시설명);

 
    } catch (error) {
      console.log("error", error);
      continue;
    }

    // 썸네일이 로드될 때까지 기다림
  }
})();

우선은 코드를 위와 같이 작성하고, 실행하였다.

node index.js

그러면 네이버 이미지에서 시설명으로 검색했을 때, 첫번째 이미지의 소스를 supabase의 places 테이블의 image_url 필드에 저장하게 된다.

네이버에 장소명을 검색하고 이미지 탭을 보았을 때 첫번째 이미지의 className이 _fe_image_tab_content_thumbnail_image 이고, 해당 부분에 src 가 들어있는 것을 확인할 수 있다. 해당 src 를 긁어와서 supabase의 places테이블에 넣는 것이다.

<크롤링 하는 모습>

<장소카드에 이미지들이 적용된 모습>

검색 결과가 없는 경우, 장소이미지를 못가져오기도 했고, 크롤링하면서 다른 경우, 장소명과 맞지 않는 이미지를 가져오기도 했지만, 무려 12000여개의 이미지 소스를 크롤링해와서 프로젝트에 적용했다는 점이 매우 뿌듯하다.!! 그것도 발표 2일 전에.ㅎ
이제 진짜 발표준비 해야지.........😇

profile
느리더라도 조금씩, 꾸준히

0개의 댓글