[miniP]웹크롤링 실습1

mokyoungg·2020년 4월 12일
2

전체코드

import requests                       #웹 페이지의 HTML을 가져오는 모듈
from bs4 import BeautifulSoup         #HTML을 파싱하는 모듈

#웹 페이지를 가져온 뒤 BeautifulSoup 객체로 만듦
response = requests.get('https://www.koreabaseball.com/Record/Player/HitterBasic/Basic1.aspx')
soup = BeautifulSoup(response.content, 'html.parser')

table = soup.find('table', { 'class': 'tData01 tt' })  #<table class ="tData01 tt">을 찾음

data = []                            #데이터를 저장할 리스트 생성
for tr in table.find_all('tr'):     #모든 <tr> 태그를 찾아서 반복( 각 지점의 데이터를 가져옴)
    tds = list(tr.find_all('td'))   # 모든 <td> 태그를 찾아서 리스트로 만듦
                                    #(각 세부 기록을 리스트로 만듦)
    for td in tds:                 #<td> 태그 리스트 반복(각 세부 기록을 가져옴)
        if td.find('a'):       #<a> 태그 안에서 선수이름을 가져옴
            player = td.find('a').text       #<a> 태그 안에서 선수 이름을 가져옴
            bat_average = tds[3].text        #<td> 태그 리스트의 '네 번째'(인덱스 3)에서 타율을 가지고 온다.
            hit = tds[8].text                #<td> 태그 리스트의 '아홉번째'(인덱스 8)에서 안타를 가져온다.
            homerun = tds[11].text           #<td> 태그 리스트의 '열두번째'(인덱스 11)에서 홈런을 가져온다.
            RBI = tds[13].text               #<td> 태그 리스트의 '열네번째'(인덱스 13)에서 타점을 가져온다.
            data.append([player, bat_average, hit, homerun, RBI])   #dat 리스트에 선수, 타율, 안타를 추가

data  # data 표시.

with open('2019KBObatter.csv', 'w') as file:         # 2019KBObatter.csv. 파일을 쓰기 모드로 열기
    file.write('player,bat_average,hit,homerun,RBI\n')            # 컬럼 이름 추가
    for i in data:                                   # data를 반복하면서
        file.write('{0},{1},{2},{3},{4}\n'.format(i[0], i[1], i[2], i[3],i[4]))  #선수이름,타율,안타수를 줄 단위로 저장

#%matplotlib inline을 설정하면 matplotlib.pyplot의 show 함수를 호출하지 않아도
#주피터 노트북 안에서 그래프가 표시됨
%matplotlib inline
import pandas as pd          # 데이터를 저장하고 처리하는 패키지
import matplotlib as mpl     # 그래프를 그리는 패키지
import matplotlib.pyplot as plt   # 그래프를 그리는 패키지

# csv 파일을 읽어 DataFrame 객체로 만듦. 인덱스 컬럼은 player로 설정
df = pd.read_csv('2019KBObatter.csv', index_col='player', encoding='euc-kr')
df            # df 표시

# 상위 10명만 모아서 DataFrame 객체로 만듦
topb_df = df.loc[['양의지', '페르난데스', '박민우', '이정후', '강백호', '고종욱', '로하스', '박건우', '유한준', '채은성']]
topb_df   # topb_df 표시

모듈 가지고 오기

import requests                       #웹 페이지의 HTML을 가져오는 모듈
from bs4 import BeautifulSoup         #HTML을 파싱하는 모듈

#웹 페이지를 가져온 뒤 BeautifulSoup 객체로 만듦
response = requests.get('https://www.koreabaseball.com/Record/Player/HitterBasic/Basic1.aspx')
soup = BeautifulSoup(response.content, 'html.parser')

import로 모듈 가지고 오기

import requets

모듈이란 특정 기능을 .py 파일 단위로 작성한 것이다.
각종 변수, 함수, 클래스를 담고 있는 파일이다.

from bs4 import BeautifulSoup

bs4 모듈에서 BeautifulSoup 변수? 함수? 가지고 온다.?
bs4 패키지에서 BeautifulSoup 모듈을 가지고 온다?
코딩 도장에선 이러한 형태는 모듈에서 변수를 가지고 온다고 하는데
bs4가 패키지 아닌가?

reponse = requests.get('url')

url에 get 요청한다.

HTTP get에 대하여
https://hongsii.github.io/2017/08/02/what-is-the-difference-get-and-post/

HTTP는 웹상에서 클라이언트와 서버 간에 요청/응답으로 데이터를 주고 받을 수 있는 프로토콜이다.
클라이언트가 HTTP 프로토콜을 통해 서버에게 요청을 보내면 서버는 요청에 맞는 응답을 클라이언트에게 전송한다. 이 때, HTTP 요청에 포함되는 HTTP 메소드는 서버가요청을 수행하기 위해 해야할 행동을 표시하는 용도로 사용한다. 이 HTTP 메소드에는 GET과 POST가 있다.
GET은 서버로부터 정보를 조회하기 위해 설계된 메소드이다.(중략)

request와 response 객체의 개념 이해
https://wickedmagic.tistory.com/120

웹 서버와 클라이언트는 요청에 대한 응답으로 웹 서비스를 한다.
웹 서비스를 위한 클라이언트 웹 서버 사이의 요청에 관련된 정보는 request 객체에 저장되어 관리된다. 응답에 관련된 정보는 response 객체에 저장되고 관리된다.

url(서버?)에 get의 방법으로 정보를 요청하였다.(requests)
이에 서버 응답에 관련된 정보는 response 객체에 저장되었다.

soup = BeautifulSoup(reponse.content, 'html.parser')

BeautifulSoup(markup, 'html.parser')
https://www.crummy.com/software/BeautifulSoup/bs4/doc.ko/

BeaitifulSoup는 HTML과 XML 파일로부터 데이터를 뽑아내기 위한 파이썬 라이브러리이다.
문서를 해석하려면, 문서를 BeautifulSoup 구성자에게 건네주자.(soup 만들기)
'html.parser'는 파이썬의 내장 HTML 해석기이다.

마크업 언어란?
문서가 화면에 표시되는 형식을 나타내거나 데이터의 논리적인 구조를 명시하기 위한 규칙들을 정의한 언어의 일종이다. 데이터를 기술한 언어라는 점에서 프로그래밍 언어와는 차이가 있다.

파이썬 라이브러리 BeautifulSoup를 활용하여 response 객체에 저장된 정보를 html.parser 해석기로 해석한다.

웹에서 정보 가져오기

table = soup.find('table', { 'class': 'tData01 tt' })  #<table class ="tData01 tt">을 찾음

data = []                            #데이터를 저장할 리스트 생성
for tr in table.find_all('tr'):     #모든 <tr> 태그를 찾아서 반복( 각 지점의 데이터를 가져옴)
    tds = list(tr.find_all('td'))   # 모든 <td> 태그를 찾아서 리스트로 만듦
                                    #(각 세부 기록을 리스트로 만듦)
    for td in tds:                 #<td> 태그 리스트 반복(각 세부 기록을 가져옴)
        if td.find('a'):       #<a> 태그 안에서 선수이름을 가져옴
            player = td.find('a').text       #<a> 태그 안에서 선수 이름을 가져옴
            bat_average = tds[3].text        #<td> 태그 리스트의 '네 번째'(인덱스 3)에서 타율을 가지고 온다.
            hit = tds[8].text                #<td> 태그 리스트의 '아홉번째'(인덱스 8)에서 안타를 가져온다.
            homerun = tds[11].text           #<td> 태그 리스트의 '열두번째'(인덱스 11)에서 홈런을 가져온다.
            RBI = tds[13].text               #<td> 태그 리스트의 '열네번째'(인덱스 13)에서 타점을 가져온다.
            data.append([player, bat_average, hit, homerun, RBI])   #dat 리스트에 선수, 타율, 안타를 추가

data  # data 표시.

table = soup.find('table', { 'class': tData01 tt' })

해석본(soup)에서 find 함수를 활용하여 url에서 가지고 온 정보 중 table 태그 안에 tData01 tt라는 이름을 클래스를 찾는다. 그리고 이를 table 객체에 할당한다(?)

data = []

데이터를 저장한 리스트를 생성한다.

for tr in table.find_all('tr):
	tds = list(tr.find_all('td'))

table의 모든 tr 태그를 찾아서 꺼내는 것을 반복한다.
여기서는 tr 태그에 양의지, 이정후와 같은 선수별 데이터가 들어있고
반복할 때마다 선수들의 각 데이터를 가져온다.

그리고 tr에서 찾은 모든 td 태그를 리스트로 만들고 tds에 할당한다.

크롤링하는 페이지의 데이터 표시 형태가 선수목록(선수별)(세로줄, td)에 양의지 타율 홈런 안타수 1루타 2루타... 의 형태(가로줄, tr)로 되어있는데 모든 tr 태그를 가지고 와서 양의지를 비롯한 선수별로 세부 데이터를 가지고 온 뒤 각 세부 데이터(td에 되어있음)를 가지고 옴 즉, table의 모든 데이터를 가지고 온다.

tr 태그 : table row 약자로, 가로줄을 만드는 역할을 한다.
td 태그 : table data 약자로, 셀을 만드는 역할을 한다.

for 문의 기본 구조
for 변수 in range(횟수) 또는 리스트(또는 튜플, 문자열):
반복할 코드

for td in tds:
    if td.find('a'):
    player = td.find('a').text
    bat_average = tds[3].text
    hit = tds[8].text
    homerun = tds[11].text
    RBI = tds[13].text
    data.append([player, bat_average, hit, homerun, RBI])
data

반복문 안에 반복문이 있는 형태로, 함께 진행되는 반복문이다.
tr에서 찾은 모든 td의 리스트인 tds에서 td를 꺼내보는 일을 반복한다.

만약 꺼내본 td에서 a태그를 찾는다면(if문) a 태그의 text 속성에서 선수 이름을 가져온다. (크롤링의 대상이 되는 웹페이지에서, 선수이름은 a태그로 되어있음)
타율(bat)은 리스트의 4번째(3번 인덱스), 안타(hit)은 9번째(8번 인덱스), 홈런(homerun)은 12번째(11번 인덱스), 타점(RBI)는 14번째(13번 인덱스)에서 각 텍스트를 가져오며 텍스트가 나타내는 정보에 맞는 이름으로 변수를 만들고 할당한다.(?)

이는 모든 데이터가 아닌 필요한 데이터가 가지고 오기 위해 쓰인 코드이다.
(각종 데이터가 너무 많아서 인덱스를 통해 필요한 데이터만 찾음)

text 속성
<태그>텍스트</태그>에서 태그 안에 있는 텍스트를 가지고 온다.

필요한 값을 가져온 뒤 data 리스트 안에 [player, bat_average, homerun, RBI]처럼 리스트 형태로 추가하여 data를 표시한다.

append 리스트에 요소추가
https://wikidocs.net/14

append(x)는 리스트의 맨 마지막에 x를 추가하는 함수다.

>>> a = [1, 2, 3]
>>> a.append(4)
>>> a
[1, 2, 3, 4]

csv 파일로 저장하기

with open('2019KBObatter.csv', 'w') as file:         # 2019KBObatter.csv. 파일을 쓰기 모드로 열기
    file.write('player,bat_average,hit,homerun,RBI\n')            # 컬럼 이름 추가
    for i in data:                                   # data를 반복하면서
        file.write('{0},{1},{2},{3},{4}\n'.format(i[0], i[1], i[2], i[3],i[4]))  #선수이름,타율,안타수를 줄 단위로 저장

with ... as

(wiht open(파일경로, 모드) as 파일객체
https://devpouch.tistory.com/79

파일 스트림을 다루는데 있어서 파이썬에서 제공하는 강력한 기능이다.
파일을 다루는 처리를 할 때는 필수적으로 파일 오픈과 파일 닫기 과정을 거치게 된다.
코드가 복잡해지면 개발자는 파일을 열어놓고 닫지 않는 실수를 할 경우가 생긴다.
with... as구문을 사용하게 되면 파일을 열고 해당 구문이 끝나면 자동으로 닫히게 한다.

파일열기 모드
https://wikidocs.net/26

  • r : 읽기모드, 파일을 읽기만 할 때 사용
  • w : 쓰기모드, 파일에 내용을 쓸 때 사용
  • a : 추가모드, 파일의 마지막에 새로운 내용을 추가할 때 사용한다.

아무튼 코드를 실행하면 프로젝트 폴더(이전에 지정한 저장 장소)에 2019KBObatter.csv 파일이 생성된다고 하는데 왜인지는 모르겠다. 주피터 노트북에서 저장 경로를 설정하긴 했는데, 이 코드 자체에서 저장 명령을 할 수 있는건지는 모르겠다.

file.write(데이터\n)
https://soooprmx.com/archives/9143

주어진 데이터를 파일에 쓴다. 이 동작을 하기 위해서는 파일을 쓰기 모드로 열어야 한다.

앞서 쓰기 모드로 연 파일에 player, hit... 등의 데이터를 쓰고 \n(개행, 줄바꿈) 한다.
아마 이는 player에 들어가있는 데이터를 모두 쓴다는 얘기는 아니고 각 세로열의 제목 역할(?)을 하는 것 같다.

for in data:
	file.write('{0},{1},{2},{3},{4}\n'.format(i[0], i[1], i[2], i[3],i[4]))

개행된 이후, data를 반복하며 줄 단위로 저장한다?

문자열.format()
https://programmers.co.kr/learn/courses/2/lessons/63

문자열의 대괄호 자리에 format 뒤의 괄호안에 있는 값을 하나씩 넣는다.
문자열에 포함된 대괄호 개수보다 format 안에 들어있는 값의 수가 많으면 정상 작동한다.

number = 20
welcome = '환영합니다'
base = '{} 번 손님 {}'

#아래 3개의 print는 같은 값을 출력
print(number,'번 손님',welcome)
print(base.format(number,welcome))
print('{} 번 손님 {}'.format(number,welcome))
#=>20 번 손님 환영합니다

data 리스트의 모습은 아마 이런 형태일 것이다.
data = ['선수이름', '타율', '안타', '홈런', '타점'] 처럼 생긴 리스트에 각 인데스를(0에서 4, 여기선 아마 해당하는 숫자들) 집어넣는 코드인듯.
근데 이 {0},{1},{2}.. 가 뭔지 모르겠다.

가지고 온 데이터를 그림?그래프?표?로 표현하기

#%matplotlib inline을 설정하면 matplotlib.pyplot의 show 함수를 호출하지 않아도
#주피터 노트북 안에서 그래프가 표시됨
%matplotlib inline
import pandas as pd          # 데이터를 저장하고 처리하는 패키지
import matplotlib as mpl     # 그래프를 그리는 패키지
import matplotlib.pyplot as plt   # 그래프를 그리는 패키지

# csv 파일을 읽어 DataFrame 객체로 만듦. 인덱스 컬럼은 player로 설정
df = pd.read_csv('2019KBObatter.csv', index_col='player', encoding='euc-kr')
df            # df 표시

%matplotlib inline 을 설정하면 matplotlib.pyplot의 show 함수를 호출하지 않아도
주피터 노트북 안에서 그래프가 표시된다고 하는데 솔직히 무슨말인지 모르겠다.

import 모듈 as 땡떙
모듈을 가지고 오는데 이름을 땡떙으로 가져온다.

pandas
https://doorbw.tistory.com/172

pandas는 파이썬에서 사용하는 데이터분석 라이브러리로, 행과 열로 이루어진 데이터 객체를 만들어 다룰 수 있게 하여 보다 안정적으로 대용량의 데이터를 처리하는데 매우 편리한 도구이다.
pandas는 기본적으로 정의되는 자류구조인 Series와 Data Frame을 사용한다.

Matplotlib와 pyplot
pyplot https://kongdols-room.tistory.com/72

matplotlib는 전체를 아우르는 패키지이다.
pyplot은 matplotlib에서 지원하는 모듈 중 하나다.
pyplot은 사용환경 인터페이스를 제공한다.
인터페이스는 겉으로는 드러나지 않으며 자동으로 figure와 axws를 생성하며 정의된 플롯을 얻을 수 있도록 도와준다. 무슨 말이지?

df = pd.read_csv(''2019KBObatter.csv', index_col='player', encoding='euc-kr')
df

2019..csv 파일을 읽어 DataFrame 객체로 만든다.
인덱스 칼럼은 player로 설정하고 한글을 읽기위해 인코딩한다.

index_col
https://datascienceschool.net/view-notebook/c5ccddd6716042ee8be3e5436081778b/

테이블 내의 특정한 열을 행 인덱스로 지정하고 싶으면 index_col

원하는 정보만 가져오기

# 상위 10명만 모아서 DataFrame 객체로 만듦
topb_df = df.loc[['양의지', '페르난데스', '박민우', '이정후', '강백호', '고종욱', '로하스', '박건우', '유한준', '채은성']]
topb_df   # topb_df 표시

선수들이 너무 많아 상위 10명만 보여주고 싶어 사용하였다.

pandas dataFrame의 loc
http://yeyej.blogspot.com/2016/02/pandas-dataframe-iloc-loc-ix.html

.loc는 label을 통해 값을 찾을 수 있다.

이 라벨이 언제 지정된거지? index_col로 된건가..

아무튼 이렇게 되었다. 코딩도장의 예제를 조금 변형시켜 실습하였다.
KBO홈페이지에서 정보를 가져와 상위 타자 10명의 데이터를 정리하였다.
이렇게 따라하고 만들었는데 깃허브에 어떻게 올려야할지 모르겠다.
py파일이나 pynb? 파일로 깃허브에 올리면 해당 주소로 들어갔을 때 그냥 파일이 다운받아진다. 만들어진 결과물을 어떻게 인터넷 페이지로 볼 수 있는지 찾아봤는데 모르겠다. 내 정보 검색 능력이 부족한 것 같다. 주피터노트북에서 작성한 코드를 py나 pynb가 아닌 html 파일로 다운 받아 깃허브에 업로드하였다. 해당 페이지에 들어가보니 정리한 정보만 나오는게 아니고 주피터노트북에서 작성한 모든 코드들이 나온다. 어떻게 해야할지 모르겠다.
파이썬이나 자바스크립트나 모르는게 너무 많은데 더 큰 문제는 뭘 모르는지 모르는 것이다. 부끄럽다. 뇌를 한 번 세척하고 싶다.

profile
생경하다.

1개의 댓글

comment-user-thumbnail
2020년 4월 14일

굉장히 이해하기 쉽게 리뷰하셨네요 !! 덕분에 배우고 갑니다 ㅎㅎ

답글 달기