Study/Python

파이썬 라이브러리 - BeautifulSoup (웹 데이터 수집 라이브러리)

김 도경 2024. 9. 30. 22:27

[2024.09.30] 필수 온라인 강의 Part2 파이썬 라이브러리 활용 CH05 웹 데이터 수집 라이브러리(BeautifulSoup)

 

    • 웹 크롤링이란?
      - 인터넷 상에 존재하는 데이터를 자동으로 수집하는 행위
      - 데이터 분석가에게 데이터를 탐색하고 원하는 조건에 맞는 데이터를 직접 수집/저장하기 위한 목적으로 사용
      1. 웹 페이지 정보 가져오기 : 파이썬 Requests 라이브러리 사용
      2. HTML 소스를 파싱(분석)하여 원하는 정보 얻기 : 파이썬 BeautifulSoup 라이브러리 사용

      - 기본 개념
      1. 브라우저로 접속하고 싶은 주소(url) 입력
      2. 브라우저가 해당 주소의 서버에게 "페이지 구성 정보를 줘:라고 요청(request)
      3. 웹 서버는 구성에 필요한 코드(html) 형태로 전달 (response)
      4. 브라우저는 서버가 전달해준 정보(html)을 해석해서 사용자 화면을 보여줌

    • BeautifulSoup : 웹 데이터 수집 라이브러리
      - HTML 및 XML에서 데이터를 쉽게 처리하는 파이썬 라이브러리
      - HTML은 태그로 이루어져있고, 공백과 변화하는 소스가 많음 : 오류가 있을 가능성 높음
          -> BeautifulSoup를 이용하여 오류를 잡아서 고친 후 데이터 전달됨
      - pip install beautifulsoup4
      - from bs4 import BeautifulSoup
      - https://www.crummy.com/software/BeautifulSoup/bs4/doc/ 에서 참고

    • HTML 코드 작성
html =  """
	<html>
		<body>
			<h1 id = 'title'>Selena 파이썬 라이브러리 활용!</h1>
			<p id = 'body'>오늘의 주제는 웹 데이터 수집</p>
			<p class = 'scraping'>삼성전자 일별 시세 불러오기</p>
			<p class = 'scraping'>이해 쏙쏙 Selena 수업!</p>
		</body>
	<html>
	"""

 

  • HTML 파싱
    - soup = BeautifulSoup (html, 'html.parser')
    - html을 파이썬에서 읽을 수 있게 파싱(파이썬 객체로 변환)
    - html이라는 변수에 저장한 html 소스코드를 .parser를 붙여 변환
# BeautifulSoup 함수를 이용하여 soup 객체 생성
# html이라는 변수에 저장한 html 소스코드를 .parser를 붙여 변환
soup = BeautifulSoup(html, 'html.parser')

 

  • BeautifulSoup 데이터를 텍스트로 변환
for text in soup:
	print(text)

- soup : soup의 데이터를 모두 가져와서 텍스트로 반환

- soup.contents : soup의 데이터를 모두 가져와서 리스트로 반환

- soup.stripped_strings : 공백도 함께 제거하여 텍스트로 반환

for text in soup:
  print(text)

<html>
<body>
<h1 id="title">Selena 파이썬 라이브러리 활용!</h1>
<p id="body">오늘의 주제는 웹 데이터 수집</p>
<p class="scraping">삼성전자 일별 시세 불러오기</p>
<p class="scraping">이해 쏙쏙 Selena 수업!</p>
</body>
<html>
</html></html>
for stripped_text in soup.stripped_strings:          # 공백도 함께 제거하여 텍스트로 반환
  print(stripped_text)
  
Selena 파이썬 라이브러리 활용!
오늘의 주제는 웹 데이터 수집
삼성전자 일별 시세 불러오기
이해 쏙쏙 Selena 수업!

 

  • Find 함수
scraping = soup.find(class_='scraping')
scraping.string

- find 함수는 id, class, element 등을 검색 가능
- find : 조건에 해당하는 첫 번째 정보만 검색 : 클래스 이름을 알 경우, class_ 형태로 사용

title = soup.find(id='title')               # id 값이 'title'인 조건에 해당하는 첫 번째 정보만 검색
print(title)
scraping = soup.find(class_='scraping')             # class 값이 'scraping'인 조건에 해당하는 첫 번째 정보만 검색   # 클래스 이름을 알 경우, class_ 형태로 사용
print(scraping)

 

- find_all : 조건에 해당하는 모든 정보 검색

scraping_all = soup.find_all(class_='scraping')     # class 값이 'scraping'인 조건에 해당하는 모든 정보 검색
print(scraping_all)

 

- string : 태그 내부의 텍스트만 출력

scraping.string          # 태그 내부의 텍스트만 출력

 


  • BeautifulSoup 웹 크롤링 3단계 과정 

1. Request : 웹 페이지의 URL 이용해서 HTML 문서를 요청. 첫 번째 단계인 Request를 위해 import urllib.request 불러오기

import requests               # 웹 페이지의 URL을 이용해서 HTML 문서를 요청하기 위해 필요한 라이브러리

 

2. Response : 요청한 HTML 문서를 회신

3. Parsing : 태그 기반으로 파싱(일련의 문자열을 의미 있는 단위로 분해)

  • 네이버 금융에 접속하여 실습하기
    - F12(개발자도구) URL 찾기
    1. 네이버 금융 홈페이지 접속 : https://finance.naver.com/
    2. 삼성전자(code : 005930) 검색
    3. 시세 메뉴 클릭 후 URL 확인 : https://finance.naver.com/item/main.naver?code=005930
    4. 키보드 F12(개발자도구) 클릭 > 메뉴 Elements 클릭 > 키보드 Ctrl과 F (검색 단축키) 클릭 > '일별 시세' 검색 > scr 값 복사
         : src="/item/sise_day.naver?code=005930"

 

1번 단계. Request 진행하기

- 웹 ㅔ이지의 URL을 이용해서 HTML 문서를 요청
- requests.get(stock_url, headers = headers) : url 값을 파라미터 값으로 입력, 해당 사이트는 반드시 헤더 저어보를 요구하기 때문에 파라미터 값을 헤더 입력


    1. url 저장
      - stock_url = 'https://finance.naver.com//item/sise_day.naver?code=005930'
      - stock_url이라는 변수에 네이버 금융 사이트의 삼성전자 시세 정보가 담긴 URL 저장

    2. header라는 변수에 넣어주기
      - header에 User-Agent를 넣어줄것.

      -  user-agent란, 웹 크롤링을 진행하면 종종 페이지에서 아무것도 받아오지 못하는 경우 발생!

                  이유는 대부분 서버에서 봇을 차단하기 때문 : 이걸 해결 해주는 것 : 디폴트 값으로 이용하는 게 좋음

      - user-agent 확인 사이트 : http://www.useragentstring.com/

# header에 user-agent 값 저장
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'}

 

 - 웹 페이지의 URL 이용해서 HTML 문서를 요청

requests.get(stock_url, headers = headers)

 

    - URL 값을 파라미터 값으로 입력
    - 해당 사이트는 반드시 헤더 정보를 요구하기 때문에 파라미터 값으로 헤더 입력

 

2번 단계. Response
 - 요청한 HTML 문서를 회신 : response 변수에 요청한 HTML 문서를 회신하여 저장

response = requests.get(stock_url, headers = headers)

- 서버에서 요청을 받아 처리한 후, 요청자에게 응답 줌
- HTML 코드 형태

 

3번 단계. Parsing
 - 태그 기반으로 파싱(일련의 문자열을 의미 있는 단위로 분해)

soup = BeautifulSoup(response.text, 'html.parser')

- html을 파이썬에서 읽을 수 있게 파싱(파이썬 객체로 변환)
- Response.text에 저장한 html 소스코드를 .parser를 붙여 변환
- parser는 파이썬의 내장 메소드

 

  • 반복구문으로 일별 종가 구현 : 200일 동안의 일별 종가 정보 가져오는 반복문 구현
    - 반복문 코드 설명
    1) 200일 일별 종가 정보는 1 Page 당 10일의 일별 종가 정보 담겨있어서 20 Page 필요
    2) 일별 종가 담긴 URL과 Header 정보로 requests.get 함수 구현
    3) 요청한 HTML 문서를 회신하여 response 변수에 저장
    4) BeautifulSoup함수로 HTML을 읽을 수 있도록 파싱하여 soup 변수에 저장
    5) Page 개수만큼 20번 반복
                  -  "tr" 태그 조건에 해당하는 모든 정보를 검색하여 parsing_list 변수에 저장
    6) 1 Page 당 10일의 일별 종가 정보 담겨있어서 10번 반복
                  - "td" 태그의 align가 "center"인 값들 중 0번째 조건에 해당하는 정보 검색하여 출력
                  -  "td" 태그의 class가 "num"인 값들 중 0번째 조건에 해당하는 정보 검색하여 출력
# 200일 동안의 일별 종가 정보 가져오는 반복문(1페이지 당 10일 정보 담겨있음)
for page in range(1, 21):
  print (str(page))

  # url + page 번호 합치기
  stock_url = 'http://finance.naver.com/item/sise_day.nhn?code=005930' +'&page='+ str(page)

  # header 정보
  headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'}

  # request : 웹 페이지의 URL, header 이용해서 HTML 문서 요청
  # response : 요청한 HTML 문서 회신
  response = requests.get(stock_url, headers = headers)

  # parsing : HTML을 읽을 수 있도록 파싱
  # soup 변수에 BeautifulSoup의 객체 생성
  soup = BeautifulSoup(response.text, 'html.parser')

  # "tr" 태그 조건에 해당하는 모든 정보 검색
  parsing_list = soup.find_all("tr")

  # None 값은 걸러주기 위한 변수 생성
  isCheckNone = None

  # 페이지당 일별 종가 출력하기 위한 반복문 <들여쓰기 주의>
  for i in range(1, len(parsing_list)):
    if(parsing_list[i].span != isCheckNone):
      print(parsing_list[i].find_all("td", align="center")[0].text,
            parsing_list[i].find_all("td", class_="num")[0].text)

    # None 값은 걸러주기 위한 조건문 <들여쓰기 주의>
    # .span()는 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 돌려주는 함수

      # parsing_list[i] : i번째 parsing_list, i 번째 "tr" 태그 값
      # .find_all("td", align="center")[0].text : "td" 태그의 align가 "center"인 값들 중 0번째 값
      # .find_all("td", class_="num")[0].text : "td" 태그의 class가 "num"인 값들 중 0번째 값

 

  • Pandas를 이용한 웹데이터 수집
    1) Pandas 라이브러리와 Requests 라이브러리 이용
    2) 200일 일별 종가 정보는 1 Page 당 10일의 일별 종가 정보 담겨있어서 20 Page 필요
    3) 일별 종가 담긴 URL과 Header 정보로 requests.get 함수 구현
    4) pandas.read_html 함수를 통해 HTML 불러와서 파싱
    5) concat 함수를 이용하여 dataframe 끝에 추가하고 싶은 요소를 추가하여 dataframe 리턴
    6) dropna 함수를 통해 결측 값 제거
import pandas as pd   # pandas 라이브러리
import requests       # requests 라이브러리

stock_data = pd.DataFrame()   # 빈 데이터프레임을 생성하여 이후 각 페이지에서 가져온 데이터를 추가

# code = 회사 코드, page = 일별 시세 테이블의 페이지 수 (200 행의 데이터 불러오려면 20 페이지 입력)
for page in range(1, 21): # 1. 페이지 순회
   stock_url = 'http://finance.naver.com/item/sise_day.nhn?code=005930' +'&page='+ str(page)

   #  header 정보
   headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'}

   response = requests.get(stock_url, headers = headers)     # response : 요청한 HTML 문서 회신
        # request : 웹 페이지의 URL, header 이용해서 HTML 문서 요청


   df_page = pd.read_html(response.text, header=0)[0]     # 2. response.text로 응답을 주면 HTML 코드이기 때문에 read_html로 불러오기

   # 3. concat() : 여러 DataFrame을 하나로 결합할 때 사용
   stock_data = pd.concat([stock_data, df_page], ignore_index=True)
      # 주의!! : pandas 2.0.0 버전 이후부터 'append()' Method가 완전히 제거되었기 때문에 더 이상 작동하지 않음. concat() 사용

stock_data = stock_data.dropna()   # 4. 결측값 행 제거(for문 외부에 위치하도록 들여쓰기 주의)
stock_data

- Pandas 일별 시세 테이블 구현 코드 설명
    : pandas 2.0.0 버전 이후부터 'append()' Method가 완전히 제거되었기 때문에 더 이상 작동
1. 1부터 20까지 순회하는 for문
   -데이터를 가져올 웹 페이지 주소에 `&page=`와 숫자를 붙여 1 페이지부터 20 페이지까지의 데이터를 가져오기 위한 것.

2. `pd.read_html(response.text, header=0)[0]` 코드를 세부적으로 설명.
     먼저 `pd.read_html()`는 HTML 페이지의 모든 표를 읽어서 각 표를 DataFrame 객체로 저장한 리스트를 반환.
    `response.text`는 HTML 내용을 문자열로 변환하여 가져옵니다. `header=0`는 첫 번째 행을 열 이름으로 사용한다는 의미.
    즉, 표의 첫 번째 행이 데이터프레임의 열 이름이 됩니다. `[0]`는 이 리스트에서 첫 번째 DataFrame 객체를 선택하는 것을 의미
    HTML 문서에 여러 개의 표가 있을 수 있기 때문에, 일반적으로 원하는 표를 선택하기 위해 인덱스를 사용.

3. 현재 stock_data 데이터프레임과 df_page 데이터프레임을 결합.
     `ignore_index=True`는 기존 인덱스를 무시하고 새로운 연속적인 인덱스를 생성함

4. 데이터프레임에 있는 누락값을 제거. 이는 데이터를 추가로 분석하기 전에 데이터를 정리하는 일반적인 작업