인터넷 상에서 데이터 활용 방법

인터넷 상에는 수많은 데이터 정보가 있다. 그리고 공개된 데이터들 중에서 아래와 같이 다양한 형식들을 읽어볼 수 있다.

  • URL 상의 CSV
    • CSV 파일을 local에 다운로드 받은 후에 pandas.read_csv(url)를 사용하여 읽어볼 수 있다. 또는 URL을 알고 있다면 지정해서 바로 넘겨줄 수도 있다.
  • 크롤링을 사용하여 읽기
    • 가공되지 않은 데이터를 처리할 때는 크롤링을 사용해야 한다.
    • 파이썬의 requests/Selenium 라이브러리를 통해 처리한다.
    • pandas.read_html(url, ...)
      1. 사이트에 따라 URL을 제공하는 경우 내부적으로 requests 라이브러리 활용하여 특정 웹 페이지의 dataframe을 반환받을 수 있다.
    • pandas-datareader 라이브러리(0.7.0 버전 기준)
      1. 내부적으로 requests 라이브러리 활용
      2. 지원 데이터 소스: FRED, Nasdaq, Yahoo Finance, Google Finance 등을 손쉽게 활용 가능하다. 하지만 정식 API가 아닌 크롤링을 사용하는 것이기에 버전에 따라 오류가 발생할 수 있다.
    • seaborn 라이브러리
      1. 데이터 시각화를 도와주는 그래픽 차트 라이브러리, 예제용 데이터 제공
      2. https://github.com/mwaskom/seaborn-data

 

주식 종목코드(KOSPI, KOSDAQ) 가져오기

환경 설정을 위해 인터넷 상의 데이터를 가져와야 하므로, 한국 거래소(KRX)로 접속한다.

한국 거래소(KRX) 상장회사검색

[시장정보] -> [상장현황] -> KOSPI 및 KOSDAQ을 클릭하여 조회 -> CSV 파일을 로컬 디렉터리에 저장한다.

 

pandas.read_csv 활용

import pandas as pd

df = pd.read_csv('data/kospi.csv', error_bad_lines=False)
df

CSV 파일을 읽어보니 ParserError가 발생하여 'error_bad_lines=False'로 막아놨다. 위의 코드를 쥬피터 노트북에 넣으면 코스피에 상장된 기업명과 업종코드들을 차트로 확인할 수 있다.

 

import pandas as pd

df = pd.read_csv('data/kospi.csv', error_bad_lines=False)
print(df.shape)
df.head()

위의 예제 코드를 일반적인 Pandas의 Dataframe 활용 예제로 바꿔본 결과이다. 801행, 12열의 테이블임을 확인할 수 있고 'head'를 사용함으로써 상위 5개 행을 볼 수 있다.

 

import pandas as pd

df = pd.read_csv('data/kospi.csv', index_col='기업명',error_bad_lines=False)
print(df.shape)
df.head()

이전의 예제 코드에서는 행의 인덱스가 0부터 1까지 순차적인 일련번호로 구성되어 있었다. 이때 인덱스를 정의할 수 있는데 pd.read_csv를 사용할 때 'index_col'을 전달 인자로 넣으면 지정된 열이 기준이 되어 테이블이 표시된다.

 

df.loc['AK홀딩스']

loc 인덱서를 활용하여 'AK홀딩스'를 출력한 결과이다. 종목코드, 업종코드, 업종 등 'AK홀딩스'와 관련된 정보들만 확인 가능하다. 이와 같이 pd.read_csv를 사용하여 코스피나 코스닥에 대한 원하는 기업명의 정보들을 효율적으로 손쉽게 검색 가능하다.

 

pandas.read_html

HTML 문자열로부터 문자열 내에 테이블 태그(Tag)가 있다면 태그 내용을 수집(parsing)해서 dataframe화 시켜준다.

  • 웹페이지 크롤링을 쉽게 도와주는 라이브러리
  • 위의 기능을 활용 불가능한 곳도 있기 때문에 만능은 아니다.
    1. 활용 가능 여부를 판단하기 위한 기준은 해당 페이지 상에서 우클릭 후 페이지 소스 보기를 통해 찾고자 하는 데이터에 대한 테이블 태그가 있는지 확인을 해야 한다.
    2. 하지만 해당 페이지에 DDoS(Distributed Denial of Service) mitigration이 적용되어 있을 경우 동작이 어렵다.
  • 웹 페이지 상의 HTML 테이블을 한 번에 로딩하기 위한 목적이다.
  • HTML 테이블에 데이터 외에 다른 문자열이 있을 경우에 곤란하다.
    1. 직접 크롤링하는 것이 간편하다.

 

df = pd.read_html('https://astro.kasi.re.kr/life/pageView/5')
df

한국천문연구원 천문지식정보의 월별 음양력 웹 페이지를 import하여 하나의 Dataframe으로 읽는다. 그런데 결과를 보면 한글이 깨져서 이상한 출력 결과를 나타낸다. 한글이 깨진 부분이 인코딩 부분이기 때문에 올바른 출력을 위해서는 웹페이지 import 시에 인코딩 조건이 필요하다.

 

df_list = pd.read_html('https://astro.kasi.re.kr/life/pageView/5', encoding='utf-8')
df_list

import 인코딩 조건으로 'utf-8'을 전달 인자로 넣어주면 한글이 깨지지 않고 올바르게 출력된 것을 확인할 수 있다. 하지만 모양이 Dataframe 형태가 아니다. 그 이유는 리스트 안에 Dataframe이 포함되었기 때문이다. 위와 같이 리스트 안에 포함된 이유는 웹 페이지 상에는 리스트 안에 여러 개의 Dataframe을 넣어 구성된 경우가 있을 수 있기 때문이다. 따라서 list가 반환되었다.

 

len(df_list)

df = df_list[0]

print(df.shape)
df.head()

리스트의 길이를 구해보면 1이다. 따라서 리스트에 dataframe이 한 개 들어있다는 것을 확인할 수 있다. 리스트의 인덱스를 활용하여 0번 인덱스를 df 참조 변수에 넣고 출력해보면 dataframe 형태로 읽혀 지는 것을 손쉽게 확인 가능하다.

 

pandas-datareader

Pandas에 있는 별도의 third party 라이브러리이다. 이를 사용하기 위해서는 라이브러리를 설치해야 하며 이미 설치되어 있더라도 업데이트를 해주어야 한다. 그 이유는 위의 라이브러리는 버전 업데이트가 빠른 외부 API 및 외부 웹 페이지를 크롤링하기 때문이다.

  • 최신 버전의 pandas-datareader 업데이트는 필수이다.
    1. 명령 프롬프트 또는 Windows PowerShell을 실행한다.
    2. pip install --upgrade pandas-datareader

 

위의 라이브러리를 사용하기 전에 우선 활용할 데이터 종목을 알아본다. Yahoo finance를 사용하여 증권 정보를 가져올 것이고, 해당 정보는 Yahoo finance(https://finance.yahoo.com/)에서 확인 가능하다.

위의 사이트에서 볼 수 있듯이, 삼성전자의 종목 코드는 005930.KS임을 확인할 수 있다. 알아낸 증권 정보를 활용하여 위의 라이브러리를 사용한다.

 

import pandas_datareader as pdr

pdr.get_data_yahoo('005930.KS', '2020-03-01')

datareader를 통한 증권 정보를 import한다. 그리고 Yahoo finance에서 확인한 종목코드 및 검색하고 싶은 기간을 지정한다. 위의 출력 결과를 보면 2020-03-01은 휴일이라 데이터가 없는 것을 확인할 수 있고 주식 거래가 이루어진 날들의 데이터 수집 결과들을 손쉽게 얻어올 수 있다.

 

결론

Pandas를 사용하여 인터넷 상의 데이터를 손쉽게 수집할 수 있다. 하지만 '월별 음양력'과 같이 모든 데이터들을 수집하여 읽을 수 있는 것은 아니기 때문에 웹 페이지에 따른 고급 크롤링 기술이 필요할 수 있다.

Pandas

Pandas는 파이썬용 데이터 전처리 및 분석 라이브러리이다. 머신러닝에서도 데이터 전처리 수행의 비중이 80% 이상을 차지하며 Pandas의 필요성이 증가하고 있는 추세이다. 또한 프로그래밍 언어 R과 사용되는 부분이 비슷하다. 이러한 Pandas는 주요 자료구조로써 대표적으로 2가지가 있다.

 

Series

  • Series는 1차원 데이터를 표현한다.
  • Index의 개념이 있다.
    • Index는 파이썬의 리스트/튜플의 인덱스 및 사전의 key와 유사하다.
    • 차이점으로는 같은 값의 인덱스를 중복 지정 가능하다는 점이다.
    • 리스트 및 튜플로부터 시리즈를 만들 수 있다.
    • 사전의 Key를 사용하여 새로운 시리즈를 만들 수 있다.

Series의 인덱스는 아래와 같은 특성이 있다.

  리스트/튜플의 인덱스 사전의 Key 시리즈의 인덱스
중복여부 0부터 1씩 증가하는 숫자 중복을 절대 허용하지 않음

사전의 Key와 유사

중복 허용

순서개념 O X O

 

import pandas as pd
pd.__version__ # pandas version 확인

Pandas를 사용할 때는 import를 사용하고 이름이 너무 길기 때문에 통상적으로 pd로 선언한다. 그리고 Pandas 버전에 따라 코드 수행이 틀릴 수 있다.

 

series = pd.Series(['a', 'b', 'c'])
series

series[2]

위와 같이 새로운 시리즈를 생성할 수도 있다. 'a'에 접근하기 위해서는 0, 'b'에 접근하기 위해서는 1, 'c'에 접근하기 위해서는 2라는 인덱스를 사용해야 한다.

 

series = pd.Series(['a', 'b', 'c'], index=[3,5,3])
series

series.index

series[5]

series[3]

시리즈 내 index를 정의할 수도 있다. 시리즈를 새로 선언할 때 index 전달 인자를 생성하여 각각 값 별로 인덱스를 지정해줄 수 있다. 위의 예시와 같이 인덱스 3에 접근하면 중복된 값 2개에 모두 접근하는 것을 확인할 수 있다. 그리고 인덱스 값을 출력하였을 때를 보면 전달 인자에 넣어준 값들 순서대로 배정된 것도 확인 가능하다.

 

series[-2]

리스트/튜플 인덱스와는 다르게 시리즈의 인덱스는 음수가 지원이 되지 않는다. 따라서 사용하면 KeyError가 발생한다. 이는 반드시 지정되어 있는 인덱스만을 사용한다는 뜻이다.

 

Dataframe

  1. Dataframe은 2차원 데이터를 표현한다.
  2. Excel의 Worksheet와 유사하지만 Pandas는 다양한 이미지나 차트 없이 순수하게 데이터만 표현한다.
  3. 리스트/튜플/사전의 2차원 데이터로부터의 생성을 지원한다.
  4. Excel, CSV, Database 등으로부터의 생성을 지원한다.
  5. Index(행에서의 index), Columns(열에서의 index)
    • 각 행의 레이블은 시리즈로 표현되기 때문에 중복 가능하다.

 

df = pd.DataFrame([
    [10, 55, 32],
    [100, 200, 300],
    [600, 23, 40],
])
df

df[1] # column(열) 읽기

df.loc[1] # row(행) 읽기

Dataframe은 위와 같이 생성 가능하다. 2차원 데이터를 만들어야하기 때문에 내부적으로 리스트나 사전 모두 가능하다. 또한 만들어진 Dataframe을 통해 각각의 행과 열을 위와 같이 시리즈 형태로 읽을 수 있다.

 

df[3] = [20, 30, 40]
df

Dataframe 내 인덱스를 정의할 수 있다. 위는 3열을 dataframe에 추가한 결과이다.

 

df = pd.read_excel('data/score.xlsx')
df

Excel 및 기타 DB와의 호환도 가능하다. csv, excel, sql 등 여러 데이터베이스로부터 읽을 수 있고 지원 가능하다. 

 

df = pd.read_excel('data/score.xlsx')
print(df.shape) # 행/열 확인
df.head() # 상위 5개 확인

Dataframe 사용법, 일반적으로 어떠한 dataframe을 만들 때에는 dataframe이 생성되고 나서 shape을 출력하고 그에 대해 상위 5개만을 확인한다. 이렇게 사용하면 읽은 데이터가 몇 행 몇 열인지와 상위 5개의 데이터를 확인 가능하므로 데이터의 구조를 쉽게 파악 가능하다.

 

df = df.set_index('이름')
df

Dataframe에서는 df.set_index()를 사용하여 인덱스를 정의할 수도 있다. 기본 인덱스가 아닌 '이름' 항목을 인덱스로 정의하고 싶을 경우는 위와 같이 df.set_index()를 사용하여 재할당을 통해 인덱스를 바꿔줄 수 있다.

 

df.loc['잡스']

df['국어']

특정 행/열에 표시된 데이터를 확인하고 싶을 때는 위와 같이 loc 인덱서나 정의된 dataframe을 활용한다. 

 

df['평균'] = (df['국어'] + df['수학'] + df['영어']) // 3 # 평균
df

사칙연산도 수행 가능하다. 위의 코드는 덧셈과 나눗셈을 통한 평균 계산이며, 결과 값을 새로운 열로 추가하였다.

 

df.to_excel('data/score_result.xlsx')

새롭게 만든 dataframe을 excel로 저장하기 위해서는 위의 코드를 입력하면 되고 그 결과물로 파일이 잘 저장된 것을 확인할 수 있다.

 

결론

Dataframe에 대한 이해는 매우 중요하다. 그 이유는 주로 dataframe을 통해 데이터를 처리하게 될 것이며 시리즈만을 단독으로 생성하여 사용할 일이 거의 없기 때문이다. 시리즈는 dataframe 내의 데이터를 행/열 단위로 접근할 때 접근하는 자료 구조로써 사용하게 된다.

Jupyter Notebook 사용 이유

  웹 브라우저 기반의 python shell로서 이미지나 차트 등을 손쉽게 사용 가능한 장점이 있다. 실행되는 코드와 그에 대한 결과가 하나의 ipynb 파일로 저장되기 때문에 손쉽게 재실행 가능하며 동료와 공유 가능하다. 또한 웹으로 구동되기에 다른 컴퓨터에서 접속 가능하다.

 

Jupyter Notebook 작업 환경

  인터넷 익스플로어(=Internet Explorer)나 엣지(=Microsoft Edge)도 좋지만, 최신의 모질라 파이어폭스(=Firefox) 또는 크롬 브라우저(=Chrome)를 추천한다. 또한 쥬피터 노트북 사용 시 자동으로 사용하고자 하는 브라우저를 띄우기 위해서는 기본 브라우저 설정을 해줘야 한다.

 

Jupyter Notebook 활용

  쥬피터 노트북 파일의 이름을 위의 네모 박스를 더블 클릭하여 바꿀 수 있다. 그리고 실제 파일명을 바꾸는 것이기 때문에 'P02.ipynb'로 파일이 생성된 결과를 확인할 수도 있다. 파일 형태이기 때문에 다른 동료에게 메일이나 메신저를 통해 쉽게 공유 가능하다. 따라서 공유받은 동료 역시 웹브라우저 상에서 쉽게 확인 가능한 장점이 있다. 

 

  Cell에 값을 입력한 후 실행시키고자 할 때 상단의 Run을 클릭한다. 이와 같이 Cell 단위로 수행이 가능하며 하나의 Cell에는 여러 라인의 코드를 입력할 수 있다.

 

  쥬피터 노트북을 사용하며 유용한 단축키가 있는데 'Shift + Enter'이다. 위의 Run 버튼을 누르지 않고도 cell을 실행 가능하며 그 다음 cell로 넘어가게 된다. 그리고 'Enter'를 입력하면 현재 cell을 편집할 수 있다.

 

  쥬피터 노트북의 단축키를 보려면 위와 같이 'Help' 메뉴에서 'Keyboard Shortcuts'을 클릭하면 된다. 단축키들을 미리 확인해두면 추후 작업 시에 더 효율적으로 진행 가능하여 산출물의 생산성을 극대화 할 수 있다.

 

## Header
### Header

+ Python
+ Pandas
    - Series
    - Dataframe

'Code'를 'Markdown'으로 변경 후에 위의 code를 입력해본다.

  실행 결과 위와 같은 문서가 생성되는 것을 확인할 수 있다. 이 문서에 사용된 형식이 Markdown 문법이다. 이렇게 다양한 Markdown 문법을 활용할 수 있다. 따라서 별도의 워드를 사용할 필요가 없다.

  이때 Cell에서 shortcuts으로 'Esc + Y'를 사용하면 Code, 'Esc + M'을 사용하면 Markdown으로 변환되므로 참고한다. Cell 삭제를 할 때에는 'Esc + dd'를 누르거나 상단 아이콘에서 가위 모양을 클릭한다.

 

  다음은 Kernel의 유형 및 설정이다. 현재 쥬피터 노트북의 코드를 실행하는 주체를 Kernel이라 한다. 현재 선택된 Kernel은 Python 3이고 쥬피터 노트북은 다양한 Kernel을 지원한다. 설정에 따라 Java, Javascript, C#과 같은 다양한 언어들을 Kernel로 설정 가능하다.

 

import time

print('hello')
time.sleep(3)
print('world')

위와 같은 예제 코드를 실행시켰을 때 아래와 같은 변화를 확인할 수 있다.

In [*]이 3초 간의 sleep 이후에 In [2]로 바뀐다. 이 의미는 현재 cell의 실행이 완료되고 나서 숫자 할당이 이루어진다는 것이다.

 

  [*]은 Kernel이 실행 중임을 의미하는데, 어떠한 코드를 Kernel에서 실행할 때 너무 오래 걸려서 중단하고 싶다면 Kernel의 항목 중 'Interrupt'라는 메뉴를 선택하면 된다.

만약 'Interrupt'를 실행해도 [*] 상태에서 빠져나오지 않는다면 'Restart'를 클릭하여 Kernel을 재시작해준다. 'Restart'를 클릭하면 오른쪽 상단에 'Kernel Ready' 문구가 빠르게 뜨고 사라진다. Kernel을 재시작하게 되면 기존에 코드들은 모두 초기화되기 때문에 화면에 보이는 기존 코드들의 남은 내역들은 단순한 'Log' 상태이다. 따라서 Kernel을 재시작하게 되면 각각의 Cell들을 새롭게 다시 시작하거나 Kernel의 항목 중 'Restart & Run All'을 클릭하면 된다.

  Kernel의 실행 순서는 항상 상위의 cell이 하위의 cell보다 먼저 실행되지는 않는다. In [숫자]의 번호를 반드시 확인해야 하며 순서를 바꿔도 번호 순대로 실행되기 때문에 주의하여야 한다. 그리고 삭제해도 Kernel 로그에 남아있기 때문에 코드 실행이 된다.

데이터 분석이란?

  데이터를 적절하게 가공하여, 더 잘 이해하기 위해 정리하는 것. Excel에서도 데이터 분석이 가능하지만 파이썬을 활용하게 되면 Excel과 더불어 수많은 라이브러리를 통해 보다 효율적으로 분석 가능하다.

 

Anaconda Python(아나콘다 파이썬)

  과학 계산에 특화된 python 배포판이다. 아나콘다 파이썬을 설치하면 Pandas 라이브러리, Ipython, 쥬피터 노트북, Numpy 등 다양한 라이브러리가 설치된다. 따라서 데이터 분석을 할 때 훨씬 편리하게 환경 구축이 가능하다.

 

파이썬 설치 방법 및 주의사항

  데이터 분석을 위해서는 아나콘다 파이썬 하나로 충분하기 때문에 기존 공식배포판이 설치되어 있으면 제거해야 한다. 또한 설치 시에는 환경 변수 옵션을 반드시 체크해야 한다. 다운로드 링크는 아래와 같다.

Anaconda python download link: https://www.anaconda.com/download/

 

설치는 아래를 따라하며 옵션을 선택해주면 된다.

 

설치가 끝난 후에 cmd.exe를 실행시켜서 확인한다.

 

Jupyter Notebook

  웹 브라우저를 통해 파이썬 코드를 실행하고 그 결과를 보여주는 파이썬 shell이다. 쥬피터 노트북의 원래 이름은 Ipython notebook이었다. Ipython notebook일 때는 파이썬 언어만 지원했지만, 쥬피터 노트북이 된 이후에는 파이썬 이외에도 Java 및 C# 등 다양한 언어를 지원하고 있다.

  위는 Power shell로 실행시킨 ipython이다. ipython은 python shell이고 python보다 기능이 더 많은 shell이다. 하지만 Power shell 및 명령 프롬프트(cmd.exe)는 글자만 출력할 수 있는 한계가 있다. 보통의 실행 환경에서는 글자만 입력을 받고 출력을 하게 된다.

  데이터 분석을 하기 위해서는 이미지나 차트가 보여지면 훨씬 효율적인 분석이 가능해지기 때문에 쥬피터 노트북이 등장하였다. 쥬피터 노트북은 웹 브라우저를 통해 실행되므로, 웹 브라우저를 통해 차트나 이미지 등 다양한 포맷을 볼 수 있는 python shell이 쥬피터 노트북이 된다. 설치된 version은 아래와 같이 확인 가능하다.

 

  쥬피터 노트북을 사용하여 분석하면 코드와 함께 실행 결과까지 함께 파일로서 기록되므로 하나의 문서가 된다. 따라서 공유하기 편리하고 협업의 효율이 높아질 수 있다.

 

Jupyter Notebook으로 데이터 분석 방법

1. 접속 암호 설정

jupyter notebook password 명령어를 실행하여 암호 입력을 수행한다.

2. Jupyter Notebook 서버 실행

jupyter notebook을 입력하면 서버가 실행되며 쥬피터 노트북을 활용하는 동안에는 종료하면 안 된다.

3. http://localhost:8888로 접속 후, 설정한 암호 입력

4. 새로운 Python 노트북을 생성하면, 노트북 안에서 cell 단위로 코드 실행 가능

localhost에서 오른쪽 상단의 New에서 Python 3을 클릭한다. 

untitled의 파일이 생성되고 각 cell마다 python code를 실행시킬 수 있다. 그리고 cell의 마지막 코드 내용은 출력된다.

 

Python 라이브러리 실행(dataframe 및 index)

import pandas as pd

df = pd.read_excel('환율-20180914.xls', index_col='통화명') # Excel 읽기
print(df.shape)
df.head() # 상위 5개열의 데이터만 출력

위의 코드를 실행해보자.

  index는 행을 구분하는 기준이며 위 코드에서는 '통화명'으로 지정되었다. dataframe은 Table을 의미한다. 행열을 모르기 때문에 보통 많이 쓰는 기법인 df.shape를 사용하여 확인(44, 6)하였다. 

import pandas as pd
# 한글 폰트 설정
%matplotlib inline
from matplotlib import rc
from matplotlib import pyplot as plt

df = pd.read_excel('환율-20180914.xls', index_col='통화명') # Excel 읽기
df.head()

plt.rcParams['axes.unicode_minus'] = False
rc('font', family='Malgun Gothic') # Windows

df2 = df.sort_values('송금 - 보내실 때',ascending=True) # 지정 빌드 오름차순 정렬

# 지정 2개 col에 대한 가로형 막대 그래프를 10 inch * 10 inch 크기로 그린다.
df2[['송금 - 보내실 때','송금 - 받으실 때']].plot(kind='barh', figsize=(10,10))

위는 또 다른 예시 코드이다.

 

 

 

 

 

 

soup 객체를 생성하고 HTML 문서를 출력(prettify)

# HTML 문서의 데이터를 가져오기 위해 import
from bs4 import BeautifulSoup

page = open('sample.html', 'rt', encoding='utf-8').read() # HTML 파일 읽고 문자열 리턴
soup = BeautifulSoup(page, 'html.parser')  # Soup 객체 생성

# Soup 객체를 이용하여 문서를 출력
print(soup.prettify())

  BeautifulSoup은 HTML 문서 내의 데이터를 검색해서 작업하기 편리한 클래스이다. 위의 코드와 같이 HTML 문서의 데이터를 가져오기 위해 bs4 패키지 내부의 BeautifulSoup을 import 한다. BeautifulSoup 함수의 생성자에 page 인자를 넘기고 'html.parser'를 사용하면 HTML 문서를 읽고 처리할 수 있다. 그리고 soup이라는 검색이 용이한 객체를 만들어서 내부의 prettify 함수를 사용하면 HTML 문서를 보기 좋게 가공하여 출력할 수 있다.

 

특정 태그를 모두 검색(find_all)

# HTML 문서의 데이터를 가져오기 위해 import
from bs4 import BeautifulSoup

page = open('sample.html', 'rt', encoding='utf-8').read() # HTML 파일 읽고 문자열 리턴
soup = BeautifulSoup(page, 'html.parser')  # Soup 객체 생성

# HTML 문서 내부에 있는 <p>태그를 모두 검색
print(soup.find_all('p'))

  특정 태그를 모두 검색하기 위해서는 find_all() 함수를 사용해야 한다. 이 함수를 위의 코드와 같이 사용하면, 문서 내부의 <p> 태그를 모두 검색할 수 있다.

 

특정 태그 하나만 검색(find)

# HTML 문서의 데이터를 가져오기 위해 import
from bs4 import BeautifulSoup

page = open('sample.html', 'rt', encoding='utf-8').read() # HTML 파일 읽고 문자열 리턴
soup = BeautifulSoup(page, 'html.parser')  # Soup 객체 생성

# HTML 문서 내부에 있는 <p>태그 하나만 검색
print(soup.find('p'))

  특정 태그의 개수가 너무 많다면 find() 함수를 사용하여 특정 태그의 '1개'만 검색할 수도 있다. 결과를 확인해보면 검색하고자 하는 특정 태그 중에 가장 처음의 하나를 찾아 반환한다.

 

# HTML 문서의 데이터를 가져오기 위해 import
from bs4 import BeautifulSoup

page = open('sample.html', 'rt', encoding='utf-8').read() # HTML 파일 읽고 문자열 리턴
soup = BeautifulSoup(page, 'html.parser')  # Soup 객체 생성

# HTML 문서 내부에 있는 <p>태그 하나만 검색
# print(soup.find('p'))
print(soup.find('p').get_text())

  find 함수에서 get_text를 사용하면 다른 정보(tag, id 등)를 제외한 텍스트 파일만 가져온다.

 

# HTML 문서의 데이터를 가져오기 위해 import
from bs4 import BeautifulSoup

page = open('sample.html', 'rt', encoding='utf-8').read() # HTML 파일 읽고 문자열 리턴
soup = BeautifulSoup(page, 'html.parser')  # Soup 객체 생성

# <p>태그를 찾아서 내부의 문자열만 출력
for tag in soup.find_all('p'):
    print(tag.get_text())

  for문을 사용하면 HTML 문서 전체의 특정 태그(='p')를 찾아 내부의 문자열만 연속해서 출력 가능하다.

 

특정 스타일을 이용하여 검색의 범위를 좁히는 방법(find_all, class_)

# HTML 문서의 데이터를 가져오기 위해 import
from bs4 import BeautifulSoup

page = open('sample.html', 'rt', encoding='utf-8').read() # HTML 파일 읽고 문자열 리턴
soup = BeautifulSoup(page, 'html.parser')  # Soup 객체 생성

# HTML 문서 내부에 있는 <p>태그 중에 class="outer-text"만 검색
print(soup.find_all('p', class_='outer-text'))

  find_all() 함수에서 class_ 옵션을 사용하면 위의 코드처럼 특정 클래스가 포함된 문자열이 포함되어 검색되기 때문에 검색의 범위를 좁힐 수 있다.

 

특정한 태그를 사용하지 않고 속성을 사용하여 검색(find_all, id)

# HTML 문서의 데이터를 가져오기 위해 import
from bs4 import BeautifulSoup

page = open('sample.html', 'rt', encoding='utf-8').read() # HTML 파일 읽고 문자열 리턴
soup = BeautifulSoup(page, 'html.parser')  # Soup 객체 생성

#태그의 id속성이 id="first"인 경우를 검색
print(soup.find_all(id='first'))

  find_all() 함수에서 id 옵션을 사용하면 특정한 id 속성값(='first')이 포함된 문자열을 포함하여 검색할 수 있다.

 

결론

  파이썬의 BeautifulSoup 모듈을 사용하면 HTML 문서 내부의 데이터를 쉽게 크롤링 할 수 있다. 그리고 BeautifulSoup 모듈은 문서의 태그, 특정 스타일의 태그, 내부의 콘텐츠만 가져오는 기능이 있다.

크롤링이란?

웹페이지에서 필요한 데이터를 추출하는 작업을 의미한다.

웹 크롤링을 하기 위해서는 1) pip 명령어로 BeautifulSoup 모듈을 설치하고 2) Html에 대한 기본적인 이해가 필요하며 3) CSS의 선택자에 대한 이해가 필요하다.

 

BeautifulSoup란?

파이썬에 추가할 수 있는 크롤링을 위한 라이브러리이다. 결국, 웹에 있는 데이터를 가져와서 추출할 경우에 사용하는 외부 라이브러리로 추가 설치가 필요하다.

파이썬 개발환경에 새로운 모듈을 설치하려면 PIP(Python Install Package)를 이용하여 쉽게 설치가 가능하다. pip3 list는 설치된 목록을 볼 수 있다. pip3 install BeautifulSoup4는 새로운 패키지를 설치하는 명령어이다. pip3 uninstall BeautifulSoup4는 설치된 패키지를 제거하는 명령어이다.

 

웹크롤링에 필요한 라이브러리 설치

어떠한 웹페이지에 나와 있는 목차들을 한 번에 리스트로 가져오기 위해서는 Html이나 CSS의 기술을 약간이라도 이해하고 있어야 한다. 그리고 BeautifulSoup 라이브러리 및 request 라이브러리의 도움이 필요하다.

 

pip install BeautifulSoup4
pip install requests

예제 코드와 같이 pip 명령어를 사용해서 필요한 모듈 및 라이브러리를 설치할 수 있다. 그리고 Anaconda python을 설치하면 이미 설치되어 있기 때문에 따로 설치할 필요가 없다.

 

웹사이트 소스 확인

웹에서 페이지 소스 보기(V)를 통해 웹사이트 소스를 확인할 수 있다. 웹사이트들은 대부분 기술적으로 복잡하다. 그렇기 때문에 전체의 데이터를 가져오는 것보다는 일부 데이터를 가져온다. 보통 페이지 소스 보기를 사용하면 특정 어떤 태그의 스타일들을  볼 수 있다.

 

웹사이트의 구조

대부분의 웹사이트는 복잡하게 되어 있다.그래서 약간의 내용들을 미리 학습하거나 알고 있어야 한다. 그래서 앞으로 그 내용들을 진행한다.

 

HTML 태그 파싱

# web_test_01.py 
import urllib.request # 웹서버와 통신하기 위한 모듈
from bs4 import BeautifulSoup # 태그를 파싱하기 위한 모듈

page = urllib.request.urlopen('https://www.google.com/')
soup = BeautifulSoup(page, 'html.parser') # 검색이 용이한 soup객체 생성

print(soup.find_all("td")) # <td> 태그를 검색

코드 실행 결과

웹서버와 통신하기 위해서는 request 모듈의 도움이 필요하고 태그들을 파싱 하기 위해서는 BeautifulSoup 모듈의 도움이 필요하다. 위의 코드는 BeautifulSoup 클래스 생성자 쪽에 Google 웹 서버 쪽에서 준 문자열 데이터를 받아서 html 태그를 파싱 하는 코드이다. 파싱을 완료하면 무수히 많은 td 태그들을 검색이 용이한 soup 객체의 find_all 함수를 사용하여 td 태그를 한 번에 모두 가져온다.

보통 테이블 태그 안에 tr 태그가 한 라인을 의미하고 td태그는 한 columns을 의미한다. 이와 같이, 파이썬을 어느정도 알고 있으면 웹페이지에 있는 데이터들을 원하는 범위만큼 바로 불러올 수 있다.

 

결론

파이썬으로 데이터 추출을 위해 크롤링을 하는 경우 BeautifulSoup을 사용하면 쉽게 데이터를 추출할 수 있다. 결국, 웹 크롤링을 위해서는 BeautifulSoup과 requests가 필요하다. 이를 통해 HTML 문서 안에 있는 특정 태그들을 가져올 수 있다.

re 모듈은 정규식 표현을 사용할 수 있다. 리스트로 태그를 나열하면 해당 태그들을 검색한다. lambda 함수를 정의해서 태그 속성이 2개인 경우만 정의한다. 태그 중에 align 속성이 center인 경우만 검색한다. 태그의 id 속성이 para로 끝나는 경우만 검색한다. HTML 속성을 생성해서 Class 속성만 검색한다.

 

HTML 태그로 검색에 용이한 객체 만들기

from bs4 import BeautifulSoup
doc = ['<html><head><title>Page title</title></head>', \
    '<body><p id="firstpara" align="center">This is paragraph <b>one</b></p>', \
    '<p id="secondpara" align="blah">This is a paragraph <b>two</b></p>', '</html>']

soup = BeautifulSoup(''.join(doc), 'html.parser')
#태그를 정렬해서 보여주기
print(soup.prettify())

doc 참조 변수에 리스트 형태로 HTML 태그를 담았다. 검색이 용이한 soup 객체를 만들고 생성자인 join 메소드를 사용한다. 이 메소드는 리스트를 받으면 문자열로 리턴한다. 그리고 prettify 메소드를 사용하면 태그 정렬이 된다.

결과를 보면 HTML 태그가 들여쓰기 및 내어쓰기가 되어 출력된 것을 확인할 수 있다. <p> 태그 안에는 align이나 id 속성을 가지고 있는 것을 확인할 수 있다.

 

re 모듈 활용

from bs4 import BeautifulSoup
import re

doc = ['<html><head><title>Page title</title></head>', \
    '<body><p id="firstpara" align="center">This is paragraph <b>one</b></p>', \
    '<p id="secondpara" align="blah">This is a paragraph <b>two</b></p>', '</html>']

soup = BeautifulSoup(''.join(doc), 'html.parser')

#문자열 패턴을 손쉽게 정의할 수 있는 정규표현식 패턴을
#사용할 수 있는 re
tagsStartingWithB = soup.findAll(re.compile('^b'))
print([tag.name for tag in tagsStartingWithB])

re는 regular expression의 약자이다. 파이썬에 내장되어 있는 모듈이다. 정규 표현식 패턴을 정의할 수 있는 모듈이다. findAll은 전체를 다 검색해서 리스트로 리턴한다. re 모듈의 compile 함수를 써서 b라고 하는 어떤 형태로 시작하는 모든 태그를 의미한다. 파이썬의 리스트 내장 문법을 사용하여 for in문을 사용해서 리스트의 이름을 출력한다. 결과를 보면 <b> 태그가 올라온 것을 확인할 수 있다.

 

리스트를 나열하여 검색하기

from bs4 import BeautifulSoup
import re

doc = ['<html><head><title>Page title</title></head>', \
    '<body><p id="firstpara" align="center">This is paragraph <b>one</b></p>', \
    '<p id="secondpara" align="blah">This is a paragraph <b>two</b></p>', '</html>']

soup = BeautifulSoup(''.join(doc), 'html.parser')

#리스트로 태그를 나열하면 해당 태그들을 검색합니다. 
print(soup.find_all(['title', 'p']))

위의 예제 코드처럼 <title> 및 <p> 태그를 리스트에 넣고 find_all 함수를 사용하면 전부 출력 가능하다.

 

lambda() 함수 활용하기

from bs4 import BeautifulSoup
import re

doc = ['<html><head><title>Page title</title></head>', \
    '<body><p id="firstpara" align="center">This is paragraph <b>one</b></p>', \
    '<p id="secondpara" align="blah">This is a paragraph <b>two</b></p>', '</html>']

soup = BeautifulSoup(''.join(doc), 'html.parser')

#람다함수를 정의해서 태그의 속성들이 2개인 경우만 검색합니다. 
print(soup.find_all(lambda tag:len(tag.attrs) == 2))

파이썬에는 lambda() 함수 문법이 있다. 1회성 함수를 정의할 수 있는 문법이다. soup 객체가 갖고 있는 내부 데이터를 입력 데이터인 tag로 받는다. attrs가 2로 지정된 것은 속성이 2개가 있는 것만 함수를 정의하여 한 번 가져온다.

결과를 보면 <p> 태그만 aling 및 id의 속성으로 인해 2개의 결과가 나온 것을 확인할 수 있다.

 

re 모듈에 compile 함수를 이용하여 특정 패턴으로 끝나는 결과 찾기

from bs4 import BeautifulSoup
import re

doc = ['<html><head><title>Page title</title></head>', \
    '<body><p id="firstpara" align="center">This is paragraph <b>one</b></p>', \
    '<p id="secondpara" align="blah">This is a paragraph <b>two</b></p>', '</html>']

soup = BeautifulSoup(''.join(doc), 'html.parser')

#태그의 id속성이 para로 끝나는 경우만 검색합니다.
print(soup.find_all(id=re.compile("para$")))

예제 코드와 같이 re 모듈의 compile 함수를 사용하여 para$를 쓰면 para로 끝나는 패턴을 찾는다. 출력 결과의 id 속성을 보면 para로 끝나는 형태만 출력된 것을 확인할 수 있다.

 

특정 스타일을 지정하여 검색하는 방법

# #다시 간단한 HTML소스를 생성해서 class속성을 통해 검색합니다.
soup =BeautifulSoup("""
    Bob's<b>Bold</b>Barbeque Sauce now available
    <b class="hickory">Hickory</b> and <b class="lime">Lime</a>
    """, "html.parser")

# #<b>태그 중에 class=lime이라고 되어 있는 태그를 검색합니다.
print(soup.find("b", {"class":"lime"}))

#태그중에 align속성이 "center"인 경우만 검색합니다. 
# print(soup.find_all(align="center"))

<b> 태그 중에 class 속성이 lime으로 지정되어 있는 태그만 검색한다. 

 

find() 함수를 이용하여 간단하게 원하는 콘텐츠만 검색해보기

# #다시 간단한 HTML소스를 생성해서 class속성을 통해 검색합니다.
soup =BeautifulSoup("""
    Bob's<b>Bold</b>Barbeque Sauce now available
    <b class="hickory">Hickory</b> and <b class="lime">Lime</a>
    """, "html.parser")

# #<b>태그 중에 class=lime이라고 되어 있는 태그를 검색합니다.
print(soup.find("b", {"class":"lime"}).get_text())

get_text 함수를 사용하면 앞 뒤에 있는 태그를 없애고 원하는 컨텐츠(문자열)만 검색 가능하다.

 

결론

파이썬에 내장되어 있는 re 모듈을 BeautifulSoup과 같이 사용하면 보다 강력한 검색이 가능하다.

Sys 모듈

함수 설명
argv 명령행 인자
getrefcount 참조 개수 반환
path 파이썬의 PATH
stdin, stdout, stderr 표준 입력, 출력 에러 객체
exit 스크립트 종료

 

import sys

print(sys.argv)
my = sys.argv

print(my[1])

예제 코드와 같이 sys를 수행하기 위해선 명령 인자를 받아야 한다. C언어에서는 argc, argv를 받는데 그 부분과 같은 기능이다. 명령 프롬프트로 출력 결과를 확인할 수 있다. 그리고 출력 결과와 같이 argv에 들어간 리스트의 1번 인자는 aa인 것도 확인 가능하다.

 

import sys

myList = [10, 20, 30]
print(sys.getrefcount(myList))

참조 개수를 구하는 함수이다. 첫 번째에서는 참조 개수가 2인데 그 이유는 참조 개수를 구하기 위해서 getrefcount 함수에서 참조를 하기 때문이다.

 

import sys

myList = [10, 20, 30]
myList1 = myList

print(sys.getrefcount(myList))

예제 코드와 같이 얕은 복사를 통해 참조 개수를 다시 보면 2개에서 3개로 늘어난 것을 확인할 수 있다.

 

import sys

sys.stdout.write('aa\n')
sys.stdout.write('bb')

print 말고도 stdout을 통해서도 write가 가능하다.

 

Random 모듈

함수 설명
randrange(시작, 끝) 범위의 숫자 반환
shuffle(시퀀스) 시퀀스를 임의로 배치
choice(시퀀스) 임의로 선택

임의의 난수를 발생시킬 때 사용한다.

 

import random

for n in range(5):
    print(random.randint(1, 5))

예제 코드와 같이 randint를 사용하면 범위 내에서 임의의 숫자를 발생시켜 준다. 그리고 중복 데이터 발생 가능하다.

 

import random

myList = [1, 2, 3, 4, 5]
print(myList)

random.shuffle(myList)
print(myList)

shuffle을 이용하면 데이터를 임의로 재배치해주며 중복 없는 데이터를 얻을 때 유용하게 사용 가능하다.

 

import random

myList = [1, 2, 3, 4, 5]

print(myList)
print(random.choice(myList))
print(random.sample(myList, 2))

choice를 사용하면 임의로 하나 선택 해준다. Choice가 하나만 선택 가능하다면 sample은 여러 개를 선택 가능하다. 선택할 개수는 인자로 넣어주면 된다.

 

import random

lotto = [n for n in range(1, 17)]
print(lotto)

for n in range(5):
    print(random.sample(lotto, 6))

Random 모듈의 함수들을 응용해서 간단한 로또 시스템을 만들 수도 있다. 로또는 우선 중복을 허용하지 않음을 고려한다. 따라서 randint보단 shuffle을 사용하는게 좋다.

+ Recent posts