부동산웹에서 위도 경도를 활용해 geohash를 구하고 이를 활용해 매물id, 매물 정보를 가져와보자.

Z부동산 웹 > 원룸을 선택 > F12 개발자도구 >  Network > Fetch/XHR > 클리어 해주기 > 망원동 입력 > 망원동 클릭

위에 방식이 이해가 가지 않는다면 Web과 Web Scarping / Crawling1과 2를 보고오길 추천한다.

 

다음과 같이 검색 결과 Preview에서 lat, lng(위도, 경도)값을 확인하였다.

다음 Preview 에서 itemID, lat, lng를 확인할 수 있다. 

 

list에서는 POST방식으로 데이터를 response한 것을 확인하였다. 그리고 itemId가 저장되어있다.

Preview를 확인해보니 원룸에 대한 정보가 나와있는 것을 확인하였다.

 

마지막으로 geohash값을 확인해주고 데이터를 수집할 준비를 마쳤다.

geohach는 위도 경도값(점)에서 어느정도까지 떨어져있는지 나타내는 값이다.

 

위도, 경도값 > geohash값 > itemId, lat, lng > list안에 itemId 조회 > 데이터 수집

다음가 같은 과정을 통해 데이터를 수집해보자.

 

동이름으로 위도, 경도 구하기

다음 검색기록을 확인하여 request URL을 확인한다.

 

다음 url은 한글데이터를 숫자와 특수기호를 통해 전달하기 편한 형태로 되어있다.

url을 구하기 위해서는 아래 디코딩 사이트의 디코딩을 활용하여 사용가능한 url로 변환해주자.

 

URL Decoder/Encoder

 

meyerweb.com

 

Request Method가 GET방식임으로 requests.get()을 활용한다.

import requests
import pandas as pd


addr = '망원동'
url = f'https://apis.zigbang.com/v2/search?leaseYn=N&q={addr}&serviceType=원룸'
response = requests.get(url)
data = response.json()['items'][0]
lat, lng = data['lat'], data['lng']
lat, lng
(37.556785583496094, 126.9013442993164)

 

 

위도 경도로 geohash알아내기

얻은 url을 활용해 다음과 같이 lat과 lng을 구했다면 geohash2를 활용해 geohash를 구해보자

!pip install geohash2

우선 geohash2를 install해주자. 시간이 좀 걸릴 수 있다.

다음 코드를 활용해 geohash를 구해준다.

import geohash2
geohash = geohash2.encode(lat, lng, precision = 5)
geohash
  • wydjx

 

geohash로 매물 아이디 가져오기

마찬가지로 request URL을 확인하고 이를 디코딩해준다.

GET방식으로 response해야된다는 것을 확인하였다.

 

코드를 쓰면서 response를 찍어보고 데이터 구조를 파악하길 바란다. 그리고 왜 item_ids를 리스트 컴프리헨션을

사용해 짜게 되었는지 이해하길 바란다.

url = f'https://apis.zigbang.com/v2/items/oneroom?geohash={geohash}&depositMin=0&rentMin=0&salesTypes[0]=전세&salesTypes[1]=월세&domain=zigbang&checkAnyItemWithoutFilter=true'
response = requests.get(url)
item_ids = [data['itemId'] for data in response.json()['items']]
len(item_ids), item_ids[:5]
(372, [42196796, 42078100, 42149571, 42184803, 42204685])

 

매물 아이디로 매물 정보 가져오기

마지막은 POST방식으로 진행되었다.

url = 'https://apis.zigbang.com/v2/items/list'
params = {'domain': "zigbang", 'item_ids': item_ids}
response = requests.post(url, params)
response
<Response [200]>
data = response.json()['items']
df = pd.DataFrame(data)
df = df[df['address1'].str.contains(addr)].reset_index(drop=True)
df = df[['item_id','sales_title', 'deposit', 'rent', 'size_m2', 'floor', 'building_floor', 'title', 'address1']]
df.tail(2)

 

하나의 함수로 만들기

 

#5. functjon
def oneroom(addr):
    url = f'https://apis.zigbang.com/v2/search?leaseYn=N&q={addr}&serviceType=원룸'
    response = requests.get(url)
    data = response.json()['items'][0]
    lat, lng = data['lat'], data['lng']
    
    geohash = geohash2.encode(lat, lng, precision = 5)
    
    url = f'https://apis.zigbang.com/v2/items/oneroom?geohash={geohash}&depositMin=0&rentMin=0&salesTypes[0]=전세&salesTypes[1]=월세&domain=zigbang&checkAnyItemWithoutFilter=true'
    response = requests.get(url)
    item_ids = [data['itemId'] for data in response.json()['items']]
    
    url = 'https://apis.zigbang.com/v2/items/list'
    params = {'domain': "zigbang", 'item_ids': item_ids}
    response = requests.post(url, params)
    data = response.json()['items']
    df = pd.DataFrame(data)
    df = df[df['address1'].str.contains(addr)].reset_index(drop=True)
    return df[['item_id','sales_title', 'deposit', 'rent', 'size_m2', 'floor', 'building_floor', 'title', 'address1']]
    
df = oneroom('망원동')
df.tail(2)

'<프로그래밍> > [python-WebCrawling]' 카테고리의 다른 글

Web Crawling5  (0) 2024.09.23
Web과 Web Scarping / Crawling 2(NAVER API)  (1) 2024.09.21
Web과 Web Scraping / Crawling 1  (1) 2024.09.19

네이버 > 증권 > 리서치 > 종목분석 리포트

 

정적페이지인것을 확인

1page에서 새로고침으로 확인

 

 

ctrl+F로 원하는 데이터가 있는지 확인

 

 

우클릭

 

아래 둘이 다름

 

 

ctrl+F로 [ "128. ]을 검색 > 있는것을 확인 > [ "129. ]를 검색하고 바로 위에 있는 128버전을(가장 최신) 확인한다.

 

주소창에 붙여넣으면 다운 완료됨 > 그다음 exe파일만 폴더로 가져오기

 

 

 

 

 

 

 

------------------------------

 

 

다음 > 뉴스 > 

 

LV2 > 연습문제 > 뒤에 있는 큰 수 찾기

https://school.programmers.co.kr/learn/courses/30/lessons/154539

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

def check(index, arr):
    len_arr = len(arr)
    max_n = 0
    for j in range(index, len_arr):
        if j <= len_arr-2:
            if arr[index] < max(arr[index : j+2]):
                return max(arr[index : j+2])
        else:
            return -1
    

def solution(numbers):
    answer = []
    max_n = max(numbers)
    for i in range(len(numbers)):
        if (i == (len(numbers)-1)) or (numbers[i] == max_n):
            answer.append(-1)
        elif numbers[i] < numbers[i+1]:
            answer.append(numbers[i+1])
        elif numbers[i] >= numbers[i+1]:
            answer.append(check(i, numbers))
            
    return answer

 

10번 ~ 22번 문제 시간초과

 

 

def check(index, arr, n):
    len_arr = len(arr)
    max_n = 0
    if index+n <= len_arr:
        if arr[index] < max(arr[index : index+n]):
            return max(arr[index : index+n])
        else:
            return check(index, arr, n+1)
    else:
        return -1
    

def solution(numbers):
    answer = []
    max_n = max(numbers)
    for i in range(len(numbers)):
        if (i == (len(numbers)-1)) or (numbers[i] == max_n):
            answer.append(-1)
        elif numbers[i] < numbers[i+1]:
            answer.append(numbers[i+1])
        
            
        elif numbers[i] >= numbers[i+1]:
            answer.append(check(i, numbers, 1))
    return answer

 

6번 ~ 22번 문제 시간초과

재귀가 더 시간을 많이 잡아먹음

구조적인 문제가 분명

 

마지막으로 구조는 그대로 가져가고

tru / except문을 활용해보았다.

def solution(numbers):
    answer = []
    max_n = max(numbers)
    for i in range(len(numbers)):
        if (i == (len(numbers)-1)) or (numbers[i] == max_n):
            answer.append(-1)
        elif numbers[i] < numbers[i+1]:
            answer.append(numbers[i+1])
        else:
            for k in range(1, len(numbers[i:])+1):
                try:
                    # print("try문 들어옴")
                    # print(i, numbers[i], k)
                    if numbers[i:].index(numbers[i]+k) >= 0:
                        answer.append(numbers[i]+k)
                        # print(answer)
                        check = 1
                        break
                except:
                    check = 0
                    continue
                    
            if check == 0:
                answer.append(-1)
                    
                
        
    return answer

 

잘못된 부분이 있는지 실패가 많이 떴다.

결국 찾아보고 stack 자료구조를 활용해 뒤에서부터 비교하는 방법으로 많이 풀이한 것을 알았다.

그래서 다음과 같이 풀이했다.

이 풀이에서 가장 중요한 부분은 앞에서 부터 비교하던걸 뒤에서 부터 비교하는 거라 생각한다.

앞에서 비교할 때는 내 다음것을 고려해야 되지만, 뒤로 비교하면 나보다 작다면 적용해주고 나보다 크다면 없애면 된다!

 

def solution(numbers):
    n = len(numbers)
    answer = [-1] * n  # 정답을 -1로 초기화
    stack = []  # 스택을 초기화

    for i in range(n - 1, -1, -1):  # 배열을 오른쪽에서 왼쪽으로 순회
        # 현재 숫자보다 작은 숫자는 스택에서 제거
        while stack and stack[-1] <= numbers[i]:
            stack.pop()
        
        # 스택이 비어 있지 않다면 스택의 맨 위가 '뒤에 있는 큰 수'
        if stack:
            answer[i] = stack[-1]
        
        # 현재 숫자를 스택에 추가
        stack.append(numbers[i])

    return answer

 

 

만약 이해하기 어렵다면 다음 print문을 같이 사용하면 좋을거 같다.

def solution(numbers):
    n = len(numbers)
    answer = [-1] * n  # 정답을 -1로 초기화
    stack = []  # 스택을 초기화

    for i in range(n - 1, -1, -1):  # 배열을 오른쪽에서 왼쪽으로 순회
        # 현재 숫자보다 작은 숫자는 스택에서 제거
        print('index = ', i, 'stack =',stack, 'numbers =', numbers[i])
        while stack and stack[-1] <= numbers[i]:
            stack.pop()
            print('stack pop')
        
        # 스택이 비어 있지 않다면 스택의 맨 위가 '뒤에 있는 큰 수'
        if stack:
            answer[i] = stack[-1]
        
        # 현재 숫자를 스택에 추가
        stack.append(numbers[i])
        print('answer - ',answer)
        print('stack =',stack)
        
    
    return answer

Web과 Web Crawling1에선 네이버에서 동적인 데이터를 가져와 봤다.

해당 과정은 N사에서 발생하는 트래픽 비용을 감당하기에 너무 막대한 데이터를 가져간다면 기업측에 손해가 크기에

정상적인 방법은 아니라고 소계했다. 그래서 D사를 살펴보면 이러한 기능을 차단한 것을 알 수 있다.

 

다음금융(https://finance.daum.net)에서 개발자모드를 킨다. > Network에서 모든 기록을 없앤다. > 환율에 들어간다.

 

다음에서 Wab Crawling을 해보면 403의 response code를 보내온다.

import requests
import pandas as pd
# 1. URL
url = 'https://finance.daum.net/api/exchanges/summaries'
response = requests.get(url)
response
<Response [403]>

 

우리가 request를 할때 url뿐만 아닌 header의 User-Agent값이 무엇인지 영향을 준다.

지금 우리는 브라우저가 아닌 python을 활용하였기 때문에 User-Agent가 py로 되어있다.

서버에서 요구하는 User-Agent가 일치하지 않기에 request단계에서 오류가 난 것이다.

User-Agent는 개발자 화면에서 스크롤을 아래로 내려주면 확인할 수 있다.

 

그러나 이런 User-Agnet는 Client에서 생성하기에 조작이 가능하다.

headers를 지정해주면 되는데 여기에는 User-Agent와 referer을 지정해주어야 된다.

import requests
import pandas as pd
# 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/128.0.0.0 Safari/537.36',
            'referer':'https://finance.daum.net/exchanges' }
response = requests.get(url, headers = headers)
response
<Response [200]>

 

제대로 response되는걸 확인했으면 데이터를 얻어올 수 있다.

data = response.json()['data']
df = pd.DataFrame(data)[['currencyCode', 'currencyName', 'country', 'basePrice']]
df.head()

다음과 같이 환률 데이터를 확인하였다.

 

지금까지의 방식은 웹에서 요청하는 request url을 활용해 response받는 데이터를 가져온 것이다.

이러한 방식을 사용하지 말라고 다양한 사이트에서 API를 제공한다.

네이버에서 제공하는 API를 활용해 데이터를 다루어보자!

 

네이버 데이터랩

네이버의 검색 트렌드 및 급상승검색어 이력, 쇼핑 카테고리별 검색 트렌드 제공

datalab.naver.com

네이버 데이터랩에 접속하여 검색어 트랜드로 들어간다.

그리고 다음과 같이 sns 검색어로 트랜드를 확인해보자.

그럼 다음과 같은 데이터를 생성해준

해당 그래프를 대세였던 페이스북은 점점 사용하지 않고 인스타그램과 트위터를 더 선호하는 것을 확인하였다.

 

이러한 네이버 데이터랩이 아닌 API를 활용하여 데이터를 가져와보자

API는 사용자를 밝히기위해 로그인과 app들록을 요구한다.

그리고 key값 제공해 API로 데이터를 무료로 가져갈 수 있게한다.

기업의 입장에서 어떤 사용자가 데이터를 가져가고, 너무 과도하게 가져가지는 않는지 관리 감독을 할 수 있다.

 

 

https://developers.naver.com/main/

 

NAVER Developers

네이버 오픈 API들을 활용해 개발자들이 다양한 애플리케이션을 개발할 수 있도록 API 가이드와 SDK를 제공합니다. 제공중인 오픈 API에는 네이버 로그인, 검색, 단축URL, 캡차를 비롯 기계번역, 음

developers.naver.com

Naver Developers에 접속해보자

Request Token(key값)을 얻기 위해서 애플리케이션 등록을 해야된다.

들어가면 새로운 애플리케이션을 등록하라고 한다.

 

약관에 동의하고 넘어가면 계정을 설정하기 위해 본인 인증을 거친다.

휴대폰 인증을 통해 사용자 인증을 진행하고, 애플리케이션 정보를 확인하자.

[ 비로그인 오픈 API 서비스 환경 ]항목은 네이버로그인 연동하기위해 필요한 것으로 우리는 WEB설정으로 활용한다.


등록을 완료하면 Client ID와 Client Secret을 제공받는다. 

이를 활용하여 데이터 분석을 해보도록 하자.

Documents > 서비스 API > 데이터랩으로 이동한다.

여기서 API 활용법을 알아보자. 왼쪽메뉴에서 [ 네이버 통함 검색어 트렌드 조회 ]를 클릭한다.

 

이 페이지에서 우리는 요청URL / 프로토콜 / 메서드/ 파라미터 등을 확인할 수 있다.

 

이제 자료를 확인했으니 python코드로 데이터를 request해보자.

import pandas as pd
import requests
import json

CLIENT_ID, CLIENT_SECRET = '개인 CLIENT_ID을 입력하세요', '개인 CLIENT_SECRET을 입력하세요'

 

url을 입력 > 파라미터 데이터 입력(형식은 [ 네이버 통함 검색어 트렌드 조회 ] 에서 확인)

> header 지정 (naver Developer 하단의 참고사항 및 요청 예 참고) > request 방식 확인 

 

response = requests.post(url, data = json.dumps(params), headers = headers)

이부분이 없다면 keywords에 한글이 들어간 상태로 전달하면, 전달되는 과정에서 데이터가 깨지면서 오류가 난다.

json패키지 안에 있는 dumps()를 활용하여 영문자 또는 특수문자로 변형한다.

# 1. URL
url = 'https://openapi.naver.com/v1/datalab/search'
# 2. request > response(json)
params = {
    'startDate' : '2018-01-01', 'endDate' : '2023-12-31', '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,
}

response = requests.post(url, data = json.dumps(params), headers = headers)
# response.text
response
<Response [200]>

 

데이터 parsing을 통해 원하는 데이터를 찾아본다.

#3. parsiong
data = response.json()['results']
data

 

리스트 안에 딕셔너리가 있는 포멧임을 확인하였다.

dfs[]의 index값 0, 1, 2에는 각각 트위터, 페이스북, 인스타의 데이터가 들어가있다.

concat을 활용하여 하나의 DF으로 생성해준다.

#4. preprocessing
dfs = []
for row in data:
    df = pd.DataFrame(row['data'])
    df['title'] = row['title']
    dfs.append(df)
    
# len(dfs), dfs[2]
result = pd.concat(dfs, ignore_index=True)
result.tail(2)

 

이 데이터를 plot으로 그려보면 데이터랩에서 얻었던 결과와 같은 plot을 확인할 수 있다.

import matplotlib.pyplot as plt
pivot_df.plot(figsize = (20, 5))
plt.show()

'<프로그래밍> > [python-WebCrawling]' 카테고리의 다른 글

Web과 Web Scarping / Crawling 3(응용)  (0) 2024.09.24
Web Crawling5  (0) 2024.09.23
Web과 Web Scraping / Crawling 1  (1) 2024.09.19

웹 크롤링을 배우기 전에 웹에 대해 먼저 찾아보자

웹은 Client와 Server로 이루어져 있다.

Client는 브라우저를 통해 Server에 데이터를 요청하는데 이를 Request라고 한다.

Server는 들어온 요청에 대해 알맞은 데이터를 Client로 전송하는데 이를 Response라고 한다.

  • Client - Request : Browser를 사용하여 Server에 데이터를 요청 
  • Server - Response : Client의 Browser에서 데이터를 요청하면 요청에 따라 데이터를 Client로 전송

이러한 return은 브라우저에서 하용하는 주소의 형태로 오는데 이를 URL이라 한다.

우리가 말하는 URL은 Uniform Resource Locator의 약자로 각각 항목에 따라 주소를 구성하게 된다.

 

HTTP의 Request 방식이 Response 받은주소의 의미는 다음과 같다.

http:// Protocol /main/ Path
News Sub Domain read.nhn Page ( File )
naver.com Primary Domain mode=LSD Query
80 Port #da_727145 Fragment

 

전체적인 웹의 구조는 Client에서 Internet을 통해 Server로 Request를 요청한다.

서버는 이를 받아 WAS( Web Application Server )에서 DB를 통해 데이터를 TXT의 형태로 전달받아 Internet을 통해

Client 즉 Browser에 전달한다. 우리가 웹으로 보는 이 페이지도 이러한 과정을 갖게된다.

여기서 발생하는 트래픽은 Server에서 감당하는데 데이터 사용에 대해 Server에서 계속 비용을 내고있는 것이다.

 

Internet은 컴퓨터로 연결하여 TCP/IP 프로토콜을 이용하여 정보를 주고 받는 컴퓨터 네트워크이다.

해저케이블을 사용하여 전세계 컴퓨터에 접속하고있다.

우리가 사용하는 wifi또한 일정한 범위 안에서 무선으로 사용하지 공유기는 유선 인터넷 선으로 연결되어있고

아래 사진과 같은 해저케이블을 통해 자료가 전달된다.

예를들어 서울에서 미국 켈리포니아에 자료를 전송한다고 할 때, 인터넷 선을 통해 전달하기에 cost가 많이

발생한다.

 

 

크롤링을 이해하기 위해 추가로 알아두면 좋은 것을 정리한 것이다.

<HTTP의 Request 메서드는 Get방식과 Post방식이 있다.>

  • Get
    • URL에 Query 포함 
    • Query(데이터) 노출, 전송 가능 데이터 작음 
  • Post 
    • Body에 Query 포함
    • Query(데이터) 비노출, 전송 가능 데이터 많음

 

<HTTP Status Code >

Client와 Server가 데이터를 주고 받은 결과 정보 

  • 2xx - Success - 성공에 관한 정보
  • 3xx - Redirect  - 요청을 완료하려면 추가적인 작업이 필요함(페이지 이동)
  • 4xx - Request Error - 보통 404라고 많이 경험한 Request과정에서 에러
  • 5xx - Server Error - Server의 문제로인해 Response하지 못하는 에러

 

<Cookie, Session, Cache>

  • Cookie
    • Client의 Browser에 저장하는 문자열 데이터 
    • 사용예시 : 로그인 정보, 내가 봤던 상품 정보, 팝업 다시보지 않음 등 
  • Session
    • Client의 Browser와 Server의 연결 정보
    • 사용예시 : 자동 로그인 
  • Cache
    • Client, Server의 RAM(메모리)에 저장하는 데이터
    • RAM에 데이터를 저장하면 데이터 입출력이 빠름

 

<Web Language & Framework >

  • Client (Frontend)
    • HTML, CSS - Bootstrap, Semantic UI, Materialize, Material Design Lite
    • Javascript - react.js, vue.js, angular, jQuery
  • Server (Backend) 
    • Python - Django, Flask, FastAPI
    • Java - Spring
    • Ruby - Rails
    • Scala - Play
    • Javascript - Express(node.js)

크롤링이 여러 페이지를 이동하며 데이터를 수집한다면 스크레핑은 틍정 데이터를 수집하는 작업이다.

< Scraping & Crawling >

  • Scraping
    • 특정 데이터를 수집하는 작업 
  • Crawling
    • 웹서비스의 여러 페이지를 이동하며 데이터를 수집하는 작업
    • spider, web crawler, bot 용어 사용

 

 

[ 웹페이지의 종류 ]

  • 정적인 페이지 : 웹 브라우져에 화면이 한번 뜨면 이벤트에 의한 화면의 변경이 없는 페이지
  • 동적인 페이지 : 웹 브라우져에 화면이 뜨고 이벤트가 발생하면 서버에서 데이터를 가져와 화면을 변경하는 페이지

페이지가 정적인지 동적인지 알아보는 방법은 간단하다 화면에서 구성된 요소를 클릭했을 때 페이지가 새로고침된다면, 즉 페이지의 URL이 바뀐다면 동적인 페이지이고 새로고침 되지 않는다면 정적인 페이지 이다.

두 페이지 모두 HTML을 활용하지만 JSON의 경우 페이지의 한 부분이 다른 URL을 활용한 부분적인 요소로 들어간다.

python에서 데이터를 분석할 때에도 다음과 같은 차이가 있다.

우선 response하는 과정에서 돌아오는 타입이 각각 JSON과 HTML로 다르다.

다른 데이터 타입으로 인해 북석하는 방법에도 차이가 있다.

 

requests 이용

  • 받아오는 문자열에 따라 두가지 방법으로 구분
    • json 문자열로 받아서 파싱하는 방법 : 주로 동적 페이지 크롤링할때 사용
    • html 문자열로 받아서 파싱하는 방법 : 주로 정적 페이지 크롤링할때 사용

selenium 이용

  • 브라우져를 직접 열어서 데이터를 받는 방법

크롤링 방법에 따른 속도

  • requests json > requests html >>>> selenium

 

Web Scraping / Crawling

이번에는 동적페이지에서 Web Scraping / Crawling을 하는 방법에 대해 알아보자.

먼저는 scraping으로 하나의 데이터를 가져와보자.

국내 증시에 들어가 자료를 살펴본다.

우리가 알고싶은것은 현재 날짜에 따른 코스피 지를 알기 원한다.

 

F12를 눌러 개발자 도구를 활성화 시킨다.

 

화살표 표시를 선택하고 화면에서 원하는 곳을 클릭하 구성된 HTML파일의 요소를 바로 확인할 수 있다.

 

휴대폰 모양이 있는 칸을 눌러주면 다음과 같이 화면이 변한다.

이때 주소창을 클릭하여 Enter를 누르면 모바일 화면으로 볼 수 있다.

모바일 환경에서 국내에 코스피를 선택해준다. 그리고 스크롤을 내려 일별 시세를 확인하였다.

개발자툴에서 Network를 선택해준다. 그리고 Clear버튼으로 네트워크 기록을 지워준다.

 

그리고 하단에 더보기를 눌러주면 넷트워크 활동이 감지된다. 여기서 GET방식으로 Request를 하였고

Status Code도 200으로 정상적인 Response가 이루어 진것을 확인하였다.

그럼 우리는 Request URL을 복사하여 활용해 보도록 하자.

 

저 URL을 열어보면 다음과 같이 딕셔너리의 형태로 자료가 저장되어있는것을 확인할 수 있다.

jupyter lab / notebook을 활용하여 데이터를 확인해보겠다.

먼저 requests와 pandas를 import해준다.

import requests
import pandas as pd

 

url을 지정해준다.

이때 다음과 같이 f스트링을 활용하여 주석처리되어있는 코드를 활용해도 된다.

직접 페이지 사이즈와 페이지를 조정해 얻고싶은 데이터를 얻을 수 있다.

# page_size, page = 60 , 1
# url = f'https://m.stock.naver.com/api/index/KOSPI/price?pageSize={page_size}&page={page}'
url = 'https://m.stock.naver.com/api/index/KOSPI/price?pageSize=10&page=2'

 

다음과 같이 requsets.get 메서드를 활용하여 저장하고 출력하면 <Response [200]>라는 결과를 확인할 수 있다.

만약 문법이 낯설다면 shift+tab을 활용하여 문법을 확인할 수 있다.

#shift + tab 하면 문법 사용법이 나옴
# 200은 success
response = requests.get(url)
print(response)
<Response [200]>

 

데이터 타입을 살펴보면 다음과 같이 나온다.

type(response)
requests.models.Response

 

.json을 활용하여 리스트 또는 딕셔너리의 형태로 자료를 저장해주고 이를 pandas의 DataFrame을 사용해 DF으로

만들어 준 결과 다음과 같은 자료를 확인할 수 있다.

data = response.json()
pd.DataFrame(data)

 

그럼 이제 날짜별로 종가를 확인하는 DF을 만들고 마무리 하도록 하자.

전체코드는 다음과 같다.

url = 'https://m.stock.naver.com/api/index/KOSPI/price?pageSize=10&page=2'
response = requests.get(url)
data = response.json()
df = pd.DataFrame(data)
df = df[['localTradedAt', 'closePrice']]
display(df)

 

다른 예제를 한번 살펴보자

이번에는 원달러 환율 데이터를 수집해보겠다.

네이버 > 증권 > 시장지표 순으로 이동하고 F12를 눌러 개발자모드는 활성화해 모바일 버전으로 이동하였다.

Network에서 기록을 모두 지워주고 환율을 볼수있도록 미국 USD를 클릭한다.

 

들어간 페이지에서 아래로 내리면 '일별 시세'에 더보기란이 있다.

클릭하고 어떤 URL로 데이터를 request하는지 확인한다.

 

여기서 얻은 url을 request.get해주서 response되는지 확인해보자

import requests
import pandas as pd

page = 1
url = f'https://m.stock.naver.com/front-api/marketIndex/prices?category=exchange&reutersCode=FX_USDKRW&page={page}'
response = requests.get(url)
response
<Response [200]>

 

response code가 200임으로 잘 전달하고 전달 받았다는것을 확인 할 수 있다.

 

response.text를 찍어보면 우리가 이전에 봤던 text데이터와는 구조가 조금 다른것을 볼 수 있다.

url을 인터넷 주소에 찍어보니 다음과 같은 구성이였고, 우리가 원하는 'localTradedAt'와 'closePrice'는 result안에 있다.

 

이전에는 response전체에 .json을 해줬다면, 지금은 우리가 원하는 데이터를 갖고 있는 result만은 .json() 해준다.

그리고 'localTradedAt', 'closePrice'를 데이터프레임으로 만들어주고 확인한다.

data = response.json()['result']
df = pd.DataFrame(data)[['localTradedAt', 'closePrice' ]]
df

다음과 같이 일별 달러 환률을 확인할 수 있다.

 

<시각화>

이번에는 코스피, 코스닥, 달러 환률을 확인하여 시각화 하고 어떤 관계를 갖고 있는지 확인해보자.

import requests
import pandas as pd

def stock_price(code = 'KOSPI', page=1, page_size=60):
    url = f'https://m.stock.naver.com/api/index/{code}/price?pageSize={page_size}&page={page}'
    response = requests.get(url)
    data = response.json()
    return pd.DataFrame(data)[['localTradedAt', 'closePrice']]

def kosdaq_stock_price(code = 'KOSDAQ',page=1, page_size=60):
    url = f'https://m.stock.naver.com/api/index/{code}/price?pageSize={page_size}&page={page}'
    response = requests.get(url)
    data = response.json()
    return pd.DataFrame(data)[['localTradedAt', 'closePrice']]

def exchange_rate(code = 'FX_USDKRW', page = 1, page_size = 60):
    url = f'https://m.stock.naver.com/front-api/marketIndex/prices?category=exchange&reutersCode={code}&page={page}&pageSize={page_size}'
    response = requests.get(url)
    data = response.json()['result']
    return pd.DataFrame(data)[['localTradedAt', 'closePrice' ]]

#데이터 수집
page_size = 30
kp_df = stock_price('KOSPI', page_size = page_size)
kd_df = stock_price('KOSDAQ', page_size = page_size)
usd_df = exchange_rate(page_size=page_size)

#데이터 전처리
kp_df['closePrice'] = kp_df['closePrice'].apply(lambda data: float(data.replace(',', '')))
kd_df['closePrice'] = kd_df['closePrice'].apply(lambda data: float(data.replace(',', '')))
usd_df['closePrice'] = usd_df['closePrice'].apply(lambda data: float(data.replace(',', '')))

 

함수화를 통해 쉽게 호출하도록 하고 str형태로 저장되어 있는 데이터를 'lambda'를 활용하여 소수점을 없애고 float자료형으로 저장해준다.

 

<참고>

  • apply 뒤에 오는 함수에 넣어서 결과 출력 그리고 다시 덮어쓰기 
  • 함수를 lambda로 작성 (parameter : return code) / , 제거 및 float형으로 변경

이렇게 만들어진 데이터를 시각화 하여 확인해보자

import matplotlib.pyplot as plt

plt.figure(figsize=(20, 3))
plt.plot(kp_df['localTradedAt'], kp_df['closePrice'], label='kospi')
plt.plot(kd_df['localTradedAt'], kd_df['closePrice'], label='kosdaq')
plt.plot(usd_df['localTradedAt'], usd_df['closePrice'], label='usd')
plt.xticks(kp_df['localTradedAt'][::5])
plt.grid()
plt.legend()

plt.show()

이렇게만 보면 서로 어떤 관계를 갖고있느지 확인하기 어렵다.

그렇기 떄문에 데이터 스케일링을 통해 각각 자료의 가장 큰 값을1, 가장 작은값을 0으로 스케일링 해준다.

 

<데이터 스케일링>

  • min max scaling

 

수식으로 확인해보면 가장 큰값과 가장 작은값은 각가 1과 0이 될 수밖에 없는 구조이다.

파이썬에서는 sklearn.preprocessing안에 minmax_scale을 사용하면 쉽게 구현할 수 있다.

from sklearn.preprocessing import minmax_scale
minmax_scale(kp_df['closePrice'])
array([0.47509007, 0.34704066, 0.31930005, 0.30221307, 0.        ,
       0.0517756 , 0.11610911, 0.15908389, 0.31976325, 0.34704066,
       0.77848688, 0.86273803, 0.82830674, 0.76639218, 0.90818322,
       0.90519815, 0.95028307, 0.96922285, 1.        , 0.96634071,
       0.94318065, 0.82856408, 0.94626866, 0.6748842 , 0.55651055,
       0.54004117, 0.38630983, 0.22316006, 0.28327329, 0.04518785])

 

모든 값들이 0 <= x <=1임을 확인할 수 있다.

이를 그래프로 확인하면 스케일링을 통해 한눈에 파악하기 쉬워진다.

import matplotlib.pyplot as plt

plt.figure(figsize=(20, 3))
plt.plot(kp_df['localTradedAt'], minmax_scale(kp_df['closePrice']), label='kospi')
plt.plot(kd_df['localTradedAt'], minmax_scale(kd_df['closePrice']), label='kosdaq')
plt.plot(usd_df['localTradedAt'], minmax_scale(usd_df['closePrice']), label='usd')
plt.xticks(kp_df['localTradedAt'][::5])
plt.grid()
plt.legend()
plt.show()

 

<상관관계 분석>

상관계수를 통해 데이터 집합의 상관도를 분석해보자

  • 상관계수의 해석
    • -1에 가까울수록 서로 반대방향으로 움직임
    • 1에 가까울수록 서로 같은방향으로 움직임
    • 0에 가까울수록 두 데이터는 관계가 없음

데이터 전처리를 통해 코스피, 코스닥, 달러 환률을 하나의 DataFrame으로 만들어준다.

#데이터 전처리 merge
# kp_df
# kd_df
# usd_df

merge_df = pd.merge(kp_df, kd_df, on = 'localTradedAt')
merge_df = pd.merge(merge_df, usd_df, on = 'localTradedAt')
merge_df.columns = ['Date', 'Kospi', 'Kosdaq', 'Usd']
merge_df.head()

iloc를 사용하여 상관계수를 분석하면

merge_df.iloc[:, 1:].corr()

다음과 같은 상관계수를 확인할 수 있다.

그래프와 상관계수를 같이 보았을 때 다음과 같은 분석을 할 수 있다.

  • 원달러 환율이 높으면 달러를 원화로 환전하여 코스피 지수를 구매한다.
  • 원달러 환율이 낮으면 코스피지수를 판매하여 달러로 환전한다

 

 

이번 시간에는 Web과 Web Scraping / Crawling에 대한 개념과 요소, 간단한 예제를 살펴보았다.

이러한 방식을 사용하면 서버 비용을 N사에서 처리하게 되는데 N사에서는 제대로 서비스 하지 않는데 Server에서는

데이터를 처리했다고 요금을 청구한다. 이러한 방법을 사용하지 않고 API를 활용하여 Crawling하도록 하자.

'<프로그래밍> > [python-WebCrawling]' 카테고리의 다른 글

Web과 Web Scarping / Crawling 3(응용)  (0) 2024.09.24
Web Crawling5  (0) 2024.09.23
Web과 Web Scarping / Crawling 2(NAVER API)  (1) 2024.09.21

Python을 배우는 이유?

  • 컴퓨터의 CPU, RAM, SSD를 활용하기 위해서 학습
  • 컴퓨터와 의사소통을 잘 하기 위해서

<Python과 컴퓨터의기능>

  1. 변수선언 : RAM을 사용하는 문법 / 식별자(저장공간을 구분하는 문자열) : PEP20, PEP8(autopep8, flake8) - 코딩 작성 가이드
  2. 데이터타입 : RAM을 효율적으로 사용하기 위한 문법 : int, float, bool, str, list, tuple, dict, set
  3. 연산자 : CPU사용 문법 / 산술, 비교, 논리, 할당, 멤버 ...
  4. 조건문, 반복문 : if, elif, else / while, for, break, continue, range() ...
  5. 함수 : 반복 사용되는 코드를 묶어서 코드 작성 및 실행 : def, return, scope(global), lambda ...
  6. 클래스 : 변수, 함수를 묶어서 코드 작성 및 실행 / 객체지향 구현 - class, self, special methods(생성자)
  7. 모듈 : 변수, 함수, 클래스를 파일로 묶어서 코드를 작성 및 실행 - 확장자 .py
  8. 패키지 : 여러개의 모듈을 디렉토리로 묶어서 관리 : 버전정보 - import, from, as
  9. 입출력 : SSD를 사용하는 문법 - RAM(object) -> SSD(file) / SSD(file) -> SSD(file)RAM(object) : pickle

 

Class

  • 변수, 함수를 묶어서 코드를 작성 및 실행
  • 객체지향 구현 : 실제세계를 모델링하여 프로그램을 개발하는 개발 방법론 - 협업이 용이함
  • 함수 사용 : 함수선언(코드 작성) -> 함수호출(코드 실행)
  • 클래스 사용
    • 클래스 선언(코드 작성 : 설계도 작성) -> 객체 생성(메모리 사용 : 제품 생산) -> 메서드 실행(코드 실행 : 제품 사용)
    • 메서드 : 클래스 안에 선언되는 함수
    • 자동차 공장을 예시로 생각해보면 이해하기 편함
  • class 식별자 : UpperCamelCase, PascalCase(0), snake_case(X : 변수, 함수)

 

Class의 간단한 예제

# 1. 클래스 선언 :코드 작성
# 예금계좌 : Account : Balance, deposit(), withdraw(), transfer(), owner()


class Account2:
    def __init__(self, balance, name):
        self.balance = balance
        self.name = name
    
    def deposit(self, amount):
        if amount >= 0:
            self.balance += amount
        else:
            print("입금 금액은 양수여야됩니다.")
            
    def withdraw(self, amount):
        if amount >= 0:
            if self.balance >= amount:
                self.balance -= amount
            else:
                print("잔액부족")
        else:
            print("입금 금액은 양수여야됩니다.")
        
    def trasfer(self, target, amount):
        if amount >= 0:
            if self.balance >= amount:
                target.balance += amount
                self.balance -= amount
            else:
                print("잔액부족")
        else:
            print("송금 금액을 양수로 입력하세요")
            
    def owner(self):
        print(f'계좌의 주인 : {self}')

 

객체생성

# 2. 객체 생성 : 메모리 사용
acc1 = Account2(0, '민수')
acc2 = Account2(0, '서영')

 

함수들이 있는지 확인

# dir() : 객체안에 들어있는 변수, 함수 목록 출력
print(dir(acc1))
dir(acc1)[-3:]
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'balance', 'deposit', 'withdraw']
['balance', 'deposit', 'withdraw']

 

1000원씩 잔고를 설

acc1.balance = 1000
acc2.balance = 1000
print(acc1.balance, acc2.balance)
1000 1000

 

메서드를 실행해서 확인

# 3. 메서드 실행 : 코드 실행
acc1.deposit(1000)
acc2.withdraw(400)
print(acc1.balance, acc2.balance)
2000 600

 

나머지 메서드(송금과 주인확인) 실행

acc1.trasfer(acc2, 500)
print(acc1.balance, acc2.balance)
acc1.owner()
1000 1600
계좌의 주인 : 민수

 

일정 높이보다 낮으면 걸리는 림보문제이다.

다음과 같이 풀이하였다.

n = int(input())
rimbo = list(map(int, input().split()))
pass_check = True

for i in rimbo:
    if (i <= 160):
        print('I', i)
        pass_check = False
        break
    
if (pass_check == True):
    print('P')

'<코딩테스트> > [기타 문제-기초]' 카테고리의 다른 글

[012] 아이큐왕  (0) 2024.09.24
[010] 기억상실  (0) 2024.09.15
[007~009] 문제풀이  (0) 2024.09.15
[006]가장 큰 나머지  (0) 2024.09.15
[005] ID만들기  (1) 2024.09.15

A, B, N이 주어진다.

하루에 A만큼 기억하다 B만큼 까먹는다 할때 N까지 도달하기 위한 날을 구하는 문제다.

A, B, N = map(int, input().split())

result = 0
day = 0
while(True):
    day += 1
    result += A 
    if result >= N:
        break
    result-= B
    
print(day)

다음과 같이 단순하게 코딩을 해보았다.

그러나 여기서 수식을 대입하면 단순 반복이 아닌 효율적인 방법을 찾을것 같았다.

관계를 찾는다면 반복문보다 단순 계산이 훨씬 효율적일 것이다.

 

그래서 수식으로 관계를 살펴보았다.

A, B, N을 각각 7, 3, 25이라 했을 때 다음과 같은 결과를 얻는다.

 

즉 6일차에 목표를 달성하는 것이다.

밤은 배제하고 일만 봤을 때 우리는 다음과 같은 식을 만들 수 있다. 7+(n-1)(7-3) 

정리해보면 다음과 같은 수식이 성립하고 조건에 맞는 n을 찾는다면 우리는 쉽게 찾을 수 있다. 7+(n-1)(7-3) >= 25

(n-1)(7-3) >= 25-7 ------>  n >= (25-7) / (7-3) + 1  이다. 이를 기호로 나타내면 day >= (N-A) /(A-B) + 1 인 정수인 것이다.

 

그래서 다음과 같이 코딩하였다.

A, B, N = map(int, input().split())

if ((N-A)/(A-B) + 1) % 1 == 0: # 1로 나누었을때 나누어 떨어지는지 = 정수인지 판별
    day = int((N-A)/(A-B) + 1)
else:
    day = int((N-A)/(A-B) + 1) + 1

    
print(day)

'<코딩테스트> > [기타 문제-기초]' 카테고리의 다른 글

[012] 아이큐왕  (0) 2024.09.24
[011] 림보  (0) 2024.09.19
[007~009] 문제풀이  (0) 2024.09.15
[006]가장 큰 나머지  (0) 2024.09.15
[005] ID만들기  (1) 2024.09.15

[007]

숫자n을 입력받았을 때 범위를 지정해 등급을 출력하는 문제이다.

n = int(input())

if n>=200:
    print('A')
elif n>=180:
    print('B')
elif n>=150:
    print('C')
else:
    print('D')

조건문을 통해 범위를 지정하는것이 가장 효율적이라고 생각된다.

 

 

[008]

 

부모님의 키를 입력받으면 평균을 구해 자녀의 키를 예측하는 문제이다.

f, m = map(int, input().split())
print((f+m)//2)

 

소수점 아래는 버림으로 다음과 같이 간단하게 구할 수 있다

 

 

[009]

 

삼각형의 3각이 180이 되는지 확인하는 문제다

t1, t2, t3 = map(int, input().split())

if (t1 == 0) or (t2 == 0) or (t3 == 0):
    print('F')
else:
    if sum([t1, t2, t3]) == 180: #sum(iterable, start = n) 또는 sum(iterable)의 형태로 list로 묶어줘야 된다.
        print('P')
    else: 
        print('F')

 

'<코딩테스트> > [기타 문제-기초]' 카테고리의 다른 글

[011] 림보  (0) 2024.09.19
[010] 기억상실  (0) 2024.09.15
[006]가장 큰 나머지  (0) 2024.09.15
[005] ID만들기  (1) 2024.09.15
[004] 아스키코드  (1) 2024.09.12

숫자 n을 입력받고 수n, m으로 나눴을 때 더 큰 나머지를 출력하는 문제이다.

n = int(input())
print(max((n%5, n%7)))

n =5, m=7일때 정말 간단하게 풀이된다.

'<코딩테스트> > [기타 문제-기초]' 카테고리의 다른 글

[010] 기억상실  (0) 2024.09.15
[007~009] 문제풀이  (0) 2024.09.15
[005] ID만들기  (1) 2024.09.15
[004] 아스키코드  (1) 2024.09.12
[003] 8진수, 16진수  (0) 2024.09.11

+ Recent posts