파일 입출력

|

Python에서의 파일 입출력

일반적으로 파일 입출력(IO, Input/Output)은 다음과 같은 형태를 가집니다.

file = open(filepath, mode="r")
#...
file.close()

하지만, with 문을 사용할 경우 구문이 종료되면 파일을 자동으로 닫아주기 때문에 좀 더 효율적으로 사용할 수 있습니다.

with open(filepath, mode="r") as file:

파일에서 바이너리 데이터를 읽을 경우 다음과 같은 함수들을 사용할 수 있습니다.

  • file.read() : 모든 데이터를 문자열이나 바이너리 형태로 읽는다.
  • file.read(n) : n 개의 바이트를 읽는다.
  • file.readline() : 한 라인을 읽는다.
  • file.readlines() : 모든 라인을 문자열 리스트 형태로 읽는다.
  • file.write(line) : 한 라인을 기록한다.
  • file.writelines(lines) : 문자열 리스트를 기록한다.

Python vs 아니콘다(Anaconda) 차이점

|

Python vs 아나콘다

Python과 아나콘다(Anaconda)의 차이점은 다음과 같습니다.

Image

Python은 파이썬 공식 홈페이지에서 받을 수 있으며, pip 툴만을 포함하고 있습니다. 필요한 패키지나 라이브러리 등을 설치할 때 모두 수동으로 해줘야 합니다.

아나콘다는 Python 기본 패키지에 각종 수학/과학 라이브러리들을 같이 패키징해서 배포하는 버전입니다. 여기에서 다운로드 할 수 있습니다. 아나콘다에 포함된 툴들로는 대표적으로 panda, numpy, scipy, sklearn, matplotlib, Jupyter Notebook 등이 있습니다.


설치시 유의 사항

Python이나 아나콘다 중 하나만 설치하는 것을 추천합니다. 둘 다 설치할 경우 중복되는 파일들이 많으며 환경 변수 충돌 등의 문제가 발생할 수도 있습니다.


어떤 것을 선택할 것인가

아나콘다에 포함되어 있는 라이브러리들이 불필요한 경우에는 기본 Python만 설치해도 무관합니다. 하지만, 요즘 유행하는 인공지능이나 빅데이터 관련 개발을 할 경우에는 결국 아나콘다에 포함된 라이브러리들을 설치할 가능성이 높기 때문에 애초에 아나콘다를 설치하는 것이 더 유리합니다. 일일이 라이브러리들을 설치하다보면 의존성 문제 등이 발생할 수도 있기 때문입니다.


아나콘다 사용법

conda search python

설치되어 있는 Python의 버전 또는 라이브러리들의 버전을 확인할 수 있습니다.


conda create -n py36 python=3.6.1 anaconda

3.6.1 버전의 Python을 사용하는 py36이라는 환경을 생성합니다. 생성된 환경은 아래의 명령어를 이용해서 활성화/비활성화를 할 수 있습니다.

active py36
deactive py36


conda install python=3.6.1

위 명령어는 아나콘다의 Python 기본 버전을 설정하는 명령어입니다.

링크에 있는 컨텐츠를 전부 내려 받는 방법

|

Python 3.x 기반의 코드입니다.

상대 경로 사용 방법

소스 코드

from urllib.parse import urljoin

baseUrl = "http://snowdeer.github.io/blog/categories/"

print(urljoin(baseUrl, "/python/2017/05/01/difference-between-python-2-7-x-and-python-3-x/"))
print(urljoin(baseUrl, "/python/2017/11/03/download-file-from-network/"))
print(urljoin(baseUrl, "/python/2017/11/04/get-html-body/"))


실행 결과

http://snowdeer.github.io/python/2017/05/01/difference-between-python-2-7-x-and-python-3-x/
http://snowdeer.github.io/python/2017/11/03/download-file-from-network/
http://snowdeer.github.io/python/2017/11/04/get-html-body/

위의 예제와 같이 baseUrl 변수를 기본 URL 아래의 하위 카테고리 경로까지 지정했음에도, urljoin을 이용해서 만들어진 상대 경로들은 올바르게 만들어지는 것을 확인할 수 있습니다.


재귀적으로 하위 링크들의 컨텐츠를 모두 내려받는 코드

아래의 코드를 활용하면 특정 웹 페이지의 하위 링크들을 재귀적으로 방문하면서 모두 저장하게 됩니다. 단, BeautifulSoup 라이브러리를 이용하여 웹페이지 파싱을 통해 하위 링크들을 검색하기 때문에 저장하려는 대상 페이지에 따라 파싱 코드를 따로 작성을 해야 합니다.

소스 코드

from bs4 import BeautifulSoup
from urllib.request import *
from urllib.parse import *
from os import makedirs
import os.path, time, re

# 이미 처리가 끝난 파일인지 확인하기 위한 용도
finished_files = {}

# HTML 문서 내부의 링크 추출
def get_links_in_html(html, base):
    soup = BeautifulSoup(html, "html.parser")
    links = soup.select("link[rel='stylesheet']")
    links += soup.select("a[href]")
    result_list = []

    for a in links:
        href = a.attrs['href']
        url = urljoin(base, href)   # 링크를 절대 경로로 변환
        result_list.append(url)

    return result_list

# URL로부터 파일 다운로드
def download_file(url):
    out = urlparse(url)
    file_path = "./" + out.netloc + out.path
    if re.search(r"/$", file_path):
        file_path += "index.html"

    folder_path = os.path.dirname(file_path)

    if os.path.exists(file_path):
        return file_path

    if not os.path.exists(folder_path):
        print("make folder: ", folder_path)
        makedirs(folder_path)

    try:
        print("download file: ", url)
        urlretrieve(url, file_path)
        time.sleep(1)
        return file_path

    except:
        print("download failed: ", url)
        return None

def analyze_html(url, root_url):
    file_path = download_file(url)

    if file_path is None:
        return

    if file_path in finished_files:
        return

    finished_files[file_path] = True
    print("analyze_html: ", url)

    html = open(file_path, "r", encoding="utf-8").read()
    links = get_links_in_html(html, url)

    for link_url in links:
        if link_url.find(root_url) != 0:
            if not re.search(r".css$", link_url):
                continue

            if re.search(r".(html|htm)$", link_url):
                analyze_html(link_url, root_url)
                continue

            download_file(link_url)

if __name__ == "__main__":
    url = "https://docs.python.org/3.7/library/"
    analyze_html(url, url)


실행 결과

make folder:  ./docs.python.org/3.7/library
download file:  https://docs.python.org/3.7/library/
analyze_html:  https://docs.python.org/3.7/library/
make folder:  ./docs.python.org/3.7/_static
download file:  https://docs.python.org/3.7/_static/pydoctheme.css
download file:  https://docs.python.org/3.7/_static/pygments.css

BeautifulSoup 라이브러리 활용 웹페이지 파싱(Parsing)

|

Python 3.x 기반의 코드입니다.

BeautifulSoup 라이브러리 설치

pip를 이용해서 설치합니다. 콘솔창에서 다음 명령어를 입력합니다.

> pip install beautifulsoup4


예제 코드

기본적인 사용법

from bs4 import BeautifulSoup

html = """

    
        

Hello, BeautifulSoup

This is a example.

BeautifulSoup helps to scrap web page easily.

</heml> """ soup = BeautifulSoup(html, "html.parser") h1 = soup.html.body.h1 p1 = soup.html.body.p p2 = p1.next_sibling.next_sibling print("h1 = " + h1.string) print("p1 = " + p1.string) print("p1 = " + p2.string) </pre>
## id 요소를 활용한 파싱
from bs4 import BeautifulSoup

html = """

    
        

Hello, BeautifulSoup

This is a example.

BeautifulSoup helps to scrap web page easily.

</heml> """ soup = BeautifulSoup(html, "html.parser") h1 = soup.find(id="title") p1 = soup.find(id="first") print("h1 = " + h1.string) print("p1 = " + p1.string) </pre>
또한 아래의 코드를 이용하여 파싱이 잘 되었는지 확인할 수 있습니다.
soup = BeautifulSoup(html, "html.parser")
print(soup.prettify())

## 기상청 페이지 정보 파싱하기
import urllib.request as req
from bs4 import BeautifulSoup

REST_API = "http://www.kma.go.kr/weather/forecast/mid-term-rss3.jsp"
values = {
    'stnId': '108'
}
url = REST_API + "?" + "stnId=108"
res = req.urlopen(url)

soup = BeautifulSoup(res, "html.parser")

title = soup.find("title")
wf = soup.find("wf")

print("title = " + title.string)
print("wf = " + wf.string)

기상청 날씨 정보를 HTML 형태로 가져오기

|

Python 3.x 기반의 코드입니다. 기상청 페이지의 HTML 데이터를 가져오는 예제입니다. 중간에 stnId라는 변수가 나오는데, 지역 코드입니다.

지역 지역 코드
전국 108
서울, 경기도 109
강원도 105
충청북도 131
충청남도 133
경상북도 143
전라북도 146
전라남도 156
경상남도 159
제주도 184


예제 코드

import urllib.request
import urllib.parse

REST_API = "http://www.kma.go.kr/weather/forecast/mid-term-rss3.jsp"

values = {
    'stnId': '108'
}
params = urllib.parse.urlencode(values)

url = REST_API + "?" + params

data = urllib.request.urlopen(url).read()

text = data.decode("UTF-8")
print(text)