Python을 이용한 웹 크롤링: 효율적인 데이터 수집 방법

웹 크롤링이란?

웹 크롤링은 웹 페이지를 방문하고 자동으로 정보를 수집하는 과정을 말합니다. 이를 통해 빠르고 효과적으로 웹에서 원하는 정보를 찾아낼 수 있습니다. Python과 같은 프로그래밍 언어를 사용하면, 특정 웹사이트의 HTML을 읽고 분석, 원하는 정보만 추출이 가능합니다.

간단한 웹 크롤링 예제


import requests
from bs4 import BeautifulSoup

# 웹 페이지 가져오기
response = requests.get('https://www.example.com')

# BeautifulSoup 객체 생성
soup = BeautifulSoup(response.text, 'html.parser')

# 원하는 정보 추출
headlines = soup.find_all('h1')

for headline in headlines:
    print(headline.text)

위의 Python 코드는 www.example.com 이라는 웹페이지의 모든 h1 태그를 크롤링하여 출력하는 예제입니다.
requests 라이브러리로 웹 페이지 정보를 가져오고, BeautifulSoup 라이브러리로 HTML을 파싱해 원하는 정보를 추출합니다.


Python과 웹 크롤링

Python의 특징

Python은 간결하고 가독성 높은 코드로 유명하여 초보자가 응용 프로그래밍을 배우기 쉽습니다. 배우기 쉽고 강력한 언어로, 다양한 분야에서 사용됩니다. 특히, Python은 라이브러리가 풍부하여 데이터 분석, 웹 개발, 자동화 등에 활용됩니다.

Python이 적합한 웹 크롤링 이유

Python은 웹 크롤링에 적합한 언어입니다. 그 이유는 다음과 같습니다.

– 간결하고 직관적인 문법: 파이썬의 코드는 짧고, 누가 봐도 이해하기 쉽습니다. 이는 웹 크롤링과 같이 복잡한 과정을 단순화하여 이해하고 디버깅하는 데 도움이 됩니다.
– 웹 크롤링에 유용한 라이브러리가 풍부합니다. Beautiful Soup, Scrapy, Selenium 등의 라이브러리를 사용하면 웹사이트의 HTML 소스 코드에서 데이터를 쉽게 추출할 수 있습니다.
– 데이터 분석에 강점을 가진 언어로서, 크롤링한 데이터를 분석하고 가공하는 데 유리합니다.

간단한 실습: BeautifulSoup 라이브러리로 웹 크롤링하기


import requests
from bs4 import BeautifulSoup

# 웹 페이지 가져오기
response = requests.get('https://www.example.com')

# BeautifulSoup 객체 생성
soup = BeautifulSoup(response.text, 'html.parser')

# 원하는 정보 추출
headlines = soup.find_all('h1')

for headline in headlines:
    print(headline.text)

위의 Python 코드는 www.example.com 이라는 웹페이지의 모든 h1 태그를 크롤링하여 출력하는 예제입니다. Python의 requests 라이브러리로 웹 페이지 정보를 가져오고, BeautifulSoup 라이브러리를 이용해 HTML 코드를 파싱하여 원하는 정보를 추출합니다.


웹 크롤링 준비하기

필요한 라이브러리 이해

Python 웹 크롤링에는 주로 requests, BeautifulSoup, Scrapy 등의 라이브러리를 사용합니다.

requests: Python에서 HTTP 요청을 보내는 라이브러리로, 웹 페이지의 HTML 데이터를 가져오는데 사용됩니다.

BeautifulSoup: HTML과 XML 파일에서 데이터를 추출하기 위한 Python 라이브러리로, 파싱을 쉽게 도와줍니다.

Scrapy: 웹 사이트에서 데이터를 추출하는 등의 복잡한 웹 크롤링 작업을 위한 오픈 소스 프레임워크입니다.

개발환경 설정

Python과 필요한 라이브러리를 설치하고 환경을 설정하는 방법은 다음과 같습니다.

– Python 설치: Python 공식 홈페이지(https://www.python.org/)에서 맞는 버전을 선택해 설치합니다.

– 필요한 라이브러리 설치: pip라는 패키지 관리자를 이용해 requests와 BeautifulSoup를 설치합니다.


pip install requests beautifulsoup4

이렇게 함으로써 웹 크롤링을 위한 기본적인 환경 설정을 마칠 수 있습니다. 이제 Python 코드를 작성하여 웹 크롤링을 수행할 준비가 완료되었습니다.


웹 데이터의 이해

HTML/CSS/JS 기본

웹 페이지는 주로 HTML, CSS, JavaScript로 이루어져 있습니다.

HTML(HyperText Markup Language): 웹 페이지의 기본적인 구조를 정의합니다. HTML 태그들로 구성되며, 웹 크롤링에서는 이 태그들을 통해 원하는 정보를 찾아냅니다.

CSS(Cascading Style Sheets): 웹 페이지의 디자인을 담당합니다. 배경색, 폰트 스타일, 위치 배치 등을 정의합니다.

JavaScript: 웹 페이지의 동작을 설명합니다. 사용자와 상호작용하거나 웹 페이지 내에서 데이터를 비동기적으로 전달하는 등 다양한 기능을 수행합니다.

위 세 가지 요소가 결합되어 웹 페이지가 사용자에게 보여집니다.

웹 데이터의 구조

웹 데이터는 주로 HTML 형식으로 이루어져 있습니다. HTML은 다양한 태그들로 구성되어 있는데, 이는 웹 크롤링에서 중요한 역할을 합니다. 웹 크롤러는 이러한 태그들을 통해 원하는 정보를 찾아냅니다.

예를 들어 다음과 같은 HTML 코드가 있다고 가정해봅니다.


<html>
    <body>
        <h1>My First Heading</h1>
        <p>My first paragraph.</p>
    </body>
</html>

여기에서 원하는 정보는 h1 태그 혹은 p 태그 안에 포함되어 있을 것입니다. 이런 식으로 웹 크롤링은 특정 HTML 태그 안의 정보를 찾아내는 작업을 포함하게 됩니다.


Python 웹 크롤링 기초

간단한 웹 크롤링 예제

Python의 requests와 BeautifulSoup 라이브러리를 이용하면 간단하게 웹 크롤링을 수행할 수 있습니다. 아래 코드는 “https://www.python.org/”에서 Python의 내용을 크롤링하는 예제입니다.


import requests
from bs4 import BeautifulSoup

url = "https://www.python.org/"
response = requests.get(url)

soup = BeautifulSoup(response.text, 'html.parser')
print(soup.title)  # 'Welcome to Python.org' 를 출력

결과 해석 및 저장

위 예제에서 soup.title은 해당 웹 페이지의 태그 안의 내용을 반환합니다. 결과 해석은 웹 페이지의 HTML 구조에 따라 달라집니다. 따라서 원하는 결과를 얻기 위해서는 웹 페이지의 구조를 잘 파악하는 것이 중요합니다.</p> <p>크롤링한 데이터를 저장하기 위해선 여러 방법이 있습니다. 가장 간단한 방법은 텍스트 파일에 쓰는 것입니다. 아래는 크롤링한 title을 텍스트 파일에 쓰는 예제입니다.</p> <pre class="wp-block-code language-python"><code class="language-python"> with open('output.txt', 'w') as f: f.write(str(soup.title)) </code></pre> <p>이와 같이 간단한 웹 크롤링을 통해 원하는 정보를 가져올 수 있습니다. 그러나 이보다 복잡한 데이터를 크롤링하려면 더 진보된 기법과 도구가 필요합니다.</p> <hr> <h2><span class="ez-toc-section" id="Python%EC%9D%84_%EC%9D%B4%EC%9A%A9%ED%95%9C_%EC%9B%B9_%ED%81%AC%EB%A1%A4%EB%A7%81_%EC%8B%AC%ED%99%94"></span>Python을 이용한 웹 크롤링 심화<span class="ez-toc-section-end"></span></h2> <h3><span class="ez-toc-section" id="%EB%8F%99%EC%A0%81%ED%8E%98%EC%9D%B4%EC%A7%80_%ED%81%AC%EB%A1%A4%EB%A7%81"></span>동적페이지 크롤링<span class="ez-toc-section-end"></span></h3> <p>동적페이지는 자바스크립트를 이용해 페이지 내용을 동적으로 변화시키는 웹페이지를 의미합니다. 이런 웹페이지에서 데이터를 크롤링하려면 웹드라이버(예: Selenium)이 필요합니다. 아래는 Selenium을 이용한 동적페이지 크롤링 예제입니다.</p> <pre class="wp-block-code language-python"><code class="language-python"> from selenium import webdriver url = "http://example.com/some/javascript/website" driver = webdriver.Chrome("/path/to/chromedriver") driver.get(url) # 페이지 로딩완료를 기다린다(예: sleep(5)) import time time.sleep(5) html = driver.page_source soup = BeautifulSoup(html, 'html.parser') print(soup.title) # 동적으로 로드된 페이지의 제목을 출력 driver.quit() </code></pre> <h3><span class="ez-toc-section" id="%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%9D%B4_%ED%95%84%EC%9A%94%ED%95%9C_%ED%8E%98%EC%9D%B4%EC%A7%80_%ED%81%AC%EB%A1%A4%EB%A7%81"></span>로그인이 필요한 페이지 크롤링<span class="ez-toc-section-end"></span></h3> <p>로그인이 필요한 페이지에서 크롤링을 수행하려면 로그인 과정을 통해 세션을 유지해야 합니다. 이를 위해선 `requests.Session`을 사용하고, 로그인 페이지에서 필요한 폼 데이터를 전송하면 됩니다. 아래 예제는 로그인이 필요한 페이지에서의 크롤링 방법을 보여줍니다.</p> <pre class="wp-block-code language-python"><code class="language-python"> import requests from bs4 import BeautifulSoup login_url = "http://example.com/login" data = { "username": "myusername", "password": "mypassword" } with requests.Session() as s: response = s.post(login_url, data=data) url = "http://example.com/my_page" response = s.get(url) soup = BeautifulSoup(response.text, 'html.parser') print(soup.title) </code></pre> <p>로그인 페이지와 폼 데이터는 웹사이트마다 다르므로 크롤링하기 전에 해당 웹사이트의 구조를 잘 파악하는 것이 중요합니다.</p> <hr> <h2><span class="ez-toc-section" id="%ED%81%AC%EB%A1%A4%EB%A7%81_%EB%8D%B0%EC%9D%B4%ED%84%B0_%EC%A0%95%EC%A0%9C%ED%95%98%EA%B8%B0"></span>크롤링 데이터 정제하기<span class="ez-toc-section-end"></span></h2> <h3><span class="ez-toc-section" id="%EB%8D%B0%EC%9D%B4%ED%84%B0_%ED%8F%AC%EB%A7%B7%ED%8C%85"></span>데이터 포맷팅<span class="ez-toc-section-end"></span></h3> <p>크롤링한 데이터는 이용하기에 앞서 적절한 형태로 포맷팅되어야 합니다. 즉, 불필요한 공백이나 기호를 제거하고, 통일된 형태로 데이터를 변경하는 것이 필요합니다. 아래는 교체, 삭제, 공백 제거 등의 작업을 수행하는 예제 코드입니다.</p> <pre class="wp-block-code language-python"><code class="language-python"> # 크롤링한 데이터 data = " Here is some Example data with unneeded symbols!... " # 불필요한 문자 및 공백 제거 data = data.replace("Example", "Real") # 'Example'을 'Real'로 교체 data = data.replace("!", "") # 느낌표 제거 data = data.strip() # 앞뒤 공백 제거 print(data) # 결과: 'Here is some Real data with unneeded symbols...' </code></pre> <h3><span class="ez-toc-section" id="%EB%8D%B0%EC%9D%B4%ED%84%B0_%ED%81%B4%EB%A6%B0%EC%A7%95"></span>데이터 클린징<span class="ez-toc-section-end"></span></h3> <p>데이터 클린징은 null 값, 중복값, 잘못된 값 등을 제거하거나 보정하여 데이터의 품질을 높이는 과정입니다. beautifulsoup의 `get_text()` 함수를 이용하면 HTML 태그를 제거할 수 있고, pandas의 `dropna()`, `drop_duplicates()` 함수 등을 이용하면 null 값이나 중복된 값 등을 효과적으로 처리할 수 있습니다.</p> <pre class="wp-block-code language-python"><code class="language-python"> import pandas as pd # 예시 데이터프레임 생성 df = pd.DataFrame({ 'A': [1, 2, None, 4, 5, 5], 'B': ['a', 'b', 'b', None, 'e', 'e'], }) # null 값 제거 df = df.dropna() # 중복값 제거 df = df.drop_duplicates() print(df) </code></pre> <p>위의 코드는 DataFrame이 주어졌을 때 null 값을 제거하는 방법과 중복된 값을 제거하는 방법을 보여줍니다. 데이터 크리닝은 크롤링된 데이터의 품질을 향상시키는 중요한 단계이므로 주의 깊게 수행해야 합니다.</p> <hr> <h2><span class="ez-toc-section" id="%ED%81%AC%EB%A1%A4%EB%A7%81_%EC%9E%90%EB%8F%99%ED%99%94_%EB%B0%8F_%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81"></span>크롤링 자동화 및 스케줄링<span class="ez-toc-section-end"></span></h2> <h3><span class="ez-toc-section" id="%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81_%EB%B0%A9%EB%B2%95"></span>스케줄링 방법<span class="ez-toc-section-end"></span></h3> <p>크롤링 작업을 정기적으로 자동화하려면 스케줄링이 필요합니다. Python에서 스케줄링을 구현하는 다양한 방법이 있습니다. 가장 간단한 방법 중 하나는 `schedule` 라이브러리를 사용하는 것입니다. </p> <pre class="wp-block-code language-python"><code class="language-python"> import schedule import time def job(): print("크롤링 작업 수행") # 매일 13시에 크롤링 작업 수행 schedule.every().day.at("13:00").do(job) while True: schedule.run_pending() # 예약된 작업 실행 time.sleep(1) # 다음 작업까지 1초 대기 </code></pre> <h3><span class="ez-toc-section" id="%EC%97%90%EB%9F%AC_%ED%95%B8%EB%93%A4%EB%A7%81"></span>에러 핸들링<span class="ez-toc-section-end"></span></h3> <p>크롤링 중에 에러와 예외상황을 무시하지 않고 적절히 처리해야 합니다. `try-except`문을 사용하여 예외상황을 처리하고, 에러 메시지를 로깅하여 추후 문제를 쉽게 파악하도록 할 수 있습니다.</p> <pre class="wp-block-code language-python"><code class="language-python"> import requests import logging url = "http://example.com" try: response = requests.get(url) response.raise_for_status() except requests.exceptions.HTTPError as errh: logging.error(f"HTTP Error: {errh}") except requests.exceptions.ConnectionError as errc: logging.error(f"Error Connecting: {errc}") except requests.exceptions.Timeout as errt: logging.error(f"Timeout Error: {errt}") except requests.exceptions.RequestException as err: logging.error(f"Error: {err}") </code></pre> <p>이런 방식으로 예외가 발생한 경우에만 해당 코드 블록이 실행되고, 에러메시지를 기록하게 함으로써 원인 분석 및 문제 해결에 도움을 줄 수 있습니다. </p> <hr> <h2><span class="ez-toc-section" id="%EC%9B%B9_%ED%81%AC%EB%A1%A4%EB%A7%81%EC%9D%98_%EC%9C%A4%EB%A6%AC%EC%A0%81_%EB%B2%95%EC%A0%81_%EA%B3%A0%EB%A0%A4%EC%82%AC%ED%95%AD"></span>웹 크롤링의 윤리적, 법적 고려사항<span class="ez-toc-section-end"></span></h2> <p>웹 크롤링은 다양한 정보를 수집하는 강력한 방법이지만, 반드시 윤리적, 법적 지침을 따라야 합니다. 잘못 사용하면 개인 정보 침해, 저작권 침해, 규정 위반이 될 수 있으므로 주의해야 합니다.</p> <h3><span class="ez-toc-section" id="robotstxt_%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0"></span>robots.txt 확인하기<span class="ez-toc-section-end"></span></h3> <p>웹사이트의 `robots.txt` 파일은 웹크롤러가 접근할 수 있는 영역을 정의합니다. 따라서 크롤링을 진행하기 전에 꼭 해당 파일을 확인해야 합니다.</p> <pre class="wp-block-code language-python"><code class="language-python"> import requests from urllib.parse import urlparse, urljoin url = "http://example.com" parsed_url = urlparse(url) # robots.txt의 위치를 구하는 코드 robot_url = urljoin(url, "/robots.txt") # robots.txt 내용 출력 response = requests.get(robot_url) print(response.text) </code></pre> <h3><span class="ez-toc-section" id="%EC%82%AC%EC%9A%A9%EC%9E%90_%EB%8F%99%EC%9D%98_%EB%B0%8F_%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4_%EC%B7%A8%EA%B8%89%EB%B0%A9%EC%B9%A8"></span>사용자 동의 및 개인정보 취급방침<span class="ez-toc-section-end"></span></h3> <p>사용자의 명시적 동의 없이 개인정보를 수집하는 것은 불법입니다. 또한 수집한 정보의 사용 목적, 보관 기간 등을 사전에 고지해야 하는 법적 의무가 있습니다. 이러한 내용은 일반적으로 웹사이트의 ‘개인정보 취급방침’에서 확인할 수 있습니다.</p> <h3><span class="ez-toc-section" id="%EC%A0%80%EC%9E%91%EA%B6%8C_%EB%B2%95%EB%A5%A0_%EC%A4%80%EC%88%98"></span>저작권 법률 준수<span class="ez-toc-section-end"></span></h3> <p>모든 웹 콘텐츠는 원칙적으로 저작권의 보호를 받습니다. 따라서 어떠한 창작물이라도 무단으로 크롤링해 사용하는 것은 저작권 침해가 될 수 있으니 주의해야 합니다.</p> <hr> <h2><span class="ez-toc-section" id="%EC%9B%B9_%ED%81%AC%EB%A1%A4%EB%A7%81_%ED%99%9C%EC%9A%A9%EC%82%AC%EB%A1%80"></span>웹 크롤링 활용사례<span class="ez-toc-section-end"></span></h2> <p>웹 크롤링은 다양한 분야에서 활용되고 있습니다. 여러 기업은 웹 크롤링을 통해 경쟁사 비교 분석, 고객 피드백 모니터링, 시장 분석 등을 진행하며, 이 외에도 소셜 미디어 분석, 뉴스 모니터링 등에 활용되고 있습니다.</p> <h3><span class="ez-toc-section" id="%EB%84%A4%EC%9D%B4%EB%B2%84_%EC%98%81%ED%99%94_%ED%8F%89%EC%A0%90_%ED%81%AC%EB%A1%A4%EB%A7%81_%EC%98%88%EC%8B%9C"></span>네이버 영화 평점 크롤링 예시<span class="ez-toc-section-end"></span></h3> <p>영화 리뷰와 평점을 크롤링하여 감정 분석이나 트렌드 분석을 수행할 수 있습니다.</p> <pre class="wp-block-code language-python"><code class="language-python"> import requests from bs4 import BeautifulSoup url = "https://movie.naver.com/movie/point/af/list.nhn" response = requests.get(url) soup = BeautifulSoup(response.text, 'html.parser') table = soup.find('table', class_='list_netizen') for row in table.find_all('tr'): columns = row.find_all('td') if len(columns) > 1: print("영화 제목:", columns[1].text.strip()) print("평점:", columns[2].text.strip()) print("리뷰:", columns[3].text.strip()) print("-"*50) </code></pre> <h2><span class="ez-toc-section" id="%EB%A7%88%EB%AC%B4%EB%A6%AC"></span>마무리<span class="ez-toc-section-end"></span></h2> <p>웹 크롤링은 인터넷에 존재하는 방대한 데이터를 수집하고 분석하는 중요한 방법입니다. 정보의 접근성을 향상시키며, 기업이나 연구자들에게 가치 있는 인사이트를 제공합니다. 단, 크롤링을 진행할 때는 운영지침과 법률을 준수하며, 웹사이트의 로딩 시간에 영향을 미치지 않도록 적절한 주기로 수행해야 합니다.</p> <hr> </div> <footer class="entry-meta" aria-label="Entry meta"> <span class="cat-links"><span class="gp-icon icon-categories"><svg viewBox="0 0 512 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><path d="M0 112c0-26.51 21.49-48 48-48h110.014a48 48 0 0143.592 27.907l12.349 26.791A16 16 0 00228.486 128H464c26.51 0 48 21.49 48 48v224c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V112z" /></svg></span><span class="screen-reader-text">Categories </span><a href="https://www.deviantceblog.com/category/it/" rel="category tag">IT</a></span> <span class="tags-links"><span class="gp-icon icon-tags"><svg viewBox="0 0 512 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><path d="M20 39.5c-8.836 0-16 7.163-16 16v176c0 4.243 1.686 8.313 4.687 11.314l224 224c6.248 6.248 16.378 6.248 22.626 0l176-176c6.244-6.244 6.25-16.364.013-22.615l-223.5-224A15.999 15.999 0 00196.5 39.5H20zm56 96c0-13.255 10.745-24 24-24s24 10.745 24 24-10.745 24-24 24-24-10.745-24-24z"/><path d="M259.515 43.015c4.686-4.687 12.284-4.687 16.97 0l228 228c4.686 4.686 4.686 12.284 0 16.97l-180 180c-4.686 4.687-12.284 4.687-16.97 0-4.686-4.686-4.686-12.284 0-16.97L479.029 279.5 259.515 59.985c-4.686-4.686-4.686-12.284 0-16.97z" /></svg></span><span class="screen-reader-text">Tags </span><a href="https://www.deviantceblog.com/tag/python/" rel="tag">Python</a></span> <nav id="nav-below" class="post-navigation" aria-label="Posts"> <div class="nav-previous"><span class="gp-icon icon-arrow-left"><svg viewBox="0 0 192 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M178.425 138.212c0 2.265-1.133 4.813-2.832 6.512L64.276 256.001l111.317 111.277c1.7 1.7 2.832 4.247 2.832 6.513 0 2.265-1.133 4.813-2.832 6.512L161.43 394.46c-1.7 1.7-4.249 2.832-6.514 2.832-2.266 0-4.816-1.133-6.515-2.832L16.407 262.514c-1.699-1.7-2.832-4.248-2.832-6.513 0-2.265 1.133-4.813 2.832-6.512l131.994-131.947c1.7-1.699 4.249-2.831 6.515-2.831 2.265 0 4.815 1.132 6.514 2.831l14.163 14.157c1.7 1.7 2.832 3.965 2.832 6.513z" fill-rule="nonzero" /></svg></span><span class="prev"><a href="https://www.deviantceblog.com/it-58/" rel="prev">C#을 이용한 웹 스크래핑: 효율적인 데이터 수집 방법</a></span></div><div class="nav-next"><span class="gp-icon icon-arrow-right"><svg viewBox="0 0 192 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M178.425 256.001c0 2.266-1.133 4.815-2.832 6.515L43.599 394.509c-1.7 1.7-4.248 2.833-6.514 2.833s-4.816-1.133-6.515-2.833l-14.163-14.162c-1.699-1.7-2.832-3.966-2.832-6.515 0-2.266 1.133-4.815 2.832-6.515l111.317-111.316L16.407 144.685c-1.699-1.7-2.832-4.249-2.832-6.515s1.133-4.815 2.832-6.515l14.163-14.162c1.7-1.7 4.249-2.833 6.515-2.833s4.815 1.133 6.514 2.833l131.994 131.993c1.7 1.7 2.832 4.249 2.832 6.515z" fill-rule="nonzero" /></svg></span><span class="next"><a href="https://www.deviantceblog.com/it-60/" rel="next">Node.js를 활용한 간단한 REST API 구현하기: 실전 가이드</a></span></div> </nav> </footer> </div> </article> <div class="comments-area"> <div id="comments"> <div id="respond" class="comment-respond"> <h3 id="reply-title" class="comment-reply-title">Leave a Comment <small><a rel="nofollow" id="cancel-comment-reply-link" href="/it-59/#respond" style="display:none;">Cancel reply</a></small></h3><form action="https://www.deviantceblog.com/wp-comments-post.php" method="post" id="commentform" class="comment-form" novalidate><p class="comment-form-comment"><label for="comment" class="screen-reader-text">Comment</label><textarea id="comment" name="comment" cols="45" rows="8" required></textarea></p><label for="author" class="screen-reader-text">Name</label><input placeholder="Name *" id="author" name="author" type="text" value="" size="30" required /> <label for="email" class="screen-reader-text">Email</label><input placeholder="Email *" id="email" name="email" type="email" value="" size="30" required /> <label for="url" class="screen-reader-text">Website</label><input placeholder="Website" id="url" name="url" type="url" value="" size="30" /> <p class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes" /> <label for="wp-comment-cookies-consent">Save my name, email, and website in this browser for the next time I comment.</label></p> <p class="comment-subscription-form"><input type="checkbox" name="subscribe_comments" id="subscribe_comments" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;" /> <label class="subscribe-label" id="subscribe-label" for="subscribe_comments">Notify me of follow-up comments by email.</label></p><p class="comment-subscription-form"><input type="checkbox" name="subscribe_blog" id="subscribe_blog" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;" /> <label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog">Notify me of new posts by email.</label></p><p class="form-submit"><input name="submit" type="submit" id="submit" class="submit" value="Post Comment" /> <input type='hidden' name='comment_post_ID' value='497' id='comment_post_ID' /> <input type='hidden' name='comment_parent' id='comment_parent' value='0' /> </p><p style="display: none !important;"><label>Δ<textarea name="ak_hp_textarea" cols="45" rows="8" maxlength="100"></textarea></label><input type="hidden" id="ak_js_1" name="ak_js" value="218"/></p></form> </div><!-- #respond --> </div><!-- #comments --> </div> </main> </div> </div> </div> <div class="site-footer"> <footer class="site-info" aria-label="Site" itemtype="https://schema.org/WPFooter" itemscope> <div class="inside-site-info grid-container"> <div class="copyright-bar"> 2024 deviantce </div> </div> </footer> </div> <nav id="generate-slideout-menu" class="main-navigation slideout-navigation" itemtype="https://schema.org/SiteNavigationElement" itemscope> <div class="inside-navigation grid-container grid-parent"> <button class="slideout-exit has-svg-icon"><span class="gp-icon pro-close"> <svg viewBox="0 0 512 512" aria-hidden="true" role="img" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1em" height="1em"> <path d="M71.029 71.029c9.373-9.372 24.569-9.372 33.942 0L256 222.059l151.029-151.03c9.373-9.372 24.569-9.372 33.942 0 9.372 9.373 9.372 24.569 0 33.942L289.941 256l151.03 151.029c9.372 9.373 9.372 24.569 0 33.942-9.373 9.372-24.569 9.372-33.942 0L256 289.941l-151.029 151.03c-9.373 9.372-24.569 9.372-33.942 0-9.372-9.373-9.372-24.569 0-33.942L222.059 256 71.029 104.971c-9.372-9.373-9.372-24.569 0-33.942z" /> </svg> </span> <span class="screen-reader-text">Close</span></button><div class="main-nav"><ul id="menu-primary-split-2" class=" slideout-menu"><li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-94"><a href="https://deviantceblog.com/">Home</a></li> <li class="menu-item menu-item-type-taxonomy menu-item-object-category current-post-ancestor current-menu-parent current-post-parent menu-item-208"><a href="https://www.deviantceblog.com/category/it/">IT</a></li> <li class="menu-item-separator menu-item menu-item-type-custom menu-item-object-custom menu-item-97"><a>Menu Item Separator</a></li> </ul></div> </div><!-- .inside-navigation --> </nav><!-- #site-navigation --> <div class="slideout-overlay"> </div> <!--[if lte IE 11]> <script data-jetpack-boost="ignore" src='https://www.deviantceblog.com/wp-content/themes/generatepress/assets/js/classList.min.js?ver=3.3.0' id='generate-classlist-js'></script> <![endif]--> <script type="application/ld+json" class="rank-math-schema">{"@context":"https://schema.org","@graph":[{"@type":["Person","Organization"],"@id":"https://www.deviantceblog.com/#person","name":"user","logo":{"@type":"ImageObject","@id":"https://www.deviantceblog.com/#logo","url":"https://deviantceblog.com/wp-content/uploads/2023/07/cropped-cropped-deviantce-1.png","contentUrl":"https://deviantceblog.com/wp-content/uploads/2023/07/cropped-cropped-deviantce-1.png","caption":"\ub514\ube44\uc548\uce20 \ube14\ub85c\uadf8","inLanguage":"en-US","width":"512","height":"512"},"image":{"@type":"ImageObject","@id":"https://www.deviantceblog.com/#logo","url":"https://deviantceblog.com/wp-content/uploads/2023/07/cropped-cropped-deviantce-1.png","contentUrl":"https://deviantceblog.com/wp-content/uploads/2023/07/cropped-cropped-deviantce-1.png","caption":"\ub514\ube44\uc548\uce20 \ube14\ub85c\uadf8","inLanguage":"en-US","width":"512","height":"512"}},{"@type":"WebSite","@id":"https://www.deviantceblog.com/#website","url":"https://www.deviantceblog.com","name":"\ub514\ube44\uc548\uce20 \ube14\ub85c\uadf8","alternateName":"deviantce blog","publisher":{"@id":"https://www.deviantceblog.com/#person"},"inLanguage":"en-US"},{"@type":"ImageObject","@id":"https://i0.wp.com/www.deviantceblog.com/wp-content/uploads/2023/07/cropped-cropped-deviantce-1.png?fit=512%2C512&ssl=1","url":"https://i0.wp.com/www.deviantceblog.com/wp-content/uploads/2023/07/cropped-cropped-deviantce-1.png?fit=512%2C512&ssl=1","width":"512","height":"512","inLanguage":"en-US"},{"@type":"WebPage","@id":"https://www.deviantceblog.com/it-59/#webpage","url":"https://www.deviantceblog.com/it-59/","name":"Python\uc744 \uc774\uc6a9\ud55c \uc6f9 \ud06c\ub864\ub9c1: \ud6a8\uc728\uc801\uc778 \ub370\uc774\ud130 \uc218\uc9d1 \ubc29\ubc95 - \ub514\ube44\uc548\uce20 \ube14\ub85c\uadf8","datePublished":"2023-10-29T19:24:23+09:00","dateModified":"2023-10-29T19:24:23+09:00","isPartOf":{"@id":"https://www.deviantceblog.com/#website"},"primaryImageOfPage":{"@id":"https://i0.wp.com/www.deviantceblog.com/wp-content/uploads/2023/07/cropped-cropped-deviantce-1.png?fit=512%2C512&ssl=1"},"inLanguage":"en-US"},{"@type":"Person","@id":"https://www.deviantceblog.com/author/deviantce/","name":"deviantce master","url":"https://www.deviantceblog.com/author/deviantce/","image":{"@type":"ImageObject","@id":"https://secure.gravatar.com/avatar/10ddc4840e8a02143af22b9470281ede?s=96&d=mm&r=g","url":"https://secure.gravatar.com/avatar/10ddc4840e8a02143af22b9470281ede?s=96&d=mm&r=g","caption":"deviantce master","inLanguage":"en-US"}},{"@type":"BlogPosting","headline":"Python\uc744 \uc774\uc6a9\ud55c \uc6f9 \ud06c\ub864\ub9c1: \ud6a8\uc728\uc801\uc778 \ub370\uc774\ud130 \uc218\uc9d1 \ubc29\ubc95 - \ub514\ube44\uc548\uce20 \ube14\ub85c\uadf8","datePublished":"2023-10-29T19:24:23+09:00","dateModified":"2023-10-29T19:24:23+09:00","articleSection":"IT","author":{"@id":"https://www.deviantceblog.com/author/deviantce/","name":"deviantce master"},"publisher":{"@id":"https://www.deviantceblog.com/#person"},"description":"\uc6f9 \ud06c\ub864\ub9c1\uc740 \uc6f9 \ud398\uc774\uc9c0\ub97c \ubc29\ubb38\ud558\uace0 \uc790\ub3d9\uc73c\ub85c \uc815\ubcf4\ub97c \uc218\uc9d1\ud558\ub294 \uacfc\uc815\uc744 \ub9d0\ud569\ub2c8\ub2e4. \uc774\ub97c \ud1b5\ud574 \ube60\ub974\uace0 \ud6a8\uacfc\uc801\uc73c\ub85c \uc6f9\uc5d0\uc11c \uc6d0\ud558\ub294 \uc815\ubcf4\ub97c \ucc3e\uc544\ub0bc \uc218 \uc788\uc2b5\ub2c8\ub2e4. Python\uacfc \uac19\uc740 \ud504\ub85c\uadf8\ub798\ubc0d \uc5b8\uc5b4\ub97c \uc0ac\uc6a9\ud558\uba74, \ud2b9\uc815 \uc6f9\uc0ac\uc774\ud2b8\uc758 HTML\uc744 \uc77d\uace0 \ubd84\uc11d, \uc6d0\ud558\ub294 \uc815\ubcf4\ub9cc \ucd94\ucd9c\uc774 \uac00\ub2a5\ud569\ub2c8\ub2e4.","name":"Python\uc744 \uc774\uc6a9\ud55c \uc6f9 \ud06c\ub864\ub9c1: \ud6a8\uc728\uc801\uc778 \ub370\uc774\ud130 \uc218\uc9d1 \ubc29\ubc95 - \ub514\ube44\uc548\uce20 \ube14\ub85c\uadf8","@id":"https://www.deviantceblog.com/it-59/#richSnippet","isPartOf":{"@id":"https://www.deviantceblog.com/it-59/#webpage"},"image":{"@id":"https://i0.wp.com/www.deviantceblog.com/wp-content/uploads/2023/07/cropped-cropped-deviantce-1.png?fit=512%2C512&ssl=1"},"inLanguage":"en-US","mainEntityOfPage":{"@id":"https://www.deviantceblog.com/it-59/#webpage"}}]}</script><script> window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/14.0.0\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/14.0.0\/svg\/","svgExt":".svg","source":{"concatemoji":"https:\/\/www.deviantceblog.com\/wp-includes\/js\/wp-emoji-release.min.js?ver=6.2.6"}}; /*! This file is auto-generated */ !function(e,a,t){var n,r,o,i=a.createElement("canvas"),p=i.getContext&&i.getContext("2d");function s(e,t){p.clearRect(0,0,i.width,i.height),p.fillText(e,0,0);e=i.toDataURL();return p.clearRect(0,0,i.width,i.height),p.fillText(t,0,0),e===i.toDataURL()}function c(e){var t=a.createElement("script");t.src=e,t.defer=t.type="text/javascript",a.getElementsByTagName("head")[0].appendChild(t)}for(o=Array("flag","emoji"),t.supports={everything:!0,everythingExceptFlag:!0},r=0;r<o.length;r++)t.supports[o[r]]=function(e){if(p&&p.fillText)switch(p.textBaseline="top",p.font="600 32px Arial",e){case"flag":return s("\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f","\ud83c\udff3\ufe0f\u200b\u26a7\ufe0f")?!1:!s("\ud83c\uddfa\ud83c\uddf3","\ud83c\uddfa\u200b\ud83c\uddf3")&&!s("\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f","\ud83c\udff4\u200b\udb40\udc67\u200b\udb40\udc62\u200b\udb40\udc65\u200b\udb40\udc6e\u200b\udb40\udc67\u200b\udb40\udc7f");case"emoji":return!s("\ud83e\udef1\ud83c\udffb\u200d\ud83e\udef2\ud83c\udfff","\ud83e\udef1\ud83c\udffb\u200b\ud83e\udef2\ud83c\udfff")}return!1}(o[r]),t.supports.everything=t.supports.everything&&t.supports[o[r]],"flag"!==o[r]&&(t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&t.supports[o[r]]);t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&!t.supports.flag,t.DOMReady=!1,t.readyCallback=function(){t.DOMReady=!0},t.supports.everything||(n=function(){t.readyCallback()},a.addEventListener?(a.addEventListener("DOMContentLoaded",n,!1),e.addEventListener("load",n,!1)):(e.attachEvent("onload",n),a.attachEvent("onreadystatechange",function(){"complete"===a.readyState&&t.readyCallback()})),(e=t.source||{}).concatemoji?c(e.concatemoji):e.wpemoji&&e.twemoji&&(c(e.twemoji),c(e.wpemoji)))}(window,document,window._wpemojiSettings); </script><script src='https://www.deviantceblog.com/wp-includes/js/jquery/jquery.min.js?ver=3.6.4' id='jquery-core-js'></script><script src='https://www.deviantceblog.com/wp-includes/js/jquery/jquery-migrate.min.js?ver=3.4.0' id='jquery-migrate-js'></script><script id='ez-toc-scroll-scriptjs-js-after'> jQuery(document).ready(function(){document.querySelectorAll(".ez-toc-section").forEach(t=>{t.setAttribute("ez-toc-data-id","#"+decodeURI(t.getAttribute("id")))}),jQuery("a.ez-toc-link").click(function(){let t=jQuery(this).attr("href"),e=jQuery("#wpadminbar"),i=0;30>30&&(i=30),e.length&&(i+=e.height()),jQuery('[ez-toc-data-id="'+decodeURI(t)+'"]').length>0&&(i=jQuery('[ez-toc-data-id="'+decodeURI(t)+'"]').offset().top-i),jQuery("html, body").animate({scrollTop:i},500)})}); </script><script src='https://www.googletagmanager.com/gtag/js?id=GT-K5LQQNB' id='google_gtagjs-js' async></script><script id='google_gtagjs-js-after'> window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);} gtag('set', 'linker', {"domains":["www.deviantceblog.com"]} ); gtag("js", new Date()); gtag("set", "developer_id.dZTNiMT", true); gtag("config", "GT-K5LQQNB"); </script><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3705698241315974" crossorigin="anonymous"></script><script> document.documentElement.classList.add( 'jetpack-lazy-images-js-enabled' ); </script><script id="google_gtagjs" src="https://www.googletagmanager.com/gtag/js?id=G-J77CZG4XXD" async></script><script id="google_gtagjs-inline"> window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'G-J77CZG4XXD', {} ); </script><script>document.getElementById( "ak_js_1" ).setAttribute( "value", ( new Date() ).getTime() );</script><script id="generate-a11y">!function(){"use strict";if("querySelector"in document&&"addEventListener"in window){var e=document.body;e.addEventListener("mousedown",function(){e.classList.add("using-mouse")}),e.addEventListener("keydown",function(){e.classList.remove("using-mouse")})}}();</script><script>window.addEventListener( 'load', function() { document.querySelectorAll( 'link' ).forEach( function( e ) {'not all' === e.media && e.dataset.media && ( e.media = e.dataset.media, delete e.dataset.media );} ); var e = document.getElementById( 'jetpack-boost-critical-css' ); e && ( e.media = 'not all' ); } );</script><script id='generate-offside-js-extra'> var offSide = {"side":"right"}; </script><script src='https://www.deviantceblog.com/wp-content/plugins/gp-premium/menu-plus/functions/js/offside.min.js?ver=2.3.1' id='generate-offside-js'></script><script src='https://www.deviantceblog.com/wp-content/plugins/jetpack-boost/jetpack_vendor/automattic/jetpack-image-cdn/dist/image-cdn.js?minify=false&ver=132249e245926ae3e188' id='jetpack-photon-js'></script><script src='https://www.deviantceblog.com/wp-content/plugins/easy-table-of-contents/vendor/js-cookie/js.cookie.min.js?ver=2.2.1' id='ez-toc-js-cookie-js'></script><script src='https://www.deviantceblog.com/wp-content/plugins/easy-table-of-contents/vendor/sticky-kit/jquery.sticky-kit.min.js?ver=1.9.2' id='ez-toc-jquery-sticky-kit-js'></script><script id='ez-toc-js-js-extra'> var ezTOC = {"smooth_scroll":"1","visibility_hide_by_default":"","scroll_offset":"30","fallbackIcon":"<span class=\"\"><span style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span>"}; </script><script src='https://www.deviantceblog.com/wp-content/plugins/easy-table-of-contents/assets/js/front.min.js?ver=2.0.52-1690892633' id='ez-toc-js-js'></script><script id='generate-menu-js-extra'> var generatepressMenu = {"toggleOpenedSubMenus":"1","openSubMenuLabel":"Open Sub-Menu","closeSubMenuLabel":"Close Sub-Menu"}; </script><script src='https://www.deviantceblog.com/wp-content/themes/generatepress/assets/js/menu.min.js?ver=3.3.0' id='generate-menu-js'></script><script id='generate-navigation-search-js-extra'> var generatepressNavSearch = {"open":"Open Search Bar","close":"Close Search Bar"}; </script><script src='https://www.deviantceblog.com/wp-content/themes/generatepress/assets/js/navigation-search.min.js?ver=3.3.0' id='generate-navigation-search-js'></script><script src='https://www.deviantceblog.com/wp-includes/js/comment-reply.min.js?ver=6.2.6' id='comment-reply-js'></script><script src='https://www.deviantceblog.com/wp-content/plugins/jetpack-boost/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.js?minify=false&ver=83ec8aa758f883d6da14' id='jetpack-lazy-images-polyfill-intersectionobserver-js'></script><script id='jetpack-lazy-images-js-extra'> var jetpackLazyImagesL10n = {"loading_warning":"Images are still loading. Please cancel your print and try again."}; </script><script src='https://www.deviantceblog.com/wp-content/plugins/jetpack-boost/jetpack_vendor/automattic/jetpack-lazy-images/dist/lazy-images.js?minify=false&ver=2e29137590434abf5fbe' id='jetpack-lazy-images-js'></script><script src='https://www.deviantceblog.com/wp-content/plugins/highlighting-code-block/assets/js/prism.js?ver=1.7.0' id='hcb-prism-js'></script><script src='https://www.deviantceblog.com/wp-includes/js/clipboard.min.js?ver=2.0.11' id='clipboard-js'></script><script id='hcb-script-js-extra'> var hcbVars = {"showCopyBtn":"1","copyBtnLabel":"Copy code to clipboard"}; </script><script src='https://www.deviantceblog.com/wp-content/plugins/highlighting-code-block/build/js/hcb_script.js?ver=1.7.0' id='hcb-script-js'></script><script defer src='https://stats.wp.com/e-202438.js' id='jetpack-stats-js'></script><script id='jetpack-stats-js-after'> _stq = window._stq || []; _stq.push([ "view", {v:'ext',blog:'221714609',post:'497',tz:'9',srv:'www.deviantceblog.com',j:'1:12.3'} ]); _stq.push([ "clickTrackerInit", "221714609", "497" ]); </script></body> </html>