[출처 : http://corioli.tistory.com/17]


5.1 파일 목록 얻기

(1) glob.glob(wildcard) - 유닉스 경로명 패턴 스타일로 파일 목록을 얻을 수 있다.


(2) os.listdir(path) - 지정된 디렉토리의 전체 파일 목록을 얻을 수 있다.


(3) dircache.listdir(path) - os.listdir(path)와 동일한 파일 목록을 전달한다.

path가 변경되지 않았을 때, dircache.listdir()은 다시 디렉토리 구조를 읽지 않고 이미 읽은 정보를 활용

dircache.annotate(head, list) - 일반 파일명과 디렉토리명을 구분해주는 함수


5.2 디렉토리 다루기

os.chdir(path) - 작업하고 있는 디렉토리 변경

os.getcwd() - 현재 프로세스의 작업 디렉토리 얻기

기타 여러 함수가 있다.


5.3 파일 이름 다루기

os.path.abspath(filename) - 파일의 상대 경로를 절대 경로로 바꾸는 함수

os.path.exists(filename) - 주어진 경로의 파일이 있는지 확인하는 함수

os.curdir() - 현재 디렉토리 얻기

os.pardir() - 부모 디렉토리 얻기

os.sep() - 디렉토리 분리 문자 얻기


5.4 경로명 분리하기

os.path.basename(filename) - 파일명만 추출

os.path.dirname(filename) - 디렉토리 경로 추출

os.path.split(filename) - 경로와 파일명을 분리

os.path.splitdrive(filename) - 드라이브명과 나머지 분리 (MS Windows의 경우)

os.path.splitext(filename) - 확장자와 나머지 분리

[출처 : http://python.kr/viewtopic.php?t=25357&start=0&postdays=0&postorder=asc&highlight=]

파이썬으로 두 문자열간의 유사도를 측정하는 edit distance를 구현해 보았습니다. 

근데 영어는 예상한 대로 잘 나오는데 한글의 경우 2byte라 그런지 예상대로 

잘 안됩니다. 

내혈안 -> 냉혈한 을 계산 할 경우 3 이 나옵니다. 

자소분해한 것을 가지고 계산해도 

ㄴㅐㅎㅕㄹㅇㅏㄴ -> ㄴㅐㅇㅎㅕㄹㅎㅏㄴ 3이 나옵니다. 

영문처럼 위의 문자열 변경시 2군데만 수정하면 되므로 2 가 나오도록 하고 싶은 
데 어찌 하면 될까요?

> 답변

유니코드로 바꿔서 처리하세요. 

코드:

In [6]: edit.levenshtein(u"내혈안", u"냉혈한") 
Out[6]: 2 

In [7]: edit.levenshtein("내혈안", "냉혈한") 
Out[7]: 3 

감사합니다. 알려주신대로 unicode로 변경하여 처리하니 원하는대로 유사도가 나옵니다. 

코드:

def edit_distance(first , second): 
    first_len , second_len = len(first) , len(second) 
  
    if first_len > second_len : 
        first , second = second , first 
        first_len , second_len = second_len , first_len 
  
    print first_len 
    print second_len 
    current = range(first_len+1) 
  
    for i in range(1,second_len+1): 
        previous , current = current , [i]+[0]*second_len 
  
        for j in range(1,first_len+1): 
            add , delete = previous[j]+1 , current[j-1]+1 
            change = previous[j-1] 
            if first[j-1] != second[i-1]: 
                change = change + 1 
  
            current[j] = min(add , delete , change) 
  
    return current[first_len] 



sFirstStr = sys.argv[1] 
sSecondStr = sys.argv[2] 
  
sFirst = unicode(sFirstStr,'cp949') 
sSecond = unicode(sSecondStr,'cp949') 


nDistanceValue = edit_distance(sFirst,sSecond) 
  
print nDistanceValue 
[출처 : http://songhl1.tistory.com/7]

상속 - is a,  합성 - has a

아마 객체 지향 언어를 공부했다면, 위의 단어가 떠오를 것이다. python 역시 객체지향이기 때문에 합성과 상속을 사용한다.

Example : Inheritance (상속)
---------------------------------------
# Inheritance Example
class ListChild(list):

    def listMake(self, A):
        tlist = self[:]                             # list copy
        for x in A:
              if x not in tlist:                    # If the element is new and is not in list
                    tlist.append(x)             # Append new element to list
         return ListChild(tlist)             # return updated list
---------------------------------------

Example : Compostion(합성)
---------------------------------------
class ListChild:
    def __init__(self, d=None):
        self.data = d
    def listMake(self, A):
        res = self.data[:]                      # list copy
        for x in A:
             if x not in res:
                   res.append(x)
        return Set(res)
    def __getitem__(self, k):
        return self.data[k]
    def __repr__(self):
        return 'self.data'
---------------------------------------
위에서 data를 res로 복사했다고 생각하기 쉬운데, 사실 그게 아니라 data가 실제 list data가 있는 곳을 point 하고 있는 것이다. 즉, res = self.data[:] 이렇게 하면 data를 새로운 공간에 복사하는 것이 아니라 res 역시 list data가 존재하는 곳을 point 하고 있는 것이다. 
python에서는 이를 C와 같이 pointer라고 부르기 보다 symbol 이라 한다. (맞나..;;)

위에서 본 바와 같이 inheritance와 composition의 차이점을 짐작할 수 있을 것이다. 하지만 좀 더 이를 구체적으로 적어보자면,
- 합성이 구현하기 쉽다고 한다... 개인차인듯(내생각)
- 상속은 super class를 상속받아서 새로 method만 추가하면 되지만, super class에서 어떠한 method들이 제공되는지 알고 있어야 한다. 즉 super class의 정보를 모른다면 잘 사용하기 힘들다. 반면에 합성은 적어도 어떠한 method들을 사용할지 명확히 안다. (자기 안에 다 있으니까)
- 상속의 경우 super class의 수정은 조심해야 한다. 다른 클래스들이 참조할 수도 있으니, 반면에 합성은 수정이 쉽다.
- 상속의 경우 다형성을 제공, 즉 sub classing이 용이 (당연하건가..)
- 상속은 superclass의 이미지를 갖게 되므로 superclass의 종료시점을 가지게 된다. 반면에 합성은 객체의 생성과 소멸을 정할 수 있다.
- 두 기법은 당연히 같이 사용된다. 그게 좋다. 예를 들면 super class는 동물이고 이를 '사자'가 상속한다. 또한 '사자'는 '갈기'를 가지고 있다. '갈기'를 표현하기 위해서는 합성(has-a)을 사용하는 것이 좋다. 
 * 물론 여기서 일관성이 유지되는가를 고려해줘야 한다. 즉, 어떤 class가 여러가지로 표현이 가능하다면, (ex. 사람을 표현할 때, 이 사람은 고용인 일 수도 있으며, 엔지니어 일 수도 있고, 가계주인 일 수 있다. 동시에 말이다.) 이럴 경우에는 일관성을 위해서 합성을 사용하는 것이 좋을 것 같다.
[출처 : http://blog.jangc.net/17]


목표 : 레이싱 모델 사진 모으기 -_-*
세부사항 : 레이싱 모델 갤러리의 임이의 두 게시물 사이의 게시물들에 첨부되어 있는 모델 사진 다운받기.
알고 있어야 하는 것들 : HTML, 정규식


진행은 인터프리터를 켜놓고 따라하면 될꺼야.

우선 게시물 기본 주소를 얻어오자!
( http://gall.dcinside.com/list.php?id=racinggirl&no= )
이건 뭐..프로그래밍으로 하는게 아니니 설명 패스!

그리고 까먹기 전에 아무 변수에나 넣어두자.
알다시피 파이썬은 아무때나 변수를 만들 수 있으니까 필요할때 만들면 돼~

>>> racingGirlUrl = 'http://gall.dcinside.com/list.php?id=racinggirl&no='


파이썬에서 문자열은 '나 "로 감싸면 되고 차이는 없어. 문자열이 여러줄일땐 '''나 """로 감싸면 되고. 뭐 그냥 그렇다구요~

저 주소를 어떻게 쓸꺼냐면, 주소 뒤에 게시물 번호만 바꿔서 for로 돌리는거야..

>>> for no in range(170710, 170720):
>>>     print racingGirlUrl + str(no)


웁스, 차근차근 설명하려 했는데 단번에 너무 많은게 나와버렸네.
 * for
 * range
 * 들여쓰기
이정도가 저거에서 볼만한 게 아닌가 싶네.

# 들여쓰기
우선 들여쓰기부터 보면, C나 자바는 함수나, for, if등 구역을 나눌 때 {}를 사용하잖아?
그런데 {}사용하면서도 사람들이 들여쓰기도 다 하잖아. 그래서 파이썬은 {}를 안쓰고 그냥 들여쓰기를
구역 구분으로 사용해. 
{}가 컴퓨터를 위한거고 들여쓰기가 인간을 위한 것이었다면 파이썬은 그냥 들여쓰기만 사용해서 컴퓨터와 인간이 같은걸 보게 하는거지.

# for
파이썬에서 for문은 시퀀스 자료형 탐험용이라고 보면 될 것 같아.
그러니까... 
for A in B: 라고하면 B는 시퀀스 자료형 오는거고 A는 우리가 하니씩 받을 변수가 되는거지.

아, 그리고 if, while, for등의 끝에는 :를 붙여줘.

# range
range는 주어진 숫자 사이의 리스트를 만드는 함순데.
range(10)하면 [0,1,2,3,4,5,6,7,8,9]가 되는거고
range(5, 10)하면 [5,6,7,8,9]가 되는거고, 막 이래.

# 기타
print는 화면 출력, str은 문자열로 변경.
문자열끼리 연결할때는 +로 연결해주면 되는거야~


자자. 이제 주소도 생성했으니 게시물 속 알맹이를 얻어와야지!
파이썬은 기본으로 제공되는 모듈이 참 많은데 그중에 urllib라는게 있어.
모듈을 불러오는건 두가지 방법이 있는데 그건 그냥 넘어가고.
모듈 불러오기!

>>> import urllib

모듈 불러오고 하는건 생긴게 자바랑 비슷하게 생겼더라고.
아무튼 저 모듈에 urllib.urlopen이란 함수가 있는데 url에 연결해서 파일 오브젝트를 넘겨줘..

우리가 필요한건 아까 그 for문에서 만든 주소니까 거기에 추가하면 되겠지.

>>> for no in range(170710, 170720):
>>>     url = racingGirlUrl + str(no)
>>>     f = urllib.urlopen(url)


파이썬이 스크립트 언어라서 좋은게, 모듈이나 클래스 등의 변수, 메소드 등이 뭐가 있나 모르겠으면
dir해보면 대충 알 수 있어. 이게 참 좋더라.

>>> dir(f) 해보면 대충
['__doc__', '__init__', '__iter__', '__module__', '__repr__', 'close','fileno', 'fp', 'geturl', 'headers', 'info', 'next', 'read','readline', 'readlines', 'url']


이런식으로 나올꺼야.
뭐..이름만 나오니까 메소든지 변순지는 알 수 없지만...대충 감은 오잖아?

>>> f.read()   를 하면 아까 읽어들인 페이지의 HTML이 좍~ 출력되겠지-_- 
근데 이렇게 하면 for문이 끝나고 마지막에 남은 f의 내용만 본거니까 나머지도 다 보기위해 이것도 for문으로 넣자.

>>> for no in range(170710, 170720):
>>>     url = racingGirlUrl + str(no)
>>>     f = urllib.urlopen(url)
>>>     html = f.read()

아무튼 게시물의 html도 읽어왔으니 이제 사진 주소를 찾아야지!

이것도 코딩으론 모르겠고. 손으로 직접-_-;
대충 html을 보니까.. http://image.dcinside.com/download.php로 시작하는걸 받으면 되겠네.

이제 정규식을 쓰자고.
파이썬 정규식 모듈은 re야. 
(모듈 이름 쓰기가 얼마나 귀찮았으면 두자로 했을까. 게으른 사람들 같으니-_-;)

>>> import re


뭐..정규식 모듈이 해주는게 이것저것 있지만 우리가 필요한건 문자열에서 원하는 문자열만 뽑아내는거니 그것만 보자구.
(아까 말했듯, dir(re)를 해보면 re 모듈에 들어있는 클래스, 함수 등을 볼 수 있다오~)

우리가 찾는 함수는 이것! re.findall

아 근데 이 함수 인자가 어떻게돼더라...잘 모르겠을 땐
>>> help(re.findall)  해보면 대충 설명이 나오지.

내가 사용할 정규식은 http://image.dcinside.com/download.php[^']+ 이거야.
(정규식에 대한 설명은 패스.)

합쳐서 아까 그 for문에 넣어주면 

>>> for no in range(170710, 170720):
>>>     url = racingGirlUrl + str(no)
>>>     f = urllib.urlopen(url)
>>>     html = f.read()
>>>     imageUrlList = re.findall("http://image.dcinside.com/download.php[^']+", html)


이렇게 되겠다. 하하 이제 사진 주소들도 얻어왔으니 다운받아서 저장하는 것만 남았군. 

파이썬 기본 자료형에 리스트라는게 있는데, 뭐 이름에서도 알 수 있듯 시퀀스 자료형이야.
리스트의 문법은 []로 감싸는거고, 
[]는 빈거 [1]는 1이 있는 리스트, [1, 'bob'] 이런 1이랑 'bob'이 있는 리스트. [1, 'bob', ['gopa']] 이건 리스트 안에 리스트가 있는거.

이 얘길 갑자기 왜하냐면 방금 findall하면 넘어오는게 찾은 주소들의 리스트거든. 
우리가 하려는건 받은 리스트를 순서대로 돌면서 그 주소의 사진을 urlopen으로 열어서 내 컴퓨터에 저장하는 것.
여태까지 했던거의 반복이니까 설명 없이 바로 작성할께.

>>> fileNo = 0
>>> for no in range(170710, 170720):
>>>     url = racingGirlUrl + str(no)
>>>     f = urllib.urlopen(url)
>>>     html = f.read()
>>>     imageUrlList = re.findall("http://image.dcinside.com/download.php[^']+", html)
>>>     for url in imageUrlList:
>>>         contents = urllib.urlopen(url).read()
>>>         file(str(fileNo)+'.jpg', 'w').write(contents)
>>>         fileNo = fileNo + 1


이정도쯤 되겠네.

쓰면서 for문 안에껄 함수로 빼낼껄. 하는 후회를 하긴 했는데, 뒤늦게 생각난거라 고치기가 귀찮네.

완성 소스
-----------------------
import re
import urllib
fileNo = 0
racingGirlUrl = 'http://gall.dcinside.com/list.php?id=racinggirl&no='
for no in range(170710, 170720):
        url = racingGirlUrl + str(no)
        f = urllib.urlopen(url)
        html = f.read()
        imageUrlList = re.findall("http://image.dcinside.com/download.php[^']+", html)
        print imageUrlList
        for url in imageUrlList:
                print fileNo
                contents = urllib.urlopen(url).read()
                file(str(fileNo)+'.jpg', 'wb').write(contents)
                fileNo = fileNo + 1


-----------------
아~ 레모들 이쁘네~

간단한 것 몇가지 추가하자면,
파이썬에서 주석은 #로 시작하면 되고,
html 파싱해서 뭐 찾고 그러는거면 파이썬엔 beautifulSoap이란걸 많이 쓰더라. 편해.


----
이랬음 좋겠다 저랬음 좋겠다 글올려줘~
[출처 : http://blog.jangc.net/485]


크롤링이 좋은 짓은 아니지만, 살다보면 필요한 때가 있다.

이런 작업은 주로 스크립트 언어로 수행이 되며, 나는 파이썬을 이용한다. 

파이썬으로 웹페이지를 다운 받는 코드는 다음과 같다.

import urllib
url = 'http://blog.jangc.net'
s = urllib.urlopen(url).read()
file('download.html', 'w').write(s)


그런데 이게 보면 어떤 페이지들은 브라우져로 접속할 때와 다른 페이지가 받아진다. 

이런 경우를 보면 대게 유저 에이전트를 채워주면 해결된다.


user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
values = {}
headers = { 'User-Agent' : user_agent }

data = urllib.urlencode(values)
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
s = response.read()
file('download.html', 'w').write(s)


코드는 위와 같다. 

보면, user_agent라는 것에 모질라 어쩌구 MSIE 어쩌구 이런식으로 써있는데, 
브라우져들이 저 값을 보내준다. 그런데 첫번째 코드에선 그 값이 없기 때문에 
서버에서 로봇으로 판단해서 기능을 제한한 것이다.

그리고 추가로 values라고 된 부분에 넘겨줄 파라메터들을 설정할수도 있다. 

예를들면 
values = {'user_id':'jangc', 'password':'bimil'}
이렇게.



크롤링은 일반적으로 서버에 큰 부하를 주니, 꼭 필요한 경우에만 사용하는 하자.

끝.

+ Recent posts