- 파이썬을 사용한 웹 스크래핑 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. 사람인 사이트 확인
검색창에서 '데이터'를 검색을 하게 되면 주소창에서 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 값을 사용합니다. 이를 통해 웹 스크래퍼가 웹사이트에 접근하는 데 있어 더 높은 성공률을 보일 수 있습니다
- 사람인 사이트에서 채용정보가 확인되는 html 구조를 확인한다.
div class= 'item_recruit' 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
for page in range(1, int(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로 한 곳에 모았다.
- 첫 번째 줄은 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으로 접근하는데 어려움이 있었다
위치는 확인했으나 접근 방법이 너무 어렵다 ;;
'데이터 공부 > 크롤링 공부' 카테고리의 다른 글
[웹크롤링]Python으로 잡코리아 채용 공고 크롤링하기 (0) | 2025.01.15 |
---|