이 가이드에서는 다음을 배울 수 있습니다:
- 링크드인 스크레이퍼 란 무엇인가
- 링크드인 웹 스크래핑과 API를 통한 데이터 수집의 비교
- LinkedIn 로그인 장벽 우회 방법
- 파이썬으로 LinkedIn 스크래핑 스크립트 구축 방법
- 더 간단하고 효과적인 방법으로 LinkedIn 데이터를 얻는 방법
자, 시작해 보겠습니다!
링크드인 스크레이퍼란 무엇인가요?
링크드인 스크레이퍼는 링크드인 페이지에서 데이터를 자동으로 추출하는 도구입니다. 일반적으로 프로필, 채용 공고, 회사 페이지, 기사 등 인기 있는 페이지를 대상으로 합니다.
스크레이퍼는 해당 페이지에서 핵심 정보를 수집하여 CSV 또는 JSON과 같은 유용한 형식으로 제공합니다. 이 데이터는 리드 생성, 구직 활동, 경쟁사 분석, 시장 동향 파악 등 다양한 용도로 활용할 수 있습니다.
링크드인 웹 스크래핑 vs 링크드인 API
LinkedIn은 개발자가 플랫폼과 연동하여 일부 데이터를 가져올 수 있도록 공식 API를 제공합니다. 그렇다면 왜 LinkedIn 웹 스크래핑을 고려해야 할까요? 그 답은 간단하며 네 가지 핵심 포인트로 요약됩니다:
- API는 LinkedIn이 정의한 데이터의 일부만 반환하며, 이는 웹 스크래핑을 통해 얻을 수 있는 데이터보다 훨씬 적을 수 있습니다.
- API는 시간이 지남에 따라 변경될 수 있어 접근 가능한 데이터에 대한 통제력이 제한됩니다.
- LinkedIn의 API는 주로 마케팅 및 영업 통합에 초점을 맞추고 있으며, 특히 무료 계정 사용자에게 해당됩니다.
- LinkedIn API는 월 수십 달러의 비용이 들지만, 여전히 데이터와 데이터 추출이 가능한 프로필 수에 엄격한 제한이 있습니다.
LinkedIn 데이터 수집 두 가지 접근 방식의 비교는 다음과 같은 요약표로 정리됩니다:
| 측면 | LinkedIn API | 링크드인 웹 스크래핑 |
|---|---|---|
| 데이터 가용성 | LinkedIn이 정의한 데이터 하위 집합으로 제한됨 | 사이트의 모든 공개 데이터에 대한 접근 |
| 데이터에 대한 통제권 | LinkedIn이 제공하는 데이터 통제 | 사용자가 검색한 데이터에 대한 완전한 제어 |
| 주요 목적 | 주로 마케팅 및 영업 통합을 위한 것 | 모든 LinkedIn 페이지 타겟팅 가능 |
| 비용 | 월 수십 달러의 비용 발생 가능 | 직접 비용 없음 (인프라 비용 제외) |
| 제한 사항 | 월별 프로필 및 데이터 수 제한 | 엄격한 제한 없음 |
자세한 내용은 웹 스크래핑과 API 비교 가이드를 참조하십시오.
LinkedIn에서 스크래핑할 데이터 유형
LinkedIn에서 스크래핑할 수 있는 데이터 유형의 일부 목록은 다음과 같습니다:
- 프로필: 개인 정보, 경력, 학력, 인맥 등
- 기업: 회사 정보, 직원 목록, 채용 공고 등
- 채용 공고: 직무 설명, 지원서, 자격 요건 등
- 직위: 직무명, 회사, 근무지, 급여 등
- 기사: 게시된 글, 사용자가 작성한 기사 등
- LinkedIn Learning: 과정, 인증, 학습 경로 등
LinkedIn 로그인 벽을 피하는 방법
시크릿 모드(또는 로그아웃 상태)에서 Google 검색 후 LinkedIn 채용 정보 페이지를 직접 방문하면 다음과 같은 화면이 표시됩니다:

위 페이지는 LinkedIn에서 구직 활동이 로그인 후에만 가능하다고 생각하게 할 수 있습니다. 로그인 장벽 뒤의 데이터를 스크래핑하는 것은 LinkedIn의 서비스 약관을 위반할 수 있어 법적 문제로 이어질 수 있으므로 피해야 합니다.
다행히 차단 없이 채용 정보 페이지에 접근할 수 있는 간단한 방법이 있습니다. LinkedIn 홈페이지에 접속한 후 “채용 정보” 탭을 클릭하기만 하면 됩니다:

이번에는 구직 검색 페이지에 접근할 수 있습니다:

브라우저의 주소 표시줄에서 확인할 수 있듯이, 이번에는 브라우저의 URL에 특별한 쿼리 매개변수가 포함되어 있습니다:
https://www.linkedin.com/jobs/search?trk=guest_homepage-basic_guest_nav_menu_jobs&position=1&pageNum=0
특히 trk=guest_homepage-basic_guest_nav_menu_jobs 인수가 LinkedIn의 로그인 벽 적용을 차단하는 핵심 요소로 보입니다.
하지만 이로 인해 모든 LinkedIn 데이터에 접근할 수 있다는 뜻은 아닙니다. 일부 섹션은 여전히 로그인이 필요합니다. 그러나 곧 보게 될 것처럼, 이는 LinkedIn 웹 스크래핑에 있어 큰 제약이 되지 않습니다.
LinkedIn 웹 스크래핑 스크립트 구축: 단계별 가이드
이 튜토리얼 섹션에서는 LinkedIn에서 뉴욕 소재 소프트웨어 엔지니어 직무의 채용 공고 데이터를 스크래핑하는 방법을 배웁니다:

검색 결과 페이지에서 시작하여 구인 목록 URL을 자동으로 수집한 후, 상세 페이지에서 데이터를 추출할 것입니다. 링크드인 스크레이퍼는 웹 스크래핑에 가장 적합한 프로그래밍 언어 중 하나인 파이썬으로 작성됩니다.
파이썬으로 LinkedIn 데이터 스크래핑을 수행해 봅시다!
1단계: 프로젝트 설정
시작하기 전에 컴퓨터에 Python 3이 설치되어 있는지 확인하세요. 설치되어 있지 않다면 다운로드하여 설치 마법사를 따라 설치하세요.
이제 아래 명령어를 사용하여 스크래핑 프로젝트용 폴더를 생성하세요:
mkdir linkedin-scraper
linkedin-scraper는 Python LinkedIn 스크레이퍼의 프로젝트 폴더를 나타냅니다.
이 폴더로 이동하여 가상 환경을 초기화하세요:
cd linkedin-scraper
python -m venv venv
선호하는 Python IDE에서 프로젝트 폴더를 로드하세요. Python 확장 기능이 설치된 Visual Studio Code나 PyCharm Community Edition을 사용하면 됩니다.
프로젝트 폴더 내에 scraper.py 파일을 생성하세요. 이 파일은 다음과 같은 구조를 가져야 합니다:

현재 scraper.py는 빈 Python 스크립트이지만 곧 원하는 스크래핑 로직이 포함될 것입니다.
IDE 터미널에서 가상 환경을 활성화하세요. Linux 또는 macOS에서는 다음 명령을 실행합니다:
./env/bin/activate
Windows에서는 다음과 같이 실행하세요:
env/Scripts/activate
훌륭합니다! 이제 웹 스크래핑을 위한 Python 환경이 준비되었습니다.
2단계: 스크래핑 라이브러리 선택 및 설치
코딩을 시작하기 전에 대상 사이트를 분석하여 작업에 적합한 스크래핑 도구를 결정해야 합니다.
먼저 앞서 설명한 대로 인코그니토 모드로 LinkedIn 채용 검색 페이지를 엽니다. 인코그니토 모드를 사용하면 로그아웃 상태가 유지되고 캐시된 데이터가 스크래핑 과정에 방해되지 않습니다.
다음과 같은 화면이 표시됩니다:

LinkedIn은 브라우저에 여러 팝업을 표시합니다. Selenium이나 Playwright 같은 브라우저 자동화 도구를 사용할 때 이런 팝업은 처리하기 번거로울 수 있습니다.
다행히 서버에서 반환된 페이지의 HTML 소스 코드를 확인하면 페이지의 대부분의 데이터가 이미 포함되어 있음을 알 수 있습니다:

마찬가지로 개발자 도구(DevTools)의 “네트워크” 탭을 확인하면 페이지가 상당한 동적 API 호출에 의존하지 않음을 알 수 있습니다:

즉, LinkedIn 채용 페이지의 대부분 콘텐츠는 정적입니다. 따라서 LinkedIn 데이터를 스크래핑하기 위해 브라우저 자동화 도구가 필요하지 않습니다. HTTP 클라이언트와 HTML 파서를 조합하면 채용 데이터 수집에 충분합니다.
따라서 LinkedIn 채용 정보를 스크래핑하기 위해 두 개의 Python 라이브러리를 사용할 것입니다:
- Requests: GET 요청 전송 및 웹 페이지 콘텐츠 추출을 위한 간단한 HTTP 라이브러리.
- Beautiful Soup: 웹 페이지에서 데이터를 쉽게 추출할 수 있게 해주는 강력한 HTML 파서입니다.
활성화된 가상 환경에서 다음 명령어로 두 라이브러리를 설치합니다:
pip install requests beautifulsoup4
그런 다음 scraper.py 파일에서 다음과 같이 라이브러리를 임포트합니다:
from bs4 import BeautifulSoup
import requests
좋습니다! 이제 LinkedIn 스크래핑을 시작하는 데 필요한 모든 준비가 완료되었습니다.
3단계: 스크래핑 스크립트 구조화
본 섹션 서두에서 설명한 바와 같이, LinkedIn 스크레이퍼는 두 가지 주요 작업을 수행합니다:
- LinkedIn 채용 검색 페이지에서 채용 공고 URL을 추출합니다
- 각 특정 채용 페이지에서 채용 정보 추출
스크립트를 체계적으로 유지하기 위해 scraper.py 파일을 두 개의 함수로 구성하세요:
def retrieve_job_urls(job_search_url):
pass
# 구현 예정...
def scrape_job(job_url):
pass
# 구현 예정...
# 함수 호출 및 데이터 내보내기 로직...
아래는 두 함수의 역할입니다:
retrieve_job_urls(job_search_url): 채용 검색 페이지 URL을 받아 채용 페이지 URL 목록을 반환합니다.scrape_job(job_url): 채용 공고 페이지 URL을 받아 제목, 회사, 지역, 설명 등의 채용 정보를 추출합니다.
그런 다음 스크립트 마지막 부분에서 이 함수들을 호출하고 추출된 채용 정보를 저장하기 위한 데이터 내보내기 로직을 구현하세요. 이제 그 로직을 구현할 시간입니다!
4단계: 구직 검색 페이지 연결
retrieve_job_urls() 함수에서 requests 라이브러리를 사용하여 인자로 전달된 URL로 대상 페이지를 가져옵니다:
response = requests.get(job_url)
이 작업은 내부적으로 대상 페이지에 HTTP GET 요청을 수행하고 서버가 반환한 HTML 문서를 가져옵니다.
응답에서 HTML 콘텐츠에 접근하려면 .text 속성을 사용하세요:
html = response.text
훌륭합니다! 이제 HTML을 파싱하고 채용 공고 URL을 추출할 준비가 되었습니다.
5단계: 채용 공고 URL 가져오기
앞서 가져온 HTML을 파싱하려면 Beautiful Soup 생성자에 전달하세요:
soup = BeautifulSoup(html, "html.parser")
두 번째 인수는 사용할 HTML 파서를 지정합니다. html.parser는 Python 표준 라이브러리에 포함된 기본 파서입니다.
다음으로 LinkedIn 채용 검색 페이지의 채용 카드 요소를 검사합니다. 카드 중 하나를 마우스 오른쪽 버튼으로 클릭하고 브라우저 개발자 도구에서 “검사”를 선택하세요:

구인 카드의 HTML 코드에서 확인할 수 있듯이, 다음 CSS 선택자를 사용하여 구인 URL을 추출할 수 있습니다:
[data-tracking-control-name="public_jobs_jserp-result_search-card"]
참고: 웹 스크래핑에서 노드 선택에 data-* 속성을 사용하는 것이 이상적입니다. 이러한 속성은 테스트나 내부 추적에 자주 사용되기 때문에 시간이 지나도 변경될 가능성이 적기 때문입니다.
이제 페이지를 살펴보면 일부 채용 정보 카드가 로그인 초대 요소 뒤에 흐릿하게 표시될 수 있습니다:

걱정하지 마세요. 이는 프론트엔드 효과일 뿐입니다. 기본 HTML에는 여전히 채용 페이지 URL이 포함되어 있으므로 로그인 없이도 해당 URL을 얻을 수 있습니다.
LinkedIn 채용 페이지에서 채용 공고 URL을 추출하는 로직은 다음과 같습니다:
job_urls = []
job_url_elements = soup.select("[data-tracking-control-name="public_jobs_jserp-result_search-card"]")
for job_url_element in job_url_elements:
job_url = job_url_element["href"]
job_urls.append(job_url)
select() 는 주어진 CSS 선택자에 맞는 모든 요소를 반환하며, 여기에는 채용 링크가 포함됩니다. 그런 다음 스크립트는:
- 이 요소들을 순회합니다
hrefHTML 속성(구인 페이지 URL)에 접근합니다- 해당 URL을
job_urls리스트에 추가합니다
위 논리가 익숙하지 않다면 Beautiful Soup 웹 스크래핑 가이드를 참고하세요.
이 단계가 끝나면 retrieve_job_urls() 함수는 다음과 같이 보일 것입니다:
def retrieve_job_urls(job_search_url):
# 페이지 HTML을 가져오기 위한 HTTP GET 요청 수행
response = requests.get(job_search_url)
# HTML 접근 및 파싱
html = response.text
soup = BeautifulSoup(html, "html.parser")
# 스크래핑된 데이터 저장 위치
job_urls = []
# 스크래핑 로직
job_url_elements = soup.select("[data-tracking-control-name="public_jobs_jserp-result_search-card"]")
for job_url_element in job_url_elements:
# 채용 페이지 URL 추출 및 리스트에 추가
job_url = job_url_element["href"]
job_urls.append(job_url)
return job_urls
대상 페이지에서 아래와 같이 이 함수를 호출할 수 있습니다:
public_job_search_url = "https://www.linkedin.com/jobs/search?keywords=Software%2BEngineer&location=New%20York%2C%20New%20York%2C%20United%20States&geoId=102571732&trk=public_jobs_jobs-search-bar_search-submit&position=1&pageNum=0"
job_urls = retrieve_job_urls(public_job_search_url)
잘하셨습니다! 이제 LinkedIn 스크레이퍼의 첫 번째 작업을 완료하셨습니다.
6단계: 채용 정보 스크래핑 작업 초기화
이제 scrape_job() 함수에 집중하세요. 이전과 마찬가지로 requests 라이브러리를 사용하여 제공된 URL에서 채용 페이지의 HTML을 가져오고 Beautiful Soup으로 파싱하세요:
response = requests.get(job_url)
html = response.text
soup = BeautifulSoup(html, "html.parser")
링크드인 채용 정보 스크래핑은 다양한 정보 추출을 포함하므로, 과정을 단순화하기 위해 두 단계로 나누어 진행하겠습니다.
단계 #7: 채용 정보 수집 — 1부
링크드인 데이터 스크래핑을 시작하기 전에, 채용 공고 상세 페이지가 어떤 데이터를 포함하고 어떻게 추출하는지 이해하기 위해 해당 페이지를 살펴봐야 합니다.
이를 위해 시크릿 모드에서 채용 공고 페이지를 열고 개발자 도구(DevTools)를 사용하여 페이지를 검사하세요. 채용 공고 페이지의 상단 섹션에 집중하세요:

다음과 같은 데이터를 추출할 수 있습니다:
<h1>태그의 직무 제목[data-tracking-control-name="public_jobs_topcard-org-name"]요소에서 제공하는 회사명.topcard__flavor--bullet에서 위치 정보.num-applicants__caption노드에서 지원자 수
scrape_job() 함수에서 HTML을 파싱한 후, 다음 로직을 사용하여 해당 필드를 추출하세요:
title_element = soup.select_one("h1")
title = title_element.get_text().strip()
company_element = soup.select_one("[data-tracking-control-name="public_jobs_topcard-org-name"]")
company_name = company_element.get_text().strip()
company_url = company_element["href"]
location_element = soup.select_one(".topcard__flavor--bullet")
location = location_element.get_text().strip()
applicants_element = soup.select_one(".num-applicants__caption")
applicants = applicants_element.get_text().strip()
strip() 함수는 스크랩된 텍스트의 앞뒤 공백을 제거하는 데 필요합니다.
다음으로 페이지의 급여 섹션에 집중합니다.
.salary CSS 선택자를 통해 이 정보를 가져올 수 있습니다. 모든 직무에 이 섹션이 있는 것은 아니므로, 해당 HTML 요소가 페이지에 존재하는지 확인하는 추가 로직이 필요합니다:
salary_element = soup.select_one(".salary")
if salary_element is not None:
salary = salary_element.get_text().strip()
else:
salary = None
.salary가 페이지에 없을 경우 select_one()은 None을 반환하며, salary 변수는 None으로 설정됩니다.
훌륭합니다! LinkedIn 채용 공고 페이지에서 일부 데이터를 추출하셨습니다.
단계 #8: 채용 정보 데이터 추출 — 파트 2
이제 LinkedIn 채용 공고 페이지 하단 부분, 특히 직무 설명부터 시작해 보겠습니다:

다음 코드를 사용하면 .description__text .show-more-less-html 요소에서 직무 설명 텍스트에 접근할 수 있습니다:
description_element = soup.select_one(".description__text .show-more-less-html")
description = description_element.get_text().strip()
마지막으로 LinkedIn 웹 스크래핑에서 가장 까다로운 부분은 기준(criteria) 섹션을 처리하는 것입니다:

이 경우 페이지에 어떤 데이터가 정확히 존재할지 예측할 수 없습니다. 따라서 각 항목을 <이름, 값> 쌍으로 처리해야 합니다. 기준 데이터를 스크래핑하려면:
.description__job-criteria-list liCSS 선택자를 사용하여<ul>요소를 선택합니다.- 선택된 요소들을 반복 처리하며, 각 요소에 대해:
.description__job-criteria-subheader에서항목 이름을 스크래핑합니다.description__job-criteria-text에서 항목 값을 추출합니다- 스크랩한 데이터를 사전 형태로 배열에 추가합니다
다음 코드 줄로 LinkedIn 스크래핑 로직을 구현합니다:
criteria = []
criteria_elements = soup.select(".description__job-criteria-list li")
for criteria_element in criteria_elements:
name_element = criteria_element.select_one(".description__job-criteria-subheader")
name = name_element.get_text().strip()
value_element = criteria_element.select_one(".description__job-criteria-text")
value = value_element.get_text().strip()
criteria.append({
"name": name,
"value": value
})
완벽합니다! 방금 채용 정보를 성공적으로 스크래핑했습니다. 다음 단계는 스크래핑한 모든 LinkedIn 데이터를 객체로 모아 반환하는 것입니다.
9단계: 스크랩된 데이터 수집
이전 두 단계에서 스크랩한 데이터를 사용하여 채용 공고 객체를 채우고 함수에서 반환합니다:
job = {
"url": job_url,
"title": title,
"company": {
"name": company_name,
"url": company_url
},
"location": location,
"applications": applicants,
"salary": salary,
"description": description,
"criteria": criteria
}
return job
이전 세 단계를 완료한 후 scrape_job() 은 다음과 같아야 합니다:
def scrape_job(job_url):
# HTTP GET 요청을 보내 페이지 HTML 가져오기
response = requests.get(job_url)
# 응답에서 HTML 텍스트 접근 및 파싱
html = response.text
soup = BeautifulSoup(html, "html.parser")
# 스크래핑 로직
title_element = soup.select_one("h1")
title = title_element.get_text().strip()
company_element = soup.select_one("[data-tracking-control-name="public_jobs_topcard-org-name"]")
company_name = company_element.get_text().strip()
company_url = company_element["href"]
location_element = soup.select_one(".topcard__flavor--bullet")
location = location_element.get_text().strip()
applicants_element = soup.select_one(".num-applicants__caption")
applicants = applicants_element.get_text().strip()
salary_element = soup.select_one(".salary")
if salary_element is not None:
salary = salary_element.get_text().strip()
else:
salary = None
description_element = soup.select_one(".description__text .show-more-less-html")
description = description_element.get_text().strip()
criteria = []
criteria_elements = soup.select(".description__job-criteria-list li")
for criteria_element in criteria_elements:
name_element = criteria_element.select_one(".description__job-criteria-subheader")
name = name_element.get_text().strip()
value_element = criteria_element.select_one(".description__job-criteria-text")
value = value_element.get_text().strip()
criteria.append({
"name": name,
"value": value
})
# 수집된 데이터를 모아 반환
job = {
"url": job_url,
"title": title,
"company": {
"name": company_name,
"url": company_url
},
"location": location,
"applications": applicants,
"salary": salary,
"description": description,
"criteria": criteria
}
return job
구직 데이터를 수집하려면 retrieve_job_urls()가 반환한 구직 URL을 반복 처리하며 이 함수를 호출하세요. 그런 다음 스크랩된 데이터를 jobs 배열에 추가합니다:
jobs = []
for job_url in job_urls:
job = scrape_job(job_url)
jobs.append(job)
훌륭합니다! LinkedIn 데이터 스크래핑 로직이 이제 완성되었습니다.
10단계: JSON으로 내보내기
LinkedIn에서 추출한 채용 정보 데이터는 이제 객체 배열에 저장됩니다. 이 객체들은 평면 구조가 아니므로 JSON과 같은 구조화된 형식으로 내보내는 것이 합리적입니다.
Python은 별도의 종속성 없이도 데이터를 JSON으로 내보낼 수 있습니다. 이를 위해 다음과 같은 로직을 사용할 수 있습니다:
file_name = "jobs.json"
with open(file_name, "w", encoding="utf-8") as file:
json.dump(jobs, file, indent=4, ensure_ascii=False)
open() 함수는 jobs.json 출력 파일을 생성하고, json.dump()로 데이터를 채웁니다. indent=4는 JSON의 가독성을 위한 들여쓰기를 보장하며, ensure_ascii=False는 비-ASCII 문자가 올바르게 인코딩되도록 합니다.
코드가 작동하도록 하려면 Python 표준 라이브러리에서 json을 반드시 임포트해야 합니다:
import json
11단계: 스크래핑 로직 마무리
이제 LinkedIn 스크래핑 스크립트는 기본적으로 완성되었습니다. 그래도 몇 가지 개선할 수 있는 부분이 있습니다:
- 스크래핑 작업 수 제한
- 스크립트 진행 상황을 모니터링하기 위한 로깅 추가
첫 번째 항목이 중요한 이유는 다음과 같습니다:
- LinkedIn 스크레이퍼의 과도한 요청으로 대상 서버에 부담을 주지 않도록 해야 합니다
- 단일 페이지에 몇 개의 작업이 있는지 알 수 없음
따라서 다음과 같이 스크랩할 작업 수를 제한하는 것이 합리적입니다:
scraping_limit = 10
jobs_to_scrape = job_urls[:scraping_limit]
jobs = []
for job_url in jobs_to_scrape:
# ...
이렇게 하면 스크래핑되는 페이지 수가 최대 10개로 제한됩니다.
그런 다음 스크립트 실행 중 진행 상황을 모니터링하기 위해 print() 문을 추가하세요:
public_job_search_url = "https://www.linkedin.com/jobs/search?keywords=Software%2BEngineer&location=New%20York%2C%20New%20York%2C%20United%20States&geoId=102571732&trk=public_jobs_jobs-search-bar_search-submit&position=1&pageNum=0"
print("LinkedIn 검색 URL에서 일자리 검색 시작...")
job_urls = retrieve_job_urls(public_job_search_url)
print(f"{len(job_urls)}개의 채용 공고 URL을 가져왔습니다.")
scraping_limit = 10
jobs_to_scrape = job_urls[:scraping_limit]
print(f"{len(jobs_to_scrape)}개의 채용 공고를 스크래핑 중입니다...")
jobs = []
for job_url in jobs_to_scrape:
print(f"{job_url}에 대한 데이터 추출 시작")
job = scrape_job(job_url)
jobs.append(job)
print(f"작업 스크래핑 완료")
print(f"n스크래핑된 {len(jobs)}개 작업 JSON으로 내보내기")
file_name = "jobs.json"
with open(file_name, "w", encoding="utf-8") as file:
json.dump(jobs, file, indent=4, ensure_ascii=False)
print(f"Jobs successfully saved to "{file_name}"n")
로깅 로직은 스크레이퍼의 진행 상황을 추적하는 데 도움이 될 것입니다. 여러 단계를 거친다는 점을 고려하면 이는 필수적입니다.
12단계: 모든 것을 통합하기
LinkedIn 스크래핑 스크립트의 최종 코드는 다음과 같습니다:
from bs4 import BeautifulSoup
import requests
import json
def retrieve_job_urls(job_search_url):
# HTTP GET 요청을 통해 페이지 HTML 가져오기
response = requests.get(job_search_url)
# HTML 접근 및 파싱
html = response.text
soup = BeautifulSoup(html, "html.parser")
# 스크래핑된 데이터 저장 위치
job_urls = []
# 스크래핑 로직
job_url_elements = soup.select("[data-tracking-control-name="public_jobs_jserp-result_search-card"]")
for job_url_element in job_url_elements:
# 채용 페이지 URL 추출 및 목록에 추가
job_url = job_url_element["href"]
job_urls.append(job_url)
return job_urls
def scrape_job(job_url):
# 페이지 HTML 가져오기 위한 HTTP GET 요청 전송
response = requests.get(job_url)
# 응답에서 HTML 텍스트에 접근하여 파싱
html = response.text
soup = BeautifulSoup(html, "html.parser")
# 스크래핑 로직
title_element = soup.select_one("h1")
title = title_element.get_text().strip()
company_element = soup.select_one("[data-tracking-control-name="public_jobs_topcard-org-name"]")
company_name = company_element.get_text().strip()
company_url = company_element["href"]
location_element = soup.select_one(".topcard__flavor--bullet")
location = location_element.get_text().strip()
applicants_element = soup.select_one(".num-applicants__caption")
applicants = applicants_element.get_text().strip()
salary_element = soup.select_one(".salary")
if salary_element is not None:
salary = salary_element.get_text().strip()
else:
salary = None
description_element = soup.select_one(".description__text .show-more-less-html")
description = description_element.get_text().strip()
criteria = []
criteria_elements = soup.select(".description__job-criteria-list li")
for criteria_element in criteria_elements:
name_element = criteria_element.select_one(".description__job-criteria-subheader")
name = name_element.get_text().strip()
value_element = criteria_element.select_one(".description__job-criteria-text")
value = value_element.get_text().strip()
criteria.append({
"name": name,
"value": value
})
# 수집된 데이터를 모아 반환
job = {
"url": job_url,
"title": title,
"company": {
"name": company_name,
"url": company_url
},
"location": location,
"applications": applicants,
"salary": salary,
"description": description,
"criteria": criteria
}
return job
# LinkedIn 채용 검색 페이지의 공개 URL
public_job_search_url = "https://www.linkedin.com/jobs/search?keywords=Software%2BEngineer&location=New%20York%2C%20New%20York%2C%20United%20States&geoId=102571732&trk=public_jobs_jobs-search-bar_search-submit&position=1&pageNum=0"
print("LinkedIn 검색 URL에서 일자리 검색 시작...")
# 페이지의 각 일자리별 개별 URL 가져오기
job_urls = retrieve_job_urls(public_job_search_url)
print(f"{len(job_urls)}개의 채용 공고 URL을 가져왔습니다.")
# 페이지에서 최대 10개 채용 공고만 스크래핑
scraping_limit = 10
jobs_to_scrape = job_urls[:scraping_limit]
print(f"{len(jobs_to_scrape)}개의 채용 공고를 스크래핑 중입니다...")
# 각 채용 공고 페이지에서 데이터 스크래핑
jobs = []
for job_url in jobs_to_scrape:
print(f"{job_url} 데이터 추출 시작")
job = scrape_job(job_url)
jobs.append(job)
print(f"채용 공고 스크래핑 완료")
# 추출된 데이터를 CSV로 내보내기
print(f"n추출된 {len(jobs)}개 일자리 데이터를 JSON으로 내보냄")
file_name = "jobs.json"
with open(file_name, "w", encoding="utf-8") as file:
json.dump(jobs, file, indent=4, ensure_ascii=False)
print(f"Jobs successfully saved to "{file_name}"n")
다음 명령어로 실행하세요:
python scraper.py
LinkedIn 스크레이퍼는 다음 정보를 기록해야 합니다:
LinkedIn 검색 URL에서 일자리 검색 시작...
60개의 일자리 URL 검색 완료
10개 일자리 스크래핑 중...
"https://www.linkedin.com/jobs/view/software-engineer-recent-graduate-at-paypal-4149786397?position=1&pageNum=0&refId=nz9sNo7HULREru1eS2L9nA%3D%3D&trackingId=uswFC6EjKkfCPcv0ykaojw%3D%3D"에서 데이터 추출 시작
구직 정보 스크래핑 완료
# 간결함을 위해 생략...
"https://www.linkedin.com/jobs/view/software-engineer-full-stack-at-paces-4090771382?position=2&pageNum=0&refId=UKcPcvFZMOsZrn0WhZYqtg%3D%3D&trackingId=p6UUa6cgbpYS1gDkRlHV2g%3D%3D"
구직 정보 수집 완료
수집된 10개 구직 정보를 JSON으로 내보냄
"jobs.json" 파일에 구직 정보 저장 성공
스크립트가 발견한 최대 60개 채용 페이지 중 10개 채용 정보만 스크랩했습니다. 따라서 스크립트가 생성한 jobs.json 출력 파일에는 정확히 10개의 채용 공고가 포함됩니다:
[
{
"url": "https://www.linkedin.com/jobs/view/software-engineer-recent-graduate-at-paypal-4149786397?position=1&pageNum=0&refId=UKcPcvFZMOsZrn0WhZYqtg%3D%3D&trackingId=UzOyWl8Jipb1TFAGlLJxqw%3D%3D",
"title": "소프트웨어 엔지니어 - 신입 졸업생",
"company": {
"name": "PayPal",
"url": "https://www.linkedin.com/company/paypal?trk=public_jobs_topcard-org-name"
},
"location": "뉴욕, NY",
"applications": "200명 이상의 지원자",
"salary": null,
"description": "간결함을 위해 생략...",
"criteria": [
{
"name": "경력 수준",
"value": "해당 없음"
},
{
"name": "고용 형태",
"value": "정규직"
},
{
"name": "직무",
"value": "엔지니어링"
},
{
"name": "산업 분야",
"value": "소프트웨어 개발, 금융 서비스, 기술, 정보 및 인터넷"
}
]
},
// 기타 8개 직위...
{
"url": "https://www.linkedin.com/jobs/view/software-engineer-full-stack-at-paces-4090771382?position=2&pageNum=0&refId=UKcPcvFZMOsZrn0WhZYqtg%3D%3D&trackingId=p6UUa6cgbpYS1gDkRlHV2g%3D%3D",
"title": "소프트웨어 엔지니어 (풀스택)",
"company": {
"name": "Paces",
"url": "https://www.linkedin.com/company/pacesai?trk=public_jobs_topcard-org-name"
},
"location": "Brooklyn, NY",
"applications": "Over 200 applicants",
"salary": "$150,000.00/yr - $200,000.00/yr",
"description": "간결함을 위해 생략...",
"criteria": [
{
"name": "경력 수준",
"value": "초급"
},
{
"name": "고용 형태",
"value": "정규직"
},
{
"name": "직무 기능",
"value": "엔지니어링 및 정보 기술"
},
{
"name": "산업 분야",
"value": "소프트웨어 개발"
}
]
},
]
자, 이제 끝! 파이썬으로 LinkedIn 웹 스크래핑하는 건 그리 어렵지 않네요.
LinkedIn 데이터 스크래핑 간소화
우리가 만든 스크립트는 LinkedIn 스크래핑을 단순한 작업처럼 보이게 할 수 있지만, 실제로는 그렇지 않습니다. 로그인 장벽과 데이터 난독화 기술은 특히 스크래핑 작업을 확장할 때 빠르게 도전 과제가 될 수 있습니다.
게다가 LinkedIn은 자동화된 스크립트가 너무 많은 요청을 보내는 것을 차단하기 위한 속도 제한 메커니즘을 갖추고 있습니다. 일반적인 해결책은 Python에서 IP 주소를 회전시키는 것이지만, 이는 추가적인 노력이 필요합니다.
게다가 LinkedIn은 지속적으로 진화하고 있으므로 스크래퍼 유지 관리 노력과 비용이 무시할 수 없다는 점도 명심하세요. LinkedIn은 채용 공고부터 기사까지 다양한 형식의 데이터 보물창고를 보유하고 있습니다. 이 모든 정보를 추출하려면 서로 다른 스크래퍼를 구축하고 모두 관리해야 합니다.
Bright Data의 LinkedIn 스크레이퍼 API로 번거로움을 잊으세요. 이 전용 도구는 필요한 모든 LinkedIn 데이터를 스크레이핑하여 코드 없이 통합하거나, 어떤 HTTP 클라이언트로도 호출 가능한 간단한 엔드포인트를 통해 전달합니다.
전체 스크래핑 아키텍처를 관리할 필요 없이 LinkedIn 프로필, 게시물, 기업, 채용 공고 등을 단 몇 초 만에 스크래핑하세요.
결론
이 단계별 튜토리얼에서는 LinkedIn 스크레이퍼가 무엇이며 어떤 유형의 데이터를 추출할 수 있는지 알아보았습니다. 또한 LinkedIn에서 채용 공고를 스크레이핑하는 Python 스크립트를 작성했습니다.
문제는 LinkedIn이 자동화된 스크립트를 차단하기 위해 IP 차단 및 로그인 벽을 사용한다는 점입니다. 저희 LinkedIn 스크레이퍼로 이러한 문제를 해결하세요.
웹 스크래핑은 원하지 않지만 데이터에는 관심이 있으시다면, 바로 사용 가능한 LinkedIn 데이터 세트를 확인해 보세요!
스크레이퍼 API를 사용해 보거나 데이터셋을 살펴보려면 지금 바로 Bright Data 무료 계정을 생성하세요.