«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Recent Posts
Today
Total
관리 메뉴

짜리몽땅 매거진

[Python] selenium으로 워크넷 동적 크롤링하기 본문

Data/Python

[Python] selenium으로 워크넷 동적 크롤링하기

쿡국 2024. 4. 15. 14:19

 

지난번 beautifulsoap 라이브러리를 활용해 당근마켓 인기매물 리스트를 정적 크롤링해보았다. 오늘은 워크넷 구인구직 플랫폼에서 검색어와 클릭에 따라 원하는 정보를 크롤링하는 동적 크롤링을 학습했다. 정적 크롤링과 동적 크롤링의 개념 및 차이에 대해서는 지난 포스팅을 참고하면 된다.

 

[Python] beautifulsoap으로 당근마켓 정적 크롤링하기

당근마켓은 모바일에 최적화된 서비스 구조이기 때문에 웹크롤링을 통해서는 한정적인 정보만을 가져올 수 밖에 없다. 하지만 나의 동네 인기 매물을 검색했을 때 인기 게시글들의 '제목', '주

zzarimongddang.tistory.com


워크넷 구인구직 사이트에서 중장년층과 상용직 필터만 클릭했을 때, 뜨는 구인구직 게시글들의 제목, 기업명, 경력정보, 학력정보, 세부 내용 등을 크롤링해야했다. 아래 소스코드와 주석을 참고하면서 살펴보자.

 

Step 1. 필요한 모듈 import

#필요 모듈 import

import requests
from bs4 import BeautifulSoup
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By

 

- requests: HTTP 요청을 보내고 받는 사용된다. 웹페이지 내용을 가져와서 분석하는 주로 활용된다.

- BeautifulSoup: 라이브러리는 HTML XML 문서를 구문 분석하고 검색하는 사용된다. 웹페이지에서 원하는 정보를 추출하고 가공하는 효과적이다.

- pandas: 데이터 핸들링  분석에 사용되며 주로 데이터프레임 형식의 데이터 다루고 분석하는 특화되어 있다.

- selenium: 패키지는 브라우저를 제어하는 사용된다. 웹 페이지의 동적인 요소들을 처리하기 위해 사용되며, 사용자의 인터랙션과 같은 동작이 필요한 경우에 특히 유용하다.

- webdriver: 모듈은 Selenium 패키지에서 제공하는 드라이버를 사용하여 브라우저를 제어한다. 웹페이지를 열고, 클릭하거나 텍스트를 입력하고, 페이지의 요소를 찾는 등의 작업을 수행할 수 있다.

Step 2. 빈 리스트 생성 및 url 호출

# 데이터를 가져올 빈 리스트 생성
companies = []
titles = []
addresses = []
careers = []
educations = []
moneys = []
contents = []

# url 호출 및 webdriver 초기화
base_url = "https://www.work.go.kr/empInfo/empInfoSrch/list/dtlEmpSrchList.do?careerTo=&keywordJobCd=&occupation=&templateInfo=&shsyWorkSecd=&rot2WorkYn=&payGbn=&resultCnt=10&keywordJobCont=N&cert=&cloDateStdt=&moreCon=more&minPay=&codeDepth2Info=11000&isChkLocCall=&sortFieldInfo=DATE&major=&resrDutyExcYn=&eodwYn=&sortField=DATE&staArea=&sortOrderBy=DESC&keyword=&termSearchGbn=all&carrEssYns=&benefitSrchAndOr=O&disableEmpHopeGbn=&webIsOut=&actServExcYn=&maxPay=&keywordStaAreaNm=N&emailApplyYn=&listCookieInfo=DTL&pageCode=&codeDepth1Info=11000&keywordEtcYn=&publDutyExcYn=&keywordJobCdSeqNo=&exJobsCd=&templateDepthNmInfo=&computerPreferential=&regDateStdt=&employGbn=&empTpGbcd=1&region=&infaYn=&resultCntInfo=10&siteClcd=all&cloDateEndt=&sortOrderByInfo=DESC&currntPageNo=1&indArea=&careerTypes=&searchOn=Y&tlmgYn=&subEmpHopeYn=&academicGbn=&templateDepthNoInfo=&foriegn=&mealOfferClcd=&station=&moerButtonYn=Y&holidayGbn=&srcKeyword=&enterPriseGbn=all&academicGbnoEdu=noEdu&cloTermSearchGbn=all&keywordWantedTitle=N&stationNm=&benefitGbn=&keywordFlag=&notSrcKeyword=&essCertChk=&isEmptyHeader=&depth2SelCode=&_csrf=d7a01b06-a193-4fa9-a0bf-fd178130cd2b&keywordBusiNm=N&preferentialGbn=&rot3WorkYn=&pfMatterPreferential=B&regDateEndt=&staAreaLineInfo1=11000&staAreaLineInfo2=1&pageIndex={}&termContractMmcnt=&careerFrom=&laborHrShortYn=#viewSPL"
driver = webdriver.Chrome()

 

Step 3. 페이지 파싱

# 페이지 파싱

for page_index in range(1, 461):
    url = base_url.format(page_index)
    driver.get(url)
    response = driver.page_source
    soup = BeautifulSoup(response, 'html.parser')

크롤링에서 파싱은 꼭 거쳐야하는 부분인데, 워크넷 웹페이지으 특성상 페이지로 나눠져있는 데이터를 불러와야 했다. 따라서 for문을 사용해 총 460페이지의 데이터를 파싱한다.

 

Step 4. 게시글 세부내용 정보 가져오기

	# 빨간부분 정보 가져오기
    contents_element = driver.find_elements(By.CLASS_NAME, 'mt10')
    
    # 리스트에 정보 추가
    if contents_element:
        contents_text = [content.text.strip() for content in contents_element]
        contents.extend(contents_text)
    else:
        print(f"df2: Page {page_index}에서 요소를 찾을 수 없습니다.")

 

앞선 페이지를 파싱하는 for문에 종속되어서 적어야하는 코드들이다. 이 코드에서 가져오려는 정보는 각 게시글들의 세부 내용인데 뒤에서 언급할 타 정보와는 다르게 javascript로 작성된 내용이기 때문에 selector를 복사하는 것이 아닌 class_name인 'mt10'을 가져와 리스트에 정보를 추가하였다. 또한 이 정보를 알기 쉽게 '빨간 부분'이라 지칭하였다.

 

Step 5. 게시글 세부내용을 제외한 정보 가져오기

# 1~10번째 줄 정보 가져오기
    for i in range(1, 11):
        company_element = soup.select_one(f'#list{i} > td:nth-child(2) > a')
        title_element = soup.select_one(f'#list{i} > td:nth-child(3) > div > div > a')
        address_element = soup.select_one(f'#list{i} > td:nth-child(3) > div > p:nth-child(3) > em:nth-child(3)')
        career_element = soup.select_one(f'#list{i}> td:nth-child(3) > div > p:nth-child(3) > em:nth-child(1)')
        education_element = soup.select_one(f'#list{i} > td:nth-child(3) > div > p:nth-child(3) > em:nth-child(2)')
        money_element = soup.select_one(f'#list{i} > td:nth-child(4) > div > p:nth-child(1)')
        
        # 각 리스트에 담기
        if company_element and title_element and address_element and career_element and education_element and money_element:
            companies.append(company_element.text.strip())
            titles.append(title_element.text.strip())
            addresses.append(address_element.text.strip())
            careers.append(career_element.text.strip())
            educations.append(education_element.text.strip())
            money_text = money_element.text.strip().replace('\n', '')  # 줄바꿈 문자 제거
            money_parts = money_text.split()
            if len(money_parts) > 1:
                moneys.append(' '.join(money_parts[:2]))  # 금액과 단위만 포맷팅
            else:
                moneys.append(money_parts[0])
        else:
            print(f"df1: list{i}에서 요소를 찾을 수 없습니다.")

 

빨간부분을 제외한 정보들은 모두 selector를 복사해서 가져온다. 한 페이지마다 총 10개의 리스트가 있기 때문에 반복문을 사용해서 가져오고, 앞서 생성한 빈 리스트에 각 정보들을 담는다.

 

Step 6. 필요 없는 정보 제거하기

# (빨간부분) 공백이거나 필요하지 않은 정보 삭제
contents = [item for item in contents if item.strip() != '' and not item.startswith('D-') and '채용시까지' not in item and '오늘마감' not in item]

 

앞서 가져온 빨간 부분 정보에서 필요하지 않은 부분은 제거하는 과정을 거친다.

 

Step 7. 데이터프레임 생성 및 파일로 출력

# 데이터프레임 생성
df_final = pd.DataFrame({
    'Company': companies,
    'Title': titles,
    'Address': addresses,
    'Careers': careers,
    'Educations': educations,
    'Contents': contents
})

# CSV 파일로 저장
df_final.to_csv('워크넷 크롤링 최종.csv', index=False)

# Excel 파일로 저장
df_final.to_excel('워크넷크롤링 최종.xlsx', index=False)

 

각 정보들을 담은 리스트를 데이터프레임으로 변환한 뒤, 보기 쉽게 csv파일과 excel파일로도 저장한다.