본문 바로가기
Web Study/노마드코더

노마드코더 python 웹 스크래퍼 만들기 - 3

by 쿠리의일상 2023. 6. 16.

indeed 홈페이지 스크래퍼 만들기

https://kr.indeed.com/ 

indeed 홈페이지 상에서 봇이 크롤링하면 막아뒀기 때문에(403 error) 읽어올 수가 없다.

 

Selenium 을 사용한 우회

봇이라서 크롤링을 막아뒀다면 봇이 아닌 브라우저라고 우회하여 읽어오게 해준다. 이를 위한 Selenium 을 사용해본다

https://www.selenium.dev/

 

Selenium

Selenium automates browsers. That's it!

www.selenium.dev

브라우저의 자동화를 가능하게 해준다.

pip install selenium

pip install webdriver_manager

드라이버와 셀레니움을 설치해준다.

강의의 덧글을 참고하여 사용법을 알아 보았다... 나는 정상 작동 됐고

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager

브라우저가 꺼지는 걸 방지해주기 위한 옵션도 추가해주었다.

options = Options()
    options.add_experimental_option('detach', True)  # 브라우저 꺼짐 방지
    browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()),
                               options=options)
    base_url = f'https://kr.indeed.com/jobs?q={keyword}'
    browser.get(base_url)

이렇게 셀레니움과 크롬을 사용하여 자동화되는 브라우저를 실행시키고 그 브라우저 상에서 get 메서드로 이동해준다.

 

스크래핑 해주기

혼자 HTML 구조를 보며 작성해보았다.

results = []
    soup = BeautifulSoup(browser.page_source, 'html.parser')
    jobs = soup.find_all('ul', class_='jobsearch-ResultsList')
    for job in jobs:
        contents = job.find_all('td', class_='resultContent')
        for content in contents:
            infos = content.find_all('span')
            anchor = content.find('a')
            link = anchor['href']
            title = infos[0]
            company = infos[1]
            job_info = {
                'title': title.string,
                'company': company.string,
                'link': f"https://kr.indeed.com/viewjob?{link}",
            }
            print(job_info)
            results.append(job_info)
            print('///////////')

 

None

무언가 없을 때 사용하는 자료형

 

이건 강의 내용대로 스크래퍼를 작성해준 것

.select_one() 은 CSS Selector 를 사용하여 DOM에 접근하여 크롤링할 수 있게 해준다.

def extract_indeed_jobs(keyword):
    options = Options()
    options.add_experimental_option('detach', True)  # 브라우저 꺼짐 방지
    browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()),
                               options=options)
    base_url = f'https://kr.indeed.com/jobs?q={keyword}'
    browser.get(base_url)

    soup = BeautifulSoup(browser.page_source, 'html.parser')
    job_list = soup.find('ul', class_='jobsearch-ResultsList')
    jobs = job_list.find_all('li', recursive=False)
    results = []
    for job in jobs:
        zone = job.find('div', class_='mosaic-zone')
        if zone == None:
            anchor = job.select_one('h2 a')
            link = anchor['href']
            title = anchor['aria-label']
            company = job.find('span', class_='companyName')
            location = job.find('div', class_='companyLocation')
            job_data = {
                'link': f"https://kr.indeed.com/viewjob?{link}",
                'company': company.string,
                'location': location.string,
                'position': title,
            }
            results.append(job_data)
    for result in results:
        print(result)
        print('/////')
    return results


extract_indeed_jobs('python')

 

페이지별로 스크래핑 하기

pagination 을 보면

총 5개의 페이지와 > 다음 버튼이 존재

페이지를 이동하여 스크래핑을 해야하는데 저 페이지 범위 내로 스크래핑 하도록 하기 위해 일단 pagination  정보를 가져와준다.

def get_page_count(keyword):
    options = Options()
    options.add_experimental_option('detach', True)  # 브라우저 꺼짐 방지
    browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()),
                               options=options)
    base_url = f'https://kr.indeed.com/jobs?q={keyword}'
    browser.get(base_url)

    soup = BeautifulSoup(browser.page_source, 'html.parser')
    paginations = soup.find('nav', attrs={'aria-label': 'pagination'})
    if paginations == None:
        return 1
    pages = paginations.select('div a')
    count = len(pages)
    if count >= total_pages:
        return total_pages
    else:
        return count

위의 조건문을 살펴보자면 pagination 이 None 이란 것은 원하는 nav 태그를 찾지 못했을 경우이므로, 이는 pagination 이 없이 검색 결과가 1페이지로 이루어져 있다는 의미이다.

그다음 select 로 CSS 선택자를 사용하여 안의 div a 를 찾아준다. 그 개수가 페이지의 개수이므로 페이지의 수가 5보다 크거나 같으면 5개로, 그 이하면 현재 페이지 개수만큼 반환하게 해주는 함수를 작성해준다.

 

def extract_indeed_jobs(keyword):
    results = []
    pages = get_page_count(keyword)
    for page in range(pages):
        options = Options()
        options.add_experimental_option('detach', True)  # 브라우저 꺼짐 방지
        browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()),
                                   options=options)
        base_url = f'https://kr.indeed.com/jobs?q={keyword}&start={page * onepage_list_count}'
        browser.get(base_url)

        soup = BeautifulSoup(browser.page_source, 'html.parser')
        job_list = soup.find('ul', class_='jobsearch-ResultsList')
        jobs = job_list.find_all('li', recursive=False)

        for job in jobs:
            zone = job.find('div', class_='mosaic-zone')
            if zone == None:
                anchor = job.select_one('h2 a')
                link = anchor['href']
                title = anchor['aria-label']
                company = job.find('span', class_='companyName')
                location = job.find('div', class_='companyLocation')
                job_data = {
                    'link': f"https://kr.indeed.com/viewjob?{link}",
                    'company': company.string,
                    'location': location.string,
                    'position': title,
                }
                results.append(job_data)
        for result in results:
            print(result)
            print('/////\n/////')
        return results

페이지의 개수를 가져와서 url 부분에 넣어주고, 각 페이지마다 자동화 반복시켜서 스크래핑 시켜주는 함수를 완성했다.

 

wwr 과 indeed 크롤링 해주기 예시

from extractors.wwr import extract_wwr_jobs
from extractors.indeed import extract_indeed_jobs

keyword = input('What do you want to search for?')
indeed = extract_indeed_jobs(keyword)
wwr = extract_wwr_jobs(keyword)

jobs = indeed + wwr  # list 끼리 합치기
for job in jobs:
    print(job)
    print('////////////\n////////////')

정상적으로 크롤링 해왔음을 알 수 있다!