파이썬 기초 강좌: 12. 네트워크 프로그래밍 예제와 실습 (Python Basics – Networked programs Examples and Exercises)

파이썬 기초 강좌 – 네트워크 프로그램에 관심이 있으신가요? 이 블로그 글에서는 Python을 사용하여 HTTP 프로토콜을 통해 웹 페이지를 가져오고, 데이터를 파싱하는 방법을 설명합니다. 소켓을 통해 데이터를 전송하고, urllib과 BeautifulSoup을 활용한 실용적인 예제들을 통해 네트워크 프로그래밍의 기초를 배우실 수 있습니다. 바이너리 파일 처리와 웹 스크래핑까지 다루는 이 글을 통해 더 깊이 있는 네트워크 프로그래밍 세계로 빠져보세요.

파이썬 기초 강좌 - 네트워크 프로그래밍 예제와 실습

1. 파이썬 기초 강좌 – 하이퍼텍스트 전송 프로토콜(HTTP)

  • HTTP 프로토콜은 웹의 기본 요소이며, Python의 socket 라이브러리에서 지원됩니다.
  • 소켓은 두 프로그램 간의 양방향 통신 채널을 제공하며, 네트워크를 통해 데이터를 읽고 쓸 수 있습니다.

2. 세계에서 가장 간단한 웹 브라우저

  • HTTP를 설명하는 가장 간단한 방법은 Python에서 기본 웹 브라우저를 만드는 것입니다.
  • 이 브라우저는 웹 서버에 연결하여 GET 요청을 보내고, 서버의 응답을 표시합니다.
  • (실습)
# socket1.py

import socket

mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysock.connect(('data.pr4e.org', 80))
cmd = 'GET http://data.pr4e.org/romeo.txt HTTP/1.0\r\n\r\n'.encode()
mysock.send(cmd)

while True:
    data = mysock.recv(512)
    if len(data) < 1:
        break
    print(data.decode(), end='')

mysock.close()
  1. data.pr4e.org 서버의 80번 포트에 연결을 설정합니다.
  2. romeo.txt 파일을 가져오기 위한 GET 요청을 보냅니다.
  3. 데이터를 512바이트씩 지속적으로 받아오고, 모두 받을 때까지 출력합니다.

3. HTTP를 통해 이미지 가져오기

  • 이 섹션에서는 HTTP를 통해 바이너리 데이터를 가져와 로컬에 저장하는 예를 확장하여 설명합니다.
  • (실습)
# urljpeg.py

import socket
import time

HOST = 'data.pr4e.org'
PORT = 80
mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysock.connect((HOST, PORT))
mysock.sendall(b'GET http://data.pr4e.org/cover3.jpg HTTP/1.0\r\n\r\n')

picture = b""
while True:
    data = mysock.recv(5120)
    if len(data) < 1:
        break
    picture += data

mysock.close()

pos = picture.find(b"\r\n\r\n")
picture = picture[pos+4:]

with open("cover3.jpg", "wb") as fhand:
    fhand.write(picture)

이 파이썬 코드는 네트워크 소켓을 사용하여 HTTP 요청을 보내고, 이미지를 다운로드하는 과정을 보여줍니다. 아래는 코드의 주요 단계를 요약한 설명입니다:

  1. 소켓 생성 및 연결:
  • socket.socket(socket.AF_INET, socket.SOCK_STREAM)을 사용하여 TCP 소켓을 생성합니다.
  • mysock.connect((HOST, PORT))을 통해 지정된 호스트(data.pr4e.org)와 포트(80)에 연결합니다.
  1. HTTP GET 요청 보내기:
  • mysock.sendall(b'GET http://data.pr4e.org/cover3.jpg HTTP/1.0\r\n\r\n')을 사용하여 서버에 HTTP GET 요청을 보냅니다. 이 요청은 cover3.jpg 파일을 요청합니다.
  1. 서버 응답 수신 및 데이터 저장:
  • picture = b""를 사용하여 빈 바이트 문자열을 초기화합니다.
  • while True 루프를 통해 서버로부터 데이터를 반복적으로 수신하여 picture 변수에 추가합니다. 수신된 데이터가 없을 때 루프가 종료됩니다.
  1. 헤더 분리:
  • pos = picture.find(b"\r\n\r\n")를 사용하여 HTTP 헤더와 본문을 구분하는 위치를 찾습니다.
  • picture = picture[pos+4:]를 통해 헤더를 제거하고 이미지 데이터만 남깁니다.
  1. 이미지 파일로 저장:
  • with open("cover3.jpg", "wb") as fhand:를 사용하여 바이너리 쓰기 모드로 cover3.jpg 파일을 엽니다.
  • fhand.write(picture)을 통해 이미지 데이터를 파일에 씁니다.

4. urllib을 사용하여 웹 페이지 가져오기

  • urllib 라이브러리는 HTTP의 저수준 세부 사항을 추상화하여 웹 페이지를 가져오는 과정을 단순화합니다.
  • (실습)
# urllib1.py

import urllib.request

fhand = urllib.request.urlopen('http://data.pr4e.org/romeo.txt')
for line in fhand:
    print(line.decode().strip())
  1. urllib.request.urlopen을 사용하여 URL을 엽니다.
  2. 파일처럼 객체의 줄을 반복하면서 각 줄을 디코딩하여 출력합니다.

5. urllib을 사용하여 바이너리 파일 읽기

  • 텍스트 파일과 유사하게, 바이너리 파일도 urllib을 사용하여 가져와 로컬에 저장할 수 있습니다.
  • (실습)
# curl1.py

import urllib.request

img = urllib.request.urlopen('http://data.pr4e.org/cover3.jpg').read()
with open('cover3.jpg', 'wb') as fhand:
    fhand.write(img)
  1. URL에서 바이너리 파일을 모두 읽어옵니다.
  2. 바이너리 데이터를 로컬 파일에 ‘바이너리 쓰기’ 모드로 저장합니다.

6. HTML 파싱 및 웹 스크래핑

  • 웹 스크래핑은 HTML 페이지를 가져와서 특정 패턴을 찾기 위해 데이터를 파싱하는 작업입니다.
  • 정규 표현식이나 BeautifulSoup 같은 라이브러리를 사용하여 이를 수행할 수 있습니다.
  • (실습) BeautifulSoup 사용
# pip install beautifulsoup4

import urllib.request
from bs4 import BeautifulSoup

url = 'http://www.dr-chuck.com/page1.htm'
html = urllib.request.urlopen(url).read()
soup = BeautifulSoup(html, 'html.parser')

tags = soup('a')
for tag in tags:
    print(tag.get('href', None))
  1. URL을 열고 HTML 내용을 읽어옵니다.
  2. BeautifulSoup을 사용하여 HTML을 파싱합니다.
  3. 모든 하이퍼링크(a 태그의 href 속성)를 추출하여 출력합니다.

연습문제

연습문제 1

  • socket1.py 프로그램을 수정하여 사용자에게 URL을 입력받고, 잘못된 형식의 URL 또는 존재하지 않는 URL에 대한 오류를 처리합니다.
  • 샘플 실행:
Enter URL: http://data.pr4e.org/romeo.txt
... (출력 내용) ...
  • 정답:
import socket

url = input('Enter URL: ')
try:
    host = url.split('/')[2]
    mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    mysock.connect((host, 80))
    cmd = f'GET {url} HTTP/1.0\r\n\r\n'.encode()
    mysock.send(cmd)

    while True:
        data = mysock.recv(512)
        if len(data) < 1:
            break
        print(data.decode(), end='')

    mysock.close()
except:
    print('Invalid URL')

연습문제 2

  • socket1.py 프로그램을 수정하여 3000자 이상을 출력하지 않도록 하되, 전체 문서를 가져와 문자 수를 세어 출력합니다.
  • 샘플 실행:
... (처음 3000자) ...
Total characters: 5678
  • 정답:
import socket

url = input('Enter URL: ')
try:
    host = url.split('/')[2]
    mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    mysock.connect((host, 80))
    cmd = f'GET {url} HTTP/1.0\r\n\r\n'.encode()
    mysock.send(cmd)

    count = 0
    while True:
        data = mysock.recv(512)
        if len(data) < 1:
            break
        count += len(data)
        if count <= 3000:
            print(data.decode(), end='')

    print(f'\nTotal characters: {count}')
    mysock.close()
except:
    print('Invalid URL')

연습문제 3

  • urllib을 사용하여 연습문제 2를 복제하고, 3000자까지 출력한 후 전체 문서의 문자 수를 세어 출력합니다.
  • 샘플 실행:
... (처음 3000자) ...
Total characters: 5678
  • 정답:
import urllib.request

url = input('Enter URL: ')
try:
    fhand = urllib.request.urlopen(url)
    count = 0


while True:
    data = fhand.read(512)
    if len(data) < 1:
        break
    count += len(data)
    if count <= 3000:
        print(data.decode(), end='')

    print(f'\nTotal characters: {count}')
except:
    print('Invalid URL')

연습문제 4

  • urllinks.py 프로그램을 수정하여 HTML 문서에서 단락(p) 태그를 세고, 프로그램의 출력으로 단락 수를 표시합니다.
  • 샘플 실행:
Paragraph count: 5
  • 정답:
import urllib.request
from bs4 import BeautifulSoup

url = input('Enter URL: ')
try:
    html = urllib.request.urlopen(url).read()
    soup = BeautifulSoup(html, 'html.parser')
    tags = soup('p')
    print(f'Paragraph count: {len(tags)}')
except:
    print('Invalid URL')

연습문제 5 (고급)

  • 소켓 프로그램을 수정하여 헤더와 빈 줄을 받은 후에만 데이터를 표시합니다.
  • 샘플 실행:
... (헤더 이후 데이터) ...
  • 정답:
import socket

url = input('Enter URL: ')
try:
    host = url.split('/')[2]
    mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    mysock.connect((host, 80))
    cmd = f'GET {url} HTTP/1.0\r\n\r\n'.encode()
    mysock.send(cmd)

    header_done = False
    while True:
        data = mysock.recv(512)
        if len(data) < 1:
            break
        if not header_done:
            data_str = data.decode()
            end_header = data_str.find('\r\n\r\n')
            if end_header != -1:
                header_done = True
                data = data[end_header+4:]
            else:
                continue
        print(data.decode(), end='')

    mysock.close()
except:
    print('Invalid URL')
  1. URL 입력:
  • url = input('Enter URL: ')을 사용하여 사용자가 입력한 URL을 받습니다.
  1. 호스트 이름 추출 및 소켓 생성:
  • host = url.split('/')[2]을 통해 URL에서 호스트 이름을 추출합니다.
  • mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)을 사용하여 TCP 소켓을 생성합니다.
  1. 서버에 연결 및 HTTP GET 요청 보내기:
  • mysock.connect((host, 80))을 통해 호스트의 80번 포트에 연결합니다.
  • cmd = f'GET {url} HTTP/1.0\r\n\r\n'.encode()를 통해 GET 요청 명령을 생성하고, mysock.send(cmd)를 사용하여 서버에 보냅니다.
  1. 데이터 수신 및 헤더 분리:
  • header_done = False로 헤더가 처리되지 않았음을 표시합니다.
  • while True 루프를 통해 서버로부터 데이터를 반복적으로 수신합니다.
  • 수신된 데이터가 없으면 루프를 종료합니다 (if len(data) < 1: break).
  • 처음 수신된 데이터에서 헤더와 본문을 분리합니다. 헤더가 끝나는 위치를 찾고 (end_header = data_str.find('\r\n\r\n')), 헤더 이후의 데이터를 저장합니다.
  • 헤더가 처리되면 header_done = True로 표시하고, 본문 데이터를 출력합니다 (print(data.decode(), end='')).
  1. 소켓 닫기 및 예외 처리:
  • 데이터 수신이 완료되면 mysock.close()를 통해 소켓을 닫습니다.
  • 예외가 발생하면 (예: 잘못된 URL), Invalid URL 메시지를 출력합니다.

네트워크 프로그래밍의 세계를 탐구하면서, 우리는 소켓을 이용한 간단한 웹 브라우저 구현부터 시작하여 HTTP 프로토콜을 이해하고 데이터를 가져오는 방법을 배웠습니다. 이러한 기본적인 소켓 프로그래밍 기술은 웹 스크래핑, 데이터 수집 및 다양한 네트워크 애플리케이션 개발에 필수적입니다. 또한, Python의 urllib 및 BeautifulSoup 라이브러리를 통해 복잡한 HTML 페이지를 쉽게 파싱하고 데이터를 추출하는 방법을 익혔습니다. 이를 통해 우리는 인터넷의 방대한 데이터에 접근하고 활용할 수 있는 강력한 도구를 손에 넣게 되었습니다. 이러한 지식을 바탕으로 더 복잡한 네트워크 프로그램을 작성하고, 다양한 데이터를 수집하여 유용한 인사이트를 도출할 수 있을 것입니다. 계속해서 학습하고 실습하여 네트워크 프로그래밍의 전문가가 되기를 바랍니다.

파이썬 기초 강좌: 11. 정규표현식 예제와 실습 (Python Basics – Regular expressions Examples and Exercises)

파이썬 기초 강좌: 13. 웹서비스 사용하기 예제와 실습 (Python Basics – Using Web Services Examples and Exercises)

Leave a Comment