본문 바로가기

파이썬 & 머신러닝과 딥러닝

10일차 - 웹스크래핑 기법 학습 및 이를 기반으로 한 미니프로젝트 수행

웹데이터 수집

이때까지는 가져온 데이터로 실습했는데 확보하기 힘들어 데이터를 하나하나 입력을 해야하는 경우

그때 사용할 수 있는 방식이 웹스크래핑

웹스크래핑 : 조직적이고 자동화된 방법으로 웹사이트들을 탐색해서 원하는 정보를 획득하기

(웹크롤링이라고 할 수 없음)

 

웹 스크래핑을 위해 필요한 패키지

requests 패키지 - 웹페이지 다운로드

beautifulsoup 패키지 - 웹페이지 구조 분석

pandas 패키지 - 데이터 저장, 처리 및 분석

 

Web이 어떻게 동작?

web은 web server가 필요

각각의 web server에는 홈페이지에 관한 정보를 파일로 갖고 있음 (준비된 상태)

내 pc(client)에 web browser이 있고 주소창에 주소를 쳐서 웹페이지에 가서 원하는 페이지를 요청함 -> request

요청이 정상적이면 웹페이지는 파일을 내 PC에 전달해줌 -> responce

이 과정을 반복

-> 웹페이지의 정보를 스크래핑 하기 위해서는 web서버에서 오는 responce에 있는 파일 분석이 필요

 

주소창에 https://www.naver.com 주소 치고 엔터

: 네이버 서버에 첫번째 페이지를 보고싶다고 request를 보낸 것

 

 

웹페이지 구성 요소

HTML : 웹페이지의 골격 구조 및 콘텐트

CSS : 웹페이지 디자인

JAVA Script : 웹페이지의 동적 동작

 

주요 웹스크래핑 대상

웹페이지에서 주요 데이터 정보를 표현하고 있는 HTML이 웹스크랩핑의 대상이 되는 경우가 대부분

 

HTML(HyperText Markup Language)

웹페이지를 위한 지배적인 마크업 언어, 구조적 문서를 만들 수 있는 방법 제공

  • 하이퍼텍스트 : 서로 연결되어 있는 링크를 통해 읽는 순서가 결정되어 있지 않는 형태로 한 문서에서 다른 문서로 접근할 수 있는 텍스트
  • 마크업 언어 : 태그(문서의 구조를 표현하는 역할)를 이용해 문서와 데이터의 구조를 표시하는 언어

 

HTML 기본 구조

- 가장 기본 구조

<html> 태그를 사용해 <html> 과 </html> 사이의 모든 텍스트가 HTML 이라고 선언됨

(태그는 대소문자 구분 x)

 

- HTML은 크게 두 부분 head와 body로 구분됨

  • head : 타이틀이나, 메타데이터등의 웹페이지 자체 정보를 담고 있음
  • body : 주요 웹페이지 컨텐트를 담고 있음, 주요 크롤링 대상

<HTML> <HEAD> </HEAD> <BODY> </BODY> <HTML>

 

 

주요 HTML 태그

<P> : 본문 태그, 문단 태그

<BP> : 줄바꾸기

<HR> : 수평줄삽입

: 하이퍼링크 태그 영남대학교

<UL>, <LI> : 글머리 기호 목록 생성, 목록 요소 생성

<OL>,<LI> : 순서 번호 목록 생성, 목록 요소 생성 

<DIV> : 공간 분할 태그

 

 

위치에 따른 Tag 관계

  • Parent : 다른 태그를 포함하고 있는 태그 
  • Child : 다른 태그 내부에 있는 태그
  • Sibling

 

HTML Tag의 ID, Class 속성

  • 웹 브라우저 화면에는 변화가 없음
  • HTML 각 태그에 특별한 속성이 ID 와 CLASS 속성을 부여할 수 있다.
  • CSS 등에서 특정 태그를 지칭하기 위해 주로 활용됨
  • 웹스크래핑에도 특정 태그를 지칭하기 위해 자주 활용됨
  • ID : 중복될 수 없음

 


 

웹페이지 다운로드

requests 패키지 : 다양한 형태의 request를 웹서버에 보내 웹페이지를 다운로드한다.

get, post, put, delete 를 할 수 있다.

r = request.get("http://www.yu.ac.kr")

 

다운로드 성공 여부 확인

status_code 속성으로 확인 가능

 

요청 후 다운로드 상태 확인

항상 정상적으로 웹 요청이 끝나고 난 후 다음 단계로 진행해야한다.

 


 

웹스크래핑 기초 실습

영문으로 나온 모습

 

접속한 페이지가

영문을 사용한 웹브라우저에서 접속하면 영문으로 나오고

한글을 사용한 웹브라우저에서 접속하면 한글로 나오는 페이지임

-> /index.do 가 붙은 한글페이지로 url 수정하여 한글페이지에 직접 접근

 

한글로 request 응답 받음

 

 

BeautifulSoup 사용해보기

text를 html구조에 맞춰서 파싱 -> beutifulsoup 사용

from bs4 import BeautifulSoup 선언

 

 

soup이 root이고 자식들 출력해보기

 

html코드만 저장

 

p태그 검색

 

p태그 중에서 mini-title만 찾기


 

YU 뉴스룸 제목 정보 모아보기

페이지 우클릭 - 검사 -> element에서 html 소스 코드를 볼 수 있다.

왼쪽 상단 화살표 아이콘 선택시 검사할 페이지 요소 선택 가능

 

import requests 선언

문자 형태로 잘 출력되는 모습

 

byte 형태로 잘 출력되는 모습

 

 

BeautifulSoup : html 코드로 분석해서 text를 html구조에 맞춰서 파싱 

p태그만 출력한 모습

 

 

yu뉴스룸 구분해보기

-> class_ = mini-title이 적용된 p태그 정보 출력

mini-title이 적용된 정보 출력

 

 


 

CSS Selector (CSS 선택자)

css가 없어지면 html이 남아있어도 일렬로 전부 출력됨

css : html의 특정 부분을 선택해 어디에 넣어줘야할 지 결정

 

 

p태그 중에서 mini-title 클래스를 가진 것만 선택 -> select() 

mini-title 클래스를 가진 것만 출력

 

6개의 기사 이름 출력

-> '.' 은 클래스를 나타냄

 


 

텍스트만 가져와 리스트 만들기

get_text() 사용

리스트 형태로 문자열 내용만 가져온 모습

 

위 처럼 for문을 사용하지 않고 문자열만 가져와보기

한줄로도 리스트를 만들 수 있다.

 


 

날짜도 제목과 같이 가져와보기

첫번째 뉴스 기사에 대한 정보가 잘 출력되는 모습

 

제목과 날짜 동시에 구할 수 있다.

 

 

날짜와 제목을 튜플로 저장

제목과 날짜까지 리스트로 출력

 

 


 

Pandas의 DataFrame으로 만들어 저장

 


 

1. 컬럼인덱스를 '제목', '게시날짜' 로 변경

 

 


 

2. 정보를 수집한 날짜를 추가해보기

 

today() : 현재 시간을 저장

strftime('%Y-%m-%d') : 날짜만 가져오기

 


주기적 프로그램 실행

● crontab : 리눅스나 유닉스 서버에서 주기적으로 프로그램을 실행 시킬 때 사용

 

csv 파일 형태로 잘 저장된 모습

 


 

뉴스 이미지 파일 다운로드 하기

각각의 기사마다 이미지들도 다운받아 저장해보기

이미지의 위치 : .news-img-box 클래스 안에 있는 div태그에 포함

이미지 파일 자체를 Pandas에 넣을 수는 없다. 별도의 폴더에 저장

 

1. 이미지 주소 확보

 

이미지 태그 속성값 중 / 다음만 가져오기

 

원하는 이미지만 선택해서 주소 확보한 모습

 

전체이미지 주소 한 번에 구하기

전체 이미지 주소를 가져온 모습


 

2. 이미지 다운로드

 

plt.show로 이미지가 정상 출력된 모습

 

이미지가 정상적으로 다운로드 완료된 모습

 

현재 None인 이미지 파일명 지정해줘야함

 

 

현재 시간을 활용해 이미지 파일명 설정

정상적으로 다운로드 완료 후 이미지 파일명이 설정된 모습

 

 


 

기상데이터 자동 다운로드

 

주소창에 넣어서 전달하는 방식 : get

뒤에서 안보이게 전달하는 방식 : post

 

 

기상자료개방포털 url 가져오기 

 

가져올 url

 

csv 다운로드를 눌렀을때 payload

payload 전체 복붙하기

 

 

 

전달 받은 payload로 다시 출력 

데이터가 잘 출력되는 모습

 

이 데이터를 파일로 저장

 

test_temp.csv가 잘 출력되는 모습

 

 


 

payload 바꾸기

: 1990에서 2000으로 시작일 바꿔보기

 

 

test_temp2.csv 바뀐 시작일로 잘 출력되는 모습

 

 


 

payload를 변경하는 함수 만들어보기

-> 원하는 정보를 갖기 위해 payload의 특정 부분을 바꾸면 된다.

 

원하는 값 입력시 payload가 잘 변경되는 모습

 

원하는 날짜의 기상데이터 다운받기

 

대구2022010120231231.csv 가 입력한 데이터로 잘 출력된 모습

 

 

 

 


 

 

미니프로젝트

● 기상청 데이터를 처리해서 출력해주는 프로그램을 작성

● 지점, 지점이름, 시작날짜, 끝날짜를 입력받으면 자동으로 기상청 데이터를 다운받고

● 각 월별 최고, 최저 기온을 출력하는 프로그램을 작성하시오.

● 추가로, 각 월별 최고, 최저 기온을 그래프로도 출력하시오.

 

stnID 아는 법 : 페이지 우클릭 소스코드 보기

 

 

결과

잘 출력되는 모습

 

 

최종코드

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

#폰트적용
plt.rcParams['font.family'] = 'New Gulim'
plt.rcParams['font.size'] = 14
plt. rcParams['axes.unicode_minus'] = False # '-'나오는 거 설정

import requests

def genPayload(stnID, txtStnNm, startDay, endDay): # payload 변경
    payload = {
    
'fileType': 'csv',
'pgmNo': 70,
'menuNo': 432,
'serviceSe': 'F00101',
'stdrMg': 99999,
'startDt': startDay, # 시작하는 년도월일
'endDt': endDay, # 끝나는 년도월일
'taElement': 'MIN',
'taElement': 'AVG',
'taElement': 'MAX',
'stnGroupSns': '' ,
'selectType': 1,
'mddlClssCd': 'SFC01',
'dataFormCd': 'F00501',
'dataTypeCd': 'standard',
'startDay': startDay,
'startYear': 2000,
'endDay': endDay,
'endYear': 2024,
'startMonth': '01',
'endMonth': 12,
'sesnCd': 0,
'txtStnNm': txtStnNm, # 지점정보에 해당하는 문자열
'stnId': stnID, # 지점정보
'areaId': '',
'gFontSize': ''
    
    }
    return payload
    
 # 원하는 날짜의 기상데이터 다운받기
def downloadTempCSV(stdID, txtStnNm, startDay, endDay):
    url = 'https://data.kma.go.kr/stcs/grnd/downloadGrndTaList.do'
    payload = genPayload(stdID, txtStnNm, startDay, endDay)
    filename = txtStnNm + str(startDay) + str(endDay) + ".csv"
    r = requests.post(url, data=payload)
    if r.status_code == 200:
        with open(filename, 'wb') as f:
            f.write(r.content)
        print(filename + "다운로드 완료!")
        return filename # 파일 이름 리턴하도록
    else:
        print(filename + "다운로드 실패!")
        return None
   
def getMaxMinTempPerMonth(csv_filename):
    df = pd.read_csv(csv_filename, skiprows =7, encoding='ms949', index_col = '날짜',
                    parse_dates=['날짜'])
    max_temp = df['최고기온(℃)'].to_period('M').groupby('날짜').max() # 최고기온(℃) 그룹화
    min_temp = df['최저기온(℃)'].to_period('M').groupby('날짜').min() # 최저기온(℃) 그룹화
    
    result = pd.merge(max_temp, min_temp, on='날짜') # 날짜를 기준으로 합병
    return result
 
def main():
    while True:
        stnID = input('지점 ID를 입력하세요(0:종료) :')
        if stnID == '0':
            break
        stnname = input('지점 이름 : ')
        startDay = input('시작 날짜  : ')
        endDay = input('끝 날짜 : ')
        
        filename = downloadTempCSV(stnID, stnname, startDay, endDay)
        if filename: # 파일이름 참인 경우
            result = getMaxMinTempPerMonth(filename)
            print(result)
            result.plot()
            plt.show()
        else:
            print("입력값 에러")