본문 바로가기
공부/파이썬 Python

글래스도어 구인공고 크롤링하기! (python, csv, BeautifulSoup)

by 혼밥맨 2021. 2. 15.
반응형

글래스도어 구인공고 크롤링하기! (python, csv, BeautifulSoup)

Glassdoor 홈페이지

글래스도어(Glassdoor)는 해당 회사 직원의 익명 리뷰에 기반한 직장 및 상사 평가 사이트이며, 동시에 구인공고 웹서비스를 제공합니다. 

 

미국에서 가장 많이 쓰이는 구인공고, 구직공고 웹사이트 중 하나입니다. (링크드인, 글래스도어, 인디드 등)

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import requests
from bs4 import BeautifulSoup as soup
from openpyxl import Workbook
import time
from lxml.html import fromstring
from itertools import cycle
from fake_useragent import UserAgent
import traceback
import sys
from random import randint
import random
from google.colab import drive
drive.mount('/content/drive', force_remount=True)
 
# 무료 IP 주소 불러오기
"""
def get_proxies():
    url = 'https://free-proxy-list.net/'
    response = requests.get(url)
    parser = fromstring(response.text)
    proxies = set()
    for i in parser.xpath('//tbody/tr')[:10]:
        if i.xpath('.//td[7][contains(text(),"yes")]'):
            #Grabbing IP and corresponding PORT
            proxy = ":".join([i.xpath('.//td[1]/text()')[0], i.xpath('.//td[2]/text()')[0]])
            proxies.add(proxy)
    return proxies
"""
 
headers = {
    'accept''text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'accept-encoding''gzip, deflate, br',
    'accept-language''en-US,en;q=0.9',
    'cache-control''max-age=0',
    'sec-fetch-dest''document',
    'sec-fetch-mode''navigate',
    'sec-fetch-site''none',
    'sec-fetch-user''?1',
    'upgrade-insecure-requests''1',
    'user-agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.47'
}
 
 
for k in range(113000111610001): 
  ABC = ["A1""B1""C1""D1"]
  columns = ["post_title""location""company_name""job_desc"]
 
  write_wb = Workbook()
  write_ws = write_wb.active
 
  # Head Columns 만들기
  for (alphabet, col) in zip(ABC, columns): 
    write_ws[alphabet] = col
 
  for j in range(1311):
    URL = "https://www.glassdoor.co.in/Job/marshall-jobs-SRCH_IL.0,8_IC"+str(k)+"_IP"+str(j)+".htm"
    
    html = requests.get(URL, headers=headers) #, proxies={"http": proxy, "https": proxy})
    
    if (str(html.status_code)=="403"):
      print(html.status_code, "30mins timesleep")
      time.sleep(2400# 40mins rest
      print("slept well.. let me proxy again")
      headers = {'User-Agent':str(UserAgent().chrome)}
      proxies = get_proxies()
      proxy_pool = cycle(proxies)
      proxy = next(proxy_pool)
      if (str(html.status_code)=="200"):
        print("Great: 200")
      else:
        print("I am out")
        sys.exit()
      html = requests.get(URL, headers=headers)
      bsobj = soup(html.text, 'html.parser')
      
 
    
    bsobj = soup(html.text, 'html.parser')
 
    links = []
    links_count = 0
 
 
    for i in bsobj.findAll('a', {'class''jobLink'}):
      if (links_count%3==2):
        try:
          link = 'https://www.glassdoor.co.in' + i['href']
          links.append(link)
        except:
          print("WrongHrefError")
          pass
      else:
        pass
      links_count = links_count + 1
 
 
 
    for link in links:
      time.sleep(random.uniform(00.95* 1.5# row 추출하고 사람인 척 잠시 휴식
      
      try:
        html = requests.get(link, headers=headers)
        bsobj = soup(html.content, 'lxml')
      except:
        print("ConnectionResetError(104, 'Connection reset by peer")
        pass
 
      try:
        post_title = bsobj.findAll('div', {'class''e11nt52q6'})[0].text.strip()
        location = bsobj.findAll('div', {'class''e11nt52q2'})[0].text.strip()
        company_name = bsobj.findAll('div', {'class''e11nt52q1'})[0].text.strip()
        company_name = company_name[:company_name.find(".css")]
        JD = bsobj.findAll('div', {'class''desc'})[0].text.strip()
      
      except IndexError:
        print("INDEX_ERROR", end="")
        pass
      
      # adding a row
      write_ws.append([                   
                   post_title,
                   location,
                   company_name,
                   JD
                   ])
      
      time.sleep(random.uniform(00.95* 1.5# row 추출하고 사람인 척 잠시 휴식
 
  write_wb.save("Glassdoor"+str(k)+".xlsx"# save
  time.sleep(randint(60175)) # 사람인 척 잠시 랜덤 시간 동안 충분히 휴식 (휴식이 없으면 Google CATPCHA 및 IP주소 블락 당하기 때문)
 
  print(k, "DONE")
 
print("ALL DONE")
cs

 - 글래스도어 

복잡한 Domain

글래스도어 도메인을 보면 어떻게 자동으로 크롤링을 할 수 있을까 생각이 듭니다. 하지만 실은 간단한 도메인이 존재한다는 것입니다. 

간단한 도메인 포맷은 다음과 같습니다. "www.glassdoor.com/Job/seoul-jobs-SRCH_IL.0,5_IC3021489.htm"

여기에서 3021489가 지역번호입니다. 각 지역별 고유번호가 지정되어있습니다. 미국지역의 잡포스팅을 보고 싶기 때문에 여러 지역을 알 수 있도록 넣어줍니다. 

 

그렇게 해서 발견한 연속된 지역번호가 1130001부터 1161000까지 입니다. (더 있을 수도 있습니다. 미국을 기준으로 했습니다. 다른 지역을 포함하면 더욱 많습니다.

 

그렇게 지역번호를 순차적으로 바꿔주면서 URL에 접근하여 추출하는 전략을 이용했습니다.

Google reCAPTCHA

문제는 글래스도어 구인공고를 할 때에는 제약조건이 있었습니다.

 - 단기간 inappropriate한 접속이 발견되면 글래스도어 측에서 자동으로 그 IP 주소를 블락합니다.

 - 가끔 Google reCAPTCHA를 띄우기도 합니다.

 

이 문제를 get_proxies() 함수를 이용해서 IP주소를 바꿈으로써 해결했습니다.

 

중간 중간 time.sleep(randint(0,1)) 혹은 time.sleep(random.uniform(0, 1)) 같은 라인이 보입니다. 사람인 척을 하기 위해서 중간 중간 휴식을 취하는 라인입니다. 동일한 시간 동안 휴식이 반복되면 사람처럼 보이기 어렵습니다. 그렇기 때문에 랜덤 라이브러리를 불러왔습니다.

 

Glassdoor.xlsx

성공적으로 글래스도어 지역별로 추출할 수 있었습니다. 이후에는 여러 xlsx 엑셀 파일을 한 파일로 합칠 것입니다. 

깨진 글씨도 없고, 추출이 안 된 blank 빈칸도 없었습니다. 성공적이라고 할 수 있겠습니다.

반응형

댓글