정적 크롤링
개발자 도구 살펴보기
실제로 크롤링을 하기 전에 개발자 도구에서 어떤 부분을 크롤링해야 하는지 확인해보자.
- Elements
- 웹 페이지의 HTML 전체 소스 코드를 보여주어 우리가 원하는 데이터의 위치를 확인할 수 있음
- 이 버튼으로 원하는 부분을 클리하면 코드가 어디에 위치하는지 확인 가능
- Console
- JS를 실행해보는 곳으로 우리가 만든 소스 코드 테스트 및 웹 페이지 조작 가능
- 디버깅 시에 매우 유용
- 예제로 console 창에 alert(1)을 입력하고 엔터를 치면 팝업창이 생기는것을 확인할 수 있다.
- Sources
- 웹 페이지가 불러와질 때 같이 필요한 CSS, JS 파일, 이지미 파일 등을 보여줌
- Network
- 웹 사이트가 로딩 되면서 또는 로딩된 이후에 서버가 주고받는 모든 네트워크 통신을 보여줌 (웹 API도 확인 가능)
- Performance
- 웹 페이지가 로딩 되는데 얼마나 많은 시간이 걸렸는지 등 성능을 파악
- 가장 왼쪽 녹화 버튼을 누르고 페이지를 새로 고침하고 녹화를 종료한 상태
- Application
- 웹페이지에서 사용중인 저장소로 로컬캐시, 쿠키 등을 볼 수 있음
정적 크롤링 방법
- 원하는 웹 페이지의 html 코드를 긁어 온다.
- html 문서를 긁어올 때는 request 패키지를 사용
- 긁어온 html 문서를 파싱한다.
- BeautifulSoup4 패키지는 매우 길고 정신없는 html 문서를 잘 정리되고 다루기 쉬운 형태로 만들어 원하는 것만 쏙쏙 가져올 때 사용 (파싱)
- 파싱한 html 문서에서 원하는 것을 골라 사용한다.
- 필요한 정보의 위치와 구조를 파악해서 가져오는 단계
- BeautifulSoup4 (find/select) 함수 사용
request 사용해보기
원하는 웹 페이지의 html 문서를 가져올 때는 requests 패키지의 get() 함수를 사용하면 된다.
# requests 패키지 가져오기
import requests
# 가져올 url 문자열로 입력
url = 'https://www.naver.com'
# requests의 get함수를 이용해 해당 url로 부터 html이 담긴 자료를 받아옴
res = requests.get(url)
# 우리가 얻고자 하는 html 문서가 여기에 담기게 됨
html_text = res.text
print(html_text)
import requests as req
# 내 IP 주소 확인하기
res = req.get("https://api.ipify.org/")
print(res.text) # 218.234.6.190
print(res.status_code) # 200
print(res.raw) # <urllib3.response.HTTPResponse object at 0x0000018848789E10>
# text는 사람이 읽을 수 있게 처리됨, raw는 byte값
# raw는 텍스트로 표현될 수 없는 이미지나 동영상 파일 파싱에 사용됨
# response를 받았을 때 request가 어떤것인지 확인
print(res.request.method) # GET
print(res.request.headers) # {'User-Agent': 'python-requests/2.28.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
# request에 header 같이 보내기
res = req.get("https://api.ipify.org/", headers={"hi": "hello"})
print(res.request.headers) # {'User-Agent': 'python-requests/2.28.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'hi': 'hello'}
환율 정적 크롤링 해보기
이번에는 네이버 금융 페이지의 환율 공시 정보를 크롤링해본다.
- requests 활용한 기초적인 파싱
- find(), split() 등을 활용한 기초적인 문자열 파싱
- 정규식(regex)을 활용한 패턴 검색
- 쿼리스트링에 대한 이해
- beautifulsoup 을 활용한 편리한 html 파싱
- css selector 를 활용한 손쉬운 파싱
import requests as req
url = "https://finance.naver.com/marketindex/?tabSel=exchange#tab_section"
res = req.get(url)
html = res.text
#--------------------- find(), split() 활용한 문자열 파싱----------------------#
'''
find(): 주로 문자열의 위치를 찾는것 보다는 내가 원하는 것이 있는지 없는지 확인용으로 사용
'''
# '미국 USD' 라는 단어가 처음 등장하는 index
pos = html.find('미국 USD')
# 환율 정보 plict으로 가져오기
data = html.split('<span class="value">')[1].split('</span>')[0]
print(data) # 1,410.00
#--------------------- regluar expression 활용한 패턴 검색----------------------#
import re
# .은 줄바꿈을 인식하지 못하기 때문에 re.DOTALL 로 줄바꿈도 인식하게끔 변경
r = re.compile(r"h_lst.*?blind\">(.*?)</span>.*?value\">(.*?)</", re.DOTALL)
captures = r.findall(html)
print(captures) # [('미국 USD', '1,410.50'), ('일본 JPY(100엔)', '991.49'), ('유럽연합 EUR', '1,380.60'), ('중국 CNY', '198.44'), ('달러/일본 엔', '142.0800'), ('유로/달러', '0.9815'), ('영국 파운드/달러', '1.1263'), ('달러인덱스', '111.1000'), ('WTI', '83.49'), ('휘발유', '1721.86'), ('국제 금', '1681.1'), ('국내 금', '75670.13')]
#--------------------- 쿼리스트링 ----------------------#
'''
쿼리 스트링: 웹 요청시에 보내는 추가 인자값
사용자가 입력 데이터를 전달하는 방법중의 하나로, url 주소에 미리 협의된 데이터를 파라미터를 통해 넘기는 것
http://host:port/path?querystring
- 엔드포인트 주소 이후에 ?를 쓰는 것으로 쿼리스트링이 시작됨
- query parameters(물음표 뒤에 key=value pair)을 url 뒤에 덧붙여서 추가적인 정보를 서버 측에 전달
- 클라이언트가 어떤 특정 리소스에 접근하고 싶어하는지 정보를 담음
- 파라미터가 여러개일 경우 & 로 이어 붙임
- 문자열은 인코딩 되어 들어감
- ex) https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=1&ie=utf8&query=%EC%95%88%EB%85%95
'''
#--------------------- beautifulsoup 을 활용한 html 파싱 ----------------------#
'''
- requests: http 통신을 편하게 해줌
- beautifulsoup: html 파싱을 편하게 해줌
- html을 인식해서 어떤 위치에 어떤 정보가 있는지 파악해줌
- 특정 태그 다음에 있는 태그를 가져와서 작업할 수 있음
'''
from bs4 import BeautifulSoup as BS
url2 = "https://www.naver.com/"
res2 = req.get(url2)
# html 파싱
soup = BS(res2.text, "html.parser")
# title이라는 태그를 가져오기
print(soup.title) # <title>NAVER</title>
print(soup.title.string) # NAVER
# 미국 환율 가져오기 (해당 iframe만 가져와야함)
url3 = "https://finance.naver.com/marketindex/exchangeList.naver"
res3 = req.get(url3)
soup3 = BS(res3.text, "html.parser")
tds = soup3.find_all("td") # td 태그를 가지는 list array 반환
names = []
for td in tds:
if len(td.find_all("a")) == 0: # td안에서 a 태그를 가지는 list array 반환
continue
names.append(td.get_text(strip=True)) # 공백을 제외하고 통화명 가져오기
prices = []
for td in tds:
if "class" in td.attrs:
if "sale" in td.attrs['class']: # class라는 arrtribute를 가지는 애들 array로 가져오기
prices.append(td.get_text(strip=True))
print(names)
print(prices)
(참고) XML 파일 형식 확인해보기
XML(Extensible Markup Language)은 W3C에서 개발된, 다른 특수한 목적을 갖는 마크업 언어를 만드는데 사용하도록 권장하는 다목적 마크업 언어이다. XML 문서는 다음과 같이 자신에 대한 정보 일부를 선언하는 것으로 시작된다. <?xml version="1.0" encoding="UTF-8" ?>
. <?>는 메타데이터를 의미한다.
시작과 종료 태그가 한 쌍이 되어야 하며 아래 예시에서 day라는 태그의 데이터는 일, month라는 태그의 데이터는 월 이라고 생각하면 된다. 즉, 데이터에 이름을 붙이는 것이다. <시작태그명> 요소내용 </종료태그명>
<?xml version="1.0" encoding="UTF-8" ?>
<note>
<date>
<day>일</day>
<month>월</month>
<year>년</year>
</date>
</note>
(참고) JSON 파일 형식 확인해보기
JSON은 JavaScript 의 형식으로 파이썬의 dict 와 유사하게 생겼다. 하나의 dict를 객체라고 하고, ON은 Object Notation의 약자이다. JSON은 수많은 언어에서 지원되기 때문에 포맷이 엄격하다. 예를 들어 ““만을 사용해야 한다거나 마지막 줄은 ,가 포함되면 안된다.