AI/Python

웹 크롤링 기초: requests + BeautifulSoup 관점

jumemory 2025. 5. 29. 17:49

웹페이지의 HTML을 가져오고, 그 안에서 원하는 데이터를 추출하는 가장 기본적인 수집 방식웹 데이터 수집을 처음 배울 때 가장 먼저 익혀야 하는 방식이 바로
requests + BeautifulSoup 조합입니다.

이 방식은 크롤링의 가장 고전적이면서도 중요한 출발점입니다.
왜냐하면 많은 웹페이지는 결국 서버가 HTML 문서를 보내주고, 우리는 그 문서를 읽어 필요한 정보를 꺼내는 구조이기 때문입니다.

즉, 이 파트의 핵심은 단순합니다.

  • 웹페이지에 요청을 보낸다
  • HTML 문서를 받아온다
  • HTML 구조를 해석한다
  • 원하는 요소를 선택한다
  • 텍스트나 속성 값을 추출한다

이 흐름을 이해하는 것입니다.

이 방식은 특히 다음 상황에서 매우 유용합니다.

  • 정적 페이지 데이터 수집
  • 뉴스, 게시글, 상품 목록 등 기본 웹 크롤링
  • API 분석 전 페이지 구조 확인
  • Selenium보다 가볍게 구조를 테스트할 때
  • 크롤링의 가장 기본 원리를 익힐 때

즉, requests + BeautifulSoup는 단순한 도구 조합이 아니라,
웹 크롤링의 가장 기본적인 사고방식을 배우는 단계입니다.


1. requests와 BeautifulSoup는 각각 무슨 역할을 할까?

이 둘은 같이 자주 쓰이지만 역할이 다릅니다.

requests

웹 서버에 요청을 보내고, 응답을 받아오는 역할을 합니다.

즉:

  • 페이지를 달라고 요청하고
  • HTML 문서를 가져오는 역할

입니다.


BeautifulSoup

가져온 HTML 문서를 분석해서
원하는 태그와 텍스트를 찾는 역할을 합니다.

즉:

  • HTML 구조를 해석하고
  • 필요한 요소를 선택하고
  • 텍스트를 꺼내는 역할

입니다.


한 문장으로 정리하면

  • requests = 가져오기
  • BeautifulSoup = 분석하기

입니다.

둘을 따로 이해해야 나중에:

  • 네트워크 문제인지
  • 파싱 문제인지
  • 선택자 문제인지

를 구분할 수 있습니다.


2. HTTP 요청이란 무엇인가?

웹 크롤링을 이해하려면 아주 기초적으로 요청(request) 개념을 알아야 합니다.

2-1. 요청의 의미

브라우저에서 어떤 사이트에 들어간다는 것은
사실상 서버에 “이 페이지 주세요”라고 요청하는 것입니다.

requests도 똑같습니다.
브라우저 대신 파이썬 코드가 요청을 보내는 것입니다.


2-2. 응답(response)의 의미

서버는 요청을 받으면 응답을 보냅니다.

응답 안에는 보통:

  • HTML
  • JSON
  • 이미지
  • 상태 코드(status code)

같은 정보가 들어 있습니다.

즉, 크롤링은 본질적으로
서버와 요청-응답을 주고받는 과정입니다.


3. GET 요청이란 무엇인가?

웹페이지를 가져올 때 가장 기본적으로 쓰는 것이 GET 요청입니다.

3-1. 정의

GET은 서버에게
“이 자원의 내용을 보내주세요”
라고 요청하는 방식입니다.

즉, 웹페이지 읽기, 기사 읽기, 목록 읽기 같은 조회에 자주 쓰입니다.


3-2. requests에서 GET 요청 보내기

예:

import requests

url = "https://example.com"
response = requests.get(url)
 

이 코드는:

즉, 브라우저가 페이지를 여는 것과 비슷한 일을 코드가 하는 것입니다.


4. response 객체는 무엇을 담고 있을까?

requests.get()의 결과는 보통 response 객체입니다.

이 안에는 여러 정보가 들어 있습니다.


4-1. 상태 코드(status code)

예:

print(response.status_code)
 

대표적으로:

  • 200 → 성공
  • 404 → 페이지 없음
  • 500 → 서버 오류

즉, 상태 코드는 요청이 잘 됐는지 확인하는 기본 신호입니다.


4-2. 응답 본문(text)

예:

print(response.text)
 

이건 서버가 돌려준 HTML 문자열입니다.

즉, 우리가 BeautifulSoup로 넘겨서 분석할 재료입니다.


4-3. 응답 헤더(headers)

예:

print(response.headers)
 

여기에는 응답 형식, 인코딩, 서버 정보 등이 들어 있을 수 있습니다.

처음 단계에서는 깊게 몰라도 괜찮지만,
나중에 인코딩 문제나 차단 대응에서 중요해질 수 있습니다.


5. 상태 코드 확인은 왜 중요한가?

초보자가 자주 하는 실수는
응답을 받자마자 바로 파싱부터 하는 것입니다.

하지만 먼저 확인해야 하는 것은:

“이 요청이 정말 성공했는가?”

입니다.

예:

 
import requests

url = "https://example.com"
response = requests.get(url)

if response.status_code == 200:
print("성공")
else:
print("실패")
 

이런 식으로 먼저 상태를 확인하는 습관이 중요합니다.

왜냐하면:

  • 페이지가 없을 수 있고
  • 차단되었을 수 있고
  • 로그인 페이지로 리다이렉트될 수 있고
  • 에러 페이지 HTML을 받아왔을 수도 있기 때문입니다.

즉, HTML을 받았다고 무조건 원하는 페이지는 아닙니다.


6. BeautifulSoup란 무엇인가?

6-1. 정의

BeautifulSoup는 HTML이나 XML 문서를
파이썬에서 탐색하기 쉬운 구조로 바꿔주는 파서(parser)입니다.

쉽게 말하면:

  • HTML 문자열을 받아서
  • 태그 구조를 분석하고
  • 원하는 요소를 찾기 쉽게 만들어주는 도구

입니다.


6-2. 기본 사용 방식

예:

from bs4 import BeautifulSoup

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

이 코드는:

  • response.text 안의 HTML을
  • BeautifulSoup 객체로 바꿔서
  • 탐색할 수 있게 만듭니다.

즉, soup는
HTML 문서를 파이썬에서 다루기 편한 구조로 바꾼 결과입니다.


7. HTML 파서(parser)란 무엇인가?

7-1. 정의

파서는 문서를 읽고 구조를 분석하는 도구입니다.

BeautifulSoup에서 "html.parser"를 쓰는 것은
“이 HTML 문서를 HTML 파서로 해석하라”는 뜻입니다.


7-2. 왜 필요한가?

HTML은 그냥 긴 문자열처럼 보일 수 있습니다.
하지만 우리가 원하는 건 문자열 자체가 아니라:

  • 어떤 태그가 있는지
  • 안에 어떤 텍스트가 있는지
  • 어떤 속성을 가지는지
  • 어떤 요소 안에 들어 있는지

를 아는 것입니다.

즉, 파서는
문자열을 구조로 바꿔주는 역할을 합니다.


8. BeautifulSoup에서 가장 먼저 해보는 것: find()

8-1. 정의

find()는 조건에 맞는 첫 번째 요소 하나를 찾습니다.

예:

title = soup.find("h1")
print(title)
 

이건 첫 번째 h1 태그를 찾습니다.


8-2. 왜 중요한가?

크롤링의 첫 단계는 보통
“내가 찾고 싶은 요소 하나를 잡아내는 것”입니다.

예:

  • 첫 번째 제목
  • 첫 번째 가격
  • 특정 영역 하나

이럴 때 find()가 아주 직관적입니다.


8-3. class 조건과 함께 쓰기

예:

 
price = soup.find("span", class_="price")
 

이건:

  • span 태그 중에서
  • class="price"인 첫 번째 요소

를 찾습니다.

여기서 class_라고 쓰는 이유는
class가 파이썬 예약어와 겹치기 때문입니다.


9. 여러 요소 찾기: find_all()

9-1. 정의

find_all()은 조건에 맞는 모든 요소를 리스트처럼 반환합니다.

예:

 
items = soup.find_all("div", class_="car-item")
 

이건 class="car-item"인 모든 div를 찾습니다.


9-2. 왜 중요한가?

실제 크롤링은 하나의 요소보다
반복되는 목록 구조를 다루는 경우가 많습니다.

예:

  • 상품 카드 목록
  • 기사 목록
  • 중고차 매물 목록
  • 채용 공고 목록

이런 구조는 보통 find_all()로 잡습니다.


9-3. 반복문과 함께 사용

예:

items = soup.find_all("div", class_="car-item")

for item in items:
print(item.text)
 

이런 흐름이 크롤링의 매우 전형적인 패턴입니다.

즉:

  1. 목록 요소들을 전부 찾고
  2. 반복문으로 하나씩 꺼내고
  3. 내부 정보를 추출합니다.

10. CSS 선택자로 찾기: select() / select_one()

BeautifulSoup는 find() 계열 말고도 CSS 선택자를 활용할 수 있습니다.


10-1. select_one()

첫 번째 요소 하나를 찾습니다.

예:

 
price = soup.select_one(".price")
 

이건 class="price"인 첫 번째 요소를 찾습니다.


10-2. select()

조건에 맞는 모든 요소를 찾습니다.

예:

 
items = soup.select(".car-item")
 

이건 .car-item에 해당하는 모든 요소를 찾습니다.


10-3. 왜 중요한가?

CSS 선택자는 웹 구조를 더 직관적으로 표현할 수 있습니다.

예:

 
soup.select(".car-item .price")
 

이건:

  • .car-item 안에 있는
  • .price 요소들

을 찾습니다.

즉, HTML / CSS / DOM 파트에서 배운 selector 감각이 바로 여기서 쓰입니다.


11. find()와 select()는 어떻게 다를까?

둘 다 요소를 찾지만 스타일이 다릅니다.

find()

  • 태그 이름, 속성 조건 중심
  • 파이썬 문법 느낌이 더 강함

예:

soup.find("span", class_="price")
 

select()

  • CSS 선택자 기반
  • 웹 구조를 보는 감각과 더 가깝다

예:

soup.select_one(".price")
 

어떻게 선택하면 좋을까?

둘 다 많이 씁니다.
처음에는 둘 다 익히는 것이 좋습니다.

  • 간단한 구조 → find()
  • 복잡한 하위 구조 → select()

가 직관적일 때가 많습니다.

실무에서는 사이트 구조에 따라 더 편한 쪽을 쓰면 됩니다.


12. 텍스트 추출하기

요소를 찾았으면 그 안의 실제 텍스트를 꺼내야 합니다.


12-1. .text

예:

price = soup.find("span", class_="price")
print(price.text)
 

결과:

2500만원
 

즉, 태그 안에 있는 텍스트를 가져옵니다.


12-2. .get_text()

예:

print(price.get_text())
 

이것도 비슷하게 텍스트를 가져옵니다.


12-3. .strip()과 함께 자주 사용

예:

 
print(price.get_text().strip())
 

앞뒤 공백이나 줄바꿈을 정리하기 위해 자주 씁니다.

즉, 텍스트 추출은 거의 항상:

  • 요소 찾기
  • .text 또는 .get_text()
  • .strip()

이 흐름과 연결됩니다.


13. 속성 값 추출하기

크롤링은 텍스트만 가져오는 게 아닙니다.
링크, 이미지 주소 같은 속성 값도 자주 뽑습니다.


13-1. 링크 추출

예:

link = soup.find("a", class_="detail-link")
print(link["href"])
 

이건 href 속성 값을 가져옵니다.


13-2. 이미지 주소 추출

예:

 
img = soup.find("img", class_="car-image")
print(img["src"])
 

이건 이미지 주소를 가져옵니다.


13-3. 왜 중요한가?

실제 크롤링에서는:

  • 상세 페이지 링크
  • 이미지 URL
  • 데이터 식별용 속성
  • 숨겨진 값

같은 것이 속성에 들어 있는 경우가 많습니다.

즉, 크롤링은 텍스트 수집과 속성 수집을 둘 다 포함합니다.


14. 반복 구조 크롤링의 기본 패턴

이건 꼭 익혀야 하는 가장 중요한 패턴 중 하나입니다.

예를 들어 페이지에 매물 카드가 반복된다고 해봅시다.

 
<div class="car-item">
<span class="brand">현대</span>
<span class="price">2500만원</span>
</div>

<div class="car-item">
<span class="brand">기아</span>
<span class="price">2200만원</span>
</div>
 

이런 구조에서는 보통 다음 순서를 탑니다.

1. 반복되는 큰 덩어리를 찾는다

 
items = soup.find_all("div", class_="car-item")
 

2. 반복문으로 하나씩 돈다

 
for item in items:
 

3. 각 덩어리 안에서 세부 요소를 찾는다

 
brand = item.find("span", class_="brand").get_text(strip=True)
price = item.find("span", class_="price").get_text(strip=True)
 

4. 결과를 저장한다

 
cars.append({
"brand": brand,
"price": price
})
 

이 구조는 중고차 사이트뿐 아니라 거의 모든 목록형 크롤링에 공통적으로 등장합니다.

즉, 이 패턴은 범용적으로 꼭 익혀야 합니다.


15. 예시 코드: 가장 기본적인 requests + BeautifulSoup 흐름

 
import requests
from bs4 import BeautifulSoup

url = "https://example.com"
response = requests.get(url)

if response.status_code == 200:
soup = BeautifulSoup(response.text, "html.parser")

items = soup.find_all("div", class_="car-item")

cars = []

for item in items:
brand_tag = item.find("span", class_="brand")
price_tag = item.find("span", class_="price")

brand = brand_tag.get_text(strip=True) if brand_tag else None
price = price_tag.get_text(strip=True) if price_tag else None

cars.append({
"brand": brand,
"price": price
})

print(cars)
else:
print("요청 실패:", response.status_code)
 

이 코드 안에는 크롤링의 가장 핵심적인 흐름이 모두 들어 있습니다.

  • 요청 보내기
  • 상태 확인
  • HTML 파싱
  • 반복 요소 찾기
  • 하위 요소 추출
  • 텍스트 정리
  • 결과 저장

즉, 이 구조를 이해하면 다른 사이트에도 응용하기 쉬워집니다.


16. if tag else None 같은 패턴은 왜 자주 나올까?

실전 크롤링에서는 어떤 요소가 항상 있는 게 아닐 수 있습니다.

예:

  • 어떤 매물은 가격이 없음
  • 어떤 카드에는 주행거리가 없음
  • 어떤 페이지는 구조가 조금 다름

이때 바로 .text를 호출하면 에러가 날 수 있습니다.

그래서 이런 식으로 씁니다.

 
brand = brand_tag.get_text(strip=True) if brand_tag else None
 

즉:

  • 태그가 있으면 텍스트 추출
  • 없으면 None

으로 처리합니다.

이건 크롤링에서 매우 흔한 방어적 코딩 패턴입니다.


17. requests + BeautifulSoup가 잘 맞는 경우

이 방식은 특히 다음 상황에서 잘 맞습니다.

17-1. 페이지 HTML 안에 데이터가 이미 있는 경우

즉, 처음 응답받은 HTML만으로 필요한 데이터가 충분할 때


17-2. 페이지 구조가 비교적 단순한 경우

예:

  • 기사 목록
  • 블로그 포스트 목록
  • 정적인 상품 리스트
  • 공공데이터 페이지

17-3. 빠르게 테스트하고 싶을 때

Selenium보다 훨씬 가볍기 때문에

  • 구조 확인
  • 태그 탐색
  • 초벌 수집

에 유리합니다.


18. requests + BeautifulSoup가 잘 안 맞는 경우

반대로 다음 상황에서는 한계가 있습니다.

18-1. JavaScript 실행 후 데이터가 생기는 경우

처음 HTML에는 값이 없고, 나중에 JavaScript가 채우는 경우


18-2. 버튼 클릭, 스크롤이 필요한 경우

예:

  • 더보기 버튼
  • 무한 스크롤
  • 탭 전환
  • 로그인 후 접근

이런 건 requests만으로 어렵습니다.


18-3. 비동기 요청 구조가 복잡한 경우

이런 경우는:

  • Selenium
  • 브라우저 네트워크 분석
  • API 직접 호출

같은 방법이 필요할 수 있습니다.

즉, requests + BeautifulSoup는 매우 중요하지만
모든 웹페이지를 다 해결하는 만능 도구는 아닙니다.


19. User-Agent는 왜 가끔 언급될까?

일부 사이트는 너무 “기계적인 요청”을 싫어합니다.
그래서 요청 헤더 중 User-Agent를 확인하기도 합니다.

예시

 
headers = {
"User-Agent": "Mozilla/5.0"
}

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

이건 요청을 조금 더 브라우저처럼 보이게 할 수 있습니다.

처음 단계에서는 깊게 파지 않아도 되지만,
나중에 어떤 사이트는 기본 requests 요청을 차단할 수 있다는 정도는 알아두면 좋습니다.


20. 인코딩 문제는 왜 생길까?

가끔 크롤링한 텍스트가 깨져 보이는 경우가 있습니다.
이건 인코딩 문제일 수 있습니다.

예:

  • 한글이 이상하게 깨짐
  • 특수문자가 깨짐

이럴 때는:

  • response.encoding
  • response.apparent_encoding

같은 것을 확인해볼 수 있습니다.

이 부분은 심화 주제지만,
웹 크롤링에서 종종 만나는 현실적인 문제입니다.


21. 범용적으로 꼭 익혀야 하는 크롤링 사고방식

이 파트에서 중요한 건 도구 사용법 자체보다
크롤링을 바라보는 기본 생각입니다.


21-1. 페이지를 먼저 구조로 본다

텍스트를 바로 뽑으려 하지 말고:

  • 반복 구조가 뭔지
  • 부모 요소가 뭔지
  • 하위 요소가 뭔지

부터 봐야 합니다.


21-2. 큰 덩어리부터 찾고, 그 안에서 세부 요소를 찾는다

목록형 크롤링의 핵심 패턴입니다.

즉:

  • 매물 카드 전체
  • 그 안의 브랜드
  • 그 안의 가격

순으로 접근해야 덜 꼬입니다.


21-3. 항상 성공만 가정하지 않는다

  • 상태 코드 확인
  • 요소 존재 여부 확인
  • 구조 변경 가능성 고려

이 중요합니다.


21-4. requests와 BeautifulSoup는 시작점이다

이 둘을 잘 이해하면:

  • Selenium으로 확장
  • API 크롤링으로 확장
  • 비동기 수집으로 확장

이 쉬워집니다.

즉, 이 파트는 범용적인 웹 수집의 가장 기본 뼈대입니다.

 

 

5. 개발 상식: 오버로딩 vs 오버라이딩

블로그에 가끔 등장하는 용어도 정리해 두자. (Forwarding과 헷갈리지 말기!)

  • 오버로딩(Overloading): 같은 이름의 함수인데 파라미터(재료)를 다르게 해서 여러 개 만드는 것.
  • 오버라이딩(Overriding): 부모가 만든 함수를 자식이 자기 마음대로 재정의해서 덮어쓰는 것. (머신러닝 클래스를 상속받아 커스텀할 때 자주 쓴다!)
# myapp.py
import logging
import logging
import datetime # 예제용
logger = logging.getLogger(__name__)


def main():
    # logging.basicConfig(filename='myapp.log', level=logging.INFO)
    # 로깅 기본 설정 (스크립트 시작 시 한 번만 호출)
    log_format = '%(asctime)s - %(levelname)s - %(name)s - %(message)s'
    log_datefmt = '%Y-%m-%d %H:%M:%S' # 예: 2025-04-22 10:07:49


    logging.basicConfig(filename='myapp.log',  level=logging.INFO, # INFO 레벨 이상만 기록
                        format=log_format,
                        datefmt=log_datefmt)
    logger.info('Started')
    logger.info('Finished')


if __name__ == '__main__':
    main()
    # 이제 로깅 함수 사용 가능
    logging.debug("상세 디버깅 정보입니다. (INFO 레벨 설정 시 출력 안 됨)")
    logging.info("프로그램이 시작되었습니다. (위치: Seoul)") # 위치 정보 활용
    logging.warning("설정 파일 (~/config.ini)을 찾을 수 없어 기본값을 사용합니다.")
    logging.error("데이터베이스 연결에 실패했습니다.")
    logging.critical("시스템의 주요 구성 요소가 작동하지 않습니다!")

 

import requests
import pandas as pd
from xml.etree import ElementTree as ET
import pathlib
import multiprocessing

def get_name_to_code(target):


    payload={"bld":"dbms/MDC/STAT/standard/MDCSTAT01901",
    "locale":"ko_KR",
    "mktId":"ALL",
    "share":"1",
    "csvxls_isNo":"false",}


    request_headers = {
        "Accept": "application/json, text/javascript, */*; q=0.01",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
        "Connection": "keep-alive",
        "Content-Length": "88",
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
        "Host": "data.krx.co.kr",
        "Origin": "http://data.krx.co.kr",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
        "X-Requested-With": "XMLHttpRequest"
    }
    r= requests.post(url, data=payload, headers=request_headers)
    data = r.json()['OutBlock_1']
    rt = [y['ISU_SRT_CD'] for y in [ x for x in data if x['ISU_ABBRV'].find(f"{target}") > -1] if y['ISU_ABBRV'].find(target + '우') == -1]
    if len(rt) > 0:
        return rt[0]
    else:
        return '종목 코드가 존재하지 않음'
    # target = '삼성전자'
    # target_code = []
    # for x in data:
    #     if x['ISU_ABBRV'].find(f"{target}") > -1:
    #         target_code.append(x)




    # for y in target_code:
    #     if y['ISU_ABBRV'].find(target + "우") == -1:
    #         print(y)


def get_info(code, s_date, e_date, path="./data/"):
    url = f"https://m.stock.naver.com/front-api/external/chart/domestic/notice?symbol={code}&startTime={s_date}&endTime={e_date}&requestType=0"
    root = ET.fromstring(requests.get(url).text)
   
   
    pathlib.Path(path).mkdir(parents=True, exist_ok=True)
    return pd.DataFrame([{'date' : i.get('date'), 'information' : x.text} for i in root.iter(tag='item')  for x in i])

if __name__=="__main__":
    params = [("isu_srt_cd", "20250101", "20250525"),
        ("000020", "20250101", "20250525"),
        ("000040", "20250101", "20250525"),
        ("000050", "20250101", "20250525"),
        ("000070", "20250101", "20250525"),
        ("000075", "20250101", "20250525"),
        ("000080", "20250101", "20250525")]
    with multiprocessing.Pool(processes=6) as pool:
        pool.starmap(get_info, params)

 

import streamlit as st
from get_info_rt import get_info, get_name_to_code
from datetime import datetime
import logging, os


if os.path.isdir('./logs') == False:
    os.mkdir("./logs")


file_name = "service_" + datetime.now().strftime("%Y%m%d%H%M%S") + ".log"
log_format = '%(asctime)s - %(levelname)s - %(name)s - %(message)s'
log_datefmt = '%Y-%m-%d %H:%M:%S' # 예: 2025-04-22 10:07:49


logging.basicConfig(filename="./logs/" + file_name,  level=logging.INFO, # INFO 레벨 이상만 기록
                    format=log_format,
                    datefmt=log_datefmt)


st.title('공시정보 보여주기')


st.header("공시정보 조회")


col1, col2, col3 = st.columns(3)




with col1:
    input_code = st.text_input(label='종목명 입력해주세요',
                           placeholder="예) 삼성전자")
with col2:
    input_sdate =  st.text_input(label="시작 날짜입력",
                           placeholder="예) 20250101")
with col3:
    input_edate =  st.text_input(label="종료 날짜입력",
                           placeholder="예) 20250101")
if st.button("조회"):
    logging.info(f"{input_code} - {input_sdate} - {input_edate}")
    rt = get_info(get_name_to_code(input_code), input_sdate, input_edate)
    st.write(rt)

def mylog(func):

    def sub_func():

        print("-------")

        func()

        print("--------")

    return sub_func

 

def hello():

    print("Hello World!!")

 

result = mylog(hello)

 

 

@mylog

def hello():

    print("안녕")

hello()

 

 

#RESTful API 

myapi.py 

 

 

from fastapi import FastAPI 

 

app = FastAPI()

 

@app.get("/encore/")

async def myfunc():

    return {"message" : "ㅋㅋㅋㅋ "}

 

pip install uvicorn

 

uvicorn myapi:app --reload

 

 

 

셀레니움(Selenium) reqeust로 데이터가 안들어올때 

파이썬으로 크롬(브라우저) 제어하기

 

- 윈도우 환경에서 할 것! (다른조건에서도 사용 가능하지만 코드가 바뀜)

 

pip install selenium

pip install webdriver_manager

 

 

from selenium import webdriver

from selenium.webdriver.chrome.service import Service as ChromeService

from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))

 

#사이트 이동 

driver.get("https://www.naver.com")

#요소를 기반으로 찾기 할수 있는 By 

from selenium.webdriver.common.by import By

#CSS 요소 검색창 

click = "#query"

#해당요소 찾아서 click해 

driver.find_element(By.CSS_SELECTOR, click).click()

#해당요소 찾아서 키보드 값 보내 

driver.find_element(By.CSS_SELECTOR, click).send_keys("손흥민")

 

for x in range(100):  #-----n
    for y in range(100): #-----n제곱
        for z in range(100): #-----n세제곱곱
            pass



# for문은 3번 이상 사용 x

 

 

파보나치 수를 재귀적으로 계산하기

def fibonacci_recursive(n: int) -> int:
    """
    n번째 피보나치 수를 재귀적으로 계산하여 반환합니다.
    (F(0)=0, F(1)=1 기준)

    주의: 이 순수 재귀 방식은 n이 커질수록 동일한 계산을 반복하여
          매우 비효율적입니다 (시간 복잡도 O(2^n)).
          실제 사용 시에는 메모이제이션(동적 프로그래밍)을 사용한 최적화가 필요합니다.
    """ #help()하면 볼수있음
    if not isinstance(n, int) or n < 0:
        raise ValueError("입력값은 0 이상의 정수여야 합니다.") ##일부러 에러를 일으키는 구문 raise
    if n <= 1:  # 기본 조건: F(0) = 0, F(1) = 1
        return n
    else:
        # F(n) = F(n-1) + F(n-2)
        return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2) #---재귀함수 스스로가 스스로를 호출

 

from xml.etree import ElementTree as ET
ElementTree는 XML 형식의 데이터를 읽고 다루기 위한 도구예요.

root.iter()  XML 안에서 원하는 태그만 골라서 반복할 수 있게 만들어주는 것

if __name__=="__main__":

그럼 __name__=="파일이름" 여기서 파일 이름에는 자신이 속한 파일이름을 적고 그 파일이름을 가지고 우리가 다른곳에서 import 할수있다는거지??

 

 

 

 

배운내용 정리

for문을 3번 이상 사용하지않는것이 좋다(성능이 떨어짐)

시용자 정의 함수 안에 """ """으로 작성해준것은 help()로 함수를 호출했을때 설명문으로 나타난다

 

1. for문을 하나의 데이터로 출력하고싶을때는 리스트[] 또는 딕셔너리{}사용

- for문으로 돌고있는 데이터를 모아서 출력하고싶을때는 리스트[] 또는 딕셔너리{}를 이용해서 출력하면된다(print문으로 출력하는경우 반복된 모든 데이터를 보여주기는 하나 실질적으로 가지고있는건 for문을 다 돌고 남은 마지막 데이터이다.

 

 

3. BeautifulSoup ElementTree차이점

-BeautifulSoup HTML태그

ElementTree xml태그를 파싱해준다

 

4.API( Application Programming Interface)란

-필요한 데이터를 자동으로 가져올 수 있게 만들어 놓은 규칙이나 통로라고 생각하면 돼요.

ex)네이버가 주식 데이터를 가지고 있어요.

근데 사람이 들어가서 복사해서 붙여넣지 않아도 되게

"주소를 이렇게 주면, 날짜별 주식 정보를 줄게!" 라고 자동으로 주는 창구를 열어둔 거예요.

 

5. 데이터가 HTML에 없고 API로 불러오는 경우

-먼저 HTML을 이용해서 크롤링할 경우는 웹페이지 자체 URL을 이용하여 접근한다

그치만 찾고자하는 데이터가 HTML에 없는경우 

개발자 도구의 "Network 탭" → "XHR" 항목을 보면
서버에서 데이터를 JSON이나 XML 형태로 따로 받아오는 경우도 많다

이럴 땐 HTML 파싱이 아니라 API 주소를 찾고, 그 API를 직접 요청하면 훨씬 쉬움

 

pip install pymysql
import pymysql
import requests
from datetime import datetime

payload = {'bld':'dbms/MDC/STAT/standard/MDCSTAT01901',
'locale':'ko_KR',
'mktId':'ALL',
'share':'1',
'csvxls_isNo':'false'}

head = {'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'ko,en-US;q=0.9,en;q=0.8',
'Connection': 'keep-alive',
'Content-Length': '88',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': '__smVisitorID=m7P0H_EhYqc; _ga=GA1.1.1102792395.1748567148; _ga_808R2EHLL3=GS2.1.s1748567645$o1$g0$t1748567646$j59$l0$h0; _ga_Z6N0DBVT2W=GS2.1.s1748567148$o1$g1$t1748567656$j48$l0$h0',
'Host': 'data.krx.co.kr',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'}

r = requests.post(url, data=payload,headers=head)
data = r.json()['OutBlock_1']
conn = pymysql.connect(host='127.0.0.1', user='play', passwd='123', database='sk17', port=3306)
cur = conn.cursor()
sql = "INSERT INTO st_masters VALUES (%s, %s, %s,%s,%s, %s, %s,%s,%s,%s,%s,%s)"
for f_data in data:
    f_data['LIST_DD'] = datetime.strptime(f_data['LIST_DD'], "%Y/%m/%d")
    f_data['PARVAL']=str(f_data['PARVAL'])
    f_data['LIST_SHRS'] = f_data['LIST_SHRS'].replace(',', "")
    try:
        cur.execute(sql, [f_data['ISU_CD'],f_data['ISU_SRT_CD'],f_data['ISU_NM'],f_data['ISU_ABBRV'],f_data['ISU_ENG_NM'],
                        f_data['LIST_DD'],f_data['MKT_TP_NM'],f_data['SECUGRP_NM'],f_data['SECT_TP_NM'],f_data['KIND_STKCERT_TP_NM'],
                        f_data['PARVAL'],f_data['LIST_SHRS']])
    except Exception as e:
            print(e)
    conn.commit()

 

 

import streamlit as st
import pandas as pd
import pymysql


conn = pymysql.connect(host='127.0.0.1', user='play', passwd='123', database='sk17', port=3306)
cur = conn.cursor()


def get_sql(sql):
    cur.execute(sql)
    return cur.fetchall()


input_sql = st.text_input(label='SQL 입력')


if input_sql:
    try:
        rt = get_sql(input_sql)
    except Exception as e:
        st.write(e)

    st.write(pd.DataFrame(rt))