본문 바로가기

업무_인공지능 활용 예시

[GPT/크롤링] 주요 포털 사이트에서 크롤링이 필요한 정보를, GPT에게 시켜서 자동화하기

크롤링의 올바른 사용 의도 및 정의

포털 사이트나 온라인 페이지에서 방문하도록 설정된 URL을 기본으로 HTTP 요청을 보내고, 해당 웹페이지의 HTML 콘텐츠를 받아오는 행위. 이때 받은 HTML 콘텐츠에서 필요한 데이터를 '파싱(Parsing)' 하여 추출한다. 크롤링을 반복 시행할 주소를 다양하게 설정하거나, 크롤링 데이터를 지정할 단어를 저장하는 등 구체적인 의도가 지정되어야 한다.

 

 

*크롤링은 다른 기업이나 개인에게 귀속된 데이터를, 악용할 목적으로 사용되어서는 안 된다.

**오픈된 데이터를 불법적인 방식이 아닌 형태로 수집하고 취합할 때 올바른 용도로 사용할 수 있다.

 

그럼에도 불구하고, 요즘 시대에는 크롤링이 모든 데이터 취합의 시작일지도 모른다.

 누구나 인공지능을 사용할 수 있는 시대지만, 반대로 인공지능을 더 효율적으로 활용하기 위해서는 이제 더 많고 구체적인 정보를 더 빠르고 정확하게 효율적으로 뽑아낼 수 있는 기술을 가져야 한다. '크롤링'으로 모은 빅데이터를 나만의 '커스터마이징'된 데이터로 활용하는 것만큼 효율적인 로직은 없을 것이다.

 

  그래서 오늘은 '크롤링'을 GPT한테 시킬 수 있을지 시도해 보자.

먼저 아래와 같은 프롬프트를 이해할 수 있는가?

 

 

[프롬프트] 
파이썬으로 웹크롤러를 하나 만들 거야. 동적인 자료기 때문에 화면이 보이는 구조면 좋겠다.
특정 주소에서 옵션을 2-3개 정도 클릭해서 아래에 리스트업 된 자료의 제목, 소제목, 링크를 취합하는 크롤러고.
해당 크롤링의 시작은 최상단부터, 내가 이전 크롤링으로 모든 자료야.
처음 시작할 땐 일단 2페이지까지로 하자. 그다음 이 정보들은 엑셀파일로 저장해서 다운로드하면 돼. 이해돼?

 

여기서 용어들을 몇 가지 정리하자면,

  • '동적인 자료'라는 표현은 말 그대로 '내가 매일 똑같은 검색어'를 검색해도 '매번 다른 페이지' 결과가 나오는 경우가 많다. 이런 내용을 동적인 자료라고 명명할 수 있다.
  • '특정 주소'는 내가 크롤링할 대상의 페이지, URL을 의미한다.
  • '옵션'은 해당 특정 주소에서 추가로 수행해야 하는 역할이다. 예를 들어, '최신 글 보기' 옵션이라던지, '광고글 보지 않기' 옵션이라던지
  • '제목', '소제목', '링크' 등은 내가 이번 크롤링을 통해 취합하려는 정보의 종류를 의미한다.
  • '2페이지'로 넘어간다는 뜻은, 검색한 결과 값이 1페이지부터 N페이지까시 순차적으로 나오는 페이지에서 활용하는 내용이다.

 

위와 같이 프롬프트를 쓰면, 현재 나의 GPT에선 아래처럼 답변을 한다.

 

 

💡 네, 이해했습니다. 동적인 웹페이지에서 옵션을 클릭하고, 자료를 크롤링하여 제목, 소제목, 링크를 추출한 후, 엑셀 파일로 저장하는 크롤러를 만들면 됩니다.

이를 위해 아래와 같은 작업을 단계별로 진행하겠습니다:

  1. 필요한 라이브러리 설치: selenium, pandas, openpyxl 등이 필요합니다.
  2. 웹 드라이버 설정: selenium을 사용하여 웹 드라이버를 설정합니다.
  3. 웹페이지 접근 및 옵션 클릭: 특정 주소로 접근하여 필요한 옵션을 클릭합니다.
  4. 데이터 추출: 제목, 소제목, 링크를 추출합니다.
  5. 엑셀 파일로 저장: 추출한 데이터를 엑셀 파일로 저장합니다.

이제 파이썬 코드를 작성해 보겠습니다. 먼저, 필요한 라이브러리를 설치하고 초기 설정을 합니다.

 

 

여기서 다시 한번 GPT와 나 사이의 용어를 정리하자.

  • 라이브러리 : selenium은 구글 크롬에서 활용하는 '크롤러'다. 셀레늄은 파이썬에서 주로 쓰고, colab에선 사용이 안된다.
  • 웹 드라이버 : 크롬 드라이버를 쓴다. 가능하면, 크롬 드라이버는 자동으로 다운로드하고, 업데이트하는 버전으로 설정해야 한다.
  • 웹 페이지 : 말 그대로 주소를 들어가고, 이동하는 과정에서 벌어지는 행동들을 정의한다.
  • 엑셀 파일로 저장 : 나중에는 google API를 이용하면, 구글시트에도 바로 저장할 수 있다. 지금은 엑셀 파일로 대체한다.

 

그리고는 GPT는 아래와 같은 코드를 뽑아냈다.

 

# 필요한 라이브러리 설치
!pip install selenium pandas openpyxl

# 크롤러 코드 작성
import time
import pandas as pd
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 selenium.webdriver.common.keys import Keys

# 웹 드라이버 설정 (Chrome 드라이버 경로 설정 필요)
driver_path = 'path/to/chromedriver'  # 크롬 드라이버의 경로로 변경 필요
driver = webdriver.Chrome(driver_path)

# 웹페이지 접근
url = 'https://example.com'  # 크롤링할 웹페이지 URL로 변경 필요
driver.get(url)

# 옵션 클릭 (예시)
try:
    # 옵션1 클릭
    option1 = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((By.XPATH, 'xpath_of_option1'))  # 옵션1의 XPath로 변경 필요
    )
    option1.click()
    
    # 옵션2 클릭
    option2 = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((By.XPATH, 'xpath_of_option2'))  # 옵션2의 XPath로 변경 필요
    )
    option2.click()
    
    # 페이지가 로드될 시간을 기다림
    time.sleep(5)
    
except Exception as e:
    print(f"옵션 클릭 중 에러 발생: {e}")

# 데이터 추출 함수
def extract_data():
    data = []
    try:
        items = driver.find_elements(By.CSS_SELECTOR, 'css_selector_of_items')  # 아이템들의 CSS Selector로 변경 필요
        for item in items:
            title = item.find_element(By.CSS_SELECTOR, 'css_selector_of_title').text  # 제목의 CSS Selector로 변경 필요
            subtitle = item.find_element(By.CSS_SELECTOR, 'css_selector_of_subtitle').text  # 소제목의 CSS Selector로 변경 필요
            link = item.find_element(By.CSS_SELECTOR, 'css_selector_of_link').get_attribute('href')  # 링크의 CSS Selector로 변경 필요
            data.append({'Title': title, 'Subtitle': subtitle, 'Link': link})
    except Exception as e:
        print(f"데이터 추출 중 에러 발생: {e}")
    return data

# 첫 페이지 데이터 추출
data = extract_data()

# 2페이지로 이동 후 데이터 추출 (예시)
try:
    next_button = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((By.XPATH, 'xpath_of_next_button'))  # 다음 페이지 버튼의 XPath로 변경 필요
    )
    next_button.click()
    
    # 페이지가 로드될 시간을 기다림
    time.sleep(5)
    
    # 2페이지 데이터 추출
    data.extend(extract_data())
    
except Exception as e:
    print(f"2페이지 이동 중 에러 발생: {e}")

# 데이터프레임 생성
df = pd.DataFrame(data)

# 엑셀 파일로 저장
output_path = 'crawled_data.xlsx'
df.to_excel(output_path, index=False)

# 드라이버 종료
driver.quit()

print(f"데이터가 {output_path}에 저장되었습니다.")

 

 

자, 혹시라도 프롬프트를 위랑 똑같이 적었는데 결과물이 위와 다르다면 차라리 위 코드를 직접 복사-붙여넣기 해서

"이런 크롤링 코드"를 발전시켜서 나만의 '크롤러'를 만들 거야. 내가 말하는 조건에 맞춰서 변경해.라고 명령하는 것도 방법이다.

 

 

자, 그럼 이제 다음 미션으로 가자.

 

 

크롤링할 때, 가장 중요한 규칙-변수 설정 

먼저, 나의 규칙을 살펴보자.

 

[프롬프트]
키워드는 5개 정도이고, 아래 주소 뒤 {word}에 들어가면 돼.

웹페이지 주소 : https://section.cafe.naver.com/ca-fe/home/search/articles?q={word}

옵션 1 xpath : //[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/div/div/div/label
옵션 2 xpath : //[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/ul/li[2]/button

제목의 CSS Selector : #mainContainer > div.content > div.section_home_search > div.search_item_wrap > div.item_list > div > div:nth-child(1) > div > a:nth-child(1) > strong
소제목(카페명)의 CSS Selector : #mainContainer > div.content > div.section_home_search > div.search_item_wrap > div.item_list > div > div:nth-child(1) > div > a.cafe_info > span.cafe_name
링크를 추출해야 하는 CSS Selector : #mainContainer > div.content > div.section_home_search > div.search_item_wrap > div.item_list > div > div:nth-child(1) > a

위의 제목, 소제목은 가장 최상단 정보였고, 최하단도 참고용으로 보내줄게.

제목 : #mainContainer > div.content > div.section_home_search > div.search_item_wrap > div.item_list > div > div:nth-child(12) > div > a:nth-child(1) > strong
소제목 : #mainContainer > div.content > div.section_home_search > div.search_item_wrap > div.item_list > div > div:nth-child(12) > div > a.cafe_info > span.cafe_name
링크 : #mainContainer > div.content > div.section_home_search > div.search_item_wrap > div.item_list > div > div:nth-child(12) > div > a:nth-child(1)


2페이지의 xpath : //[@id="mainContainer"]/div[2]/div[1]/div[2]/div[3]/button[2]

참고용으로 3페이지도 보내줄게 : //[@id="mainContainer"]/div[2]/div[1]/div[2]/div[3]/button[3]

이제 이해되지? 한 번 내가 보내준 것들을 정리해서 다시 스크립트 정리해.

 

자, 이제 다시 나의 이야기를 한 번 더 정리해 보자.

 

위와 같이 내용을 보내면, GPT에게 어떤 정보를 주는지 말씀드린다.

 

1. 웹페이지 주소 : '크롤링'해야 하는 온라인 사이트
2. 옵션 1, 2 : '클릭'이라는 행동이 필요한 xPath (아래에서 후술), 주로 메뉴의 탭이나 필터링 버튼을 사용한다.
3. 제목, 소제목, 링크 : '크롤링'해야 하는 데이터의 정보 
4. CSS selector : 위 xPath와 유사한 '좌표', 혹은 '태그'의 개념. 해당 정보를 HTML 코드에서 찾게 만들어준다.
5. 최하단, 2페이지의 정보 : gpt가 스스로 처음~끝까지, 혹은 페이지 1부터 페이지 n까지 정보를 수집하는 규칙을 찾게 만들어준다.

 

 

xPath 또는 CSS selector는 위 이미지와 같이 찾을 수 있다.

'개발자 도구'를 크롬에서 활성화하고, '1. 탐색기'라고 적힌 글자 위 빨간 영역을 둘 다 클릭하면, 내가 URL 사이트에서 클릭하는 곳의 좌표를 찾을 수 있다. 화면과 우측 개발자도구가 서로 연결되어 블록 지정 되는 것을 볼 수 있다.

이때 내가 취합하려는 데이터에 마우스를 놓고 우클릭하면, '복사 >'라는 영역이 활성화된다. 여기서 xPath 또는 CSSselector 요소 값을 복사할 수 있다. 이 데이터가 바로 '좌표'역할을 하는 녀석이다.

 

위 프롬프트에서 "@", "[1]" "//", ' 이런 특수기호와 함께 보이는 내용들이 좌표라고 보면 된다. 크롤링될 때 해당 영역들의 주로 텍스트 (또는 링크 등)을 취합해 온다고 생각하면 된다.

 

 

 이렇게 생긴 프롬프트를 보내면, GPT는 해당 내용에 맞게 코드를 업데이트한다.

여기까지만 따라와도 사실 1페이지 또는 키워드를 5개 정도 지정해서 활용하기에 충분한 수준의 크롤러가 만들어진다.

 

# 필요한 라이브러리 설치
!pip install selenium pandas openpyxl

# 크롤러 코드 작성
import time
import pandas as pd
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

# 웹 드라이버 설정 (Chrome 드라이버 경로 설정 필요)
driver_path = 'path/to/chromedriver'  # 크롬 드라이버의 경로로 변경 필요
driver = webdriver.Chrome(driver_path)

# 키워드 리스트
keywords = ['keyword1', 'keyword2', 'keyword3', 'keyword4', 'keyword5']  # 검색할 키워드 리스트로 변경 필요

# 데이터 추출 함수
def extract_data():
    data = []
    try:
        items = driver.find_elements(By.CSS_SELECTOR, 'div.item_list > div')
        for item in items:
            title = item.find_element(By.CSS_SELECTOR, 'a:nth-child(1) > strong').text
            subtitle = item.find_element(By.CSS_SELECTOR, 'a.cafe_info > span.cafe_name').text
            link = item.find_element(By.CSS_SELECTOR, 'a:nth-child(1)').get_attribute('href')
            data.append({'Title': title, 'Subtitle': subtitle, 'Link': link})
    except Exception as e:
        print(f"데이터 추출 중 에러 발생: {e}")
    return data

all_data = []

# 각 키워드에 대해 크롤링
for word in keywords:
    url = f'https://section.cafe.naver.com/ca-fe/home/search/articles?q={word}'
    driver.get(url)
    
    # 옵션1 클릭
    try:
        option1 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/div/div/div/label'))
        )
        option1.click()
    except Exception as e:
        print(f"옵션1 클릭 중 에러 발생: {e}")

    # 옵션2 클릭
    try:
        option2 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/ul/li[2]/button'))
        )
        option2.click()
        
        # 페이지가 로드될 시간을 기다림
        time.sleep(5)
        
    except Exception as e:
        print(f"옵션2 클릭 중 에러 발생: {e}")

    # 첫 페이지 데이터 추출
    all_data.extend(extract_data())
    
    # 2페이지로 이동 후 데이터 추출
    try:
        next_button = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[3]/button[2]'))
        )
        next_button.click()
        
        # 페이지가 로드될 시간을 기다림
        time.sleep(5)
        
        # 2페이지 데이터 추출
        all_data.extend(extract_data())
        
    except Exception as e:
        print(f"2페이지 이동 중 에러 발생: {e}")

# 데이터프레임 생성
df = pd.DataFrame(all_data)

# 엑셀 파일로 저장
output_path = 'crawled_data.xlsx'
df.to_excel(output_path, index=False)

# 드라이버 종료
driver.quit()

print(f"데이터가 {output_path}에 저장되었습니다.")

 

 

하지만, 여기까지만 해서는 무언가 아쉽다. 조금 더 세부적이고 구체적인 내용들을 취합하는 기술이 필요하다.

다시 한번 프롬프트를 보자.

 

[프롬프트]
좋아. 아직 완성까지 몇 단계 남았어. 먼저 이것부터 하자. 크롤링해야 하는 정보가 하나 더 있어.

게시일(페이지 최상단) : #mainContainer > div.content > div.section_home_search > div.search_item_wrap > div.item_list > div > div:nth-child(1) > div > a.cafe_info > span.date

게시일(페이지 최하단) : #mainContainer > div.content > div.section_home_search > div.search_item_wrap > div.item_list > div > div:nth-child(12) > div > a.cafe_info > span.date

이것도 추가해 주고, 이렇게 다 모인 데이터를 아래처럼 다시 한 파일을 만들 거야.


No. 게시일 검색키워드 카페명 게시글 제목 링크 1 2024.07.22. 윙크 송파 강남 서초맘 한글학습지 처음 시도해 보려는데 https://cafe.naver.com/sojbb2233/608034


이런 식으로 이해돼? 그러니까 모인 데이터를 값 별로 새로운 엑셀파일로 프레임을 먼저 만들고, 키워드 순으로 저장해 주면 되겠지?

 

위 프롬프트를 보면, '게시일' 데이터와 페이지의 최상단 그리고 최하단의 xPath를 전달한 것을 알 수 있다.

또한 아래 내용은 내가 저장하고 싶은 데이터의 생김새. 즉 결과물의 DataFrame을 그대로 작성하고 붙여 넣기 해서 gpt에게 내가 원하는 모습으로 저장되게 만드는 방법 중 하나다.

 

 이와 같이 실행하면, 훨씬 더 구체적인 결과를 가져온다.

 

# 필요한 라이브러리 설치
#!pip install selenium pandas openpyxl chromedriver_autoinstaller

# 크롤러 코드 작성
import time
import pandas as pd
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
import chromedriver_autoinstaller

chromedriver_autoinstaller.install()
driver = webdriver.Chrome()

# 키워드 리스트
keywords = ['keyword1', 'keyword2', 'keyword3', 'keyword4', 'keyword5']  # 검색할 키워드 리스트로 변경 필요

# 데이터 추출 함수
def extract_data(keyword):
    data = []
    try:
        items = driver.find_elements(By.CSS_SELECTOR, 'div.item_list > div')
        for i in range(1, 13):  # 각 페이지당 12개의 글을 가져옴
            title_selector = f'div:nth-child({i}) > div > a:nth-child(1) > strong'
            subtitle_selector = f'div:nth-child({i}) > div > a.cafe_info > span.cafe_name'
            link_selector = f'div:nth-child({i}) > div > a:nth-child(1)'
            date_selector = f'div:nth-child({i}) > div > a.cafe_info > span.date'
            
            try:
                title = driver.find_element(By.CSS_SELECTOR, title_selector).text
                subtitle = driver.find_element(By.CSS_SELECTOR, subtitle_selector).text
                link = driver.find_element(By.CSS_SELECTOR, link_selector).get_attribute('href')
                date = driver.find_element(By.CSS_SELECTOR, date_selector).text
                data.append({
                    'No.': len(data) + 1,
                    '게시일': date,
                    '검색키워드': keyword,
                    '카페명': subtitle,
                    '게시글 제목': title,
                    '링크': link
                })
            except:
                # 요소가 없을 경우 패스
                pass
    except Exception as e:
        print(f"데이터 추출 중 에러 발생: {e}")
    return data

# 모든 키워드에 대한 데이터를 저장할 리스트
all_data = []

# 각 키워드에 대해 크롤링
for word in keywords:
    url = f'https://section.cafe.naver.com/ca-fe/home/search/articles?q={word}'
    driver.get(url)
    
    # 옵션1 클릭
    try:
        option1 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/div/div/div/label'))
        )
        option1.click()
    except Exception as e:
        print(f"옵션1 클릭 중 에러 발생: {e}")

    # 옵션2 클릭
    try:
        option2 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/ul/li[2]/button'))
        )
        option2.click()
        
        # 페이지가 로드될 시간을 기다림
        time.sleep(5)
        
    except Exception as e:
        print(f"옵션2 클릭 중 에러 발생: {e}")

    # 첫 페이지 데이터 추출
    keyword_data = extract_data(word)
    
    # 2페이지로 이동 후 데이터 추출
    try:
        next_button = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[3]/button[2]'))
        )
        next_button.click()
        
        # 페이지가 로드될 시간을 기다림
        time.sleep(5)
        
        # 2페이지 데이터 추출
        keyword_data.extend(extract_data(word))
        
    except Exception as e:
        print(f"2페이지 이동 중 에러 발생: {e}")

    # 모든 키워드 데이터 합치기
    all_data.extend(keyword_data)

# 데이터프레임 생성 및 엑셀 파일에 저장
df = pd.DataFrame(all_data)
df.to_excel('crawled_data_final.xlsx', index=False)

# 드라이버 종료
driver.quit()

print("데이터가 'crawled_data_final.xlsx' 파일에 저장되었습니다.")

 

 

 

여기까지만 실행해도 만족한다?

아니다, 이제 이 과정을 조금 더 인텔리 하게 그리고 루틴 하게 뽑아낼 수 있는 방법을 찾아야 한다.

즉, 매일 같은 시간에 실행되고 추가적으로 내가 이전에 모은 데이터 그다음부터 데이터를 크롤링하게끔 조절해야 한다.

 

 

[프롬프트]
이제 나는 이걸 매일 한 번씩 실시할 거야. 그러면 어떻게 될까? 당연히 매번 중복 데이터가 생기겠지?
나는 그걸 방지하기 위해서, 크롤링이 종료되는 조건을 추가할 거야.
종료 조건은 내가 방금 모은 데이터의 최신 키워드를 기준으로 최신 글이 될 거야.
즉, 크롤러가 실행되면 이전에 실행한 키워드의 각각 최신 글의 제목을 마주할 때까지 크롤링이 되는 거지. 이해되니?

 

 

이렇게 프롬프트를 제공하면, 프롬프트를 완벽하게 이해한 GPT는 놀라우리만큼 발전한 대답과 그 성과를 보인다.

 

GPT💡

네, 이해했습니다. 매번 크롤링을 실행할 때 중복 데이터를 방지하기 위해, 이전에 실행된 데이터에서 각 키워드의 최신 글의 제목을 기준으로 크롤링을 중지하도록 조건을 추가해야 합니다.

이를 위해서는 다음과 같은 절차를 따릅니다:

  1. 이전 데이터 로드: 이전에 저장된 데이터를 로드하여 각 키워드별 최신 글 제목을 확인합니다.
  2. 크롤링 중지 조건 추가: 각 키워드의 최신 글 제목을 만나면 크롤링을 중지합니다.
  3. 데이터 저장: 새로운 데이터를 이전 데이터와 합쳐서 저장합니다.

 

내용을 보시면, 아시겠지만, 상당히 똑똑하다.

 

 

크롤링의 끝, 어떻게 했는지 그 논리과정을 다시 공부하기

 나는 위 과정에서 추가적으로 '최신 글 제목을 확인하기' 그리고 새로운 데이터와 이전 데이터의 중복 데이터 제거 기능 그리고 가장 좋은 기능 중 하나로 '필터링해야 하는 정보' (예시, 카페명)를 별도로 설정하기도 했어.

그랬더니 아래와 같은 개선된 코드를 보여줬다.

 

# 필요한 라이브러리 설치
#!pip install selenium pandas openpyxl chromedriver_autoinstaller

# 크롤러 코드 작성
import time
import pandas as pd
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
import chromedriver_autoinstaller

chromedriver_autoinstaller.install()
driver = webdriver.Chrome()

# 키워드 리스트
keywords = ['keyword1', 'keyword2', 'keyword3', 'keyword4', 'keyword5']  # 검색할 키워드 리스트로 변경 필요

# 필터링할 카페명 리스트
exclude_cafes = {'다낭 도깨비 - 베트남 다낭자유여행 ,다낭여행,맛집,호텔리조트', '여행을 찾는 사람들의 모임', '코인과 쇼핑정보의 모든것'}

# 이전 데이터 로드
try:
    previous_data = pd.read_excel('crawled_data_final.xlsx')
    latest_titles = {keyword: previous_data[previous_data['검색키워드'] == keyword]['게시글 제목'].iloc[0] for keyword in keywords}
    # 최신 데이터를 삭제
    previous_data = previous_data[~previous_data['검색키워드'].isin(keywords)]
except FileNotFoundError:
    previous_data = pd.DataFrame()
    latest_titles = {keyword: None for keyword in keywords}

# 데이터 추출 함수
def extract_data(keyword, latest_title):
    data = []
    stop_crawling = False
    try:
        items = driver.find_elements(By.CSS_SELECTOR, 'div.item_list > div')
        for i in range(1, 13):  # 각 페이지당 12개의 글을 가져옴
            title_selector = f'div:nth-child({i}) > div > a:nth-child(1) > strong'
            subtitle_selector = f'div:nth-child({i}) > div > a.cafe_info > span.cafe_name'
            link_selector = f'div:nth-child({i}) > div > a:nth-child(1)'
            date_selector = f'div:nth-child({i}) > div > a.cafe_info > span.date'
            
            try:
                title = driver.find_element(By.CSS_SELECTOR, title_selector).text
                if title == latest_title:
                    stop_crawling = True
                    break
                subtitle = driver.find_element(By.CSS_SELECTOR, subtitle_selector).text
                if subtitle in exclude_cafes:
                    continue  # 제외할 카페명에 해당하면 건너뜀
                link = driver.find_element(By.CSS_SELECTOR, link_selector).get_attribute('href')
                date = driver.find_element(By.CSS_SELECTOR, date_selector).text
                data.append({
                    'No.': len(data) + 1,
                    '게시일': date,
                    '검색키워드': keyword,
                    '카페명': subtitle,
                    '게시글 제목': title,
                    '링크': link
                })
            except:
                # 요소가 없을 경우 패스
                pass
    except Exception as e:
        print(f"데이터 추출 중 에러 발생: {e}")
    return data, stop_crawling

# 모든 키워드에 대한 데이터를 저장할 리스트
all_data = []

# 각 키워드에 대해 크롤링
for word in keywords:
    url = f'https://section.cafe.naver.com/ca-fe/home/search/articles?q={word}'
    driver.get(url)
    
    # 옵션1 클릭
    try:
        option1 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/div/div/div/label'))
        )
        option1.click()
    except Exception as e:
        print(f"옵션1 클릭 중 에러 발생: {e}")

    # 옵션2 클릭
    try:
        option2 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/ul/li[2]/button'))
        )
        option2.click()
        
        # 페이지가 로드될 시간을 기다림
        time.sleep(5)
        
    except Exception as e:
        print(f"옵션2 클릭 중 에러 발생: {e}")

    # 첫 페이지 데이터 추출
    keyword_data, stop_crawling = extract_data(word, latest_titles[word])
    
    if stop_crawling:
        all_data.extend(keyword_data)
        continue
    
    # 2페이지로 이동 후 데이터 추출
    try:
        next_button = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[3]/button[2]'))
        )
        next_button.click()
        
        # 페이지가 로드될 시간을 기다림
        time.sleep(5)
        
        # 2페이지 데이터 추출
        additional_data, stop_crawling = extract_data(word, latest_titles[word])
        keyword_data.extend(additional_data)
        
    except Exception as e:
        print(f"2페이지 이동 중 에러 발생: {e}")

    all_data.extend(keyword_data)
    if stop_crawling:
        continue

# 새로운 데이터와 이전 데이터를 합침
if not previous_data.empty:
    df = pd.concat([previous_data, pd.DataFrame(all_data)], ignore_index=True).drop_duplicates(subset=['게시글 제목'], keep='first')
else:
    df = pd.DataFrame(all_data)

# 최종 데이터에서 제외할 카페명 필터링
df = df[~df['카페명'].isin(exclude_cafes)]

# 엑셀 파일에 저장
df.to_excel('crawled_data_final.xlsx', index=False)

# 드라이버 종료
driver.quit()

print("데이터가 'crawled_data_final.xlsx' 파일에 저장되었습니다.")

 

 

그리고 이 코드를 직접 공부하면서, 이후엔 또 어떤 기능까지 활용할 수 있을지 좀 더 연구가 필요하다.

그럴 땐 여기서부터 시작해야 한다.

 

"주석을 아주 구체적으로 달아서 다시 나에게 줘. 한글로 써. 그리고 각 문단 별 구체적인 지침도 함께 줘"

 

 

그리하면, 아래처럼 매우 구체적인 연습장이자, 완성품을 볼 수 있다.

 

# 필요한 라이브러리 설치
# !pip install selenium pandas openpyxl chromedriver_autoinstaller

# 필요한 라이브러리 임포트
import time
import pandas as pd
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
import chromedriver_autoinstaller
import datetime

# 오늘 날짜를 받아와 문자열로 변환
today = datetime.date.today()
date_str = today.strftime("%Y%m%d")

# 크롬 드라이버 자동 설치 및 설정
chromedriver_autoinstaller.install()
driver = webdriver.Chrome()

# 검색할 키워드 리스트
keywords = ['키워드 리스트']

# 제외할 카페명 리스트
exclude_cafes = {'카페명 리스트'}

# 이전에 저장된 데이터 불러오기
try:
    previous_data = pd.read_excel('crawled_data_final.xlsx')
    # 키워드별 최신 게시글 제목 저장
    latest_titles = {keyword: previous_data[previous_data['검색키워드'] == keyword]['게시글 제목'].iloc[0] for keyword in keywords}
    # 이전 데이터에서 해당 키워드의 데이터를 삭제
    previous_data = previous_data[~previous_data['검색키워드'].isin(keywords)]
except FileNotFoundError:
    # 파일이 없을 경우 빈 데이터프레임 생성
    previous_data = pd.DataFrame()
    latest_titles = {keyword: None for keyword in keywords}

# 데이터 추출 함수 정의
def extract_data(keyword, latest_title):
    data = []
    stop_crawling = False
    try:
        items = driver.find_elements(By.CSS_SELECTOR, 'div.item_list > div')
        for i in range(1, 13):  # 각 페이지당 12개의 글을 가져옴
            title_selector = f'div:nth-child({i}) > div > a:nth-child(1) > strong'
            subtitle_selector = f'div:nth-child({i}) > div > a.cafe_info > span.cafe_name'
            link_selector = f'div:nth-child({i}) > div > a:nth-child(1)'
            date_selector = f'div:nth-child({i}) > div > a.cafe_info > span.date'
            
            try:
                title = driver.find_element(By.CSS_SELECTOR, title_selector).text
                # 최신 게시글 제목을 만나면 크롤링 중지
                if title == latest_title:
                    stop_crawling = True
                    break
                subtitle = driver.find_element(By.CSS_SELECTOR, subtitle_selector).text
                # 제외할 카페명에 해당하면 건너뜀
                if subtitle in exclude_cafes:
                    continue
                link = driver.find_element(By.CSS_SELECTOR, link_selector).get_attribute('href')
                date = driver.find_element(By.CSS_SELECTOR, date_selector).text
                data.append({
                    'No.': len(data) + 1,
                    '게시일': date,
                    '검색키워드': keyword,
                    '카페명': subtitle,
                    '게시글 제목': title,
                    '링크': link
                })
            except:
                # 요소가 없을 경우 패스
                pass
    except Exception as e:
        print(f"데이터 추출 중 에러 발생: {e}")
    return data, stop_crawling

# 모든 키워드에 대한 데이터를 저장할 리스트
all_data = []

# 각 키워드에 대해 크롤링
for word in keywords:
    url = f'https://section.cafe.naver.com/ca-fe/home/search/articles?q={word}'
    driver.get(url)
    
    # 옵션1 클릭
    try:
        option1 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/div/div/div/label'))
        )
        option1.click()
    except Exception as e:
        print(f"옵션1 클릭 중 에러 발생: {e}")

    # 옵션2 클릭
    try:
        option2 = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[1]/div[2]/ul/li[2]/button'))
        )
        option2.click()
        
        # 페이지가 로드될 시간을 기다림
        time.sleep(5)
        
    except Exception as e:
        print(f"옵션2 클릭 중 에러 발생: {e}")

    # 첫 페이지 데이터 추출
    keyword_data, stop_crawling = extract_data(word, latest_titles[word])
    
    if stop_crawling:
        all_data.extend(keyword_data)
        continue
    
    # 2페이지로 이동 후 데이터 추출
    try:
        next_button = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="mainContainer"]/div[2]/div[1]/div[2]/div[3]/button[2]'))
        )
        next_button.click()
        
        # 페이지가 로드될 시간을 기다림
        time.sleep(5)
        
        # 2페이지 데이터 추출
        additional_data, stop_crawling = extract_data(word, latest_titles[word])
        keyword_data.extend(additional_data)
        
    except Exception as e:
        print(f"2페이지 이동 중 에러 발생: {e}")

    all_data.extend(keyword_data)
    if stop_crawling:
        continue

# 새로운 데이터와 이전 데이터를 합침
if not previous_data.empty:
    df = pd.concat([previous_data, pd.DataFrame(all_data)], ignore_index=True).drop_duplicates(subset=['게시글 제목'], keep='first')
else:
    df = pd.DataFrame(all_data)

# 최종 데이터에서 제외할 카페명 필터링
df = df[~df['카페명'].isin(exclude_cafes)]

# 엑셀 파일에 저장

# 오늘 날짜를 포함한 파일 이름 생성
file_name = f'crawled_data_{date_str}.xlsx'

# 데이터프레임을 엑셀 파일로 저장
df.to_excel(file_name, index=False)

# 드라이버 종료
driver.quit()

print(f"데이터가 '{file_name}' 파일에 저장되었습니다.")

 

그리고 아래와 같은 라이브러리에 대해 구체적인 설명도 함께하고, 단계별로 진행상황을 설명해 준다.

 

 

 💡 코드 설명

  1. 라이브러리 임포트: selenium, pandas, openpyxl, chromedriver_autoinstaller, datetime 라이브러리를 임포트 합니다.
  2. 오늘 날짜 설정: 오늘 날짜를 받아와 문자열로 변환합니다.
  3. 크롬 드라이버 설정: chromedriver_autoinstaller를 사용하여 크롬 드라이버를 자동으로 설치하고 설정합니다.
  4. 검색할 키워드와 제외할 카페명 설정: 크롤링할 키워드와 제외할 카페명을 설정합니다.
  5. 이전 데이터 로드: 이전에 저장된 데이터를 불러오고, 키워드별 최신 게시글 제목을 저장합니다. 만약 파일이 없으면 빈 데이터프레임을 생성합니다.
  6. 데이터 추출 함수 정의: 각 게시글의 제목, 소제목, 링크, 게시일을 추출하는 함수를 정의합니다. 최신 게시글 제목을 만나면 크롤링을 중지합니다.
  7. 크롤링 실행: 각 키워드에 대해 설정된 옵션을 클릭하고 데이터를 추출합니다. 최신 게시글 제목을 만나면 크롤링을 중지하고 다음 키워드로 넘어갑니다.
  8. 데이터 합치기 및 필터링: 새로운 데이터와 이전 데이터를 합쳐 중복을 제거하고, 제외할 카페명을 필터링합니다.
  9. 엑셀 파일 저장: 오늘 날짜를 포함한 파일 이름을 생성하고 데이터를 엑셀 파일로 저장합니다.
  10. 크롬 드라이버 종료: 크롬 드라이버를 종료합니다.
  11. 완료 메시지 출력: 데이터가 저장되었음을 알리는 메시지를 출력합니다.

 

 사실 매일 같은 장소에서, 같은 키워드로 데이터를 취합하는 방식은 크롤링만큼 좋은 기법이 없다.

해당 방법은 익혀두면, 엔간한 업무에서 극효율을 보인다. 특히 이런 텍스트를 끌어오는 방식도 유효하지만,

더 나아가서는 같은 페이지를 스크린샷 찍어서 아카이브 하는 방식도 고려할 수 있다.

 

 후속적으로 공유할 수 있도록 하겠다.