본문 바로가기
데이터분석/크롤링

[Selenium] 프로그래머스(Programmers) 채용공고 크롤링

by merona99 2022. 6. 29.
반응형

프로그래머스에서 채용공고 크롤링하기

 

캡스톤을 진행하면서 채용 데이터가 필요해서 '프로그래머스', '사람인'에서 크롤링을 진행했다.

둘다 하루만에 비교적 쉽게 만들 수 있었다.

 

하지만 처음 크롤링을 했을때는 라벨링값을 달지 않고 (ex) backend) 채용공고 내용만을 크롤링해서 이 채용공고가 어떤 분야의 채용공고인지 알 수 없었다.

 

그래서 별수 없이 2차 크롤링도 진행했다.

2차 크롤링에서는 '채용 본문 데이터 + 해쉬태그값'의 형태로 진행했다.

 

두 가지의 크롤링 모두 포스팅하려고 한다:)

 

 

또한 프로그래머스는 크롤링하기 편리한 구조로 되어있긴 하지만 모델에 돌릴 데이터 양으로는 채용공고가 매우 부족했다.

 

 

[프로그래머스 홈페이지]

 

프로그래머스: https://programmers.co.kr/job

 

개발자 채용

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 


[크롤링 1차 - 채용본문만 크롤링]

 

크롤링 방법

 

 

Selenium + BeautifulSoup 사용

요약(section-summary), 스택(section-stack), 포지션(section-position), 자격조건(section-requirements), 우대사항(section-preference), 개발팀&환경(section-culture)

해당 부분들을 보기좋게 세분화하여 각 컬럼으로 만들어 데이터를 가져왔다.

 

 

 

[크롤링 순서]

 

1. 모듈 불러오기

 

 

2. 크롤링 url 입력

selenium으로 url 찾고 본격 크롤링은 beautifulsoup를 사용했다.

 

 

3. 크롤링

 

 

4. 데이터 csv파일로 저장

 
원래는 section-description 부분이 있었는데 해당부분은 안적은 회사들도 있고 이후 학습할때 키워드가 중요한 부분이 아니라 다량의 데이터를 더 얻기위하여 빼었다.
 
해당 코드에서 1차로 1706개하고 끊기고 2차로 1716개 하고 끊기고 3차로 2430개 하고 끊겨서 위에처럼 모두 예외처리를 해주었다.
 
그냥 처음부터 예외처리를 하고 크롤링하기를 추천한다.

 

 

 

[소스코드]

 

from urllib.error import HTTPError
from bs4 import BeautifulSoup
from urllib.request import Request, urlopen, HTTPError
import pandas as pd
import re
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
from selenium.webdriver.common.by import By
import csv

# 크롤링 메인부분
base_url = 'https://programmers.co.kr/'
option = 'job_positions/'
code = 1
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
count_data = 0

driver = webdriver.Chrome("chromedriver")
for n in range(code, 15000, 1):
    URL = base_url + option + str(n)
    try:
        driver.get(url=URL)
        driver.implicitly_wait(3)
        html = driver.page_source
        soup = BeautifulSoup(html, 'html.parser')

        data1 = soup.find("div", {"class", "container container-position-show"})

        # 회사명, 공고제목
        if data1 is not None:
            company = data1.select_one("header > div > h2").text
            title = data1.select_one("header > div > h4").text

        # 나머지 데이터
        data2 = soup.find("div", {"class", "content-body col-item col-xs-12 col-sm-12 col-md-12 col-lg-8"})

        if data2 is not None:
            summarys = data2.select(".section-summary > table > tbody > tr")
            summary = []
            for sm in summarys:
                sm1 = sm.select_one("td:nth-of-type(2)").text
                sm2 = sm.select_one("td:nth-of-type(3)").text
                summary.append([sm1,sm2])

            stacks = data2.select(".section-stacks > table > tbody > tr > td > code")
            stack = []

            for sc in stacks:
                stack.append(sc.text)

            position = data2.select_one(".section-position")
            if position is not None:
                position = data2.select_one(".section-position").get_text()
            else:
                position = []           

            requirements = data2.select_one(".section-requirements")
            if requirements is not None:
                requirements = data2.select_one(".section-requirements").get_text()
            else:
                requirements = []   

            preference = data2.select_one(".section-preference")
            if preference is not None:
                preference = data2.select_one(".section-preference").get_text()
            else:
                preference = []    

            culture = data2.select_one(".section-culture")
            if culture is not None:
                culture = data2.select_one(".section-culture").get_text()
            else:
                culture = []           

            # 요약(section-summary)
            # 스택(section-stack)
            # 포지션(section-position)
            # 자격조건(section-requirements)
            # 우대사항(section-preference)
            # 개발팀&환경(section-culture)

            programmers_data = {
            'section-company' : company,
            'section-title' : title,
            'section-summary' : summary,
            'section-stack' : stack,
            'section-position' : position,
            'section-requirements' : requirements,
            'section-preference' : preference,
            'section-culture' : culture
            }  

            with open('./programmers.csv', 'a', encoding='utf-8', newline='') as csvfile:
                fieldnames = ['section-company', 'section-title', 'section-summary', 'section-stack', 'section-position', 'section-requirements','section-preference', 'section-culture']
                csvwriter = csv.DictWriter(csvfile, fieldnames=fieldnames)
                csvwriter.writerow(programmers_data)
            count_data += 1   
            print(n,'번째 : ', title)

    except HTTPError as e:
        print(e)
driver.close()

print('crawling programmers done!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
print('총데이터:',count_data)


# 원래는 section-description 부분이 있었는데 해당부분은 안적은 회사들도 있고 이후 학습할때 키워드가 중요한 부분이 아니라 다량의 데이터를 더 얻기위하여 빼었음.

# 1차끊김 1706, 2차끊김 1716, 3차끊김 2430

 

 

 

[결과]

 

programmers.csv - vscode

 

programmers.csv - 구글코랩

8개 컬럼으로 총 2430개의 데이터가 크롤링 되었다.

 

 


[크롤링 2차 - 채용본문 + 라벨링값 크롤링]

 

 

크롤링 방법

 

이번에는 딥러닝 모델에 학습시킬 수 있도록 라벨링과 함께 크롤링을 진행했다.

10개의 직군으로 분류한 후 아래와 같이 프로그래머스는 비슷한 직군끼리 하나의 라벨링으로 묶었다.

 

 

 

해쉬태그 분류 방법

크롤링 컬럼.hwp
0.02MB

 

 

프로그래머스 사이트

 

위처럼 선택하면 backend부분의 채용공고로 가정하였고 해당 url은 https://programmers.co.kr/job?page=1&order=recent&job_category_ids=1&job_category_ids=25&job_category_ids=6이 된다.

 

 

 

[크롤링 순서]

 

1. 모듈 불러오기

 

 

2. url 정보 입력

 

 

3. 해당 url에서 나오는 채용공고들의 id값을 찾아서 저장하는 크롤링 코드

우선 첫번째로 각 라벨링의 검색 값의 채용공고들의 id값을 따로 배열에 저장하였다.

이후에 해당 id값만큼 반복하여 크롤링을 2차로 진행하였다.

 

 

4. 갹 id값에 맞는 채용공고 크롤링

이번에는 selenium만을 사용하여 크롤링을 진행하였다.

find_element를 사용해서 본문을 가져왔다.

 

tag부분이 우리의 해쉬태그가 들어가는 부분이다.

 

 

[소스코드]

 

# 2022.04.06 4:38 programmers crawling done!
# 'stack'부분과 position, requirements, preference 부분을 'content'로 묶고 'tag'(해쉬태그)부분을 추가해 총 3개의 컬럼으로 구성한 csv파일의 크롤링코드
# 총 데이터: '3263'개

from urllib.request import Request, urlopen, HTTPError
import re
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
from selenium.webdriver.common.by import By
import csv

# Back_end 52 : https://programmers.co.kr/job?page=1&job_category_ids=1&job_category_ids=6&job_category_ids=25&order=recent
# Software 7 : https://programmers.co.kr/job?page=1&job_category_ids=17&order=recent
# System 12 : https://programmers.co.kr/job?page=1&job_category_ids=18&job_category_ids=26&job_category_ids=27&job_category_ids=59&job_category_ids=13&order=recent
# Database 11 : https://programmers.co.kr/job?page=1&job_category_ids=12&order=recent
# Network/Security 12 : https://programmers.co.kr/job?page=1&job_category_ids=9&job_category_ids=22&order=recent
# Front_end 30 : https://programmers.co.kr/job?page=1&job_category_ids=4&order=recent
# Application 17 : https://programmers.co.kr/job?page=1&job_category_ids=2&job_category_ids=3&order=recent
# Service 4 : https://programmers.co.kr/job?page=1&job_category_ids=10&order=recent
# Game 6 : https://programmers.co.kr/job?page=1&job_category_ids=7&job_category_ids=16&job_category_ids=20&order=recent
# AI 18 : https://programmers.co.kr/job?page=1&job_category_ids=5&job_category_ids=11&job_category_ids=12&order=recent

base_url = 'https://programmers.co.kr/job?page='
url_list = ['&job_category_ids=1&job_category_ids=6&job_category_ids=25','&job_category_ids=17','&job_category_ids=18&job_category_ids=26&job_category_ids=27&job_category_ids=59&job_category_ids=13','&job_category_ids=12','&job_category_ids=9&job_category_ids=22','&job_category_ids=4','&job_category_ids=2&job_category_ids=3','&job_category_ids=10','&job_category_ids=7&job_category_ids=16&job_category_ids=20','&job_category_ids=5&job_category_ids=11&job_category_ids=12']
tail_url = '&order=recent'
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
num_list = [52,7,12,11,12,30,17,4,6,18]
data = [[]for _ in range(10)]           # url 저장 리스트

# 각 해쉬태그의 url들을 찾는 크롤링코드
driver = webdriver.Chrome("./chromedriver")
for idx, num in enumerate(num_list):  
    for n in range(1, num+1):
        URL = base_url + str(n) + url_list[idx] + tail_url
        driver.get(url=URL)
        driver.implicitly_wait(time_to_wait=60)

        div = driver.find_elements(By.CLASS_NAME, "list-position-item")
        for item in div:
            tmp = item.find_element(By.TAG_NAME, "a").get_attribute('href')
            data[idx].append(tmp)
        time.sleep(2)
        
driver.close()
print('step1 done!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')

# 각 url 크롤링
hashtag = ['Back_end', 'Software', 'System', 'Database', 'Network/Security', 'Front_end', 'Application', 'Service', 'Game', 'AI']
counting = 0

driver = webdriver.Chrome("chromedriver")
for idx in range(len(data)):
    for url in data[idx]:
        driver.get(url=url)
        driver.implicitly_wait(time_to_wait=60)
        try:
            stacks = driver.find_elements(By.TAG_NAME, "code")
            stack_data = []
            for stack in stacks:
                s = stack.text
                stack_data.append(s)

            position = driver.find_element(By.CLASS_NAME, "section-position")
            requirements = driver.find_element(By.CLASS_NAME, "section-requirements")
            preference = driver.find_element(By.CLASS_NAME, "section-preference")

            content = position.text + requirements.text + preference.text

            programmers_data = {
            'stack' : stack_data,
            'content' : content,
            'tag' : hashtag[idx]
            }  

            with open('./programmers_data.csv', 'a', encoding='utf-8', newline='') as csvfile:
                fieldnames = ['stack', 'content', 'tag']
                csvwriter = csv.DictWriter(csvfile, fieldnames=fieldnames)
                csvwriter.writerow(programmers_data)

            counting += 1   
            print(counting,'번째 : ', stack_data)     
            time.sleep(3)

        except:
            continue

driver.close()
print('step2 done!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
print('총데이터:', counting, '개')

 

 

 

[결과]

 

programmers_data.csv - vscode

 

programmers_data.csv - 구글코랩

3263개의 데이터가 크롤링 되었음을 확인하였다.

생각보다 데이터 수가 적다ㅋㅋ

 

 

크롤링 완료한 데이터 첨부

programmers.csv
5.16MB
programmers_data.csv
5.26MB

 


 

질문과 피드백은 언제나 환영입니다~

 

 

반응형

댓글