파이썬/라이브러리(API)

파이썬 웹페이지 크롤링 표를 데이터프레임으로 만들기

코데방 2024. 3. 11.
728x90

표 형태 데이터 페이지 번호 가져오기

 

표가 한장이면 그냥 가져오면 되는데 보통 웹페이지에서 표 형태는 상당히 많은 데이터를 가지고 있기 때문에 여러 페이지인 경우가 많습니다. 이럴 때 마지막 페이지 번호를 알아야 반복문을 사용해 모든 데이터를 가져올 수 있기에 먼저 데이터의 처음과 마지막을 파악하는 것이 중요합니다.

 

네이버의 주식 시가총액 순 주식 데이터를 가져와보겠습니다. 아래 url에 있는 화면입니다. 

https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1

 

 

 

먼저 가장 아랫쪽에 페이지번호가 있는 html의 태그를 확인하겠습니다. 페이지번호에 마우스로 오른쪽 클릭 후 "검사" 버튼을 누르면 개발자 모드에서 코드를 볼 수 있습니다. 

 

쭉 훑어보면 "맨 뒤" 버튼과 매칭되는 클래스가 <td class=pgRR> 입니다. 해당 클래스가 가진 링크 주소가 마지막 페이지의 번호임을 알 수 있습니다. 

 

 

 

 

이제 페이지 마지막 페이지 번호를 가져오기 위해 코드를 짜봅니다.

먼저 url을 요청해 태그정보를 받습니다.

import requests
from bs4 import BeautifulSoup as bs
import time
import pandas as pd

url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1")

 

 

그리고 해당 url 객체를 BeautifulSoup으로 정리해줍니다.

url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1")
html = bs(url.text)

 

 

 

find() 함수를 써서 <td> 태그의 "pgRR" 클래스를 찾아봅니다.\

찾고 있는 가장 마지막 페이지가 들어있는 링크는 <a href> 안에 들어있는 것을 알 수 있습니다. 

url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1")
html = bs(url.text)
html.find("td", class_ = "pgRR")

 

 

 

여기서 <a> 태그만 골라내기 위해 한 번 더 find() 함수를 써주고 그 안에서도 "href"의 내용을 골라내기 위해 ["href"]로 접근할 수 있습니다.

url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1")
html = bs(url.text)
html.find("td", class_ = "pgRR").find("a")["href"]

 

 

 

문자열로 된 주소만 골라내졌습니다. 프론트앤드에서 이 형태에서는 대부분 마지막이 페이지 넘버가 되고 "="으로 구분될수밖에 없습니다. 따라서 마지막 번호를 골라내기 위해 "split()"을 사용해 마지막 배열의 숫자만 가져옵니다. 숫자만 가져와서 반복문에 사용할 예정이므로 int형으로 변환도 해줍니다. 

url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1")
html = bs(url.text)
page_num = int(html.find("td", class_ = "pgRR").find("a")["href"].split("=")[-1])

 

 

 

 

반복문으로 표 전체 데이터 가져오기

 

이제 페이지의 마지막 번호를 알아냈으니 반복문을 통해 전체 표 데이터를 가져와 배열에 넣어줍니다.

표의 데이터를 가져와 판다스에서 제공하는 데이터프레임 형태로 만들어주는건 판다스에서 제공해주는 "read_html()" 메소드 하나로 가능합니다. 

 

다만 "read_html()"로 데이터프레임을 만들어줄 때는 배열 형태로 들어가기 때문에 첫 번째 값인 [0]번을 불러와줘야 제대로 된 데이터프레임 데이터를 얻을 수 있습니다.

 

그리고 "read_html()"의 매개변수로는 꼭 "str"형태가 들어가야 하기 때문에 BeutifulSoup 객체로 되어 있는 부분을 str 타입으로 변환해서 넣어줘야 합니다. 

import requests
from bs4 import BeautifulSoup as bs
import time
import pandas as pd

url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1")
html = bs(url.text)
page_num = int(html.find("td", class_ = "pgRR").find("a")["href"].split("=")[-1])

for i in range(1, page_num +1):
    url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page={}".format(i))
    html = bs(url.text)
    table = html.find("table", class_ = "type_2")

    # 표 부분 데이터 판다스 데이터프레임으로 만들기
    df_table = pd.read_html(str(table))[0]

 

 

 

 

데이터를 긁어와보면 알겠지만 여백을 위한 표의 행 부분들이 많아서 필수 항목인 "종목명"이 NaN값인 경우가 많습니다. 이를 제거해주고 불필요한 컬럼을 제거해줍니다. 

url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1")
html = bs(url.text)
page_num = int(html.find("td", class_ = "pgRR").find("a")["href"].split("=")[-1])

# table_all = []
for i in range(1, page_num +1):
    url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page={}".format(i))
    html = bs(url.text)
    table = html.find("table", class_ = "type_2")

    # 표 부분 데이터 판다스 데이터프레임으로 만들기
    df_table = pd.read_html(str(table))[0]
    
    #불필요한 부분 제거
    df_table = df_table[df_table["종목명"].notnull()]
    del df_table["N"]
    del df_table["토론실"]

 

 

 

이제 한 페이지의 데이터프레임에 표의 내용을 제대로 넣었습니다. 이 작업을 마지막 페이지까지 반복하면서 리스트 안에다가 데이터프레임을 모아주면 되므로 리스트를 하나 만들어서 추가해줍니다.

url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1")
html = bs(url.text)
page_num = int(html.find("td", class_ = "pgRR").find("a")["href"].split("=")[-1])

table_all = []
for i in range(1, page_num +1):
    url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page={}".format(i))
    html = bs(url.text)
    table = html.find("table", class_ = "type_2")

    # 표 부분 데이터 판다스 데이터프레임으로 만들기
    df_table = pd.read_html(str(table))[0]
    
    #불필요한 부분 제거
    df_table = df_table[df_table["종목명"].notnull()]
    del df_table["N"]
    del df_table["토론실"]

    table_all.append(df_table)
    print("{}페이지 저장 완료".format(i))

 

 

 

 

한 번에 하면 아주 빠르고 좋긴 한데 크롤링을 무분별하게 하다가는 포털 쪽에서 IP 차단을 당해버릴 위험이 존재합니다. 실제로 네이버는 대부분의 사이트에서 머신의 접근을 거부하고 있습니다. 

 

이 때 필요한 것이 time 라이브러리의 "sleep()" 입니다. "sleep(초)"는 프로세스 수행을 매개변수의 초단위만큼 지연시켜줍니다. 반복문에 사용하면 반복문이 1초에 한 번 돌도록 하는 것이죠. 나름 인간이 클릭하는 속도와 비슷하게 만들어줌으로써 네이버측에서 매크로로 판단하지 않도록 해줍니다.

url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page=1")
html = bs(url.text)
page_num = int(html.find("td", class_ = "pgRR").find("a")["href"].split("=")[-1])

table_all = []
for i in range(1, page_num +1):
    url = requests.get("https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page={}".format(i))
    html = bs(url.text)
    table = html.find("table", class_ = "type_2")

    # 표 부분 데이터 판다스 데이터프레임으로 만들기
    df_table = pd.read_html(str(table))[0]
    
    #불필요한 부분 제거
    df_table = df_table[df_table["종목명"].notnull()]
    del df_table["N"]
    del df_table["토론실"]

    table_all.append(df_table)
    print("{}페이지 저장 완료".format(i))
    time.sleep(1)

 

 

 

최종 표 데이터 합치기

 

이제 "table_all"이라는 리스트에 모든 데이터프레임이 담겼습니다. 이것을 하나로 합쳐서 하나의 데이터프레임으로 만들어주면 작업이 완성됩니다. 

 

판다스의 "concat()" 메소드를 통해 합쳐주고, 합칠 때 인덱스는 초기화해줄 수 있도록 "igonore_index"값을 True로 설정해줍니다.

final_table_data = pd.concat(table_all, ignore_index = True)

 

 

 

 

728x90

댓글

💲 추천 글