[KT Aivle 3기 AI] 12일차. 웹크롤링 (Web Crawling) (1)
0. 개요
KT Aivle School 3기 AI 12일차
- 강사 : 박두진 강사님
- 주제 : 웹크롤링(Web Crawling), 파이썬 Requests 및 API를 활용한 동적 페이지 크롤링
- 내용 :
- 웹크롤링을 하는 방법을 배움
- 웹크롤링을 하기 위한 웹 관련 지식을 알려주시고,
- Python으로 웹크롤링을 알려주심
- 웹크롤링을 아무렇게나 하면 법적인 문제가 발생할 수도?
- 따라서, API 사용을 권장함!
- 12일차에는 동적페이지, 13일차에는 정적 페이지 진행
- 페이지의 내용이 바뀔 때 URL이 바뀌면 정적, 안 바뀌면 동적
1. WEB
먼저 WEB에 대한 간단한 지식 정리!
1) Server & Client Architecture
- Client : 브라우저를 통해 서버에 데이터를 요청
- Server : Client가 데이터를 요청하면 요청에 따라 데이터를 전송
2) URL
- Uniform Resource Locator
https://news.naver.com:80/main/read.nhn?mode=LSD&mid=shm&sid1=105&oid=001&aid=0009847211#da_727145
https://
: Protocolnews
: Sub Domainnaver.com
: Domain- → 여기까지 건물을 찾아 가는 것!
:80
: port (80번 포트 등 특정 포트 번호는 생략이 가능해서 평소에 안보임)- 웹 서비스를 사용할 지, 데이터 베이스를 사용할지 결정
- → 건물에서 어떤 곳을 찾아갈지 정한다.
/main/
: path (directory)read.nhn
: page (file)- → : 파일까지 접근
?mode=LSD&mid=shm&sid1=105&oid=001&aid=0009847211
: query#da_727145
: fragment- 페이지에서 특정 위치로 이동
3) Get & Post
- Request : 데이터 요청
- Response : 데이터 받기
Request 방법으로 Get 방식과 Post 방식이 있음
- Get
- Url에 데이터가 포함된다 → 데이터가 노출 됨
- 길이 제한이 있음
- Post
- Body에 데이터가 포함된다 → 데이터가 숨겨짐
4) Internet
- 인터넷이란?
- 인터넷은 컴퓨터로 연결하여 TCP/IP (Transmission Control Protocol/Internet Protocol)라는 통신 프로토콜을 이용해 정보를 주고받는 컴퓨터 네트워크
- 해저케이블을 이용하여 전세계 서버에 접속하여 인터넷을 사용하고 있다.
- 무선 인터넷
- 선이 아니라 주파수를 매체로 사용한다.
- 우리가 사용하는 단말기만 무선이다.
5) OSI 7 Layer
- Open Systems Interconnection Reference Model
- 국제표준화기구(ISO)에서 개발한 모델로, 컴퓨터 네트워크 프로토콜 디자인과 통신을 계층으로 나누어 설명한 것이다.
- 하위 계층으로 갈수록 페이로드가 증가
6) Cookie & Session & Cache
- Cookie : 하드디스크에 저장하는 데이터
- Client에 저장하는 문자열 데이터로 도메인 별도 따로 저장
- 로그인 정보, 내가 봤던 상품 정보, 팝업 다시 보지 않음
- 하나의 클라이언트에 300개, 도메인당 20개, 쿠키 하나당 4Kbyte
- Session : 접속 정보
- Server에 저장하는 객체 데이터, 브라우저와 연결시 Session ID 생성
- Session ID를 Cookie에 저장함으로 로그인 연결 유지
- 같은 브라우저로 같은 서버에 접속하면 Session ID가 같음
- 로그인 연결 정보, 원하는 객체 데이터
- Cache : 메모리에 저장하는 데이터
- Client나 Server의 메모리에 저장하여 빠르게 데이터를 가져오는 목적의 저장소
7) HTTP Status Code
- 서버와 클라이언트가 데이터를 주고 받으면 주고 받은 결과로 상태 코드를 확인할 수 있다.
- 2xx : success
- 3xx : redirection (browser cache), 저장되어 있는 것을 가져옴
- 4xx : request error
- 5xx : server error
8) Web Language & Framework
- Client
- HTML
- CSS - less, sass
- Javascript - vue.js, react.js, angelar.js, backborn.js
- Server
- Python - Django, Flask
- Java - Spring
- Ruby - Rails
- Javascript - Nodejs (Express)
- Scala - Play
2. Web Crawling (requests)
- 웹 페이지에서 데이터를 수집하는 방법을 알아보자.
1) Web Crawling 방법
(1) 웹페이지 종류
- 정적인 페이지 : 웹 브라우저에 화면이 한 번 뜨면 이벤트에 의한 화면의 변경이 없는 페이지
- 동적인 페이지 : 웹 프라우저에 화면이 뜨고 이벤트가 발생하면 서버에서 데이터를 가져와 화면을 변경하는 페이지
→ 원하는 버튼을 누르고 url의 변화가 있는지 확인. 변화가 있으면 정적, 없으면 동적
(2) requests 이용
- 받아오는 문자열에 따라 두가지 방법으로 구분
- json 문자열로 받아서 파싱하는 방법 : 주로 동적 페이지 크롤링 할 때 사용
- html 문자열로 받아서 파싱하는 방법 : 주로 정적 페이지 크롤링 할 때 사용
(3) selenium 이용
- 브라우저를 직접 열어서 데이터를 얻는 방법
- request를 막는 경우가 있기 때문(Abuse로 판단해서 막아버림)
(4) Crawling 방법에 따른 속도
- requests json > requests html > selenium
2) Web Crawling 실습
(1) Naver Stock Data
- 네이버 증권 사이트에서 주가 데이터 수집
- 수집할 데이터 : 일별 kospi, kosdaq 주가, 일별 환율(exchange rate) 데이터
- 데이터 수집 절차
- 웹서비스 분석 : url
- 서버에 데이터 요청 : request(url) > response : json(str)
- 서버에서 받은 데이터 파싱(데이터 형태를 변경) : json(str) > list, dict > DataFrame
1
2
3
4
5
import warnings
warnings.filterwarnings('ignore') # 경고문구 없애기
import pandas as pd
import requests
[1] 웹서비스 분석 : url
- pc 웹페이지가 복잡하면 mobile 웹페이지에서 수집
- 코스피 일별 종가정보를 얻어오기 위한 Url을 알아내보자.
[2] 서버에 데이터 요청 : request(url) → response : json(str)
- response의 status code가 200이 나오는지 확인
- 403이나 500이 나오면 request가 잘못되거나 web server에서 수집이 안되도록 설정이 된것임
- header 설정 또는 selenium 사용
- 200이 나오더라도 response 안에 있는 내용을 확인 → 확인하는 방법 : response.text
[3] 서버에서 받은 데이터 파싱(데이터 형태를 변경) : json(str) → list, dict → DataFrame
[4] 전체 과정 함수로 만들기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def stock_price(code='KOSPI', page=1, page_size=20):
'''This function is crawling stock price from naver.
params:
code : str : KOSPI, KOSDAQ
page : int
page_size : int
return:
type : DataFrame
'''
# 1. URL
url = f'https://m.stock.naver.com/api/index/{code}/price?pageSize={page_size}&page={page}'
# 2. request(URL) > response : json(str)
response = requests.get(url)
# 3. json(str) > list, dict > DataFrame
data = response.json()
return pd.DataFrame(data)[['localTradedAt', 'closePrice']]
1
stock_price()
localTradedAt | closePrice | |
---|---|---|
0 | 2023-02-16 | 2,475.48 |
1 | 2023-02-15 | 2,427.90 |
2 | 2023-02-14 | 2,465.64 |
3 | 2023-02-13 | 2,452.70 |
4 | 2023-02-10 | 2,469.73 |
5 | 2023-02-09 | 2,481.52 |
6 | 2023-02-08 | 2,483.64 |
7 | 2023-02-07 | 2,451.71 |
8 | 2023-02-06 | 2,438.19 |
9 | 2023-02-03 | 2,480.40 |
10 | 2023-02-02 | 2,468.88 |
11 | 2023-02-01 | 2,449.80 |
12 | 2023-01-31 | 2,425.08 |
13 | 2023-01-30 | 2,450.47 |
14 | 2023-01-27 | 2,484.02 |
15 | 2023-01-26 | 2,468.65 |
16 | 2023-01-25 | 2,428.57 |
17 | 2023-01-20 | 2,395.26 |
18 | 2023-01-19 | 2,380.34 |
19 | 2023-01-18 | 2,368.32 |
(2) Daum Exchange
https://finance.daum.net
- 서버에서 어뷰징을 막는 경우 해결 방법
- headers의 ‘user-agent’와 ‘referer’ 값을 변경해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 웹서비스분석 : URL
url = 'https://finance.daum.net/api/exchanges/summaries'
headers = {
'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
'referer' : 'https://finance.daum.net/exchanges'
}
# 2. request(URL) > response(json(str))
response = requests.get(url, headers=headers)
response
# 3. json(str) > list, dict > DataFrame
df = pd.DataFrame(response.json()['data'])
df.tail(2)
symbolCode | date | currencyCode | currencyName | currencyUnit | country | region | name | recurrenceCount | basePrice | ... | changeRate | cashBuyingPrice | cashSellingPrice | ttBuyingPrice | ttSellingPrice | tcBuyingPrice | fcSellingPrice | exchangeCommission | usDollarRate | chartImageUrl | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
40 | FRX.KRWQAR | 2023-02-16 18:12:10 | QAR | 리얄 | 1 | 카타르 | {'korName': '중동', 'engName': 'Middle East'} | 카타르 (KRW/QAR) | 567 | 351.67 | ... | 0.003288 | 0.0 | 0.0 | 0.00 | 0.0 | None | None | 3.355 | 0.2737 | {'day': 'https://t1.daumcdn.net/finance/chart/... |
41 | FRX.KRWTRY | 2023-02-16 18:12:10 | TRY | 리라 | 1 | 터키 | {'korName': '중동', 'engName': 'Middle East'} | 터키 (KRW/TRY) | 567 | 68.16 | ... | 0.001611 | 0.0 | 0.0 | 67.42 | 68.9 | None | None | 26.400 | 0.0530 | {'day': 'https://t1.daumcdn.net/finance/chart/... |
2 rows × 22 columns
3. Web Crawling (API)
- 크롤링 정책 : robots.txt
- 웹페이지의 크롤링 정책 설명한 페이지
- ex) www.naver.com/robots.txt, ted.com/robots.txt
- 크롤링법은 없지만.. 지적재산권, 서비스과부화, 데이터 사용 표준 등 법적문제가 발생할 수 있음
- 따라서, API를 사용하면 이러한 문제를 해결 가능
- API 사용 : 데이터를 가지고 있는 업체에서 데이터를 가져갈 수 있도록 하는 서비스
1) 사용 방법
위의 웹크롤링 방법과 동일하나, access token과 URL을 document에서 찾는 것만 다름.
- Client 에서 app 등록 - Server에서 access token(request token) 전달
- URL 찾기 (document에 있음)
- request(key 포함) → response
- data → DataFrame
2) 실습
(1) Naver Rest API
- 파파고 번역 api
- 통합검색어 트렌드 api
0. request token 얻기
https://developers.naver.com
- Request Token 얻기 : 애플리케이션등록 → app_key 획득
- app_key를 이용해서 데이터 가져오기
1. 파파고 번역 api
https://developers.naver.com/docs/papago/
- 사용법
https://developers.naver.com/docs/papago/papago-nmt-api-reference.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
import requests, json
# Make Function
def translate(txt, source='ko', target='en'):
# 0. request token 얻기
CLIENT_ID, CLIENT_SECRET = 'DGJwjFI3owlSfbdd88dE', '*******'
# 1. API 문서에서 URL 찾기
url = "https://openapi.naver.com/v1/papago/n2mt"
headers = {
'Content-Type' : 'application/json',
'X-Naver-Client-Id' : CLIENT_ID,
'X-Naver-Client-Secret' : CLIENT_SECRET,
}
# 2. request(URL) > response : json(str)
params = {'source' : source, 'target' : target, 'text' : txt}
response = requests.post(url, json.dumps(params), headers=headers)
# 3. json(str) : en_txt
return response.json()['message']['result']['translatedText']
1
translate('웹크롤링은 재밌습니다.')
1
'Web crawling is fun.'
2. 활용 예시
1
2
3
4
5
6
df = pd.DataFrame({
'id' : list('AB'),
'title' : ['인공지능 스쿨 화이팅', '데이터 크롤링'],
})
df
id | title | |
---|---|---|
0 | A | 인공지능 스쿨 화이팅 |
1 | B | 데이터 크롤링 |
1
2
df['en_title'] = df['title'].apply(translate)
df
id | title | en_title | |
---|---|---|---|
0 | A | 인공지능 스쿨 화이팅 | AI school, fighting! |
1 | B | 데이터 크롤링 | Crawling data |
(2) 통합검색어 트렌드 api
- 서비스 :
https://datalab.naver.com/keyword/trendSearch.naver
- 내 애플리케이션 > dss 애플리케이션 > API 설정 > 데이터랩(검색어 트렌드) 추가
- 사용법 :
https://developers.naver.com/docs/serviceapi/datalab/search/search.md#통합-검색어-트렌드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1. API 문서 : URL
url = 'https://openapi.naver.com/v1/datalab/search'
params = {
'startDate' : '2018-01-01',
'endDate' : '2023-02-16',
'timeUnit' : 'month',
'keywordGroups' : [
{'groupName' : '트위터', 'keywords' : ['트위터', '트윗']},
{'groupName' : '페이스북', 'keywords' : ['페이스북', '페북']},
{'groupName' : '인스타그램', 'keywords' : ['트위터', '인스타']},
]
}
headers = {
'Content-Type' : 'application/json',
'X-Naver-Client-Id' : CLIENT_ID,
'X-Naver-Client-Secret' : CLIENT_SECRET,
}
# 2. request(URL) > response : json(str)
response = requests.post(url, json.dumps(params), headers=headers)
# 3. json(str) > list, dict > DataFrame
results = response.json()['results']
1
2
3
4
5
6
7
8
9
# columns : period, ratio, title
dfs = []
for result in results:
df = pd.DataFrame(result['data'])
df['title'] = result['title']
dfs.append(df)
result_df = pd.concat(dfs, ignore_index=True)
result_df
period | ratio | title | |
---|---|---|---|
0 | 2018-01-01 | 14.59502 | 트위터 |
1 | 2018-02-01 | 13.02380 | 트위터 |
2 | 2018-03-01 | 12.97953 | 트위터 |
3 | 2018-04-01 | 12.82991 | 트위터 |
4 | 2018-05-01 | 13.05391 | 트위터 |
... | ... | ... | ... |
181 | 2022-10-01 | 32.86715 | 인스타그램 |
182 | 2022-11-01 | 30.52275 | 인스타그램 |
183 | 2022-12-01 | 30.16556 | 인스타그램 |
184 | 2023-01-01 | 31.27293 | 인스타그램 |
185 | 2023-02-01 | 16.00833 | 인스타그램 |
186 rows × 3 columns
1
2
3
4
5
6
7
8
9
10
11
12
# column : title(twitter, facebook, instagram)
# index : period
# value : ratio
pivot_df = result_df.pivot('period', 'title', 'ratio')
pivot_df.columns = ['instagram', 'twitter', 'facebook']
import matplotlib.pyplot as plt
%config InlineBackend.figure_formats = {'png', 'retina'} # plot 선명하게
pivot_df[:-1].plot(figsize=(20, 5))
plt.show()
This post is licensed under CC BY 4.0 by the author.