그룹장님의 요청으로 CNCF 멤버 중 오픈소스 컨설팅 분야의 회사들을 리스트 업을 하기 위해 크롤러를 제작하게 되었다.

처음에 듣고 어떻게 소스 구성을 할까 고민했는데, 그룹장님께서 ChatGPT를 활용하면 된다고 알려주셔서 이번기회에 활용해보았다.

ChatGPT란?

  • 간략하게 인공지능 대화 모델 중 하나로 학습된 대량의 데이터 기반으로 일상 및 전문적 주제까지 다양하게 활용된다.

요구사항은 아래와 같다.

  1. CNCF 멤버 중 등급이 (Platinum, Gold, Siver)인 회사들
  2. 해당 회사들의 이름, 등급, 소개글, 사이트주소

스크린샷 2023-03-27 오전 8 57 22

(바로 요런 CNCF 랜드스케이프 페이지에서 회사 마다 있는)

스크린샷 2023-03-27 오전 8 57 03

(이런 팝업을 띄우고 정보를 가져와야 한다.)

결과적으로 2. 요구사항에 대한 데이터들을 추출해야하는데 CNCF 홈페이지에서 해당 데이터를 추출하려면 회사 클릭 > 해당 회사 상세 팝업 이렇게 2depth로 되어있다.

다행히 팝업이 iframe으로 띄어지기에 url이 회사 id를 key로 제공되고 있었기에 회사리스트에서 id 파라미터를 뽑아낸뒤 결과를 추출할 계획을 세웠다.

우선 GPT 에 질의를 하여 크롤러 코드 뼈대를 손쉽게 만들고 회사 id 추출까지 성공..

리스트 추출까지 성공(생각보다 빠르게 됐음)하고 회사 id로 팝업 url을 호출한 뒤 결과값을 뽑으려는데..
팝업 페이지 경우 Dom에 정적 태그가 렌더링이 된 상태에서 동적으로 값만 바뀌는 구조다 보니 원하는 데이터들이 빈값으로 추출이 되었다……. 난감;;

매일 오전하는 스크럼 미팅에서 해당 이슈를 말씀드렸더니 selenium을 사용하면 될것 같다고 안내해주셨다.

이번에도 마찬가지로 ChatGPT에게 Selenium 으로 크롤링하는 방법을 질의하였으며, Python에서 사용하는 예제를 받을 수 있었다.

Selenium

selenium 경우 Shadow Dom을 띄워 매크로같이 동작하는 프레임워크며 웹 사이트 자동화 테스트에 많이 사용됨.

로컬 PC 에서 사용하기위해 가상 브라우저 driver를 설치해야 한다.

Chorme 브라우저를 사용할것이며 해당 driver는 https://chromedriver.chromium.org/downloads에서 자신의 버전에 맞는 파일을 다운로드하면 된다.

필수 library 설치

pip install selenium
pip install beautifulsoup4
pip install requests
pip install pandas

코드 작성

import requests
from bs4 import BeautifulSoup
from seleniumList import detailList


# 크롤링할 페이지의 URL
list_url = "https://landscape.cncf.io/pages/members"

# requests 모듈을 사용하여 URL에 요청을 보냅니다.
response = requests.get(list_url)

# BeautifulSoup을 사용하여 HTML을 파싱합니다.
soup = BeautifulSoup(response.content, "html.parser")

# 회원 목록을 포함한 요소를 찾습니다.
# grade_div = soup.find("div", {"data-section-id": "cncf-members-platinum"})
grade_div = len(soup.findAll("div",class_="mosaic-wrap"))
detailList(grade_div)

  • HTML 과 XML 문서의 구문을 분석하는 BeautifulSoup 패키지를 설치하여 해당 주소의 특정 태그값 추출
  • 회원사 목록 갯수 추출한뒤 함수호출

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import pandas as pd
import time

def detailList(count):
    data = []

    # specify the path to the chromedriver.exe file
    options = webdriver.ChromeOptions()
    options.add_experimental_option("excludeSwitches", ["enable-logging"])
    driver = webdriver.Chrome(options=options)

    url = "https://landscape.cncf.io/pages-modal/members?a=a&selected=alibaba-cloud-member"

    driver.get(url)
    for i in range(count) :
        try :
            driver.find_element(By.XPATH, '/html/body/div[1]/div[2]/div/div[1]/span[2]').click()
            time.sleep(2)

            soup = BeautifulSoup(driver.page_source, "html.parser")

            #회사명
            product_name = soup.find("div",class_="product-name").text
            #멤버 등급
            grade = soup.select_one("body > div.modal > div.modal-container > div > div.modal-content.nonoss > div.product-scroll > div.product-main > div.product-category > span > a:nth-child(3)").text
            if grade != "Platinum" and grade != "Gold" and grade!= "Silver" :
                print('pass')
                continue

            #회사 소개
            description = soup.find("div",class_="product-description").text
            #웹사이트 링크
            link = soup.find("div",class_="product-property-value col col-80").find("a")["href"]

            print("product:" + product_name)
            data.append({'Company': product_name, 'grade' : grade, 'Description': description, 'link' : link})
            
        except :
            print("No more pages left")
            break
            .....
  • Selenium을 사용하여 전달받은 파라미터 갯수만큼 반복
  • shadow dom에서 어떠한 동작을 일으킬지 아래 두가지 방법을 고민하였다.
    1. 회원사 리스트들을 하나하나 클릭 후 띄워지는 팝업 데이터 추출하기
    2. 팝업 url 접속 후 next 버튼 클릭하며 변경되는 값들을 추출하기

위 두가지 방법 중 2번을 택하여 구현하였는데, 한 화면에서 이뤄지는게 뭔가 깔끔할것 같아서 선택하였다.

문제점 해결

아래와 같이 Selenium에 내장되어있는 WebDriver를 이용해 페이지가 load 되고 특정 태그가 화면에 노출 여부 확인 후 다음 step 실행하는 구문이 있었는데, 해당 구문으로 실행할 시 중간중간 반복되는 데이터를 가져오는 이슈가 발생하였다.

원인파악은 제대로 되지 않았으며, 추측하기로 프로그램 동작시간과 웹페이지 렌더링 시간이 어긋나지 않았을까 추측해본다.

WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.CLASS_NAME, "modal-body"))) 

어떻게 해결을 할까 고민을 하다 위 구문을 삭제하고 next 버튼 액션이 있을때 강제로 2초 기다림(sleep(2)) 구문을 넣어 해결하였다.