웹 스크래핑
웹 스크래핑과 웹 크롤링의 차이점
웹 스크래핑과 웹 크롤링의 차이점은 웹 크롤링은 웹 페이지의 링크를 통해서 계속해서 정보를 찾아나가지만 웹 스크래핑은 특정 웹 사이트에서만 데이터를 추적한다는 차이가 있다. 또 스크래핑의 경우 원하는 데이터만 추출하는 기술이기 때문에 웹 크롤링과 다르게 중복된 데이터를 지워줘야하는 구문을 추가해주지 않아도 된다.
웹 크롤링 - 기존의 복사본을 만듬 웹 스크래핑 - 분석을 위한 특정 데이터를 추출하거나 새로운 것을 만듬
웹 스크래핑을 수행하기 위해서는 웹 크롤링 같은 작업을 선행해주어야 한다.
VSCODE
VSCODE에서 웹 테스트를 편하게 하기 위해서 OPEN IN BROWSER 확장 기능을 설치해주었다.
1
2
3
4
5
6
이름: open in browser
ID: techer.open-in-browser
설명: This allows you to open the current file in your default browser or application.
버전: 2.0.0
게시자: TechER
VS Marketplace 링크: https://marketplace.visualstudio.com/items?itemName=techer.open-in-browser
VSCODE의 탐색기에서 웹 파일(html)에 우클릭을 했을 때 OPEN IN … BROWSER 가 생기고 이 버튼을 통해 html을 좀 더 빠르게 열 수 있다.
VSCODE를 사용하기 전에는 NOTEPAD++로 html파일을 구경하곤했었는데, VSCODE로 html파일을 작성하니 이렇게 편하다고? 하며 놀랐다. 시작(ex <html>)을 입력하면 자동으로 끝(ex </html>)코드를 추가해주고 큰 따옴표까지 자동으로 달아주고 커서마저 그 안에 위치한 걸 보고있자니.. 감격스러웠다!
xpath
html에는 수 많은 태그가 있고, 태그 안에 태그가 있고 그 태그안에 또 다른 태그가 있고(부모, 자식 관계라고도 한다).. 식으로 만들어진다. 예를들어 /html/body/div/div/div/div/div/span/a...
식으로 말이다. 만약 사이트가 더 커지고 복잡해진다면 위의 예시보다 더 길어질 것이 분명하다. 이 때 사용하는 것이 xpath인데, 쉽게 말해서 학번, 군번같은 자신을 나타내주는 증명번호? 라고 생각해주면 된다. 만약 내가 찾을게 ‘로그인’이라면 //*[@id ="login"]/a
등의 유저가 지정해준 id(xpath)값으로 접근하는 식으로 쉽게 찾아낼 수 있다.
xpath 는 경로를 간략하게 나타낸 것이고, full xpath는 경로를 전부 나타낸 것이다
requests
터미널에서 pip install requests
를 통해 requests 모듈을 설치해주고,
1
2
3
4
5
import requests
res = requests.get("http://naver.com")
print(res.status_code)
를 해보면 ‘200’을 출력하는 것을 볼 수 있다. 이 때 200이면 정상, 403이면 접근불가를 뜻 한다.
정상적으로 접근했는지를 보려면
1
2
3
4
if res.status_code == requests.codes.ok:
print("정상")
else:
print("문제가 생김 {}".format(res.status_code))
를 해줄 수도 있지만 간단하게
1
res.raise_for_status()
를 통해 정상적으로 접근했는지를 알아낼 수도 있다. 정상적으로 접근했다면 받아온 페이지를 확인할 수 있는데,
1
print(res.text)
를 하면 받아온 페이지의 코드를 확인할 수 있다.
터미널에서 보면 굉장히 난잡한데 파일로 받아보면 좀 더 쉽게 확인할 수 있다.
1
2
with open("mycopyhtml", "w", encoding="utf8") as f:
f.write(res.text)
403이 뜨는 이유
사이트에서 데이트를 얻어올 때, 사이트에 접속하게 될텐데 이 때 사이트는 우리들의 헤더정보(출입정보)를 얻어낼 수 있다. 이 때 웹스크래핑을 방지해놓았다면(데이터 탈취, 서버 과부하등의 이유) 403이 뜨는 것이다.
정규식
정규식은 링크의 게시글에서 찾아볼 수 있듯이 ‘언어를 지정하기 위한 언어’이다. 주민등록번호의 뒷 자리, 차량의 번호 등 문자열의 일정한 패턴을 표현하는 일종의 형식 언어인데, python에서는 import re
를 통해 정규식 모듈을 사용할 수 있다.
1
2
3
4
5
6
7
8
9
10
import re
p = re.compile("ca.e")
# . : 하나의 문자를 의미 (ex: ca.e ---> care, cafe, case)
# ^ : 문자열의 시작을 의미 (ex: ^el ---> eliminate, elepant)
# $ : 문자열의 끝을 의미 (ex: se$ ---> case, base)
m = p.match("cafe")
print(m.group())
p는 보통 패턴을 뜻한다. 그래서 패턴이 ca.e이고 m을 통해 cafe가 p의 패턴에 맞는지 매칭시켜본다. 일치한다면(cafe는 일치함)정상적으로 출력하고 caffe라면 비정상이므로 애트리뷰트 에러를 발생시킬 것이다.
에러를 발생시키지않고싶다면
1
2
3
4
5
6
m = p.match("매칭시킬 문장")
if m:
print(m.group())
else:
print("매칭되지 않음")
식으로 표현할 수 있다. match함수는 주어진 문자열의 처음부터 일치하는지 확인하는 함수이기 때문에 careless 같은 경우는 care를 프린트하지만 ‘i don’t care’같은 문장은 “매칭되지 않음”을 프린트 하는걸 볼 수 있다.
i don’t care에서 care를 찾고 싶다면
m = p.search("")
처럼 search함수를 사용해주면 된다. search함수는 문자열 중에 일치하는게 있는지 확인하는 함수이다.
i don’t care를 그대로 출력하고 싶다면
m.string()
을 해주면 된다.
코드 | 설명 |
---|---|
—M | — |
group | 일치하는 문자열 반환 |
string | 입력받은 문자열 반환 |
start | 일치하는 문자열의 시작 index 반환 |
end | 일치하는 문자열의 끝 index 반환 |
span | 일치하는 문자열의 시작과 끝 index 반환 |
—P | — |
match | 문자열의 처음부터 일치하는지 확인해서 반환 |
search | 문자열 중에 일치하는 것을 반환 |
findall | 일치하는 모든 것을 리스트 형태로 반환 |
즉 re모듈을 활용하기 위해서는
-
p = re.compile(“원하는 형태”) 정규식 활용
-
m = p.match~search(“비교할 문자열”) or lst = p.findall(“비교할 문자열”)
정규식에 대해 더 공부하고 싶다면 w3school을 활용해주면 좋다. 영어가 가능하신 분들이 보기 편할 듯 하다.
User Agent
User Agent, What is my browser?의 링크에 들어가보면 자신의 User Agent를 확인할 수 있는데, 접속하는 브라우저(웨일, 크롬, 엣지 등등)에 따라 자신의 User Agent가 달라진다.
위의 403이 뜨는 이유 헤더 부분을 보면 사이트는 헤더 정보를 읽어서 접근을 거부할 수 있다고 설명했다. 이 때 자신의 User Agent를 입력해주면, 접근 거부된 사이트에서 접근을 할 수 있다.
1
2
3
4
5
6
7
8
9
import requests
url = "URL 주소"
headers = {"User-Agent":"자신의 User-Agent 주소"}
res=requests.get(url, headers=headers)
res.raise_for_status()
with open("mycopyhtml.html", "w", encodig='utf8') as f:
f.write(res.text)
문제없이 접근 거부되었던 사이트에 접근이 허용되어 올바르게 웹 정보를 얻어낸 걸 확인할 수 있다.
웹 스크래퍼 실전
웹 스크래핑을 하기 위해 pip install beautifulsoup4
와 pip install lxml
을 터미널에 입력해서 beautifulsoup4와 lxml모듈을 설치해준다. beautifulsoup4는 웹스크래핑을 위한 패키지, lxml은 구문을 분석하기 위한 파서parser 이다.
선택자
CSS에서 선택자란 선택을 해주는 요소를 뜻한다. 선택자의 종류도 여러가지가 있는데
- 전체 선택자
- 태그 선택자 태그의 이름으로 선택함 <h1>제목</h1>의 선택자 : h1
- 클래스 선택자 <div class=”info_group”> 인포메이션 </div>의 선택자 : .info_group
- ID 선택자 <div id=”articlebody”> 본문 내용 </div>의 선택자 : #articlebody
- 복합 선택자
- 속성 선택자
- 가상 선택자
등 여러가지 선택자가 존재한다
네이버 증권 사이트 스크래핑 하기
네이버 증권 사이트의 웹 정보를 스크래핑해서 엑셀 파일로 저장하고 출력하는 코드를 작성해보겠다.
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
from bs4 import BeautifulSoup
import requests
import time
import openpyxl
# # 알파벳 저장 리스트
# alphabet = []
# for i in range(97, 123):
# alphabet += chr(i)
# 엑셀 파일
xl = openpyxl.Workbook()
xlws = xl.create_sheet("주식 크롤링 데이터")
xlws["A1"] = "종목"
xlws["B1"] = "현재가"
xlws["C1"] = "평균 매입가"
xlws["D1"] = "잔고 수량"
xlws["E1"] = "평가 금액"
xlws["F1"] = "평가 손익"
xlws["G1"] = "수익률"
# 가져올 종목 코드 리스트
codes = ["005930", "001570", "000270"] # 삼성전자 # 금양 # 기아
codescount = len(codes)
for idx, code in enumerate(codes):
url = f"https://finance.naver.com/item/sise.naver?code={code}"
res = requests.get(url)
res.raise_for_status() # requests 오류 발생 확인
soup = BeautifulSoup(res.content, "lxml", from_encoding="utf8")
nowPrice = soup.select_one("#_nowVal").text.replace(",", "")
nowName = soup.select_one(".wrap_company>h2>a").text
xlws["A{}".format(idx + 2)] = nowPrice
xlws["B{}".format(idx + 2)] = nowName
xl.save("scraper.xlsx")
댓글남기기