public bigdata

파이썬을 이용한 웹 스크래핑(edwith 강의) chapter2 HTTP 본문

Python programming/웹 크롤링

파이썬을 이용한 웹 스크래핑(edwith 강의) chapter2 HTTP

public bigdata 2019. 11. 25. 22:18
# 1~4 강

Pythonlearn-12-HTTP.pptx.pdf
1.70MB

소켓(Socket)은 소프트웨어로 작성된 추상적인 개념의 통신 접속점이라고 할 수 있다 네트워크 응용 프로그램은 소켓을 통하여 통신망으로 데이터를 송수신하게 된다(원문)

포트(Port)는 하나의 컴퓨터에 실행 중인 여러 네트워크 프로그램을 구분하기 위해 부여된 번호입니다. 16비트로 구성된 번호입니다.(원문)

예를들면, 우편물이 집에 도착했는데 그 우편물이 누구의 것이냐는 것입니다.
여기서 집을 하나의 컴퓨터(호스트)라 하고, 주소를 컴퓨터의 IP, 우편물에 적힌 이름은 포트 번호라고 이해하시면 쉬울 것입니다. 즉, 컴퓨터까지는 왔는데 그 컴퓨터의 어느 프로그램이 패킷을 받을지를 알아야 하니 이런 번호가 부여됩니다.

## 1. 소켓 모듈을 통한 네트워크 연결

# 소켓의 개념
# ?
# 포트의 개념

# 놀라운 점
# 딱 코드 세줄을 가지고 공학자 50만여 명이 구축한 
# 인터넷, 프로토콜, 소프트웨어를 사용하는 것이다

# 인터넷에 연결 그리고 소켓을 연속된 문자의 흐름인 스트림 방식으로 만든다.
import socket
mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# url은 번호를 나타내고 80번인 포트 번호는 skt, kt 등의 통신사를 
# 선택하는 것이라 생각하면 된다. 아니면 이메일 연결인지, 전화통화 인지
# 구분하는 것이라 생각하면 된다.
mysock.connect(('data.pr4e.org', 80)) 

## 2. HTTP를 이용해 서버에 요청 보내기
# 핵심 키워드 # 
# 프로토콜
# HTTP
# 웹 서버

# 소켓은 HTTP라는 프로토콜에 따라서 통신한다.
# 예를 들면 전화할 때 누가 먼저 말할 것인지 등에 대한 규칙이다.
# 양쪽이 서로 받아들일 수 있는 약속 말이다.
# 도로에서 어느 라인에서 운전을 할 것인지

# http://(protocl), www.dr-chuck.com(host), page1.htm(document)

# http는 통신을 위한 프로토콜이고
# html은 그 통신에서 돌려받는 문서의 형식이다.

# 브라우저는 어떤 정보를 받아서 html, css, javascript등을 통해
# 렌더링 하여 우리에게 보여준다.

## 3. 파이썬을 이용해 웹 데이터 읽어오기
import socket

mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysock.connect(('data.pr4e.org', 80)) # 소켓 연결

# 아래 서버에 보낼 명령어에 \r\n\r\n이 있는 이유는 웹통신을 하는 커맨드 라인?
# 에서는 명령어를 작성하고 엔터 두번을 쳐서 하나의 빈줄을 생성해 주는데 
# 그에 맞게 개행한 것이라고 보면된다.

# 그리고 마지막에 보면 .encode()라는 부분은 유니코드로 작성된 명령어를
# encode하여 UTF-8로 변경해준다.
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) # 소켓이 웹 또는 어플리케이션에서 512개의 문자를 받는다.
    if (len(data) < 1): # 받아온 데이터가 공백이면 즉. 길이가 1보다 작으면
        break           # 해당 while문을 나온다.
    print(data.decode(),end='') # 웹 또는 어플리케이션에서 받아온 데이터는 반드시 decode하여 일반적으로
mysock.close()                  # 사람이 알아볼 수 있는 형태로 변경한다.

## 위 코드로 인해서 출력된 것을 보면 중간에 한 줄 띄워져 있는데
# 메타 데이터가 끝나고 본 내용이 아래에 온다는 것을 알려주는
# 약속이다.
# 4강

## 4. 문자를 표현하는 방법 및 인코딩과 디코딩
## 인코딩: 오디오, 이미지, 텍스트 등을 바이트 단위로 나타내는 형식
## 디코딩: 바이트 단위로 된 정보들을 다시 사람이 이해할 수 있는 형태로 되돌리는 작업


# ASCII
# 아스키 코드는 1 byte로 영문자와 숫자, 그리고 일부 특수문자들을 표현할 수 있습니다.
# ord() 함수를 사용하면 다음과 같이 각각의 문자에 대한 아스키 코드 값을 확인할 수 있습니다.
print(ord('H'))
# 72 ## 이 숫자들로 문자의 순서를 비교한다고 한다.
print(ord('e'))
# 101
print(ord('\n'))
# 10


# 유니코드(Unicode)

# 유니코드 체계는 이미 몇 십억개의 문자를 포함하고 있으며, 새로운 문자 몇 십억개를 더 저장할 여력이 있습니다.
# 유니코드는 문제가 용량이 너무 크다.
# 유니코드를 압축하는 UTF-8, UTF-16, UTF-32 등 다양한 방법이 있지만 가장 실용적인 방법은 UTF-8을 사용하는 것입니다.

# utp-8은 1~4bytes 사이에서 동적으로 움직인다.
# ASCII 코드까지 포함? 표현한다.

# 예전 파이썬 2.x 버전에서는 유니코드로 나타내고 싶으면 별도의 문자열 앞에 'u'라는 문자를 넣어주어야 했습니다.
# Python 2.7.10 
x = '이광춘'
print(type(x))
# <type 'str'>
x = u'이광춘'
print(type(x))
# <type 'unicode'>

# 그런데 파이썬 3.x 버전부터는 기본적으로 문자열이 유니코드로 저장이 됩니다.
# 그래서 class가 str인 거구나. 아래 예제 둘다, 디폴트 유니코드니 굳이 유니코드, str 구분할 필요 없으니까?
# Python 3.5.1
x = '이광춘'
print(type(x))
# <class 'str'>
x = u'이광춘'
print(type(x))
# <class 'str'>

# 파이썬에서는 기본적으로 문자열이 유니코드로 저장된다.
# 하지만, 파이썬 내부에서 데이터를 사용하는 것이 아니라 네트워크를 통해 데이터를 주고 받을 때는 다른 형태로 데이터를 변환해야하는 경우도 있습니다.
# 다음 코드에서 'GET http://data.pr4e.org/romeo.txt HTTP/1.0\n\n' 문자열은 유니코드입니다. 따라서 데이터를 전송하기 전에 UTF-8 byte 방식으로 
# 인코딩를 해주어야 하는데, 그럴 때 사용되는 메소드가 encode() 입니다.
'GET http://data.pr4e.org/romeo.txt HTTP/1.0\n\n'.encode()


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\n\n'.encode() # 통신할 때는 UTF-8로 encode, 디폴트 utf-8
mysock.send(cmd)

# 이제 서버에서 받은 데이터를 data라는 변수에 저장합니다. 그리고 이 데이터는 현재 디코딩이 안 되어있기 때문에 decode() 메소드를 사용해 디코딩하여 출력하면 유니코드 형태로 우리에게 보여지는 것입니다.
while True:
    data = mysock.recv(512) # 네트워크로 데이터를 받을 때는 .recv, 반대는 .send
    if (len(data) < 1):
        break
    print(data.decode())
mysock.close()
5강 
## 5강 urllib을 이용해 웹 데이터 읽어오기
# urllib : 위 강의에서 했던 연결도 만들고 get요청을 인코딩하고 응답을 가져온다.
# 그리고 받아온 걸 객체로 저장한다.

## 헤더는 보여주지 않는다. 물론 보는 방법이 있지만 여기선 skip
## decode 하는 것만 잊지 않으면 된다.
import urllib.request, urllib.parse, urllib.error

fhand = urllib.request.urlopen('http://data.pr4e.org/romeo.txt')
for hand in fhand:
    # strip 메서드는 whitespace를 지워준다.
    # whitespace:포괄적으로 띄워쓰기, 탭, 엔터까지 포괄적으로 이야기 하는 것
    print(hand.decode().strip('\n')) 


## 이 예제는 txt를 dict 함수를 통해서 공백기준으로 분리된 단어의
## 갯수를 세는 예제인데 counts[word] = counts.get(word, 0)+1 부분이
## 간단하고도 오묘한 코드인듯 하다. 
import urllib.request, urllib.parse, urllib.error
fhand = urllib.request.urlopen('http://data.pr4e.org/romeo.txt')

counts = dict()
for line in fhand:
    words = line.decode().split()
    for word in words:
        counts[word] = counts.get(word, 0)+1
print(counts)

# html로 만들어진 웹 페이지도 읽어올 수 있다.
# 보면 urllib 객체 다루는 것과 파일을 open해서 가져왔을 때와 동일하다.
import urllib.request, urllib.parse, urllib.error

fhand = urllib.request.urlopen('http://www.dr-chuck.com/page1.htm')
for line in fhand:
    print(line.decode().strip())
6강
# html로 만들어진 웹 페이지도 읽어올 수 있다.
# 보면 urllib 객체 다루는 것과 파일을 open해서 가져왔을 때와 동일하다.
import urllib.request, urllib.parse, urllib.error

fhand = urllib.request.urlopen('http://www.dr-chuck.com/page1.htm')
for line in fhand:
    print(line.decode().strip())

## BeautifulSoup를 이용한 웹 데이터 스크래핑
# BeautifulSoup란 웹페이지에서 일어날 수 있는 다양한 문제(깨진 html)등에
# 대해 해결책을 모아둔 것이라 이해
from bs4 import BeautifulSoup
import urllib.request, urllib.parse, urllib.error
url = "http://www.naver.com"
html = urllib.request.urlopen(url).read() # 바이트던 utp-8이던 알아서 처리해준다.
soup = BeautifulSoup(html, 'html.parser')

tags = soup('a')
for tag in tags:
    print(tag.get('href', None)) # None은 왜 필요하지? 




## beautifulsoup 실습
import urllib.request, urllib.parse, urllib.error
from bs4 import BeautifulSoup
import ssl

# Ignore SSL certificate errors
# 아래 코드는 그냥 ssl 인증서 에러를 무시하는 방법이라고만 알아두자
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

url = input('Enter - ')
html = urllib.request.urlopen(url, context=ctx).read()
# beautifulsoup가 필요한 이유는 정규식으로 html을 처리하기에는
# 줄바꿈 등 예외가 너무 많아 어려운 점이 많다
# 그 역할을 beautifulsoup가 대신해 준다.
soup = BeautifulSoup(html, 'html.parser')

# Retrieve all of the anchor tags
tags = soup('a') # 'a'라는 앵커태그를 가져와서 리스트로 받는다.
for tag in tags:
    print(tag.get('href', None))