파이썬3 바이블 - 제10장 연습문제

프리렉 - FREELEC

http://freelec.co.kr/book/catalogue_view.asp?UID=134

이강성저

1.

우선 ch10-01.txt 텍스트 파일을 만들어보자. 에디터로 파일을 만들어서 시작해도 좋다. 하지만 ipython의 %%file 명령을 이용하면 간단히 파일을 저장할 수 있다.

In [1]:
%%file ch10-01.txt
NumPy is the fundamental package for scientific computing with Python. It contains among other things:

# comment1..

  * a powerful N-dimensional array object
  * sophisticated (broadcasting) functions
  * tools for integrating C/C++ and Fortran code
  * useful linear algebra, Fourier transform, and random number capabilities

# comment2..

Besides its obvious scientific uses, NumPy can also be used as an efficient multi-dimensional container of generic data. Arbitrary data-types can be defined. This allows NumPy to seamlessly and speedily integrate with a wide variety of databases.

Numpy is licensed under the BSD license, enabling reuse with few restrictions.
Overwriting ch10-01.txt

In [2]:
# 파일을 읽어서 처리한다.
lineno = 0
for line in open('ch10-01.txt'):
    lineno += 1
    if line.startswith('#'):
        continue
    n = len(line.split())
    print ('{:3d} : {:3d}'.format(lineno, n))
  1 :  15
  2 :   0
  4 :   0
  5 :   6
  6 :   4
  7 :   8
  8 :  10
  9 :   0
 11 :   0
 12 :  37
 13 :   0
 14 :  12

2.

In [3]:
# 방법 1
f = open('number.txt', 'w')
for k in range(10):
    f.write('{}\n'.format(k))
f.close()
In [4]:
# 방법 2
f = open('number.txt', 'w')
s = '\n'.join([str(k) for k in range(10)])
f.write(s)
f.close()
In [5]:
# 방법 3
with open('number.txt', 'w') as f:
    s = '\n'.join([str(k) for k in range(10)])
    f.write(s)
In [6]:
# 방법 4
with open('number.txt', 'w') as f:
    L = [str(k)+'\n' for k in range(10)]
    f.writelines(L)
In [7]:
# 확인
print (open('number.txt').read())
0
1
2
3
4
5
6
7
8
9


3.

In [8]:
%%file s.txt
pig ham
cat dog
ham bird
dog pig
Overwriting s.txt

In [9]:
L = sorted(open('s.txt').read().splitlines())
L
Out[9]:
['cat dog', 'dog pig', 'ham bird', 'pig ham']
In [10]:
print ('\n'.join(L))
cat dog
dog pig
ham bird
pig ham

4.

In [11]:
# 각 라인에 단어가 두 개 이상 있다는 가정하에 문제를 풉니다.
L = sorted(open('s.txt').read().splitlines(), key=lambda a:a.split()[1])
L
Out[11]:
['ham bird', 'cat dog', 'pig ham', 'dog pig']
In [12]:
print ('\n'.join(L))
ham bird
cat dog
pig ham
dog pig

5.

In [13]:
ws = open('s.txt').read().split()
ws
Out[13]:
['pig', 'ham', 'cat', 'dog', 'ham', 'bird', 'dog', 'pig']
In [14]:
for k in range(0, len(ws), 3):
    print (' '.join(ws[k:k+3]))
pig ham cat
dog ham bird
dog pig

6.

In [15]:
import glob

# 우선 함수 테스트를 해보자
def mygrep(find, fpattern):
    flist = glob.glob(fpattern)
    for fpath in flist:  # 각 파일에 대해서
        lineno = 0
        for line in open(fpath):  # 각 라인에 대해서
            lineno += 1
            if find in line:
                print ('{}:{:}:{}'.format(fpath, lineno, line.rstrip()))

mygrep('bird', 'ch*.ipynb')
ch02.ipynb:1322:      "L = ['cat', 'dog', 'bird', 'pig', 'spam']\n",
ch02.ipynb:1335:        "2 bird\n",

In [16]:
%%file mygrep.py
# simple solution
'''
사용 예
$ python3 mygrep.py worksheets *.ipynb
'''
import glob
import sys

def mygrep(find, flist):
    for fpath in flist:  # 각 파일에 대해서
        lineno = 0
        for line in open(fpath):  # 각 라인에 대해서
            lineno += 1
            if find in line:
                print ('{}:{:}:{}'.format(fpath, lineno, line.rstrip()))

if __name__ == '__main__':
    mygrep(sys.argv[1], sys.argv[2:])    # 두 개의 인수
Overwriting mygrep.py

In [17]:
!python3 mygrep.py bird ch*.ipynb   # 사용 예
ch02.ipynb:1322:      "L = ['cat', 'dog', 'bird', 'pig', 'spam']\n",
ch02.ipynb:1335:        "2 bird\n",

In [18]:
%%file mygrep2.py

# using argparse
'''
사용 예

$ python3 mygrep.py worksheets *.ipynb
'''
import argparse
import sys

def mygrep(find, flist):
    for fpath in flist:  # 각 파일에 대해서
        lineno = 0
        for line in open(fpath):  # 각 라인에 대해서
            lineno += 1
            if find in line:
                print ('{}:{:}:{}'.format(fpath, lineno, line.rstrip()))

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('find')
    parser.add_argument('flist', nargs='+')
    args = parser.parse_args(sys.argv[1:])
    
    mygrep(args.find, args.flist)
Overwriting mygrep2.py

In [19]:
!python3 mygrep2.py bird ch*.ipynb   # 사용 예
ch02.ipynb:1322:      "L = ['cat', 'dog', 'bird', 'pig', 'spam']\n",
ch02.ipynb:1335:        "2 bird\n",

7.

In [20]:
'''
함수를 테스트 해보자.
re 모듈의 search 함수를 이용하면 정규식을 간단히 처리할 수 있다.
'''
import re
import glob

def mygrep2(find, fpattern):
    flist = glob.glob(fpattern)
    for fpath in flist:  # 각 파일에 대해서
        lineno = 0
        for line in open(fpath):  # 각 라인에 대해서
            lineno += 1
            if re.search(find, line):
                print ('{}:{:}:{}'.format(fpath, lineno, line.rstrip()))
In [21]:
mygrep2('\Wtiger\W', 'ch*.ipynb')  # \W는 숫자 또는 문자가 아닌 것과 매칭된다.
ch02.ipynb:1139:      "a = ['cat', 'cow', 'tiger']\n",
ch02.ipynb:1152:        "5 tiger\n"

8.

In [22]:
import time

def addLogLine(logfile, ip, access_page, access_time):
    f = open(logfile, 'a')
    f.write('{}:{}:{}\n'.format(ip, access_page, access_time))
In [23]:
addLogLine('weblog', '119.197.7.230', '/dokuwiki/lib/images/smileys/icon_sad.gif', time.time())
addLogLine('weblog', '119.197.7.230', '/dokuwiki/lib/images/email.png', time.time())
addLogLine('weblog', '119.197.7.230', '/dokuwiki/lib/images/email.png', time.time())
addLogLine('weblog', '119.197.7.230', '/dokuwiki/lib/tpl/dokuwiki/images/logo.png', time.time())
addLogLine('weblog', '119.63.193.130', '/', time.time())
addLogLine('weblog', '202.46.50.79', '/gugak/wp-includes/wlwmanifest.xml', time.time())
addLogLine('weblog', '119.197.7.237', '/dokuwiki/doku.php?id=start', time.time())
addLogLine('weblog', '119.197.7.237', '/dokuwiki/lib/tpl/dokuwiki/images/logo.png', time.time())
addLogLine('weblog', '119.197.7.237', '/dokuwiki/lib/tpl/dokuwiki/images/button-donate.gif', time.time())
addLogLine('weblog', '119.197.7.237', '/dokuwiki/lib/tpl/dokuwiki/images/button-php.gif', time.time())
addLogLine('weblog', '119.197.7.237', '/dokuwiki/lib/tpl/dokuwiki/images/button-html5.png', time.time())
addLogLine('weblog', '119.197.7.237', '/dokuwiki/doku.php?id=start', time.time())
addLogLine('weblog', '119.197.7.237', '/dokuwiki/lib/tpl/dokuwiki/images/button-dw.png', time.time())
In [24]:
!cat weblog     # 저장 내용 확인 (리눅스 쉘 명령)  
#!type weblog  # 윈도우에서의 명령
119.197.7.230:/dokuwiki/lib/images/smileys/icon_sad.gif:1396295526.909408
119.197.7.230:/dokuwiki/lib/images/email.png:1396295526.9096642
119.197.7.230:/dokuwiki/lib/images/email.png:1396295526.9097753
119.197.7.230:/dokuwiki/lib/tpl/dokuwiki/images/logo.png:1396295526.9098792
119.63.193.130:/:1396295526.9099813
202.46.50.79:/gugak/wp-includes/wlwmanifest.xml:1396295526.9100816
119.197.7.237:/dokuwiki/doku.php?id=start:1396295526.9102297
119.197.7.237:/dokuwiki/lib/tpl/dokuwiki/images/logo.png:1396295526.9103339
119.197.7.237:/dokuwiki/lib/tpl/dokuwiki/images/button-donate.gif:1396295526.9104326
119.197.7.237:/dokuwiki/lib/tpl/dokuwiki/images/button-php.gif:1396295526.910529
119.197.7.237:/dokuwiki/lib/tpl/dokuwiki/images/button-html5.png:1396295526.9106264
119.197.7.237:/dokuwiki/doku.php?id=start:1396295526.910733
119.197.7.237:/dokuwiki/lib/tpl/dokuwiki/images/button-dw.png:1396295526.910834

9.

In [25]:
group = {}
for line in open('weblog'):
    ip, url, tm = line.split(':')
    if ip not in group:
        group[ip] = []
    group[ip].append(url)
In [26]:
import collections
for ip in group:
    print ('-'*60)
    print (ip)
    for url, cnt in collections.Counter(group[ip]).items():
        print ('{} : {}'.format(url, cnt))
------------------------------------------------------------
119.63.193.130
/ : 1
------------------------------------------------------------
119.197.7.237
/dokuwiki/lib/tpl/dokuwiki/images/button-donate.gif : 1
/dokuwiki/doku.php?id=start : 2
/dokuwiki/lib/tpl/dokuwiki/images/button-dw.png : 1
/dokuwiki/lib/tpl/dokuwiki/images/button-php.gif : 1
/dokuwiki/lib/tpl/dokuwiki/images/button-html5.png : 1
/dokuwiki/lib/tpl/dokuwiki/images/logo.png : 1
------------------------------------------------------------
119.197.7.230
/dokuwiki/lib/images/email.png : 2
/dokuwiki/lib/images/smileys/icon_sad.gif : 1
/dokuwiki/lib/tpl/dokuwiki/images/logo.png : 1
------------------------------------------------------------
202.46.50.79
/gugak/wp-includes/wlwmanifest.xml : 1

10.

문제를 수정합니다. 암호화 방식은 파이썬2에서 사용된 sha 모듈 대신에 파이썬3의 hashlib를 사용합니다. hashlib은 다양한 보안 hash를 제공합니다. SHA1, SHA224, SHA256, SHA384, SHA512, MD5를 제공합니다.

In [27]:
import hashlib

password = 'my pass word'
encrypted1 = hashlib.sha1(password.encode()).hexdigest()
encrypted2 = hashlib.sha224(password.encode()).hexdigest()
encrypted3 = hashlib.md5(password.encode()).hexdigest()

print (encrypted1)
print (encrypted2)
print (encrypted3)
f64e382ba76a546b48d8e3b924ef1592fdc11487
8110f37f50b476befe868569bdea100ab905ac799a8d0dc9dbe16f36
ae412262cb869bbda7084e6c76097bee

In [28]:
# save password to file 'access'
def savePassword(sid, passwd):
    encrypted = hashlib.sha1(passwd.encode()).hexdigest()
    f = open('access', 'a')
    f.write('{}:{}\n'.format(sid, encrypted))
In [29]:
savePassword('gslee', '1234')
In [30]:
!cat access  # 저장된 내용 확인 (리눅스 쉘 명령)
gslee:7110eda4d09e062aa5e4a390b0a572ac0d2c0220

In [31]:
savePassword('ky', 'asbc')
In [32]:
!cat access  # 저장된 내용 확인 (리눅스 쉘 명령)
#!type access  # 윈도우
gslee:7110eda4d09e062aa5e4a390b0a572ac0d2c0220
ky:5d0048b9af7ac8205b0c293846dd9b72d55d1858

11.

In [33]:
def checkIfUserValid(sid, passwd):
    encrypted = hashlib.sha1(passwd.encode()).hexdigest()
    with open('access') as f:
        for line in f:
            tid, tpass = line.split(':')
            if tid == sid and encrypted == tpass.strip():
                return True
    return False
In [34]:
checkIfUserValid('gslee', '1234')
Out[34]:
True
In [35]:
checkIfUserValid('gslee', '12345')
Out[35]:
False

12.

본격적인 작업을 시작하기 전에 코드들을 테스트 해보자.

In [36]:
# 웹에서 페이지 읽어오기 테스트

from urllib.request import urlopen

root = 'http://docs.python.org/2/howto/webservers.html'

f = urlopen(root)
html = f.read()

print (len(html))       # 바이트열 길이
print (html[:100])      # 일부만 출력해본다.
57186
b'\n\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n  "http://www.w3.org/TR/xhtml1/DTD/'

In [37]:
# 읽어온 페이지에서 링크 추출하기
# <a>..</a> 태그만 추출하기로 한다.

import re   # 26장 정규식 참조

shtml = html.decode()        # 바이트 열을 문자열로 전환. utf-8
hrefs = re.findall('<a href="(.*?\.html)".*?>.+?</a>', shtml)   # href 안에 있는 내용만 추출한다. .html로 끝나는 링크만 찾아낸다.
hrefs
Out[37]:
['../index.html',
 'index.html',
 '../contents.html',
 '../bugs.html',
 '../index.html',
 'index.html',
 '../copyright.html',
 '../bugs.html']
In [38]:
# 각 링크의 절대 경로를 얻어낸다.
from urllib.parse import urljoin

for ref in hrefs:
    print ('{} ==> {}'.format(ref, urljoin(root, ref)))
../index.html ==> http://docs.python.org/2/index.html
index.html ==> http://docs.python.org/2/howto/index.html
../contents.html ==> http://docs.python.org/2/contents.html
../bugs.html ==> http://docs.python.org/2/bugs.html
../index.html ==> http://docs.python.org/2/index.html
index.html ==> http://docs.python.org/2/howto/index.html
../copyright.html ==> http://docs.python.org/2/copyright.html
../bugs.html ==> http://docs.python.org/2/bugs.html

In [39]:
# 중복되는 경로는 제거하기로 한다.
full_refs = list(set([urljoin(root, ref) for ref in hrefs]))
full_refs
Out[39]:
['http://docs.python.org/2/copyright.html',
 'http://docs.python.org/2/index.html',
 'http://docs.python.org/2/contents.html',
 'http://docs.python.org/2/bugs.html',
 'http://docs.python.org/2/howto/index.html']
In [40]:
# 경로 분리 테스트
from urllib.parse import urlsplit

res = urlsplit('http://docs.python.org/2/copyright.html')
print (res)
print (res.path)
SplitResult(scheme='http', netloc='docs.python.org', path='/2/copyright.html', query='', fragment='')
/2/copyright.html

자 이제 모든 준비가 되었다. 위의 코드들을 조합해보자.

In [41]:
import os
import re   # 26장 정규식 참조
from urllib.request import urlopen
from urllib.parse import urljoin

def savePage(root):
    fpath = urlsplit(root).path[1:]
    html = urlopen(root).read()
    shtml = html.decode()
    folder, fname = os.path.split(fpath)
    if not os.path.exists(folder):      # 필요하다면 디렉토리를 생성한다.
        print ('making directory : ', folder)
        os.makedirs(folder)
    print ('saving..', fpath)
    open(fpath, 'w').write(shtml)
    return shtml
In [42]:
root = 'http://docs.python.org/2/howto/webservers.html'

shtml = savePage(root)

hrefs = re.findall('<a href="(.*?\.html)".*?>.+?</a>', shtml)   # href 안에 있는 내용만 추출한다. .html로 끝나는 링크만 찾아낸다.

full_refs = list(set([urljoin(root, ref) for ref in hrefs]))

for ref in full_refs:
    shtml = savePage(ref)
making directory :  2/howto
saving.. 2/howto/webservers.html
saving.. 2/copyright.html
saving.. 2/index.html
saving.. 2/contents.html
saving.. 2/bugs.html
saving.. 2/howto/index.html

In [42]: