LAB/Python

웹 크롤링

it-lab-0130 2024. 10. 4. 19:30

 

from bs4 import BeautifulSoup #정적 페이지 웹 크롤링 시 사용되는 라이브러리
from urllib.request import urlopen
from urllib.parse import quote
# urllib의 urlopen의 기능을 활용하여 특정 url의 html문서를 가져옴 (한국어는변환)
# 때문에 브라우저에서 제공해주는 개발자 모드를 적극 활용한다.
# 대부분의 크로미움 계열 (크롬,엣지,웨일 등)은 F12를 누르면 개발자 모드로 돌입한다.

url_wikipedia = "https://ko.wikipedia.org/wiki/"
target = "뷰티풀_수프_(HTML_파서)"

html = urlopen(url_wikipedia + quote(target, safe=""))

bs = BeautifulSoup(html.read(), features="html.parser")
#print(bs.prettify) #전부 다 출력

link_list = bs.find_all(name='a')
#for link in link_list:
    #print(link)

table_data = bs.find("table","infobox vevent")
#print(table_data) # 이방식으로 table객체만 따로 가져와 하나의 객체로 만들수 있다.

table_body = table_data.find("tbody")
#print(table_body) # 이 방식으로 table객체에 있는 tbody만 가져와 하나의 객체로 만들 수 있다.

#tr은 각 행을 대표 t

th_list = []
td_list = []

for i,tr in enumerate(table_body.find_all("tr")):
    if tr.find("th") is not None :
        # th가 없을 수도 있기 때문에 조건을 걸어줌
        th_list.append(tr.find("th").text)
        td_list.append(tr.find("td").text)
    else:
        print(i)
        # Nonetype으로 문제가 발생한 인덱스 찾기 위한 프린트
print(th_list)
print(td_list)
# enumerate : 인덱스 값으로 받고 싶다
# NoneType안에 객체 text속성이 없는 오류 발생 (AttributeError)
# 어느 순간에 문제가 발생했는지를 파악하는것이 중요하다.

from bs4 import BeautifulSoup #정적 페이지 웹 크롤링 시 사용되는 라이브러리
from urllib.request import urlopen
from urllib.parse import quote
# urllib의 urlopen의 기능을 활용하여 특정 url의 html문서를 가져옴 (한국어는변환)
# 때문에 브라우저에서 제공해주는 개발자 모드를 적극 활용한다.
# 대부분의 크로미움 계열 (크롬,엣지,웨일 등)은 F12를 누르면 개발자 모드로 돌입한다.

url_wikipedia = "https://www.card-gorilla.com/card?cate=CRD"
# 동적페이지 : 유저의 동작이나 , javascript의 동작에 따라 같은 페이지에서도 여러동작이나 이벤트로 실행 되어야 데이터가 보여지는 페이지

html = urlopen(url_wikipedia)

bs = BeautifulSoup(html.read(), features="html.parser")

card_info = bs.find_all("div","card-container")
print(card_info)

from selenium import webdriver
from selenium.webdriver.common import by
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

url = "https://www.card-gorilla.com/card?cate=CRD"
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get(url)

sel = "#q-app > section > div.card > section > div > div.card_list > ul > li:nth-child(1) > div" # copy selector 한 문장
card_container = driver.find_element(by.By.CSS_SELECTOR,sel)

print(card_container.get_attribute("innerHTML"))



sel = "div.card_data > div.name > p > span" #div.클래스명
name = card_container.find_elements(by.By.CSS_SELECTOR,sel)

for tag in name:
    print(tag.text)

sel = "div.card_data > div.sale > p"
sel_2 = "i"
sel_3 = "span > b"

sale = card_container.find_elements(by.By.CSS_SELECTOR,sel)

for tag in sale:
    benefit_att = tag.find_element(by.By.CSS_SELECTOR,sel_2)
    benefit_num = tag.find_element(by.By.CSS_SELECTOR,sel_3)
    print(benefit_att.text + " : " + benefit_num.text)

if __name__ == "__main__":
    while len(driver.window_handles) != 0: # driver가 바로 끝나는걸 막기위한 코드 추가
        pass

Python 웹 크롤링

출처 입력

Python 으로 웹 크롤링을 배웠다.

www.naver.com/robots.txt를 검색하고 robots.txt 크롤링 허용 확인 후

F12를 눌러 크롤링할 html 태그를 읽어서 태그에 해당하는 클래스 이름을 기준으로 부른다.

 

웹 크롤링이란 웹상의 정보들을 탐색하고 수집하는 작업을 의미합니다. 인터넷에 존재하는 방대한 양의 정보를 사람이 일일히 파악하는 것은 불가능한 일입니다. 때문에 규칙에 따라 자동으로 웹 문서를 탐색하는 컴퓨터 프로그램, 웹 크롤러(Crawler)를 만들었습니다.

 

[정적페이지]

from bs4 import BeautifulSoup #정적 페이지 웹 크롤링 시 사용되는 라이브러리 
from urllib.request import urlopen
from urllib.parse import quote
# urllib의 urlopen의 기능을 활용하여 특정 url의 html문서를 가져옴 (한국어는변환)
# 때문에 브라우저에서 제공해주는 개발자 모드를 적극 활용한다.
# 대부분의 크로미움 계열 (크롬,엣지,웨일 등)은 F12를 누르면 개발자 모드로 돌입한다.

url_wikipedia = "https://ko.wikipedia.org/wiki/"
target = "뷰티풀_수프_(HTML_파서)"

html = urlopen(url_wikipedia + quote(target, safe=""))

bs = BeautifulSoup(html.read(), features="html.parser")
#print(bs.prettify) #전부 다 출력

link_list = bs.find_all(name='a')
#for link in link_list:
    #print(link)

table_data = bs.find("table","infobox vevent")
#print(table_data) # 이방식으로 table객체만 따로 가져와 하나의 객체로 만들수 있다.

table_body = table_data.find("tbody")
#print(table_body) # 이 방식으로 table객체에 있는 tbody만 가져와 하나의 객체로 만들 수 있다.

#tr은 각 행을 대표 t

th_list = []
td_list = []

for i,tr in enumerate(table_body.find_all("tr")):
    if tr.find("th") is not None :
        # th가 없을 수도 있기 때문에 조건을 걸어줌
        th_list.append(tr.find("th").text)
        td_list.append(tr.find("td").text)
    else:
        print(i)
        # Nonetype으로 문제가 발생한 인덱스 찾기 위한 프린트
print(th_list)
print(td_list)
# enumerate : 인덱스 값으로 받고 싶다
# NoneType안에 객체 text속성이 없는 오류 발생 (AttributeError)
# 어느 순간에 문제가 발생했는지를 파악하는것이 중요하다.
 

[동적페이지]

#웹 크롤링 
import os.path
import pandas as pd
from selenium import webdriver,common # 웹 자동화 테스트 전용
from selenium.webdriver.common import by
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.wait import WebDriverWait  # 더보기로 정보를 긁어올 동안 멈춰있어라 하기 위해 선언
import time
import random

# 동적페이지 : 유저의 동작이나 , javascript의 동작에 따라 같은 페이지에서도 여러동작이나 이벤트로 실행 되어야 데이터가 보여지는 페이지

url = "https://www.card-gorilla.com/card?cate=CRD"
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get(url)
# card를 불러오기 전에 카드 더보기를 자동으로 눌러준다
# 카드 더보기 누르면 몇개를 더 불러와서 추가되는 웹사이트 동작
sel = "#q-app > section > div.card > section > div > div.card_list > div.ftr > a.lst_more" #(카드 더보기 copy selector)
#more_btn = driver.find_element(by.By.CSS_SELECTOR,sel)
#more_btn.click()
for i in range(10): #예시를 위해 범위 조절, 기존의 경우 버튼이 사라지면 모든 데이터를 로드했다고 가정하고 다음을 진행
    try:
        more_btn = WebDriverWait(driver, timeout=10).until(
            lambda d: d.find_element(by.By.CSS_SELECTOR,sel) # 한줄로 표현해주는  함수 (a.k.a 익명 함수) 고차함수 사용 시 사용
        )
        driver.execute_script("arguments[0].click();",more_btn)
        time.sleep(round(random.random() * 2, 2) + 2)
        # 다만, 너무 지속적이고 반복적인 요청은 공격 (DDos등)으로 오인할 수 있으므로 time.sleep()과 같은 장치를 둔다
        # click()의 경우 좌표를 이용하는데 이가 오류 발생할 수 있으니 javascript코드로 클릭하도록 조정한다
    except common.exceptions.TimeoutException as e:
        break

sel = "#q-app > section > div.card > section > div > div.card_list > ul > li" # copy selector 한 문장
cards = driver.find_elements(by.By.CSS_SELECTOR,sel)

df = pd.DataFrame({"카드명": [], "카드회사": [], "혜택1": [],"혜택2": [], "혜택3": []}) #csv 파일의 컬럼명

for card in cards:
    tmp_list = []
    sel = "div"
    card_container = card.find_element(by.By.CSS_SELECTOR,sel)

    sel = "div.card_data > div.name > p > span" #div.클래스명
    name = card_container.find_elements(by.By.CSS_SELECTOR, sel)

    for tag in name:
        #print(tag.text, end="")
        tmp_list.append(tag.text)
    #print()


    sel = "div.card_data > div.sale > p"
    sel_2 = "i"
    sel_3 = "span > b"

    sale = card_container.find_elements(by.By.CSS_SELECTOR,sel)

    for tag in sale:
        benefit_att = tag.find_element(by.By.CSS_SELECTOR,sel_2)
        benefit_num = tag.find_element(by.By.CSS_SELECTOR,sel_3)
        tmp_list.append(f"{benefit_att.text} {benefit_num.text}")
        #print(benefit_att.text + " : " + benefit_num.text)
    #print("--------------------------------")
    while len(tmp_list) != 5:
        tmp_list.append(None)
    df.loc[len(df)] = tmp_list

save_path = "../data/card_info"
if not os.path.exists(save_path):
    os.mkdir(save_path)
df.to_csv(os.path.join(save_path,"cards.csv"), index=False)
driver.close() #실행 후 웹페이지 종료시키기

# if __name__ == "__main__":
#     while len(driver.window_handles) != 0: # driver가 바로 끝나는걸 막기위한 코드 추가
#         pass
 

정적 웹 페이지와 동적 웹 페이지

출처 입력

사진 삭제

사진 설명을 입력하세요.

정적 웹 페이지 (Static Web Page)

웹 서버에 이미 저장된 파일(HTML 파일, 이미지, JavaScript 파일 등)을 클라이언트에게 전송하는 웹 페이지다.

사용자는 서버에 저장된 데이터가 변경되지 않는 한 고정된 웹 페이지를 계속 보게 된다.

따라서 모든 사용자는 같은 결과의 웹 페이지를 서버에 요청하고 응답 받게 된다.

[장점]

다른 처리 없이 요청에 대한 파일만 전송하기 때문에 빠르다.

단순한 문서로 웹 서버를 구축하므로 호스팅 서버에 연결하는 비용이 적다.

[단점]

저장된 정보만 보여주기 때문에 서비스가 한정적이다.

추가, 삭제, 수정 등의 작업이 모두 코드를 직접 건드려야 하기 때문에 관리가 힘들다.

 

동적 웹 페이지 (Dynamic Web Page)

서버에 저장된 HTML 파일이 그대로 브라우저에 나오는 것이 아닌, 동적으로 만들어지는 웹 페이지

요청에 관하여 사용자는 조건에 따라 다른 결과를 받게 된다.

사용자는 상황, 시간, 요청 등에 따라 달라지는 웹 페이지를 보게 된다.

 

[동적 웹 페이지 종류]

1. CSR (Client Side Rendering)

CSR은 데이터가 없는 HTML 문서나 Static 파일만을 처음에 받아와 로드하고, 이후에 데이터를 요청하여 받아오는 방식이다.

자바스크립트를 사용하여 브라우저에서 페이지를 직접 렌더링을 진행한다.

모든 로직, 데이터 가져오기, 템플릿 및 라우팅 등은 서버가 아닌 클라이언트 측에서 처리한다.

 

2. SSR (Server Side Rendering)

CSR과 상반되게 서버에서 동적으로 데이터까지 전부 삽입하여 완성된 HTML을 넘겨준다.

서버 렌더링은 브라우저에서 응답을 받기 전에 처리되므로 클라이언트에서 데이터를 가져오거나 템플릿 작성에 대한 추가 왕복이 발생하지 않는다. (어쨌든 웹 서버에서 모든 요청이 처리된다.)

 

3. MPA (Multi Page Application)

새로운 페이지를 요청할 때마다 정적 리소스가 다운로드 되고, 그에 맞춰 전체 페이지를 다시 렌더링하는 방식이다. (즉, SSR 방식으로 렌더링한다.)

인터넷 주소창에 주소를 입력하거나 링크를 클릭하는 등의 사용자가 어떠한 요청을 하게 되면, 그에 맞는 완전한 페이지를 받아오고 다시 렌더링된다.

장점은 검색 엔진 최적화(SEO, Search Engine Optimization) 관점에서는 유리하지만, 단점으로는 새로운 페이지를 이동할 때마다 완전히 새로 렌더링 되므로 깜빡거리고 프론트엔드와 백엔드가 밀접하게 연결되어 개발이 복잡할 수 있다.

 

4. SPA (Single Page Application)

웹 애플리케이션에 필요한 모든 정적 리소스를 최초 한 번만 다운로드를 한다.

그 이후, 새로운 페이지에 대한 요청이 있을 때마다 페이지 갱신에 필요한 데이터만 전달 받고 그 정보를 기준으로 페이지를 갱신한다. (즉, CSR 방식으로 렌더링한다.)

SPA를 만드는데 사용되는 프레임워크로 React, Bue, Angular가 있다.

장점으로는 최초 접속 시 맨 첫 페이지 로딩 시간을 길어도 이후 페이지부터는 속도가 빠르다. 또한 앞선 MPA와 달리 깜빡거림이 없고 반응 속도가 좋다. 또한 로컬 데이터를 효과적으로 캐싱할 수도 있다. 단점으로는 초기 구동 속도가 느리고 SEO에 불리하다는 것이다.