본 포스팅은 노마드코더님의 온라인 강의에 대한 복습을 기록하기 위한 포스팅입니다.
※ 본 포스팅의 내용은 강의를 들은 후 필자의 개인적인 의견을 기재한 것이니, 정답이 아닐 수 있음을 참고하십시오.
강의 소개 : 파이썬으로 웹 스크래퍼 만들기 (2주 완성반)
-. 내용 : 파이썬 기초 (타입, 변수, 함수, 클래스 등 및 웹 스크래퍼 코드 작성법)
-. 비용 : 100% 무료 강의
-. 비고 : 한글 자막 제공 / 강의 100% 완료 시 10% 할인 쿠폰 제공
온라인 강의 : https://nomadcoders.co/ / 풀스택 개발자 로드맵 : https://nomadcoders.co/roadmap
유튜브 채널 : https://www.youtube.com/channel/UCUpJs89fSBXNolQGOYKn0YQ
#2.6 Extracting Titles & #2.7 Extracting Companies &
#2.8 Extracting Locations and Finishing up
1) Attribute에 따라 find(), find_all() 하기
""" main.py """
from indeed import extract_indeed_pages, extract_indeed_jobs
# "indeed" Object의 "extract_indeed_pages" Function 호출
last_indeed_pages = extract_indeed_pages()
# "indeed" Object의 "extract_indeed_jobs" Function 호출
indeed_jobs = extract_indeed_jobs(last_indeed_pages)
"""indeed.py"""
~~~~~~~
def extract_indeed_jobs(last_pages):
jobs = []
for page in range(last_pages):
result = requests.get(f"{URL}&start={page*LIMIT}")
soup = BeautifulSoup(result.text, 'html.parser')
# Indeed에서 각 일자리별 정보를 리스트로 가져오기
results = soup.find_all("div", {"class":"jobsearch-SerpJobCard"})
# 여러개의 결과에서 1개 일자리에 대한 정보 찾기
for result in results:
# h2 Tag중 클래스가 "title" 인 것
title = result.find("h2", {"class":"title"})
print(title)
return jobs
-. 이번에 설명해드리는 코드는 BeautifulSoup로 가져온 html 특정 tag에서 특정 Attribute(class, id, name 등)에
-. 해당하는 값만 가져오기 위해서 사용합니다.
-. 만약 이런 기능이 없다면 수많은 "div" tag에서 원하는 값을 찾기는 힘들겠죠?
-. 사용방법은 간단합니다. find/find_all("Tag 이름", {"Attribute 이름" : "Attribute 값"})
-. 이렇게 find() 혹은 find_all() 명령어 둘 다에서 사용이 가능합니다.
-. 여기서 내가 원하는 값의 Tag가 무엇인지, Attribute가 무엇인지는 아래의 사진을 참고하시면 됩니다.
2) find() 한 결과에서 특정 Attribute 값 가져오기
"""indeed.py"""
~~~~~~~
def extract_indeed_jobs(last_pages):
jobs = []
for page in range(last_pages):
result = requests.get(f"{URL}&start={page*LIMIT}")
soup = BeautifulSoup(result.text, 'html.parser')
# Indeed에서 각 일자리별 정보를 리스트로 가져오기
results = soup.find_all("div", {"class":"jobsearch-SerpJobCard"})
# 여러개의 결과에서 1개 일자리에 대한 정보 찾기
for result in results:
# h2 Tag중 클래스가 "title" 인 것 중에서 anchor Tag속의 attribute 가 "title"인 것
title = result.find("h2", {"class":"title"}).find("a")["title"]
print(title)
return jobs
-. 해당 코드는 Attribute 값으로 tag의 값을 가져오는 것이 아니라 tag의 이름과 Attribute의 이름으로
-. Attribute 값을 찾는 코드로, "Indeed" 사이트의 Title과 같이 .string으로 가져오기 힘든 경우 사용하면 좋다
-. 사용방법 : find("태그 이름")["Attribute 이름"]
-. 필요한 Attribute 를 찾는 방법은 아래의 사진을 참조하십시오.
3) html 소스의 양식이 다른 경우에 회사 이름 가져오기
"""indeed.py"""
~~~~~~~
def extract_indeed_jobs(last_pages):
jobs = []
for page in range(last_pages):
result = requests.get(f"{URL}&start={page*LIMIT}")
soup = BeautifulSoup(result.text, 'html.parser')
# Indeed에서 각 직업별 정보를 리스트로 가져오기
results = soup.find_all("div", {"class":"jobsearch-SerpJobCard"})
# 여러개의 결과에서 1개 일자리에 대한 정보 찾기
for result in results:
# h2 Tag중 클래스가 title 인것 중에서 anchor Tag속의 attribute 가 "title"인 것
title = result.find("h2", {"class":"title"}).find("a")["title"]
company = result.find("span", {"class":"company"})
# company 중에 "a" Tag가 있는 경우
if company.find("a") is not None:
company = str(company.find("a").string)
else:
company = str(company.string)
return jobs
-. 위 코드를 살펴보면 title과는 다르게 if - else 구문이 사용 된 것을 보실 수 있습니다.
-. 왜냐하면 company라는 Attribute가 "span" 태그 바로 아래에 있는 경우와 "span > a" 태그 아래에 있는 경우로
-. 나뉘어 있기 때문인데요
-. 이 경우와 같이 어떤 정보를 가져올 때는 반드시 여러 차례 테스트를 통해 확인해보는 것이 중요합니다.
-. html 소스는 아래 사진을 참조하시기 바랍니다. (파란색 : "span" / 빨간색 : "span > a")
4) strip(), lstrip(), rstrip()으로 가져온 값의 앞/뒤에 있는 공백(Space) 삭제하기
-. 위 사진을 보시면 위/아래의 결과가 다르게 나오는 것을 보실 수 있습니다.
-. 바로 회사명 다음에 한 줄이 띄워져 있냐 / 안 띄워져 있냐 인데요.
-. 사실은 저것이 Enter(\n 혹은 /n)이 아니라 엄청난 수의 공백(Space)으로 한 줄씩 띄워져 있는 것처럼 보이는데요
-. 이렇게 되면 Data를 처리하는데 있어서 큰 문제가 발생합니다. 대표적으로 회사명의 길이를 구할 수가 없겠죠
"""indeed.py"""
~~~~~~~
def extract_indeed_jobs(last_pages):
jobs = []
for page in range(last_pages):
result = requests.get(f"{URL}&start={page*LIMIT}")
soup = BeautifulSoup(result.text, 'html.parser')
# Indeed에서 각 직업별 정보를 리스트로 가져오기
results = soup.find_all("div", {"class":"jobsearch-SerpJobCard"})
# 여러개의 결과에서 1개 일자리에 대한 정보 찾기
for result in results:
# h2 Tag중 클래스가 title 인것 중에서 anchor Tag속의 attribute 가 "title"인 것
title = result.find("h2", {"class":"title"}).find("a")["title"]
company = html.find("span", {"class": "company"})
# company 중에 "a" Tag가 있는 경우
if company.find("a") is not None:
company = str(company.find("a").string)
else:
company = str(company.string)
# company 값의 앞/뒤에 있는 공백 삭제하기
company = company.strip()
print(company)
return jobs
-. 위의 코드와 같이 수정을 하시면 이 문제는 해결할 수 있습니다.
-. strip() : String의 좌/우 모두에서 해당 값을 제거한다.
-. lstip() : String의 좌측에 있는 해당 값을 제거한다.
-. rstip() : String의 우측에 있는 해당 값을 제거한다.
text = " abcde "
s_text = text.strip()
l_text = text.lstrip()
r_text = text.rstrip()
print(f"s_text = {s_text}")
print(f"l_text = {l_text}")
print(f"r_text = {r_text}")
""" 출력 값 """
s_text = abcde
l_text = abcde
r_text = abcde
text = "aaaabbbbaaaa"
s_text = text.strip("a")
l_text = text.lstrip("a")
r_text = text.rstrip("a")
print(f"s_text = {s_text}")
print(f"l_text = {l_text}")
print(f"r_text = {r_text}")
""" 출력 값 """
s_text = bbbb
l_text = bbbbaaaa
r_text = aaaabbbb
-. 위 코드를 보시면... 뭐 더이상 설명이 필요 없겠죠?
5) Location 값 가져오기
"""indeed.py"""
~~~~~~~
def extract_job(html):
# h2 Tag중 클래스가 title 인것 중에서 anchor Tag속의 attribute 가 "title"인 것
title = html.find("h2", {"class":"title"}).find("a")["title"]
company = html.find("span", {"class": "company"})
# company 중에 "a" Tag가 있는 경우
if company.find("a") is not None:
company = str(company.find("a").string)
else:
company = str(company.string)
# company 값의 앞/뒤에 있는 공백 삭제하기
company = company.strip()
location = html.find("span", {"class":"location"})
print(location)
return {"title": title, "company": company, "location": location}
def extract_indeed_jobs(last_pages):
jobs = []
for page in range(last_pages):
result = requests.get(f"{URL}&start={page*LIMIT}")
soup = BeautifulSoup(result.text, 'html.parser')
# Indeed에서 각 직업별 정보를 리스트로 가져오기
results = soup.find_all("div", {"class":"jobsearch-SerpJobCard"})
# 여러개의 결과에서 1개 일자리에 대한 정보 찾기
for result in results:
job = extract_job(result)
jobs.append(job)
return jobs
-. 우선 위 코드는 기존 코드에서 result 내부 값을 꺼내는 코드를 별도의 function으로 분리 해 준 코드입니다.
-. 그리고 "location" 항목이 추가된 것을 보실 수 있는데요. 하지만, 이 코드로는 원하는 값을 구할 수가 없습니다.
-. 아래 사진을 보시죠, 분명히 "span" 태그 중 class가 "location"인 항목들이 있습니다.
-. 하지만 코드 실행 결과를 보시면 None이 있는 것을 보실 수 있는데요.
-. 이 경우 .string 문자 값을 구하면 "None Type"은 문자 값을 구할 수 없으므로 에러가 발생합니다.
-. 그 이유는 일부 일자리에는 location이 없는 경우도 있기 때문입니다.
-. 이 것을 해결하려면 html 소스를 잘 살펴보셔야 합니다.
-. 아래 사진을 다시 한 번 살펴보세요. location 값은 "span" 태그에만 존재하나요?
-. 아니죠 "span"태그를 감싸고 있는 "div" 태그에 Attribute 값으로 location 값이 있는 것을 확인 하실 수 있습니다.
-. 그러므로 아래의 코드를 사용하여 location 값을 구하시면 됩니다.
"""indeed.py"""
~~~~~~~
def extract_job(html):
# h2 Tag중 클래스가 title 인것 중에서 anchor Tag속의 attribute 가 "title"인 것
title = html.find("h2", {"class":"title"}).find("a")["title"]
company = html.find("span", {"class": "company"})
# company 중에 "a" Tag가 있는 경우
if company.find("a") is not None:
company = str(company.find("a").string)
else:
company = str(company.string)
# company 값의 앞/뒤에 있는 공백 삭제하기
company = company.strip()
# # locaiton 값이 없는 일자리가 있어서 None이 출력 되는 경우 발생
# location = html.find("span", {"class":"location"})
location = html.find("div", {"class":"recJobLoc"})["data-rc-loc"]
print(location)
return {"title": title, "company": company, "location": location}
def extract_indeed_jobs(last_pages):
jobs = []
for page in range(last_pages):
result = requests.get(f"{URL}&start={page*LIMIT}")
soup = BeautifulSoup(result.text, 'html.parser')
# Indeed에서 각 직업별 정보를 리스트로 가져오기
results = soup.find_all("div", {"class":"jobsearch-SerpJobCard"})
# 여러개의 결과에서 1개 일자리에 대한 정보 찾기
for result in results:
job = extract_job(result)
jobs.append(job)
return jobs
6) id 값을 구하여 일자리 상세페이지로 이동하는 Link 구하기
-. 일자리를 Ctrl + 클릭하게 되면 상세페이지가 뜨게 되는데 url을 자세히 살펴보면
-. "jk" 라는 값에 각 일자리별 고유의 "id"가 들어 가는 것을 확인할 수 있습니다.
-. 그렇다면 이 jk 값은 어디 있을까요?
-. 바로 ("div", {"class":"jobsearch-SerpJobCard"}) 안에 있는데요 어딘가 익숙하시죠?
-. 맞습니다. results 로 값을 구해서 result로 1개씩 꺼내온 바로 그 곳에 위치해 있는데요.
-. 우리는 extract_jon(html) 이라는 function으로 분리를 했으므로, html 안에 값이 있다는 걸 알 수 있습니다.
-. 해당 값을 구하는 방법은 추가적인 설명 없이 아래 코드를 보시면 되겠습니다.
"""indeed.py"""
~~~~~~~
def extract_job(html):
# h2 Tag중 클래스가 title 인것 중에서 anchor Tag속의 attribute 가 "title"인 것
title = html.find("h2", {"class":"title"}).find("a")["title"]
company = html.find("span", {"class": "company"})
# company 중에 "a" Tag가 있는 경우
if company.find("a") is not None:
company = str(company.find("a").string)
else:
company = str(company.string)
# company 값의 앞/뒤에 있는 공백 삭제하기
company = company.strip()
# # locaiton 값이 없는 일자리가 있어서 None이 출력 되는 경우 발생
# location = html.find("span", {"class":"location"})
location = html.find("div", {"class":"recJobLoc"})["data-rc-loc"]
job_id = html["data-jk"]
print(job_id)
return {"title": title, "company": company, "location": location, "link": f"https://www.indeed.com/viewjob?jk={job_id}"}
def extract_indeed_jobs(last_pages):
jobs = []
for page in range(last_pages):
result = requests.get(f"{URL}&start={page*LIMIT}")
soup = BeautifulSoup(result.text, 'html.parser')
# Indeed에서 각 직업별 정보를 리스트로 가져오기
results = soup.find_all("div", {"class":"jobsearch-SerpJobCard"})
# 여러개의 결과에서 1개 일자리에 대한 정보 찾기
for result in results:
job = extract_job(result)
jobs.append(job)
return jobs
※ Indeed 사이트에서 일자리 정보 Scrapping 하기 전체 코드 (~#2.8)
-. Scrapping 순서
1) 페이지 가져오기
2) requests 만들기
3) jobs 가져오기
"""main.py"""
from indeed import extract_indeed_pages, extract_indeed_jobs
# "indeed" Object의 "extract_indeed_pages" Function 호출
last_indeed_pages = extract_indeed_pages()
# "indeed" Object의 "extract_indeed_jobs" Function 호출
indeed_jobs = extract_indeed_jobs(last_indeed_pages)
print(indeed_jobs)
"""indeed.py"""
import requests
from bs4 import BeautifulSoup
LIMIT = 50
# 스크래핑 하려는 INDEED_URL 저장
URL = f"https://www.indeed.com/jobs?q=python&limit={LIMIT}"
# "Indeed" 웹사이트를 스크래핑 하는 Function 생성
def extract_indeed_pages():
# url에 저장된 웹사이트를 requestsdml get Function을 사용하여 가져오기
result = requests.get(URL)
# # html을 text로 가져오기
# print(result.text)
# bs4를 이용하여 "Indeed" 검색 결과를 html로 가져오기
soup = BeautifulSoup(result.text, 'html.parser')
# "div" Tag 중 Class가 "pagination"인 것 가져오기
pagination = soup.find("div", {"class": "pagination"})
# "pagination" 변수에 저장된 html 중에서 "anchor" Tag 인 것 모두 가져와서 리스트로 저장
links = pagination.find_all("a")
# span을 저장할 빈 리스트 객체 생성
pages = []
# "links" 리스트에 저장된 값을 차례대로 "link"라는 객체로 받아온 후 "span" Tag 인 것을 "pages" 리스트에 추가 # [:-1] = "links" 리스트에 저장된 값 중 마지막 값을 제외한다는 뜻
for link in links[:-1]:
# pages.append(link.find("span").string)
# anchor Tag의 String을 가지고 와도 span의 String을 가지고 온다
pages.append(int(link.string))
# # "pages" 리스트에 저장된 값 중 마지막에서 1번째 item
# print(pages[-1])
# "pages"에 저장된 값 중 가장 큰 값(마지막 값)만 가져오기
max_page = pages[-1]
return max_page
def extract_job(html):
# h2 Tag중 클래스가 title 인것 중에서 anchor Tag속의 attribute 가 "title"인 것
title = html.find("h2", {"class": "title"}).find("a")["title"]
company = html.find("span", {"class": "company"})
# company 중에 "a" Tag가 있는 경우
if company.find("a") is not None:
company = str(company.find("a").string)
else:
company = str(company.string)
# company 값의 앞/뒤에 있는 공백 삭제하기
company = company.strip()
# # locaiton 값이 없는 일자리가 있어서 None이 출력 되는 경우 발생
# location = html.find("span", {"class":"location"})
location = html.find("div", {"class": "recJobLoc"})["data-rc-loc"]
job_id = html["data-jk"]
return {
"title": title,
"company": company,
"location": location,
"link": f"https://www.indeed.com/viewjob?jk={job_id}"
}
def extract_indeed_jobs(last_pages):
jobs = []
for page in range(last_pages):
print(f"Scrapping page : {page}")
result = requests.get(f"{URL}&start={page*LIMIT}")
soup = BeautifulSoup(result.text, 'html.parser')
# Indeed에서 각 직업별 정보를 리스트로 가져오기
results = soup.find_all("div", {"class": "jobsearch-SerpJobCard"})
# 여러개의 결과에서 1개 일자리에 대한 정보 찾기
for result in results:
job = extract_job(result)
jobs.append(job)
return jobs
#2.9 StackOverflow Pages
1) StackOverflow 파일 생성 및 Pagination 처리
"""main.py"""
from indeed import get_jobs as get_indeed_jobs
from stackoverflow import get_jobs as get_stackoverflow_jobs
# indeed_jobs = get_indeed_jobs()
stackoverflow_jobs = get_stackoverflow_jobs()
print(stackoverflow_jobs)
"""stackoverflow.py"""
import requests
from bs4 import BeautifulSoup
# 스크래핑 하려는 URL 저장
URL = f"https://stackoverflow.com/jobs?q=python"
def extract_pages():
result = requests.get(URL)
soup = BeautifulSoup(result.text, "html.parser")
pages = soup.find("div", {"class":"s-pagination"}).find_all("a")
print(pages)
def get_jobs():
last_page = extract_pages()
return []
-. "main.py"는 기존 indeed 사이트를 Scrapping 할 때와 같은 파일을 사용합니다. 단, indeed 관련 function은
-. 주석처리를 해서 stackoverflow를 테스트 함에 있어 indeed 사이트를 Scrapping 하지 않도록 설정하였습니다.
※ 본 포스팅의 내용은 강의를 들은 후 필자의 개인적인 의견을 기재한 것으로,
정답이 아닐 수 있음을 참고하십시오.
이상으로 Python으로 웹 스크래퍼 만들기 복습 6일 차를 마치겠습니다.
'Python으로 웹 스크래퍼 만들기' 카테고리의 다른 글
Python으로 웹 스크래퍼 만들기 복습 8일차[csv] (Feat. 노마드코더) (0) | 2020.07.24 |
---|---|
Python으로 웹 스크래퍼 만들기 복습 7일차 (Feat. 노마드코더) (0) | 2020.07.23 |
Python으로 웹 스크래퍼 만들기 복습 5일차[Requests, BeautifulSoup, Object, Function] (Feat. 노마드코더) (0) | 2020.07.20 |
Python으로 웹 스크래퍼 만들기 복습 4일차[웹스크래퍼] (Feat. 노마드코더) (0) | 2020.07.19 |
Python으로 웹 스크래퍼 만들기 복습 3일차[If-Else 조건문, For 반복문] (Feat. 노마드코더) (0) | 2020.07.18 |