People-Analytics201

코린이도 한다, 텍스트 크롤링 (feat. ChatGPT, 구글링) 본문

People Insight

코린이도 한다, 텍스트 크롤링 (feat. ChatGPT, 구글링)

2023. 4. 9. 08:34

본 포스팅은 PA201 구성원 "숀"님에 의해 작성된 글입니다.


안녕하세요. 숀입니다.

이슈

• 새로운 영입사이트를 도입함에 따라, 기존의 영입사이트는 서비스 및 운영이 종료될 예정
 기존의 영입사이트에 있던 영입공고 및 데이터(JD 등)들이 서비스 종료와 함께 유실될 예정
 해당 데이터는, 영입 파트에 있어서 상당히 의미 있는 비정형 데이터로 판단


목표

. 추후 분석 또는 영입에 활용할 수 있도록 해당 데이터들을 최대한 백업하고자 함


현황

 기존 영입사이트 제공 업체 : 백업 불가
 영입공고: 약 580개
 영입 담당자: 코딩은 for 구문 예제를 실행시켜 본 게 전부인 코린이


결심

 코린이지만 요즘엔 chatGPT가 코드도 써준다길래, chatGPT를 통해서 기존 영입페이지의 텍스트를 크롤링할 수 있는 코드를 작성해보기로 함.


실행

[Day 1]
 무작정 ‘텍스트 크롤링’, ‘chatGPT’를 구글링하여 관련 유튜브 영상을 찾게 됨
https://youtu.be/eap62CrRtgg

• 위의 영상을 통해 영입공고의 텍스트를 바로 크롤링하는 것이 아니라, 각 영입공고의 URL을 먼저 크롤링 해야 한다는 것을 알게 됨
 그래서 chatGPT에게 첫 번째 부탁(?)을 함. (물론 영어는 구글 번역기를 활용)

• 그 결과는, 지나가다가 개발자님의 PC를 몰래 훔쳐봤을 때와 비슷한 느낌의 답변이 나옴

• 정말로 구현(모든 영입공고의 URL을 크롤링)되었는지, 실행시켜 봄

• 예???
• 하지만 당황하지 않고, 오히려 chatGPT에게 어떻게 된거냐고 따져 물음

• 적절한 코드가 나올 때까지 ‘질문을 바꿔가며’ 계속 다시 써달라고 부탁했지만 [ ]만 출력됨
• 명령조로 질문해서, 그런가 하는 마음에 could you로 예의 바르게 질문해 봄. (소용 없었음 TㅡT)


[Day 2]
• ‘크롤링시 아무 값도 가지 오지 못함’을 키워드로 구글링하며 방법을 찾기 시작
• 그러다가 동적인 페이지의 경우, selenium 라이브러리를 이용해야 한다는 것을 알게 됨
파이썬으로 크롤링하는데 값이 안 읽어와질때 해결법

 

파이썬으로 크롤링하는데 값이 안 읽어와질때 해결법

아마 이렇게 코드를 짰을 것이다 import requests from bs4 import BeautifulSoup # HTTP GET Request req = requests.get('링크') # HTML 소스 가져오기 html = req.text # BeautifulSoup으로 html소스를 python객체로 변환하기 soup = Bea

conservative-vector.tistory.com

영입사이트가 동적인 페이지인지는 모르겠으나, selenium 라이브러리를 활용하여 코드를 써달라고 chatGPT에게 부탁함

• 그랬더니 이번에는 뭔가 새로운 오류 메시지를 확인할 수 있었음

• 오류 메시지를 바탕으로, 코드를 수정해달라고 chatGPT에게 부탁함

• 그랬더니 놀랍게도, 드디어! 결과물(URL 크롤링)이 출력됨

• 그러나, 모든 페이지의 URL을 크롤링한 것이 아니라 첫 번째 페이지의 URL만 크롤링 함
• 그래서 현재 보이는 페이지 뿐만 아니라 모든 페이지를 크롤링할 수 있도록 코드 수정을 부탁함

• ‘다음 페이지(next_button)’를 클릭할 수 있는 코드를 작성해 주었지만 오류가 발생

• 이때부터, 여러 페이지를 크롤링할 수 있는 방법을 찾아 chatGPT와 지리한 문답 공방이 이어짐


[Day 3]
• 계속해서 오류가 발생
• 돌이켜 보면, 포기할 뻔했던 가장 큰 고비 중에 하나였음
• chatGPT가 적어주는 코드를 그대로 실행하는 것으로는, 오류의 원인을 파악하기 어렵다고 생각
• 그래서 코드를 나눠서 조금씩 실행하면서, 어느 부분에서 오류가 발생하는지 파악하고자 함

• 이 과정을 통해, chatGPT가 작성한 코드를 ‘글자 덩어리’가 의미가 있는 ‘코드로써’ 조금 이해하게 됨
• chatGPT가 작성해 주는 주요 코드를 어느 정도 이해하게 되었지만, 오류는 여전함
• 그래서 다른 웹페이지에서는 될까 싶어, 크롤링을 테스트 해보았는데 놀랍게도 잘됨

• 그래서 완전히 틀린 길은 아니구나, 스스로를 다독이며 일단 하루를 마무리 함


[Day 4]
• 구글링을 통해 다른 분들이 해놓은 코드를 chatGPT에게 샘플로 보여주며 계속 수정을 요구

• 그렇지만, 여전히 첫 번째 페이지만 크롤링되거나 오류가 발생함

• 그러다 문득 다른 분들의 코드에는 range()에 1개가 아닌 2개의 인수가 있다는 것과 time.sleep() 기능을 통해, 크롬이 동작하는 과정에 시간을 주고 있다는 것을 알게 됨
• 이를 바탕으로, 해당 코드를 조금씩 수정하였고 드디어 전체 페이지(4개 페이지)의 URL을 크롤링 함
(여기서 약간 눈물이 났음 TㅡT)

• 정말 저 부분을 추가하여 크롤링이 된 것인지는 모르겠으나, 일단 기쁜 마음으로 하루를 마무리함


[Day 5]
• 오늘부터는 각 URL(영입공고) 안에 들어 있는 JD(텍스트)를 크롤링하는 코드를 작성해 보고자 함
• 앞서와 마찬가지로 chatGPT에게 해당 페이지의 URL과 html 샘플을 보여주고 코드를 부탁함

• 그랬더니, 놀랍게도 너무나도 쉽게 원하는 결과물을 얻음

• 위 결과에 크게 고무되었고, 더 많은 영역의 텍스트를 크롤링 하도록 코드 수정을 부탁함

• 그 결과 역시 만족스러웠음

• 심지어, 여러 페이지를 크롤링하는 것도 쉽게 성공하였음

• 하지만, 텍스트 데이터를 분석하기 위해서는 항목별(업무, 자격요건 등)로 크롤링 하는 것이 필요했음
• 그래서 무턱대고 chatGPT에게 항목별로 크롤링 할 수 있게, 코드 수정을 부탁함

• 하지만 결과는 엉망이었음
• 정확한 크롤링을 위해서는 메소드와 class, ID, attribute 등을 이해할 필요가 있다는 것을 알게 됨
• 이런 부분은 다행히 구글링을 통해 어느 정도 기본적인 학습을 할 수 있었음

[웹 크롤링 3/3] 원하는 데이터 직접 크롤링해보기 (실전)

 

[웹 크롤링 3/3] 원하는 데이터 직접 크롤링해보기 (실전)

🎨 웹 크롤링 (3) - 실전 연습!

velog.io

이를 통해, chatGPT에게 어떻게 코드를 수정하면 좋을지 구체적으로 부탁했고 항목별로 크롤링이 되도록 코드를 수정함


[Day6]
• 앞서 크롤링 했던 URL(영입공고)에 포함된 모든 JD(텍스트)를 크롤링 하여 엑셀 파일에 저장하도록 chatGPT에게 코드 작성을 부탁함

• 이때부터 두 번째 고비가 시작됨
• chatGPT가 작성해 준 코드를 바탕으로 크롤링을 진행하였으나, 항목별로 나눠서 크롤링이 되지 않거나 아예 크롤링이 되지 않음

• 앞서 영입공고 URL 크롤링과 마찬가지로, chatGPT와의 지리한 문답과 구글링이 이어짐
• 그러나 다행히, 메소드와 클래스에 대한 이해가 있었고 코드를 끊어가며 실행하면서 어느 정도 방향성을 가지고 조금씩 수정할 수 있었음
(이제는 제법 코드가 길어짐)


[Day7]

• 엑셀에 저장되는 크롤링 항목이 하나씩 늘어가는 것을 확인할 수 있었음

• 그렇게 항목별 클래스 또는 태그를 찾아가며, 크롤링 코드를 늘려나감

• 약간의 오류는 있지만 원하는 정보는 어느 정도 크롤링 되고 있다고 판단
• 이에, 영입사이트의 이전 공고를 포함한 모든 공고를 오픈(게시)하여 위의 코드를 실행함
• 여기서 세 번째 고비가 찾아옴
• 이전 공고를 포함해 모든 공고를 오픈하니, 총 58페이지에 걸쳐 크롤링이 필요

• 현재의 코드는 10페이지에서 ‘다음 버튼’을 눌러 11~20이 있는 페이지로 넘어가는 코드가 없었음

• 이번에도 여지없이, chatGPT에게 관련 기능이 구현되도록 코드 수정을 부탁함

• 여기에서 추가적인 구글링을 통해, 10페이지마다 ‘다음 버튼’이 클릭되도록 코드를 수정하였음

    try:
        if i % 10 != 0:
            next_page = driver.find_element(By.XPATH, f"//a[@pageindex='{i+1}']")
            next_page.click()
            time.sleep(3)    
            
        else:
            next_button = driver.find_element(By.XPATH, "//button[@class='btn btn-paging btn-small btn-circle fa fa-angle-right']")
            next_button.click()
            time.sleep(3)

    except:

    # if there is no next page button, break out of the loop
        break

최종적으로 다음과 같은 코드를 완성하게 됨
•chatGPT가 낳은(?) 코드지만, 왠지 제 자식 같은 결과물 (눈물이.. TㅡT)


[완성본]

import requests
from bs4 import BeautifulSoup
from openpyxl import Workbook
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 time

# set up webdriver
driver = webdriver.Chrome()

# navigate to webpage
driver.get("<https://kakaoent.recruiter.co.kr/app/jobnotice/list>")

# last page check
lastpage = 58

# create list to store urls
url_list = []

# create list to store titles and text
titles_jds = []

# loop through the first 4 pages
for i in range(1,lastpage):
    # find all job notice elements on the page
    job_notices = driver.find_elements(By.XPATH, "//a[contains(@href, 'jobnoticeSn=')]")

    # append the href attribute of each job notice element to the url_list
    for job_notice in job_notices:
        url_list.append(job_notice.get_attribute("href"))

    try:
        if i % 10 != 0:
            next_page = driver.find_element(By.XPATH, f"//a[@pageindex='{i+1}']")
            next_page.click()
            time.sleep(3)    
            
        else:
            next_button = driver.find_element(By.XPATH, "//button[@class='btn btn-paging btn-small btn-circle fa fa-angle-right']")
            next_button.click()
            time.sleep(3)

    except:

    # if there is no next page button, break out of the loop
        break
        
        
# close the webdriver
driver.quit()

# loop through each URL in the url_list
for url in url_list:
    # send a GET request to the webpage URL
    response = requests.get(url)

    # parse the HTML content using BeautifulSoup
    soup = BeautifulSoup(response.text, "html.parser")

    # find the element containing the title text
    title_element = soup.find("span", class_="view-bbs-title")

    # check if title_element is None before trying to access its text content
    if title_element is not None:
        title = title_element.get_text().strip()
    else:
        title = ""

    # find the element containing the text content
    if title_element is not None:
        # look for the first span element after the title element and extract its text content
        period_element = title_element.find_next("span")
        if period_element is not None:
            period = period_element.get_text().strip()
        else:
            period = ""
    else:
        period = ""

    if period_element is not None:
        # look for the first span element after the title element and extract its text content
        status_element = period_element.find_next("span")
        if status_element is not None:
            status = status_element.get_text().strip()
        else:
            status = ""
    else:
        status = ""
        
        
    if status_element is not None:
        # look for the first span element after the title element and extract its text content
        position_element = status_element.find_next("ul")
        if position_element is not None:
            position = position_element.get_text().strip()
        else:
            position = ""
    else:
        position = ""
        
    if position_element is not None:
        # look for the first span element after the title element and extract its text content
        process_element = position_element.find_next("ul")
        if process_element is not None:
            process = process_element.get_text().strip()
        else:
            process = ""
    else:
        process = ""           
            
    if process_element is not None:
        # look for the first span element after the title element and extract its text content
        org_element = process_element.find_next("ul")
        if org_element is not None:
            org = org_element.get_text().strip()
        else:
            org = ""
    else:
        org = ""

    if org_element is not None:
        # look for the first span element after the title element and extract its text content
        work_element = org_element.find_next("ul")
        if work_element is not None:
            work = work_element.get_text().strip()
        else:
            work = ""
    else:
        work = ""

    if work_element is not None:
        # look for the first span element after the title element and extract its text content
        qualification_element = work_element.find_next("ul")
        if qualification_element is not None:
            qualification = qualification_element.get_text().strip()
        else:
            qualification = ""
    else:
        qualification = "" 
        
    if qualification_element is not None:
        # look for the first span element after the title element and extract its text content
        goodtohave_element = qualification_element.find_next("ul")
        if goodtohave_element is not None:
            goodtohave = goodtohave_element.get_text().strip()
        else:
            goodtohave = ""
    else:
        goodtohave = "" 
        
    
    # append the title and text to the title_and_text_list
    titles_jds.append([title, period, status, position, process, org, work, qualification, goodtohave])

# save the title and text data to an Excel workbook
wb = Workbook()
ws = wb.active

# write header row
ws.append(['Title', 'Period', 'status', 'position', 'process','org','work','qualification','goodtohave'])

# write the data rows to the Excel workbook
for row in titles_jds:
    ws.append(row)

# save the workbook
wb.save('titles_jds.xlsx')

[느낀 점]

• 고작 for문 예제를 실행해봤던 코린이가 맨땅에 헤딩하듯, 영입사이트에서 원하는 데이터를 크롤링 할 수 있었던 것은 어쨌든 chatGPT와 구글링(+ PA 스터디)이 있었기에 가능했습니다.
• 물론 제법 많은 시간이 소요되었지만, chatGPT가 있다면 다른 코린이 담당자나 신입사원들도 충분히 해낼 수 있을 것이라고 생각합니다.
• 시작을 고민하는 분들이 있다면, 저의 미흡한 사례가 작은 용기가 되었으면 좋겠습니다.

 

Comments