본문 바로가기
데이터 공부/크롤링 공부

[웹크롤링] 파이썬 사람인 추출하기 공부/select, select_one

by 스터디마형 2024. 4. 12.

- 파이썬을 사용한 웹 스크래핑 BeautifulSoup과 requests 라이브러리를 활용하여 채용 정보를 크롤링하는 방법을 사용

 

★ 아래의 유튜브에서 보고 공부하고 따라했습니다.

 

• 출처 : 오토코더 / 파이썬(Python) - 사람인 채용정보 모두 추출(Feat. 웹크롤링)

링크 : https://www.youtube.com/watch?v=TF-6OdoZm6k

 

 

 

데이터분석 분야로 취업공고를 알아보고, 지원하는 도중, 관련 정보를 크롤링해서 볼 수 있다면 더욱 편하겠다는 생각에 크롤링을 해보자고 생각이 들어 작업을 해보게 되었다.

 

※ 진행중 발생한 문제점 :

셀리니움(selenium)을 사용하여 페이지를 넘겨가면서 페이지 한장 한장 크롤링을 진행을 할려고 했었다.

다만 사람인에서 셀레니움으로 접근하여 많은 정보를 추출할려고 할 경우 접근제한이 발생하였고 따라서 찾아보다 '오코코더'님이 작업한 내용이 있어 보고 따라해보았다.

 

 

사용된 주요 라이브러리:

1
2
3
4
from bs4 import BeautifulSoup
import requests
import pandas as pd
 
      cs

 

  • BeautifulSoup: HTML 문서를 파싱하고 요소를 추출하기 위한 라이브러리
  • requests: HTTP 요청을 보내기 위한 라이브러리
  • pandas: 데이터를 처리하고 Excel 파일로 저장하기 위한 라이브러리

 

 

A. 사람인 사이트 확인

사람인 사이트 :  https://www.saramin.co.kr/zf_user/

 

 

 

검색창에서 '데이터'를 검색을 하게 되면 주소창에서 searchword, recruitPage를 확인할 수 있다.

• searchword : 검색어

• recruitPage : 현 검색 페이지

https://www.saramin.co.kr/zf_user/search/recruit?searchTyphttps://www.saramin.co.kr/zf_user/search/recruit?search_area=main&search_done=y&search_optional_item=n&searchword=데이터&recruitPage=2&recruitSort=relation&recruitPageCount=40&inner_com_type=&company_cd=0%2C1%2C2%2C3%2C4%2C5%2C6%2C7%2C9%2C10&show_applied=&quick_apply=&except_read=&ai_head_hunting=e=search&company_cd=0%2C1%2C2%2C3%2C4%2C5%2C6%2C7%2C9%2C10&keydownAccess=&searchword=%EB%8D%B0%EC%9D%B4%ED%84%B0&panel_type=&search_optional_item=y&search_done=y&panel_count=y&preview=y&recruitPage=1&recruitSort=relation&recruitPageCount=40&inner_com_type=&show_applied=&quick_apply=&except_read=&ai_head_hunting=

※ 링크는 데이터 검색에 2page란 뜻.

 

이제 검색과 페이지를 확인했으니 BeautifulSoup과 requests를 통해 접근을 해보자.

 

[추출하고자하는 내용 정리]

1. 회사명

2. 제목

3. 마감일

4. 직종

5. 경력

6. 주소

7. url 주소

 

주요 코드 구조:

1. 사용자로부터 검색 키워드와 페이지 수 입력 받기

 


B. 반복문을 사용하여 각 페이지마다 URL 생성 및 요청 보내기


★ 참조: 크롤링시 진행이 안되는 경우가 있다.(접근 제어 및 보안)

일부 웹사이트는 특정 브라우저나 크롤러로부터의 요청을 제한하거나 차단할 수 있습니다. User-Agent를 통해 이러한 클라이언트를 식별하고 적절한 조치를 취할 수 있습니다. 웹 스크래핑을 할 때, 기본적으로 requests와 같은 라이브러리는 자체적인 User-Agent 값을 사용합니다. 하지만 일부 웹사이트는 자동화된 스크립트나 특정 브라우저가 아닌 클라이언트로부터의 요청을 차단하기도 합니다. 이럴 때, 일반적인 웹 브라우저에서 온 것처럼 요청을 위장하기 위해 Mozilla/5.0과 같은 일반적인 브라우저의 User-Agent 값을 사용합니다. 이를 통해 웹 스크래퍼가 웹사이트에 접근하는 데 있어 더 높은 성공률을 보일 수 있습니다

 

 

 

맞는 유저정보 찾기 :  https://www.useragentstring.com/pages/useragentstring.php


 

- 사람인 사이트에서 채용정보가 확인되는 html 구조를 확인한다.

 

div class= 'item_recruit' 확인

div class= 'item_recruit' 확인

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for page in range(1int(Page) + 1):
    # 페이지 번호를 포함한 URL 생성
    url = f'https://www.saramin.co.kr/zf_user/search/recruit?search_area=main&search_done=y&search_optional_item=n&searchword={Keyword}&recruitPage={page}&recruitSort=relation&recruitPageCount=40&inner_com_type=&company_cd=0%2C1%2C2%2C3%2C4%2C5%2C6%2C7%2C9%2C10&show_applied=&quick_apply=&except_read=&ai_head_hunting='
    #print(url)  # URL을 출력하여 확인
 
     # 페이지 요청 및 HTML 파싱
    response = requests.get(url, headers={'User-Agent':'Mozilla/5.0'})
    soup = BeautifulSoup(response.text, 'html.parser')
    
    # 채용 정보 항목 추출
    recruits = soup.find_all('div', class_='item_recruit')
    #recruits = soup.select('div'.item_recruit')
    
    #print(recruits)
cs

 

- for문을 사용하여 검색에 대한 정보를 희망페이지까지 찾을 수 있게 한다.

- BeautifulSoup으로 HTML 문서 파싱하고 필요한 데이터 추출

 

-  recruits = soup.find_all('div', class_='item_recruit')을 통해  모든 'item_recruit'를 추출한다.

 

 

 

C. 데이터 추출하기

★ try ~ except 중간에 자료가 빠져도 진행할 수 있도록 사용

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
32
33
34
35
36
   
    for recruit in recruits:
        try
            # title 속성이 있는 <a> 태그 선택 
            title = recruit.select_one('a[title]')['title']  
            #title = recruit.select_one('a')['title'].strip().replace(',','')
            
            #corp_name = recruit.find('strong', class_='corp_name').get_text(strip=True)
            corp_name = recruit.select_one('div.area_corp > strong > a').text.strip()
            
            #deadline = recruit.find('span', class_='job_date').get_text(strip=True
            deadline = recruit.select_one('div.job_date > span').text.strip()
            
            # <b> 태그 내의 모든 <a> 태그의 텍스트를 추출하고 리스트로 저장
            job_sectors1 = [a.text.strip() for a in recruit.select('div.job_sector > b > a')]
            job_sectors2 = [a.text.strip() for a in recruit.select('div.job_sector > a')]
            # 리스트를 문자열로 합침. 각 항목을 쉼표로 구분
            job_sector = ', '.join(job_sectors1 + job_sectors2)
 
            #job_condition = recruit.find('div', class_='job_condition').get_text(strip=True)
            job_location = recruit.select('div.job_condition > span')[0].get_text(strip=True)
      
            job_career = recruit.select('div.job_condition > span')[1].get_text(strip=True)
            
            apply_url = 'https://www.saramin.co.kr/' + recruit.select_one('a')['href']
            
            print(corp_name)
            #print(job_location)
            print('\n')
            #print('---' * 50)
            #print(title, corp_name, deadline,job_sector, apply_url)
            
        except Exception :
            print("Error processing recruit"
            pass
        
cs

 

[추출하고자하는 내용 정리]

1. 회사명:  corp_name = recruit.select_one('div.area_corp > strong > a').text.strip()

  • 이 코드는 CSS 선택자 'div.area_corp > strong > a'를 사용하여 특정 구조를 가진 요소를 찾습니다.
  • div.area_corp > strong > a 선택자는 div 태그에 area_corp 클래스가 있고, 그 직계 자식으로 strong 태그를 가지며, 그 strong 태그의 직계 자식으로 <a> 태그를 가진 구조를 찾습니다. > 기호는 직계 자식을 의미합니다.
  • select_one은 이 조건을 만족하는 첫 번째 요소를 선택합니다.
  • .text 메소드를 사용하여 선택된 요소의 텍스트 내용을 추출합니다. .strip() 메소드는 문자열 양끝의 공백을 제거합니다.

2. 제목 :

 title = recruit.select_one('a[title]')['title']    

  • 이 코드는 CSS 선택자 'a[title]'을 사용하여 title 속성을 가진 <a> 태그를 찾습니다.
  • 'a[title]' 선택자는 <a> 태그 중에서 title 속성이 있는 모든 태그를 대상으로 합니다.
  • select_one 메소드는 이러한 조건을 만족하는 첫 번째 <a> 태그를 선택합니다.
  • [title]은 해당 <a> 태그의 title 속성 값을 추출합니다. 속성 값에 접근하기 위해 대괄호 []를 사용하는 것은 Python의 딕셔너리 접근 방식을 따르는 것입니다.

3. 마감일

deadline = recruit.select_one('div.job_date > span').text.strip()

  • 이 코드는 div 태그 중 job_date 클래스를 가진 요소의 직계 자식 span 태그를 찾습니다.
  • .text를 통해 선택된 span 태그의 텍스트 내용을 가져옵니다.
  • .strip() 메소드로 텍스트 양 끝의 공백을 제거합니다.

 

4. 직종

직종 부분에서는 리스트로 가져온 이유는 굵은 글씨와, 일반 글씨가 나눠져 있는 것을 확인하였다.

굵은 글씨 부분은 b 태그 안에 a태그로 , 일반 글씨는 아래쪽에 a태그안에 있음을 확인하여 각각 가져온뒤

join을 사용하여 쉼표로 구분하여 job_sector로 한 곳에 모았다.

 

            job_sectors1 = [a.text.strip() for a in recruit.select('div.job_sector > b > a')]
            job_sectors2 = [a.text.strip() for a in recruit.select('div.job_sector > a')]
            # 리스트를 문자열로 합침. 각 항목을 쉼표로 구분
            job_sector = ', '.join(job_sectors1 + job_sectors2)
 
  • 첫 번째 줄은 div.job_sector 클래스를 가진 div 태그의 직계 자식인 b 태그 내의 모든 a 태그들의 텍스트를 추출해 리스트로 만듭니다.
  • 두 번째 줄은 div.job_sector 클래스를 가진 div 태그 내의 모든 a 태그들의 텍스트를 추출해 또 다른 리스트로 만듭니다.
  • 세 번째 줄에서는 두 리스트를 합쳐 모든 직종 정보를 쉼표로 구분된 하나의 문자열로 만듭니다.

 

5. 경력, 주소

경력과 주소는 class job_conition에서 확인이 된다. 

 

(*인덱싱 기준)span태그 중 0번째는 주소를, 1번째는 경력 부분이다.

 

job_career = recruit.select('div.job_condition > span')[1].get_text(strip=True)

job_location = recruit.select('div.job_condition > span')[0].get_text(strip=True)

  • div.job_condition 클래스를 가진 div 태그 내의 모든 span 태그들을 선택합니다.
  • [0]와 [1]을 사용하여 첫 번째와 두 번째 span 태그를 선택합니다. 이는 각각 주소와 경력 정보를 나타냅니다.
  • .get_text(strip=True) 메소드로 해당 태그들의 텍스트를 추출하고, 공백을 제거합니다.

7. url 주소

apply_url = 'https://www.saramin.co.kr/' + recruit.select_one('a')['href']

 

  • select_one('a')['href']를 통해 첫 번째 a 태그의 href 속성값(링크 주소)을 추출합니다.
  • 추출된 href 값에 기본 URL(https://www.saramin.co.kr/)을 앞에 붙여 최종적인 지원 URL을 완성합니다.

 

 

D. 추출된 데이터를  데이터프레임으로 변환/  Excel 파일로 저장

1
2
3
4
5
6
7
8
        # 데이터 리스트에 추가
        data.append([corp_name, title, deadline, job_sector, job_career,job_location, apply_url])
 
# 데이터 프레임 생성 및 Excel 파일로 저장
df = pd.DataFrame(data, columns=['회사명''제목''마감일''직종''경력''주소''url'])
df.to_excel('데이터분석 취업모음.xlsx', index=False)
 
print("Excel 파일이 저장되었습니다: ")
cs

 

 


잘 몰랐던 내용 정리(GPT)

  • select와 select_one의 차이: select 메소드는 CSS 선택자와 일치하는 모든 요소를 리스트로 반환합니다. 반면, select_one 메소드는 일치하는 첫 번째 요소만 반환합니다.
  • find와 find_all의 차이: find 메소드는 주어진 태그나 조건과 일치하는 첫 번째 요소를 반환합니다. find_all 메소드는 조건과 일치하는 모든 요소를 리스트로 반환합니다.
  • 특정 요소 접근 방법: find 또는 select 메소드를 사용하여 HTML 문서 내의 특정 요소에 접근하는 방법에 대한 질문들이 있었습니다. 특히, select_one을 사용해 특정 조건을 만족하는 요소에 접근하는 방법과 관련된 세부적인 질문이 포함되었습니다.

select, select_one, find, find_all 상세 설명 및 정리

  • select 메소드: CSS 선택자를 사용하여 문서 내에서 일치하는 모든 요소를 찾습니다. 반환된 객체는 리스트이며, 해당 선택자와 일치하는 모든 요소를 포함합니다.
  • select_one 메소드: CSS 선택자를 사용하여 문서 내에서 일치하는 첫 번째 요소만 찾습니다. 단일 객체를 반환하며, 주로 유니크한 요소에 접근할 때 사용됩니다.
  • find 메소드: 태그 이름, 속성 등을 기반으로 문서 내에서 일치하는 첫 번째 요소를 찾습니다. 단일 객체를 반환합니다.
  • find_all 메소드: find 메소드와 유사하게 작동하지만, 조건과 일치하는 모든 요소를 리스트로 반환합니다.

 

 

★ 크롤링 스스로 추가 해볼점(숙제)

 

크롤링은 할때마다 항상 막히는 부분이 생기는 것 같다.

다음은 사람인에서 확인되는 기업정도 파트에서 설립일, 대표자명, 업종, 매출액, 기업 주소 등에 접근하는 방법을 알아봐야겠다.

 

해당 부분은 단순하게 select_one으로 접근하는데 어려움이 있었다

 

 

위치는 확인했으나 접근 방법이 너무 어렵다 ;;