들어가기 전에
데이터 수집은 모든 데이터 분석의 출발점입니다. 하지만 데이터를 얻는 것이 항상 간단한 것은 아닙니다. 다양한 시스템과 소스가 분산되어 있기 때문이죠. 웹 스크래핑을 이용하여 필요한 데이터를 수집하는 방법을 소개합니다.
※ 이 글은 아래 기사 내용을 토대로 작성되었습니다만, 필자의 개인 의견이나 추가 자료들이 다수 포함되어 있습니다.
- 원문: Web Scraping Indeed for Data Analysts: Mastering the Data Collection Process
- URL: https://medium.com/@adesua/web-scraping-indeed-for-data-analysts-mastering-the-data-collection-process-fb5c8678d013
환경 설정하기
웹 스크래핑을 시작하려면 시스템에 필요한 도구가 설정되어 있는지 확인해야 합니다.
- Python: 시스템에 Python이 설치되어 있는지 확인합니다. 설치되어 있지 않은 경우 공식 Python 웹사이트에서 다운로드하여 설치할 수 있습니다.
- 라이브러리: 다음 Python 라이브러리인 Requests, BeautifulSoup, Selenium을 설치합니다. Python의 패키지 관리자인 pip를 사용하여 다음 명령어로 설치할 수 있습니다.
pip install requests
pip install beautifulsoup4
pip install selenium
도구를 설치했으면 주피터(Jupyter)를 열고 필요한 라이브러리를 가져올 차례입니다.
# Import necessary libraries
from selenium import webdriver
from bs4 import BeautifulSoup
import pandas as pd
from time import sleep
위의 코드 스니펫에서는 웹 스크래핑 프로젝트 전체에서 사용할 필수 라이브러리를 가져오고 있습니다.
- Selenium: 이 강력한 도구를 사용하면 웹 브라우저 상호 작용을 자동화할 수 있으므로 Indeed.com과 같은 동적 웹 페이지를 탐색하는 데 적합합니다.
- BeautifulSoup: HTML 및 XML 문서 구문 분석을 위한 Python 라이브러리입니다. 스크랩한 웹 페이지에서 데이터를 추출하는 데 사용합니다.
- Pandas: Python의 다목적 데이터 조작 라이브러리. 스크랩한 데이터를 구조화된 형식으로 정리하고 조작하는 데 사용합니다.
- time: 시간 관련 다양한 함수를 제공하는 Python 모듈입니다. 웹사이트 서버에 과부하가 걸리지 않도록 스크래핑 프로세스에 지연을 추가하는 데 이 모듈을 사용할 것입니다.
필요한 라이브러리를 가져왔으니 이제 웹 스크래핑을 위한 환경을 설정할 차례입니다. 웹 브라우저와의 상호 작용을 자동화하기 위해 Selenium을 사용하고 웹 페이지의 HTML 콘텐츠를 파싱하기 위해 BeautifulSoup을 사용할 것입니다. 아래 코드를 참조하세요.
# Set up Chrome options to customize the browser behavior
options = webdriver.ChromeOptions()
# Set user-agent to mimic a browser behavior
options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36')
# Initialize a Chrome WebDriver instance with customized options
driver = webdriver.Chrome(options=options)
# URL of the webpage to scrape
url = "https://ng.indeed.com/jobs?q=data+analyst&l=Nigeria&from=searchOnHP&vjk=6df200c744f62577"
# Open the URL in the Chrome WebDriver instance
driver.get(url)
# Sleep for 5 seconds to ensure the page loads completely before scraping
sleep(5)
# Get the HTML source code of the page after it has fully loaded
html = driver.page_source
# Parse the HTML using BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')
- Chrome 옵션: 웹 브라우저의 동작을 맞춤 설정하기 위해 Chrome 옵션을 설정합니다. 이 경우 실제 브라우저의 동작을 모방하기 위해 사용자 에이전트를 추가하고 있습니다.
- 웹 드라이버 인스턴스: 사용자 지정 옵션을 사용하여 크롬 웹드라이버 인스턴스를 초기화합니다. 이 웹드라이버 인스턴스를 통해 웹 페이지와 상호 작용할 수 있습니다.
- URL: 스크랩하려는 웹페이지의 URL을 정의합니다. 이 예에서는 나이지리아의 데이터 분석가에 대한 채용 공고를 Indeed.com에서 스크랩합니다.
- Open URL: 크롬 웹드라이버 인스턴스에서 지정된 URL을 엽니다.
- Sleep: 스크래핑을 시작하기 전에 페이지가 완전히 로드되도록 5초 동안 실행을 일시 중지하는 절전 타이머를 추가합니다. 이렇게 하면 불완전하거나 누락된 데이터를 스크랩하는 것을 방지할 수 있습니다.
- HTML 가져오기: WebDriver의 page_source 속성을 사용하여 페이지의 HTML 소스 코드를 가져옵니다.
- HTML 구문 분석: 웹 페이지에서 특정 정보를 추출할 수 있는 BeautifulSoup을 사용하여 HTML 소스 코드를 구문 분석합니다.
계속 진행하기 전에 대상 페이지에서 어떤 데이터를 추출할지 결정하는 것이 중요합니다. 이 프로젝트에서는 다음 정보를 수집하는 데 중점을 둘 것입니다.
- Job Title: 채용 공고의 제목
- Company: 채용 회사 이름
- Location: 근무지: 채용 공고의 위치(도시 또는 지역)
- Salary: 급여 또는 급여 범위(가능한 경우)
- Job Type: 정규직, 파트타임, 계약직 등 직무의 종류
- Date Posted: 채용 공고가 게시된 날짜
- Job Summary: 직무 책임과 요구사항에 대한 간략한 요약 또는 설명
이 모든 것이 준비되었으면 페이지가 어떻게 구성되어 있는지 이해하는 것이 중요합니다. Indeed.com으로 이동하여 검색창에 "데이터 분석가"(또는 원하는 직책)를 입력합니다. 마찬가지로 위치 표시줄에 "나이지리아"(또는 원하는 위치)를 입력합니다. 아래 이미지와 유사한 페이지 레이아웃이 표시됩니다.
HTML 요소 검사하기
구인 공고에서 데이터를 추출하려면 페이지의 HTML 요소를 검사해야 합니다. 다음 단계를 따릅니다.
- Indeed 웹 페이지에서 채용 공고 위로 마우스를 가져갑니다.
- 직책을 클릭합니다.
- 컨텍스트 메뉴에서 '검사'를 선택합니다.
이 작업을 수행하면 브라우저의 개발자 도구가 열리고 페이지의 기본 HTML 구조를 볼 수 있습니다. 아래 이미지와 같이 클래스 이름이 'job_seen_beacon'인 div 요소를 찾습니다.
'job_seen_beacon' 클래스는 인디드의 채용공고에 사용되는 공통 식별자입니다. 이 클래스는 각 개별 채용 공고를 캡슐화하여 원하는 데이터 요소를 쉽게 찾고 추출할 수 있게 해줍니다.
채용 공고에서 데이터 추출하기
다음으로 각 채용 공고에서 특정 정보를 추출해야 합니다. 아래는 바로 이 작업을 수행하도록 설계된 Python 함수 get_data(job_listing)입니다.
def get_data(job_listing):
# Extract job title
title = job_listing.find("a").find("span").text.strip()
# Extract company name if available, otherwise assign an empty string
try:
company = job_listing.find('span', class_='css-92r8pb eu4oa1w0').text.strip()
except AttributeError:
company = ''
# Extract job location if available, otherwise assign an empty string
try:
location = job_listing.find('div', class_='css-1p0sjhy eu4oa1w0').text.strip()
except AttributeError:
location = ''
# Extract salary information if available, otherwise assign an empty string
try:
salary = job_listing.find('div', class_='metadata salary-snippet-container css-5zy3wz eu4oa1w0').text.strip()
except AttributeError:
salary = ''
# Extract job type if available, otherwise assign an empty string
try:
job_type = job_listing.find('div', class_='metadata css-5zy3wz eu4oa1w0').text.strip()
except AttributeError:
job_type = ''
# Extract date posted
date_posted = job_listing.find('span', class_='css-qvloho eu4oa1w0').text.strip()
# Extract job summary
summary = job_listing.find('div', class_='css-9446fg eu4oa1w0').text.strip()
# Return a tuple containing all the extracted information
return (title, company, location, salary, job_type, date_posted, summary)
- Job Title: 채용 정보 내에서 앵커(a) 태그를 찾아 중첩된 스팬 요소의 텍스트 콘텐츠를 검색하여 직책을 추출합니다.
- Company Name: 클래스명이 'css-92r8pb eu4oa1w0'인 특정 스팬 요소를 검색하여 회사명을 추출하려고 시도합니다. 발견되면 텍스트 콘텐츠를 검색하고, 그렇지 않으면 빈 문자열을 할당합니다.
- Job Location: 마찬가지로 클래스 이름이 'css-1p0sjhy eu4oa1w0'인 div 요소를 검색하여 작업 위치를 추출하려고 합니다. 가능한 경우 텍스트 콘텐츠를 검색하고, 그렇지 않으면 빈 문자열을 할당합니다.
- Salary Information: 급여 정보를 추출하기 위해 클래스명이 '메타데이터 급여-스니펫-컨테이너 css-5zy3wz eu4oa1w0'인 div 요소를 찾습니다. 발견되면 텍스트 콘텐츠를 검색하고, 그렇지 않으면 빈 문자열을 할당합니다.
- Job Type: 클래스명이 'metadata css-5zy3wz eu4oa1w0'인 div 요소를 검색하여 직종을 추출합니다. 존재하는 경우 텍스트 콘텐츠를 검색하고, 그렇지 않으면 빈 문자열을 할당합니다.
- Date Posted: 클래스 이름이 'css-qvloho eu4oa1w0'인 스팬 요소를 찾아 텍스트 콘텐츠를 검색하여 게시된 날짜를 추출합니다.
- Job Summary: 마지막으로 클래스 이름이 'css-9446fg eu4oa1w0'인 div 요소를 찾아 텍스트 콘텐츠를 검색하여 작업 요약을 추출합니다.
이 함수는 각 채용 공고에 대해 추출된 모든 정보가 포함된 튜플을 반환합니다.
이제 get_data 함수를 정의하여 Indeed.com의 채용 공고에서 관련 데이터를 추출할 수 있게 되었습니다. 이로써 스크랩된 데이터에서 가치 있는 인사이트를 수집하는 데 한 걸음 더 가까워졌습니다.
여러 페이지 스크래핑
Indeed.com의 여러 채용 공고 페이지에서 데이터를 스크랩하려면 더 이상 사용할 수 있는 페이지가 없을 때까지 반복하는 루프를 활용합니다. 이를 위한 코드 스니펫은 다음과 같습니다.
# Loop to scrape data from multiple pages until there are no more pages available
while True:
try:
# Extract the URL of the next page if available
url = 'https://ng.indeed.com/' + soup.find('a', {'aria-label':'Next Page'}).get('href')
except AttributeError:
# If there are no more pages available, break the loop
break
# Open the next page in the browser
driver.get(url)
# Get the HTML source code of the next page
html = driver.page_source
# Parse the HTML of the next page using BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')
# Find all job listings on the next page
job_listings = soup.find_all('div', class_='job_seen_beacon')
# Iterate through each job listing on the page
for job_listing in job_listings:
# Extract data from the current job listing
record = get_data(job_listing)
# Append the extracted data to the records list
records.append(record)
# Close the Chrome WebDriver instance
driver.quit()
- 페이지 반복하기: While 루프를 사용하여 여러 페이지의 채용공고 목록을 반복합니다. 이 루프는 더 이상 사용할 수 있는 페이지가 없을 때까지 무한정 계속됩니다.
- 다음 페이지 URL 추출하기: 루프 내에서 BeautifulSoup을 사용하여 다음 페이지의 URL을 추출하려고 시도합니다. '다음 페이지' 링크가 발견되면 URL을 구성하고 다음 페이지로 이동합니다. 그렇지 않으면 루프에서 벗어납니다.
- 채용 정보 스크래핑: 각 페이지에 대해 HTML 소스 코드를 스크랩하고 BeautifulSoup을 사용하여 파싱합니다. 그런 다음 'job_seen_beacon' 클래스를 사용하여 페이지의 모든 채용 공고를 찾습니다.
- 데이터 추출: 앞서 정의한 get_data 함수를 사용하여 각 구인 목록을 반복하고 관련 데이터를 추출합니다. 추출된 데이터는 레코드 목록에 추가됩니다.
- WebDriver 닫기: 모든 페이지가 스크랩되면 시스템 리소스를 확보하기 위해 Chrome WebDriver 인스턴스를 닫습니다.
이 루프를 사용하면 Indeed.com의 여러 페이지에서 채용 공고를 체계적으로 스크랩하여 분석에 필요한 포괄적인 데이터 세트를 캡처할 수 있습니다.
데이터를 DataFrame으로 변환하고 CSV로 저장하기
추출한 구인 공고 데이터를 정리하고 추가 분석을 위해 저장하려면 레코드 목록을 DataFrame으로 변환한 다음 CSV 파일로 저장합니다. 다음은 이를 수행하는 코드입니다.
# Convert list of records into a DataFrame
df = pd.DataFrame(records, columns=['Title', 'Company', 'Location', 'Salary', 'Job Type', 'Date Posted', 'Summary'])
# Save DataFrame to a CSV file
df.to_csv('indeed_job_data.csv', index=False)
print("Data saved to job_data.csv")
- 데이터프레임 변환: pd.DataFrame() 함수를 사용하여 레코드(기록) 목록을 데이터프레임으로 변환합니다. 추출된 데이터와 일치하도록 열 이름을 '직함', '회사', '위치', '급여', '직종', '게시 날짜', '요약'으로 지정합니다.
- CSV 내보내기: 그런 다음 to_csv() 메서드를 사용하여 데이터 프레임을 'indeed_job_data.csv'라는 이름의 CSV 파일로 내보냅니다. CSV 파일에서 DataFrame 인덱스를 제외하기 위해 index=False를 설정합니다.
마지막으로 데이터가 CSV 파일에 성공적으로 저장되었음을 나타내는 확인 메시지를 인쇄합니다. 이 단계가 완료되면 이제 추출된 구인 목록 데이터가 구조화된 형식으로 저장되어 분석 또는 추가 처리를 위한 준비가 완료됩니다. 이 데이터 세트는 또 다른 데이터 분석 프로세스인 데이터 정리에 관한 다음 프로젝트에 사용될 수 있습니다. 아래 이미지는 판다 데이터 프레임에 저장된 데이터 세트입니다.
중요 참고 사항: 윤리적 고려 사항
웹 스크래핑은 데이터를 수집하는 강력한 도구가 될 수 있지만, 윤리를 염두에 두고 접근하는 것이 중요합니다. 웹사이트의 서비스 약관을 존중하고 서버 부하를 고려하는 것이 중요합니다.
웹사이트에서 데이터를 스크랩할 때는 항상 해당 웹사이트의 서비스 약관 및 robots.txt 파일을 검토하고 준수하세요. 이러한 문서에는 데이터 액세스 및 사용과 관련하여 웹사이트 소유자가 설정한 규칙과 제한 사항이 간략하게 설명되어 있습니다. 이러한 가이드라인을 무시하면 법적 문제가 발생하고 평판이 손상될 수 있습니다.
또한 스크래핑 활동이 웹사이트 서버에 미치는 영향도 염두에 두어야 합니다. 과도한 요청은 서버 리소스에 부담을 주고 다른 사용자의 서비스를 방해할 수 있습니다. 속도 제한 및 정중한 스크래핑 관행과 같은 기술을 사용하여 사용 공간을 최소화하고 모든 사용자가 웹사이트에 공정하게 액세스할 수 있도록 하세요.
이 프로젝트의 전체 코드는 [여기]에서 확인할 수 있으니 자유롭게 참고하시기 바랍니다.
'Python' 카테고리의 다른 글
파이썬을 사용하여 손쉽게 PDF 분할 및 병합하기 (2) | 2024.09.10 |
---|---|
파이썬으로 MS Word 문서 읽는 방법 (1) | 2024.08.18 |
파이썬에서 Excel 스프레드시트 작업하기 (1) | 2024.08.15 |
파이썬으로 Excel에 하이퍼링크 추가, 업데이트, 추출 또는 삭제하기 (2) | 2024.08.10 |
파이썬을 사용하여 다양한 소스의 데이터를 Excel로 가져오기 (0) | 2024.08.03 |