srtmacro-1.0
0.13MB

안녕하세요 코드사기꾼입니다.

오늘은 요즘 매우 핫한 클레이(KLAY) 코인을 무료로 받는 방법에 대해서 소개해 드리겠습니다.(절대 사기아님...)

 

 

카카오 그라운드 X에서 이번에 새로운 지갑서비스를 런칭하였는데요, 클립이라고 하는 지갑입니다.

카카오톡이랑 연동도 되고 지금까지 써봤던 어떤 암호화폐 지갑보다 접근성이 우수하네요.

 

 

카카오에서 현재 클립에 가입하시는 모든 분들께 2클레이를 무료로 지급해드리고 있습니다.(진지)

 

여러분들도 아래 링크를 통해 클립에 가입하시고 2클레이 무료로 받아가세요 (현재 시가 3000원)

지금은 개당 1500원이지만,,, 미래에는 알 수 없습니다. 기회를 놓지지 마세요

klipwallet.com/?target=/klip-event/invite-friends-popup2%3FklipId%3DKZWRhMDk1ZGFpbnZpdGVyNTMyMjA2

# Flask

 

 

1. REST API란?

 

REST API란 Representational State Transfer의 약자로 자원의 이름을 기반으로 하여 상태를 주고 받는 것을 뜻합니다.  JSON이나 XML의 형태로 데이터를 주고 받는 것이 일반적이며 URI(Uniform Resource Identifier)를 통해 자원을 명시하고 HTTP 프로토콜에서 제공하는 메소드를 활용해 해당자원에 대한 CRUD를 동작하게 하는 API입니다.

※CRUD = Create, Read, Update, Delete

 

 


 

 

2. URL과 URI의 차이

 

URL은 Uniform Resource Locator로써 해당 경로에 실제 존재하는 자원의 위치를 가르키는 것입니다. 예를 들어  http://rekt77.tistory.com/test.txt 라는 URL은 웹 서버 root 디렉터리에 실제로 존재하는 test.txt 파일을 요청하기 위한 주소입니다. 

 

반면 URI는 Uniform Resource Identifier로써 요청하는 주소가 실제 존재하는 파일이라기 보다는 기능을 하기위한 구분자로 보는 것입니다. 이전 강의에서 Flask를 통해 /user라는 URI에 요청이 들어왔을 때 특정 동작을 실행하게 끔 구현했었던 것을 기억하실 겁니다. 실제 우리 서버 안에는 user라는 파일은 존재하지 않습니다. 단지 자원의 구분자로써의 역할을 하며 접근하는 구분자에 따라 CRUD operation을 실행하게 하는 주소입니다.

 

URL은 URI의 하위 개념입니다. 정확하게 말하자면 URI는 URL의 개념을 포함하고 있기때문에 위에서 언급한 http://rekt77.tistory.com/test.txt는 URI라고도 할 수 있는 것이죠. 하지만 URI는 URL의 상위 개념이기 때문에 /user라는 주소는 URL이 될 수 없습니다.

 

 


 

 

3. Flask를 이용한 기본적인 REST API 구현

 

Flask는 API설계를 가장 간단하게 할 수 있는 프레임워크중 하나 입니다. 먼저 REST API를 구축하기위해서 데이터를 json형식으로 리턴해야 합니다. Flask는 데이터를 json형식으로 치환하기 위한 jsonify라는 함수를 제공하고 있습니다.

 

또한 요청이 들어왔을 때 HTTP 메소드 별로 실행 루틴을 다르게 구현할 수 있습니다. 또한 json형태의 데이터를 받아오기위하여 Flask내에 request라는 클래스를 사용할 것입니다.

 

사실 REST API는 GET, POST 외에 PUT과 DELETE 메소드를 사용해서 CRUD operation을 구축해야 합니다. 하지만 이 두개의 메소드는 보안상의 취약점이 존재하고 있어 일반적인 상황에서는 사용이 어렵습니다.

※ CRUD 매핑: POST - CREATEGET - READ, PUT - UPDATE, DELETE - DELETE

※ GET - URI에 주소에 파라미터를 추가하여 요청하는 방식(HTTP Header에 추가하는 방식)

※ POST - HTTP Body에 데이터를 추가하여 요청하는 방식

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from flask import Flask, request, jsonify
app = Flask(__name__)
 
@app.route('/userLogin', methods = ['POST'])
def userLogin():
    user = request.get_json()#json 데이터를 받아옴
    return jsonify(user)# 받아온 데이터를 다시 전송
 
@app.route('/environments/<language>')
def environments(language):
    return jsonify({"language":language})
 
 
if __name__ == "__main__":
    app.run()
cs

 

위 코드를 실행시키면 127.0.0.1:5000/ 아래에 두개의 URI가 생성됩니다.  userLogin과 environments인데요 userLogin은 POST 방식으로 접근해야하는 API입니다. 동작방식은 받은 json을 그대로 돌려주는 형태입니다. environments는 URI 마지막 구분자에 넣어준 값을 json형태로 반환해주는 API입니다. 이 때의 키 값은 language가 됩니다. 

 

그러면 위의 소스코드를 실행 시키고 테스트를 진행해 보겠습니다. test하기위한 도구는 Restlet 입니다.

 

링크: https://chrome.google.com/webstore/detail/restlet-client-rest-api-t/aejoelaoggembcahagimdiliamlcdmfm

 

Restlet Client - REST API Testing

Visually create and run single HTTP requests as well as complex scenarios. Automate your API tests with our CI/CD plugin.

chrome.google.com

Restlet은 Rest API를 테스트 하는데 사용하는 chrome 웹 브라우저의 익스텐션입니다.  사용방법이 간편하며 성능이 뛰어납니다. 회원가입 하시고 로그인 하시면 아래와 같은 화면을 볼 수 있습니다.

 

Restlet 메인화면

 

먼저 상단의 requests를 선택하여 요청을 생성하겠습니다. GET 메소드를 테스트 할 것이기 때문에 메소드를 GET으로 놓고 URI를 명시해줍니다. http://127.0.0.1:500/environsments/python으로 실행하겠습니다. SEND 버튼을 눌러 요청을 보내면 보시는 바와 같이 Response가 도착하는데요 200 OK라는 Status code는 웹 요청이 정상적으로 이루어 졌고 응답을 받았다고 이해하시면 됩니다. Response 헤더는 전송받은 데이터 타입에 따라 변경되는 데요 json을 받았기 때문에 application/json으로 설정되어있습니다.

 

그리고 제가 의도 한대로 json이 응답으로 도착한 것을 볼 수 있습니다.( {'language':'python'})

 

이번에는 POST를 테스트 해보겠습니다.

 

POST 실행결과

 

메소드를 POST로 설정하고 URI를 명시합니다. 이후에 HEADER 설정이 중요한데요, 우리는 json형태의 데이터를 전송할 것이기 때문에 Content-Type을 application/json으로 설정합니다. 그리고 BODY 값에 원하는 json을 넣어주시면 전송한 json이 그대로 도착할 것입니다.

 

맨 마지막 빨간색 박스를 보시면 Response가 들어있습니다. json을 받았기 때문에 역시 Contnet-Type은 application/json입니다. 그리고 Response Body값으로 {"userName":"rekt77"}이 도착한 것을 볼 수 있습니다.

'Dev > Flask' 카테고리의 다른 글

[Flask] 파이썬 Flask를 이용하여 REST API 만들기 - 1  (1) 2019.04.09

# Flask

 

 

 

1. Flask란?

 

Flask는 웹 어플리케이션 제작을 위한 프레임워크입니다. 성능은 뛰어나지만 라이트한 특성때문에 간단한 API서버 구축에 적합합니다. www.linkedin.com이 Flask를 이용해 만든 대표적인 웹사이트입니다.

 

먼저 Flask를 사용하기 위해서는 설치를 해야합니다. 커맨드 라인 프롬프트(cmd)를 열고 아래의 명령어를 입력합니다.

#Flask 설치 명령어

pip install flask 

 

 


 

 

2.  라우팅 함수

 

설치가 완료 되었으면 아래의 코드를 실행시켜 잘 동작하는지 확인 합니다.

 

1
2
3
4
5
6
7
8
9
from flask import Flask
app = Flask (__name__)
 
@app.route('/')
def hello_world():
    return 'Hello, World!'
 
if __name__ == "__main__":
    app.run()
cs

 

코드가 실행되면 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 이라는 문구가 나타납니다. 127.0.0.1은 로컬호스트를 나타내는 IP주소기 때문에 로컬호스트 포트 5000 번에 웹서버를 열었다는 이야기가 됩니다. 한번 접속해 보도록 하겠습니다. 웹 브라우저를 열고 url에 http://127.0.0.1:5000/를 입력합니다.

 

접속 결과

 

URL에 접속하게 되면 Hello, World!가 출력됩니다. 어떻게 이것이 가능한 걸 까요? 정답은 소스코드 내에 정의한 hello_world 함수 때문입니다. 함수 상단에 데코레이터를 통해 라우팅 경로를 지정해 놓았는데요 그 코드가 바로 @app.route('/') 입니다. URL의 / 경로를 요청하면 하위에 기재된 코드를 실행하는 것 입니다. 이 함수에서는 Hello, World!를 리턴하기 때문에 Hello, World!가 출력되는 것입니다. 이런식으로 경로를 설정하여 다른동작을 하게 하는것을 라우팅이라고 합니다.

 

웹이라는 것은 여러개의 경로가 존재하여 경로마다 다른 역할을 하게끔 설계되어 있습니다. 그렇기 때문에 당연히 라우팅 함수는 여러개 생성할 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask
 
app = Flask (__name__)
 
@app.route('/')
def hello_world():
    return 'Hello, World!'
 
@app.route('/user')
def hello_user():
    return 'Hello, User!'
 
if __name__ == "__main__":
    app.run()
cs
 
 

위의 코드를 실행하고 http://127.0.0.1:5000/user 에 접속해 보겠습니다.

 

http://127.0.0.1:5000/user 접속화면

 

아까와는 다르게 Hello, User!가 출력된 것을 확인 할 수 있죠? 이런식으로 라우팅 함수를 여러개 만들면 경로마다 다른 역할을 하게 할 수 있습니다. 추가적으로 라우팅 함수는 URL을 변수로 받아와서 처리하게 구현할 수 도 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
app = Flask (__name__)
 
def hello_world():
    return 'Hello, World!'
 
@app.route('/user/<userName>'# URL뒤에 <>을 이용해 가변 경로를 적는다
def hello_user(userName):
    return 'Hello, %s'%(userName)
 
if __name__ == "__main__":
    app.run()
 

 

hello_user를 보시면 라우팅 데코레이터에 <userName>이라는게 들어 있는걸 볼 수 있습니다. 이는 가변 경로에 대해 그 값을 변수로 사용하겠다는 의미입니다. 이러한 가변 경로를 변수로 사용하기 위해서는 hello_user의 인자 값으로 만들어 줘야하기 때문에 인자에 userName이 들어가 있는 것 입니다. 이를 받아서 출력하는 라우팅 함수를 만들어 실행하고 경로로 들어가 보겠습니다.

 

가변경로에 rekt77을 입력하자 화면에 출력된다.

 

 

 

 

'Dev > Flask' 카테고리의 다른 글

[Flask] 파이썬 Flask를 이용하여 REST API 만들기 - 2  (3) 2019.04.12

# 인공지능 기초 개념

 

안녕하세요 코드사기꾼입니다.
오늘은 Tensor에 대한 개념 정립을 하는 시간을 갖도록 하겠습니다. 인공지능 연구분야에서 가장 유명한 라이브러리인 TensorFlow를 정말 많은 사람들이 사용하고 있으면서도, 정작 그 의미에 대한 부분은 모르시는 분이 많은것 같아 포스팅을 해보기로 했습니다.

 

 


 

1. Tensor

 

텐서란 매우 수학적인 개념으로 데이터의 배열이라고 볼 수 있습니다. 텐서의 Rank는 간단히 말해서 몇 차원 배열인가를 의미합니다.

 

RANK TYPE EXAMPLE
0 scalar [1]
1 vector [1,1]
2 matrix [[1,1],[1,1]]
3 3-tensor [[[1,1],[1,1]],[[1,1],[1,1]],[[1,2],[2,1]]]
n n-tensor  

 

스칼라는 일반적으로 존재하는 그냥 값(1개)입니다. 벡터는 스칼라가 여러개 모인것이며 차원이 높아질 수록 아래 차원의 것을 모아 놓은 배열인 것이라고 할 수 있습니다.

 

 

 

 


 

2. 예시

 

자연어 처리를 통하여 위의 내용에 대한 예제를 구성해 보도록 하겠습니다.
Hi King
Hi Queen
Hi Jack
이라는 Sentences가 존재할 때 one hot encoding으로 벡터를 나타내면 아래와 같습니다.

 

WORD INDEX EXAMPLE
hi 0 [1,0,0,0]
king 1 [0,1,0,0]
queen 2 [0,0,1,0]
jack 3 [0,0,0,1]

 

word를 vector로 나타내었으니 단어의 vector를 기반으로 문장을 matrix로 표현할 수 있을 것 같습니다.

 

word EXAMPLE
hi king [[1,0,0,0],[0,1,0,0]]
hi queen [[1,0,0,0],[0,0,1,0]]
hi jack [[1,0,0,0],[0,0,0,1]]

문장을 위처럼 matrix로 표현하였습니다. 하지만 저희는 신경망의 input으로 보통 말뭉치를 넣게 되는데요 말뭉치는 아래와 같이 표현할 수 있습니다.

 


    hi        king         hi        queen       hi          jack 
[[[1,0,0,0], [0,1,0,0]],[[1,0,0,0], [0,0,1,0]],[[1,0,0,0], [0,0,1,0]]]

 


위의 말뭉치에서 각 word는 4차원으로 구성되어있습니다. 그리고 각 문자은 2개의 단어로 구성되어있습니다. 총 3개의 문장을 가지고 있으므로 위 모델은 (3,2,4)의 3차원 Tensor라고 볼 수 있는 것 입니다.

 

 


 

 

1. scalar

 

scalar에 해당하는 일반적인 숫자 한개를 넣어서 차원을 출력해 보았습니다.

대로 0 차원이 나왔습니다. 한개의 값만 존재하는 scalar는 0차원입니다.
그렇다면 vector는 어떨까요?

 

 


 

 

2. vector

 

vector값의 차원을 확인하기 위하여 [1,2,3,4,5]를 인풋으로 넣어봤습니다.

값이 1이 나왔습니다. 역시 vector는 1차원 데이터입니다.

 

 


 

 

3. matrix

 

matrix는 vector의 집합입니다. [[1,2,3],[4,5,6],[7,8,9]]를 인풋으로 넣었습니다.

예상에서 벗어나질 않네요 2차원이 나왔습니다. vector여러개가 모이면 2차원 데이터가 됩니다.

 

 


 

 

4. tensor

 

그렇다면 마지막으로 tensor입니다. matrix의 집합인 tensor는 당연히 3차원 부터 시작하니 최소 3이상의 수가 나올 것입니다. 저는 3d 텐서를 입력값으로 넣었으니 3이라는 수가 출력될 것입니다.

3이 잘 출력 된것을 볼 수 있습니다.

 

 


자 오늘은 tensor란 무엇인가로 시작하여 0차원 데이터 부터 고차원 데이터까지 명명법과 형태에 관하여 배워보았습니다. 오늘 배운것들을 정리하자면,


1. Tensor는 배열의 집합이다.
2. 차원의 수는 Rank와 같은말이다.
3. 배열의 차원에따라 불리는 이름이 달라진다.

 

# 인공지능 기초 개념

 

안녕하세요 코드사기꾼입니다.
오늘은 지도/비지도 학습 그리고 강화학습의 특징 및 차이점에 대해서 설명해 드리도록 하겠습니다.

자 그럼 아래 그림에서 사각형이 아닌것을 고르는 문제를 한번 생각해 보겠습니다.
1) 주황색 원 2) 남색 정사각형 3) 회색 마름모 4) 하늘색 평행사변형

 

 

첩자가 한명 있다.

 

무엇을 고르셨나요? 정답은 바로 1번 주황색원입니다. 이 문제를 풀기 전에 사각형의 종류에 대하여 공부하셨다면 바로 풀 수 있으셨을 겁니다.

 

 


 

 

1. Supervised Learning(지도학습)

 

- 정사각형은 사각형이다.
- 마름모는 사각형이다.
- 평행사변형은 사각형이다.
- 원은 원이다.

이와 같이 도형의 이름을 X라고 하고 종류를 Y라고하였을 때, 'X는 Y다' 라는것을 미리 학습 하게 되면 '다음 중 특정 Y가 아닌것은?' 이라는 질문에는 쉽게 답할 수 있습니다.
우리는 여기서 Y를 X의 Label(라벨) 이라고 하는데요, 이렇게 라벨에 대한 정답표가 있는 채로 학습하는 것을 우리는 Supervised Learning(지도 학습)이라고 합니다.

 

 


 

 

2. Unsupervised Learning(비지도학습)

 

이 문제에서는 사실 답이 자명하게 1번이었지만 사전 지식이 아닌 특징을 기반으로 분류해서 문제를 해결하는 방법도 있습니다.

- 2,3,4번은 4개의 선으로 이루어져 있지만, 1번은 1개의 곡선으로 이루어져 있다.
- 2,3,4번은 4개의 꼭지점이 존재하지만, 1번은 없다.

위와 같이 라벨을 전혀 모른채로 도형의 특성만으로 다른하나를 골라낼 수 있습니다. 이런 방식으로 학습하는 방식을 우리는 Unsupervised Learning(비지도 학습)이라고 합니다.

 

 


 

 

3. Reinforcement Learning(강화학습)

 

하지만 위의 해결방법들과 같이 어떠한 정보로써 접근하는 것이아닌 문제를 여러번 해결하여 가장높은 보상을 받는 쪽으로 정답을 유추하는 방법도 존재합니다.

- 1번을 선택하였는데 2점이 올랐고
- 2번을 선택하였는데 1점이 감소하였다.
- 3번을 선택하였는데 1점이 감소하였다.
- 4번을 선택하였는데 1점이 감소하였다.

이런 경우라면 같은문제 출현시에 1번을 답으로 고를 것입니다. 즉 문제의 현재 상태를 보고 보상을 최대한 많이 받는 쪽으로 행동을 취하게 학습시키는 방법을 Reinforcement Learning(강화학습) 이라고 합니다.

 

 

'Dev > Artificial Intelligence' 카테고리의 다른 글

[인공지능 개념] Tensor란 무엇인가?  (1) 2019.04.08

# 파이썬 웹 크롤링

안녕하세요 코드사기꾼입니다.

오늘은 파이썬 urllib을 이용하여 웹상에서 사진을 가져오는 방법에대한 설명을 드리도록 하겠습니다.

 

 


1. 이미지 다운로드 하기

 

크롤링을 하기 전에 알아야 할것은 이미지가 웹상 특정 경로에 존재하는지에 관한 유무입니다. 저는 사진을 크롤링하기 위하여 네이버 영화의 마약왕 영화 리뷰 사이트를 참고하였습니다.

사이트 링크:

https://movie.naver.com/movie/bi/mi/basic.nhn?code=157297

 

마약왕

“애국이 별게 아니다! 일본에 뽕 팔믄 그게 바로 애국인기라!”마약도 수출하면 애국이 되던 1970년대 ...

movie.naver.com

 

사이트에 접속하시면 보이는 바와 같이 사진이 노출되는데요

 

 

왼쪽상단 빨간색 박스에 사진이 존재함

 

개발자 도구를 통하여 저 사진의 URL을 알아보도록 하겠습니다. 먼저 inspect 도구를 켜서 사진을 클릭해줍니다.

 

img 태그에 src에 사진의 url이 담겨있다.

 

그러면 사진과 같이 img태그안에 src 항목에 사진의 원본 url이 담겨있는것을 확인할 수 있습니다.

저 URL을 복사하시고 아래와 같은 소스 코드를 작성합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import urllib.request
from bs4 import BeautifulSoup
 
print('Beginning file download with urllib2...')
 
url = 'https://movie.naver.com/movie/bi/mi/basic.nhn?code=157297'
req = urllib.request.Request(url)
res = urllib.request.urlopen(url).read()
 
soup = BeautifulSoup(res,'html.parser')
soup = soup.find("div",class_="poster")
#img의 경로를 받아온다
imgUrl = soup.find("img")["src"]
 
#urlretrieve는 다운로드 함수
#img.alt는 이미지 대체 텍스트 == 마약왕
urllib.request.urlretrieve(imgUrl, soup.find("img")["alt"]+'.jpg')
cs

 

위 코드를 실행하면 실행된 스크립트가 있는 폴더에 아래와 같이 사진이 생성됩니다!

 


2. 실행 결과

 

사진이 스크립트가 실행된 경로에 저장되었습니다.

 

사진파일 실행 결과

 

사진이 좀 작네요 ㅋㅋㅋ 그래도 확실하게 이미지를 받아올 수 있었습니다.

# 파이썬 웹 크롤링

 

안녕하세요 코드사기꾼입니다.
저번 강의에 이어서 오늘은 다른 사이트를 크롤링해보는 시간을 갖도록 하겠습니다.

오늘의 대상 사이트는 각종 대회 및 공모전이 등록되는 사이트인 씽굿(ThinkGood)입니다. 항상 공모전은 하고 싶은데 매일 찾아보기는 귀찮잖아요? 하지만 공모전은 타이밍이라 자주 모니터링 하지 않으면 적절한 타이밍에 등록을 하지 못할 수도 있습니다. 공모전의 내용을 크롤링해서 띄워주는 프로그램이 있다면 굉장히 유요하게 쓰일 수 있겠죠?

 


 

1. 사이트 구조파악

크롤링을 하기에 앞서 가장먼저 선행되어야 할 것이 무엇이냐고 물어보면 저는 사이트 구조파악이라고 말할 것입니다.
사이트에 관한 구조를 모르면 원하는 url을 순환하면서 크롤링을 할 수 없을 뿐만 아니라 원하는 div에서원하는 콘텐츠를 크롤링해올 수 없기 때문이죠

씽굿에 접속해서 게임/소프트웨어 공모전으로 들어가 봅니다.
사이트 링크:
https://www.thinkcontest.com/Contest/CateField.html?c=12

 

씽굿-대한민국 대표 공모전 미디어 씽굿

부지런과 즐김 나의 삶의 철학은 부지런과 즐김이다. 부지런하고 즐기는 사람은 하늘도 못 말린다고 한다. ‘세 사람이 가면 그 중엔 반드시 나의 스승이 있다(三人行必有我師)’고 했는데, 나 또한 남의 좋은 점을 본받지 않고 스스로 잘난 척 옹고집으로 산 것을 심히 후회하노라. ‘아는 자는 좋아하는 자만 못하고, 좋아하는 자는 즐기는 자만 못하다(知者不如好者 好者不如樂者)’. 필자는 지금 이 나이에도 생물수필을 쓰고 있나니, 누가 시켜서라면 죽어도 안(못)할

www.thinkcontest.com

 


 

 

씽굿 접속 화면

오늘은 간단하게 크롤링 봇을 만들거라서 게임/소프트웨어 공모전 1페이지에 노출되어있는 공모전만 파싱하도록 하겠습니다. 공모전 분류별, 페이지별로 파싱해오고 링크 및 이미지를 파싱하는 방법에 대해서는 다음에 다시 업로드하도록하겠습니다.

 

 

 

개발자도구(f12)를 켜서 가장 첫번째 공모전의 태그를 확인하겠습니다. a태그로 감싸져 있는데요 이 div의 하위 태그로 구성되어 있는 것을 확인 할 수 있습니다. div클래스는 contest-title special이네요 그런데 special이라는 것은 special 아이콘이 붙은 공모전에만 들어가는 클래스이기 때문에 모든 공모전을 파싱하려면 저대로 파싱하면 아래와 같은 결과가 나옵니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import urllib.request
from bs4 import BeautifulSoup
 
 
url = "https://www.thinkcontest.com/Contest/CateField.html?c=12"
req = urllib.request.urlopen(url)
res = req.read()
 
soup = BeautifulSoup(res,'html.parser')
contests = soup.find_all("div",class_="contest-title special")
 
#a 함수는 html포맷데이터에서 a태그만을 파싱한다.
#a 함수를 쓰는 이유는 contests-title div가 다른 태그에 여러 텍스트를 가지고있기 때문이다.
#a를 제거하고 테스트해보는것을 추천드립니다.
keywords = [each_line.a.get_text().strip() for each_line in contests]
print(keywords)
 
cs

 

special 클래스의 공모전만 파싱됨

 

그러면 모든 공모전을 파싱하려면 어떻게 해야할까요? 일반적인 공모전이 담겨있는 div의 클래스명을 확인해보겠습니다.

 

일반 공모전

일반공모전은 contest-title이라고 되어있습니다. 그러면 클래스 이름에 contest-title이라고 적으면 될까요?

네 그것도 됩니다. 왜냐하면 BeautifulSoup은 정규표현식처럼 일정부분 맞게 들어가는 클래스명은 참이라고 판단하기 때문입니다.
하지만 오늘은 다른 방법을 알려드리고자 하기 때문에 새로운 방식으로 시도해보도록 하겠습니다.

먼저 정규표현식 라이브러리인 re를 import할것입니다. 그리고 정규식을 컴파일 하기위해 re.compile함수를 사용하겠습니다. 그러면 정규표현식은 어떻게 구성하면 될까요? contest-title을 포함하고 뒤에 스페셜을 있어도 되고 없어도 된다고 명시하면 될 것 같습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import urllib.request
import re
from bs4 import BeautifulSoup
 
url = "https://www.thinkcontest.com/Contest/CateField.html?c=12"
req = urllib.request.urlopen(url)
res = req.read()
 
soup = BeautifulSoup(res,'html.parser')
#regex: .*(문자의 유형과 상관없이 그것이 반복되면 매칭), ?(그것이 있을 수도 없을 수도 있음)
#.*?(그것은 문자의 반복이지만 있을 수도 없을 수도 있음)
#re.DOTALL == .이 개행문자를 포함한 모든문자를 의미하게함
contests = soup.find_all("div",class_=re.compile("contest-title.*?",re.DOTALL))
keywords = [each_line.a.get_text().strip() for each_line in contests]
print(keywords)
cs

 

최종 결과

 

# 상속과 오버라이딩

 

1. 상속

 

상속은 사전적 정의에 따르면 부모나 친족적 관계에 있는 사람의 유산을 물려 받는 제도입니다. 클래스에서의 상속은 부모의 유산을 자식이 물려 받듯이 부모 클래스의 멤버와 메소드를 자식 클래스가 물려받을 수 있습니다. 이런 경우는 흔히 있습니다. 예를 들어 라이브러리를 제작할 때 특정 자료구조를 만들어서 모든 클래스가 이 자료구조를 이용하게 하고 싶다면 클래스의 상속을 이용하게 하면 됩니다.  추가적으로 파이썬에서 기본 제공되는 자료형 또한 클래스이기 때문에 자신만의 독특한 자료형을 만들 수 도 있겠죠.

 

상속을 이해하고자 할 때 가장 쉬운방법은 기존의 클래스를 이용하되, 내가 원하는 멤버와 메소드를 추가하는 것으로 이해 하는 것입니다.

 

클래스의 상속

먼저 총이라는 간단한 클래스가 있다고 가정해 보겠습니다. 클래스라는 것은 유형, 무형으로 존재하는 어떤 것을 코드로 나타내는 것입니다.  그러면 총(Rifle)이라는 객체는 파괴력, 탄창, 현재 남은 총알 등의 속성을 가지고 있을 테고, 기능으로는 장전이나 발사가 있을 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Rifle:
    def __init__(self,Mag, atk):
        self.__Mag = Mag
        self.__leftMag = Mag
        self.__atk = atk
    
    @property
    def Mag(self):
         return self.__Mag
    
    @property
    def leftMag(self):
        return self.__leftMag
    
    @leftMag.setter
    def leftMag(self, num):
        self.__leftMag = num
    
    @property
    def atk(self):
        return self.__atk
    
    @atk.setter
    def atk(self,num):
        self.__atk = num
        
    def Reload(self):
        self.__leftMag = self.__Mag
        
    def Shoot(self):
        if self.__leftMag:
            self.__leftMag -= 1
        else:
            print("장전하세요")
cs

 

이 클래스는 Rifle입니다. 제가 앞서 언급한 대로 생성자에 탄창, 남은 총알, 파괴력등의 요소를 만들었습니다. 또한 상속을위해 각 클래스멤버의 property와 setter를 작성하여 직접 멤버변수에는 접근하지 못하지만, 메소드를 통하여 가능하게 구현하였습니다. 탄창(Mag)은 처음 인스턴스 생성 이후 오염을 방지하기위해 setter에서 제외했습니다. Rifle클래스의 메소드로는 총이 실제로 가지고있는 기능인 발사(Shoot), 장전(Reload) 등을 포함 시켰습니다. 같은 클래스 안에서는 property나 setter를 이용하여 멤버에 접근할 필요가 없기때문에 직접접근하는 방식으로 구현하였습니다.(장전과 발사에 있는 self.__leftMag)

 

만약 총이라는 클래스를 상속받아 저격총이라는 클래스를 만들고자 하면 총이 가지고 있는 속성은 그대로 유지할 것입니다. 하지만 총이라는 클래스에는 망원경이라는 요소가 없기 때문에 속성을 하나 추가하고 망원경 조준이라는 기능을 추가해야겠죠.  그런 의미에서 멤버 변수에 Zoom을 추가하였고 메소드로는 increaseAtk을 넣어 Zoom과 파괴력을 곱해 다시 파괴력에 저장하는 코드를 작성하였습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ScopeRifle(Rifle):
    def __init__(self,Mag,atk):
        super().__init__(Mag,atk)
        self.__Zoom = 1
    
    @property
    def Zoom(self):
        return self.__Zoom
    
    @Zoom.setter
    def Zoom(self,num):
        self.__Zoom *= num
    
    def increaseAtk(self):
        self.atk *= self.__Zoom
cs

 

__init__을 보면 super().__init__이라는 구문이 있습니다. 이는 부모클래스의 생성자 때문에 필요한 구문입니다. 생성자 규칙에 따라 부모클래스는 탄창과 파괴력이라는 멤버를 필요로 합니다. 이 멤버를 채워주지 않으면 에러가 발생합니다. 

아래의 코드를 실행시키고 각각의 메소드를 테스트 해보겠습니다.

 

1
2
rifle = Rifle(5,10)
sniper = ScopeRifle(5,10)
cs

 

 

Rifle 클래스에 정의되어 있는 내용은 잘 동작하는 것을 확인하였습니다.  rifle을 상속받은 sniper도 잘 동작하는 것을 확인 할 수 있습니다. 

이번에는 sniper클래스의 고유 기능을 테스트 해보도록 하겠습니다.

 

 

제가 의도한 대로 잘 동작하고 있습니다.  atk는 부모클래스의 멤버이면서 property지만 setter를 같이 작성했기 때문에 sniper에서 접근하고 같을 변경시킬 수 있었습니다. public 하게 구현하면 사실 이런 고생은 안해도 됩니다. 하지만 객체지향 프로그래밍에서는 getter setter를 제외하고는 클래스 멤버에대한 외부 클래스의 접근을 금하고 있기때문에 property 설정을 꼭 하셔야 합니다.

 

 


 

 

2. 오버라이딩

 

총과 저격총 사이에는 명확하지 않지만 가끔씩  상속받은 클래스가 부모의 기능을 똑같이 가지고 있지만 구현 방법이 다를 때가 존재합니다. 이런 경우에는 같은 이름의 메소드를 작성하고 다른 기능을 수행하게 해야합니다. 예를 들어 산탄총의 경우를 생각해보겠습니다. 일반 총은 발사당 1개의 총알이 소비되지만 산탄총은 3발이 나간다고 칩시다. 산탄총도Shoot이라는 메소드가 당연히 필요하지만 부모 클래스의 Shoot과 다르게 동작해야 되겠죠? 파이썬에서는 부모 클래스의 메소드와 같은 이름의 메소드를 자식 클래스에 작성하여 부모 클래스의 메소드를 덮어버리는 방법이 있습니다. 이를 우리는 오버라이딩이라고 합니다.

 

1
2
3
4
5
6
7
class ShotGun(Rifle):
    def __init__(self,Mag,atk):
        super().__init__(Mag,atk)
    
    def Shoot(self):
        if self.leftMag:
            self.leftMag -= 3
cs

 

위의 코드는 Rifle클래스를 상속받아 만든 ShotGun클래스입니다.  ShotGun은 Rifle과 모든게 같지만 발사당 3개의 총알이 소비된다고 했었죠? 그 내용을 Shoot이라는 메소드를 정의하고 그 안에 구현하면 됩니다.

부모 클래스에 Shoot이라는 메소드가 있다고 하더라도 오버라이딩을 통해 이를 덮어 쓸 수 있습니다. 실제로 이 코드가 잘 작동하는지 확인 해보겠습니다.

 

 

코드가 정상적으로 작동하는 것을 확인 할 수 있습니다. 상속을 받을 때는 이런 식으로 기능을 고려해서 메소드를 다시 설계 해야할 필요가 있는 경우도 있습니다.

# 클래스

클래스란(Class),속성이나 기능을 공유하는 유사한 성질의 객체들을 하나로 그룹화한 것입니다. 클래스 내부에는 해당 클래스의 객체를 위한 클래스 멤버(Class Member)와 기능(method)의 구현 등 세부사항을 기술합니다.
클래스에 관하여 설명하기 전에 인스턴스(instance)에 관하여 알아보도록 하겠습니다.

 

 


 

 

1. 인스턴스

 

인스턴스란 클래스에 의해 만들어진 객체를 칭하는 말입니다. 예를 들어 클래스를 붕어빵 틀이라고 한다면, 인스턴스는 붕어빵이 되는 것입니다. 즉 클래스는 인스턴스를 만드는 하나의 틀입니다. 앞으로 클래스를 이용하여 인스턴스를 계속적으로 만들어 낼 수 있습니다.

캔디 클래스와 그 인스터스 캔디

 


 

 

2. 클래스 선언방법

 

클래스는 class 명령어 뒤에 클래스 이름을기술하여 선언합니다. 클래스 구문 하위에 인덴트(tab)을 넣어 데이터 구조나 함수등을 명시하면 됩니다. 예시를 보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person:
    # class Person의 멤버변수
    name = "홍길동"
    number = "01077499954"
    age = "20"
 
    # class Person의 메소드
    def info(self):
        print("제 이름은 " + self.name + "입니다.")
        print("제 번호는 " + self.number + "입니다.")
        print("제 나이는 " + self.age + "세 입니다.")
 
if __name__ == "__main__":# main namespace를 의미합니다.
    customer = Person()#Person의 객체 customer 생성
    customer.info()#info 함수 호출
 
#결과값
제 이름은 홍길동입니다.
제 번호는 01077499954입니다.
제 나이는 20세 입니다.
cs

위의 예시처럼 클래스를 구성하시면 됩니다. 클래스내의 name space에 사용할 변수를 만들어 주시면 되고 함수를 추가하여 사용하시면 됩니다. 그런데 함수에 뜬금 없는 self란 친구가 들어왔습니다. self는 현재 인스턴스 객체를 가리키는 기능을 하는 친구입니다. 쉽게 말하자면 사전적 의미대로 자기자신을 이야기 하 는 것입니다. 만약 customer라는 인스턴스를 만들었다면 self는 customer가 되는겁니다.(클래스내에 함수가 선언 될 때에는 첫번째 인자로 무조건 self가 와야합니다. 객체 사용유무와 상관없음)

 

객체에 클래스를 할당하고 싶으면 위 코드처럼 객체를 하나 만들어 클래스를 대입하면 됩니다.(customer = Person())

클래스에 있는 데이터에 접근하고자 할 때는 "."을 사용하면 됩니다. customer의 이름을 변경하고 싶을때에는 customer.name으로 접근해서 변경하면 되는겁니다. 함수의 이용도 마찬가지입니다. info 함수를 사용하고 싶을 때에는 customer.info로 사용할 수 있습니다. 하지만 이러한 접근방식은 OOP(Object Oriented Programming)의 캡슐화의 근간을 훼손하는 행위로서 getter나 setter를 이용하는것이 현명합니다.

 

파이썬에서는 모든 클래스가 public으로 작성되기 때문에 getter메소드나 setter 메소드가 없습니다. 대신 파이썬에서는 사용자가 속성에직접 접근하는 것을 막기 위해getter또는setter메서드 대신에프로퍼티(property)를 사용합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Person:
    # class Person의 멤버변수
    # __(double underscore)
    # __는 클래스외부에서 클래스 멤버에 접근하지 못하게 하기위함
    __name = "홍길동"
    __number = "01077499954"
    __age = "20"
 
    @property
    def name(self):
        return self.__name
 
    @name.setter
    def name(self, newName):
        self.__name = newName
 
    # class Person의 메소드
    def info(self):
        print("제 이름은 " + self.__name + "입니다.")
        print("제 번호는 " + self.__number + "입니다.")
        print("제 나이는 " + self.__age + "세 입니다.")
 
if __name__ == "__main__":# main namespace를 의미합니다.
    customer = Person()#Person의 객체 customer 생성
    customer.info()#info 함수 호출
    print(customer.name)
    customer.name="이태일"
    print(customer.name)
 
 # 결과 값
제 이름은 홍길동입니다.
제 번호는 01077499954입니다.
제 나이는 20세 입니다.
홍길동
이태일
cs

 

먼저 private하게 작성할 멤버변수의 이름앞에 더블 언더스코어(__, double underscore)를 붙여줍니다.그리고 아까와는 다르게 name 메소드위에 '**@'가 붙은 것을 볼 수 있습니다. 이를데코레이터(decorator)라고 하는데, 말그대로 뭔가를꾸며** 준다는 것을 의미합니다.

같은 name 이라는이름을 갖는 메소드이지만 데코레이터에 의해 서로 다른 역할을 합니다.클래스를 완성하고 __main__에서 테스트를 해보았습니다. 결과가 잘 나온 것을 볼 수 있죠? 그런데 여기에는 주의할 점이 있습니다.name은 customer 클래스의 메소드인데도 불구하고 호출 시 멤버변수처럼 접근합니다. 원래대로라면 customer.name()이 될텐데 customer.name 형식으로 호출하고 있는걸 볼 수 있죠? 코드의 생김새는 객체의 멤버에 직접 접근하는듯이 사용하지만**실제로는 메소드 호출을 통해 변수에 접근하게 됩니다.**

 


 

3. Initializing

 

자 클래스의 기본을 배웠으니 다음 예제로 넘어가겠습니다. Monster라는 클래스를 만들건데요 Monster는 체력,공격력,방어력을 가지는 클래스입니다. 공격이 가능하며 공격을 받았을 경우에 체력이 상대방의 공격력-자신의 방어력 만큼 감소한다고 칩시다. 이를 구현한 코드는 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Monster:
    def __init__(self,hp,atk,dfn):
        self.hp = hp
        self.atk = atk
        self.dfn = dfn
 
    def attack(self,target):
        target.ReduceHP(self.atk)
 
    def ReduceHP(self,atk) :
        self.hp = self.hp - (atk-self.dfn)
 
if __name__ == "__main__":
    m1 = Monster(100,10,3)
    m2 = Monster(100,10,3)
    m1.attack(m2)
    print(m2.hp)
 
#결과값
93
cs

__init__이라는 메소드에 주목해주세요. __init__은 파이썬에서 기본적으로 제공하는 클래스 생성자입니다. 아까 전에는 클래스 멤버변수를 만들고 그 값을 클래스에 직접넣어 주는 형식으로 구현했는데요, 생성자를 쓰면 클래스의 인스턴스가 생성될 때마다 다른 멤버값을 갖도록 구현 할 수 있습니다.

Monster 클래스는 제가 상상속으로 생각해 낸 괴물에 대해 코드로 구현한 것입니다. 먼저 게임에서의 몬스터는 체력과 공격력 그리고 방어력을 갖고 있죠? 이를 각각 클래스 멤버로 할당하고 각 몬스터 마다 값을 다르게 하기 위하여 생성자를 이용하였습니다.

또한 공격 기능 , 체력 감소 기능을 attack과 ReduceHP라는 메소드를 통해 구현해 보았습니다. 이 메소드를 이용하면 상대 몬스터에게 공격을 가하거나, 공격을 받았을 시에 체력이 감소되는 모습을 확인 할 수 있습니다.

그리고 객체 2개를 만들어 공격하게끔 해 보았습니다. 그 후 피 공격 객체의 체력 값을 확인해 보았더니 상대방의 공격력-자신의 방어력 만큼의 체력이 감소하였습니다. 잘 동작하는것 같네요.

+ Recent posts