반응형
반응형
http://www.flowdas.com/blog/generators-in-python/


지난 글에서는, 생산자-소비자 패턴(Producer-Consumer Pattern)의 소비자 쪽의 코드를, 함축적이면서도 자연스럽게 기술할 수 있는 방법을 제공하는 이터레이터를 살펴봤습니다. 오늘은 생산자 쪽을 들여다봅니다.

앞서 Range 라는 클래스를 통해 이터레이터를 살펴보았습니다만, 이 코드에는 불편한 부분이 있습니다. 대부분의 작업은 __next__() 메쏘드를 통해 구현되는데, 매번 예전의 상태를 복구한 후, 작업을 수행하고, 다시 상태를 저장하는 작업을 반복하게 됩니다. Range 와 같은 간단한 작업에서야 문제될 것이 없지만, 알고리즘을 기술하는 자연스러운 방식에서 벗어나 있기 때문에, 작업이 복잡해질수록 점점 문제가 됩니다. 이런 스타일의 프로그래밍은 이벤트 주도형 프로그래밍(event-driven programming)에서 자주 등장하는데, 비동기 IO(Asynchronous IO) 나 GUI 프로그래밍을 해 보셨다면 이미 익숙할 겁니다.(지금 우리가 걷고 있는 길은 이 문제들에 대한 직접적인 해결책으로 이어집니다. 하지만 아직은 갈 길이 멉니다.)

파이썬에서는 제너레이터(generator)라는 이터레이터의 확장을 제공합니다. 이터레이터가 단지 프로토콜인데 반해, 제너레이터는 yield 라는 전용의 키워드를 통해 문법적인 특별함을 더합니다. Range 를 제너레이터로 다시 구현해 보겠습니다. (되도록 이터레이터 구현과 유사하게 코드를 구성했습니다.)

>>> def Range(n):
...     c = 0
...     while c < n:
...         yield c
...         c += 1
>>> for x in Range(3):
...     print(x)
0
1
2

파이썬은 함수 정의에 yield 키워드가 등장하면 그 함수 전체를 제너레이터로 인식하고, 특별한 VM 코드를 생성해냅니다. 함수의 코드를 재구성해서 같은 일을 하는 이터레이터를 만든 다음, 원래 함수는 그 이터레이터 인스턴스를 돌려주는 함수로 바꾼다고 보시면 됩니다.

>>> Range(3)
<generator object Range at 0x1017d3370>

제너레이터는 이터레이터입니다.

>>> r = Range(3)
>>> next(r)
0
>>> next(r)
1
>>> next(r)
2
>>> next(r)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
StopIteration

이터레이터 구현과 제너레이터 구현간에는 이런 문법적인 차이가 있습니다.

  • 이터레이터 구현에서 __next__() 의 return 값은, 제너레이터 구현에서는 yield 값으로 대체됩니다.
  • 제너레이터에서 return 문은 StopIteration 예외를 일으킵니다. 함수의 끝에 도달해도 마찬가지 입니다. 파이썬 3 에서는 return 문이 값을 포함할 수도 있지만, 오늘 다룰 범위를 벗어납니다.

제너레이터를 위해 파이썬이 만들어내는 이터레이터를 제너레이터-이터레이터(generator-iterator)라고 부르는데, 이터레이터는 프로토콜인 반면 제너레이터-이터레이터는 실제 구현이 제공되는 것이라, 일반적인 이터레이터에서 지정하지 않은 동작들이 구체적으로 정의되기도 합니다.

가령 예외의 경우, 이터레이터의 __next__() 가 StopIteration 또는 그 외의 예외를 일으킨 후에 이터레이터가 어떤 상태가 되는지는 정의되어있지 않습니다. 하지만 제너레이터의 경우는 항상 이렇게 됩니다.

  • 일단 StopIteration 예외가 일어나면 그 이후에는 계속 StopIteration 예외를 일으킵니다.
  • StopIteration 이외의 예외가 일어나면, 일단 caller 에게 예외가 전달됩니다. 하지만 그 이후로는 계속 StopIteration 예외를 일으킵니다.

재진입(Reentrancy) 문제도 이터레이터에서는 정의되어있지 않습니다. 가령 __next__() 메쏘드 내에서 직접적이던 간접적이던 next(self)를 호출했을 때 어떤 일이 일어나야 할지 지정되어 있지 않다는 것입니다. 그러니 재귀적인 구현도 생각해볼 수 있습니다. 하지만 제너레이터에서는 재진입이 허락되지 않습니다.

>>> this = (lambda: (yield next(this)))()
>>> next(this)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "<console>", line 1, in <lambda>
ValueError: generator already executing

그런데 이 예에서 볼 수 있듯이 lambda 로 제너레이터를 만들어도 문제가 되지는 않습니다. 그런데 lambda 의 바디에는 문장(statement)이 아니라 표현식(expression) 이 온다는 것을 기억하십니까? yield 를 둘러싼 괄호가 yield 가 표현식임을 알려줍니다. 예 지금까지 저희는 yield 를 문장처럼 사용해 왔습니다만, 사실 yield 는 표현식입니다. 그러면 그 값은 뭘까요?

지금까지의 예 에서 yield 표현식(yield expression)의 값은 None 입니다.

>>> def G():
    while True:
        input = yield 'output'
        print(repr(input))
>>> g = G()
>>> next(g)
'output'
>>> next(g)
None
'output'

처음 인쇄된 'output' 은 next(g) 의 반환 값이고, 다음 next(g) 호출의 처음에 인쇄된 None 이 yield 표현식의 결과값입니다. 순서를 잘 기억하세요, 첫 번째 next() 에서 yield 로 값을 전달한 후에, 다음 next() 가 호출될 때야 비로소 yield 표현식이 값을 전달합니다.

caller 가 제너레이터로 값을 전달해 준다면 yield 표현식은 None 이외의 값을 가질 수 있습니다. 이를 위해 제너레이터에는 send() 라는 메쏘드가 제공됩니다.

>>> g = G()
>>> next(g)
'output'
>>> g.send('input')
'input'
'output'

None 이 send() 가 전달한 'input'으로 대체된 것을 확인하실 수 있습니다. 주의할 부분은 첫 번째 next(g) 대신 g.send('input') 을 사용할 수 없다는 것입니다.

>>> g = G()
>>> g.send('input')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator

이유는 앞에서 주의를 부탁한 실행 순서 때문입니다. 첫 번째 yield 표현식은 최초의 yield 가 일어난 후에 전달됩니다. 즉 send() 메쏘드는 yield 로 값을 전달한 후에, caller 가 next() 나 send() 를 호출해주기를 기다리고 있는 제너레이터에만 사용될 수 있습니다. 꼭 send()를 사용해야 한다면 None 을 전달할 수 있습니다.

>>> g = G()
>>> g.send(None)
'output'

next(g) 는 g.send(None) 과 같은 의미로 해석됩니다.

제너레이터에는 send() 외에도 throw() 와 close() 라는 메쏘드가 더 제공됩니다. 이 메쏘드들은 나중에 다룰 기회가 있을 겁니다만, 제너레이터에 이런 기능들이 들어간 목적이 무엇일까요? 그 것은 바로 다음에 다룰 코루틴(coroutine) 때문입니다.

반응형
반응형

http://www.flowdas.com/blog/iterators-in-python/


대부분의 파이썬 튜토리얼들과는 달리 파이썬 3.3을 중심으로 기술하고 파이썬 2를 포함한 하위 버전에서 차이를 보이면 따로 설명합니다.

파이썬의 시퀀스는 for 루프로 탐색할 수 있습니다.

>>> for e in [1,2,3]: # 리스트
...     print(e)
1
2
3
>>> for e in (1,2,3): # 튜플
...     print(e)
1
2
3
>>> for e in 'abc': # 문자열
...     print(e)
a
b
c

for 루프는 시퀀스가 아닌 딕셔너리(dictionary)에서도 가능합니다.

>>> for k in {'a':1, 'b':2, 'c':3}: # 딕셔너리
...     print(k)
b
c
a

이 경우 딕셔너리의 키를 탐색하게 되는데, 키가 전달되는 순서는 정해지지 않았습니다. 때문에 여러분이 직접 실행할 경우, 위의 예와는 다른 순서로 키가 인쇄될 수 있습니다. 이터레이터(iterator)를 제공한다면 시퀀스가 아닌 타입도 for 루프로 탐색할 수 있습니다. 이터레이터는 내장 함수 iter() 를 사용해서 얻습니다.

>>> iter({})
<dict_keyiterator object at 0x108f7afc8>

이렇게 얻은 이터레이터를 탐색하려면 내장 함수 next() 를 사용하면 됩니다.

>>> it = iter({'a':1, 'b':2, 'c':3})
>>> print(next(it))
b
>>> print(next(it))
c
>>> print(next(it))
a
>>> next(it)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
StopIteration

next() 를 사용해 한 번에 하나씩 탐색해 나가다가, StopIteration 예외가 발생하면 끝냅니다. 이러한 탐색 법에 대한 약속을 이터레이터 프로토콜(Iterator Protocol)이라고 부르는데, for 루프는 간결한 문법을 제공합니다.

>>> for e in container:
...     print(e)

는 다음과 같은 while 루프와 동일한 결과를 줍니다.

>>> it = iter(container)
>>> while True:
...     try:
...         e = next(it)
...         print(e)
...     except StopIteration:
...         break

사용자가 정의한 클래스가 이터레이터를 지원하려면 __iter__() 메쏘드를 정의하면 됩니다. iter() 는 객체의 __iter__() 메쏘드가 돌려주는 값을 이터레이터로 사용합니다. next() 에 이터레이터를 전달하면 이터레이터의 __next__() 메쏘드를 호출합니다. 때문에 최소한의 이터레이터는 이렇게 구성할 수 있습니다.

>>> class Range:
...     def __init__(self, n):
...         self.n = n
...         self.c = 0
...     def __iter__(self):
...         return self # 이터레이터인 경우는 자신을 돌려주면 됩니다.
...     def __next__(self):
...         if self.c < self.n:
...             v = self.c
...             self.c += 1
...             return v
...         else:
...             raise StopIteration
...     next = __next__ # 파이썬 2 에서는 __next__ 대신 next 를 사용합니다.
>>> for x in Range(3):
...     print(x)
0
1
2

Range 클래스는 이터레이터 외의 용도가 없습니다. 하지만 딕셔너리는 별도의 용도가 있고, 사실 딕셔너리는 이터레이터가 아닙니다.

>>> next({})
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: 'dict' object is not an iterator

딕셔너리를 for 루프에 직접 사용할 수 있는 이유는 딕셔너리 역시 __iter__() 메쏘드를 제공하고 있기 때문입니다. 비슷한 방법으로 RangeFactory 라는 클래스를 만들어보면:

>>> class RangeFactory:
...     def __init__(self, n):
...         self.n = n
...     def __iter__(self):
...         return Range(self.n)
>>> for x in RangeFactory(3):
...     print(x)
0
1
2

__next__() 메쏘드를 제공하고 있지 않기 때문에 RangeFactory 의 인스턴스는 이터레이터가 아닙니다만, __iter__() 메쏘드를 통해 이터레이터(이 경우에는 Range 인스턴스)를 제공하고 있기 때문에 for 루프에서 사용될 수 있습니다. 사실은 이 용법 때문에 이터레이터에도 큰 의미 없는 __iter__() 메쏘드를 만들어주도록 요구하고 있습니다.

이렇게 for 루프에 이터레이터를 직접 요구하지 않고, 이터레이터를 만드는 객체를 요구함으로써, 많은 경우에 for 루프가 깔끔해지기는 하지만, 간혹 혼란을 야기할 수 있는 경우도 발생할 수 있습니다.

>>> r = Range(3)
>>> for x in r:
...     print(x)
...     if x == 1:
...         break
0
1
>>> for x in r:
...     print(x)
2

두 개의 for 루프는 동일한 Range 인스턴스를 사용하고 있습니다. 반면에 RangeFactory 를 사용하는 경우는:

>>> r = RangeFactory(3)
>>> for x in r:
...     print(x)
...     if x == 1:
...         break
0
1
>>> for x in r:
...     print(x)
0
1
2

이 경우는 for 루프를 시작할 때마다 새로운 이터레이터(Range 인스턴스)가 만들어집니다. 때문에 매번 새로운 루프가 시작되는 것이지요. for 루프가 이터레이터를 재사용하는 것으로 보일 때는, 인스턴스가 이터레이터인지 이터레이터를 제공하는 객체인지를 확인하는 것이 중요할 수 있습니다.

프로그래밍을 하다 보면 한쪽에서 생산하고(produce), 다른 쪽에서 소비(consume) 하는 상황을 자주 만납니다. 이를 생산자-소비자 패턴(Producer-Consumer Pattern)이라고 하는데, 이터레이터는 소비자 쪽의 코드를 함축적이면서도 자연스럽게 기술할 수 있는 방법을 제공합니다. 이제 필요한 것은 생산자 쪽의 코드를 자연스럽게 구성할 수 있는 방법입니다. 다음에 다룰 제너레이터(Generator) 가 파이썬의 대답입니다.


반응형
반응형


import pymongo

con = pymongo.Connection('localhost', port=27017)


tweets = con.db.tweets


import requests, json

url = 'http://search.twitter.com/search.json?q=python%20pandas'

data = json.loads(requests.get(url).text)


for tweet in data['results']:

    tweets.save(tweet)


cursor = tweets.find({'from_user' : 'wesmckinn'})


tweet_fields = ['created_at', 'from_user', 'id', 'text']

result = pd.DataFrame(list(cursor), columns=tweet_fields)

반응형
반응형

지난번에 다룬 keras를 활용해서 LSTM(Long Short-Term Memory)을 이용한 감성 분석을 해보겠습니다.

 

데이터는 이전과 마찬가지로 train/test 각각 2천개 씩 배정해서 모델을 학습하고, test set에 대한 accuracy를 구하는 과정으로 진행됩니다.

 

 

Keras에는 imdb 데이터가 이미 전처리되어 저장되어있기 때문에 그대로 불러옵니다. 이때 상위 5000개의 단어만 활용하겠습니다.

 

train/test 데이터를 알맞은 형태로 바꿔주고

 

lstm을 이용해서 모델을 만들어줍니다. 모델에 대한 설명은 다른 포스팅을 통해 다루겠습니다.

 

위와 같은 옵션으로 컴파일링을 하고 fitting을 시킨 다음 test data를 학습합니다.

 

0.76의 accuracy가 나왔습니다.

 

지금까지 여러가지 statistical methods를 통해 분석을 해보았습니다. 그 결과 2000개의 train data에 대해서

SentiWordnet : 71%

Naive Bayes : 82%

Doc2Vec : 91.8%

LSTM : 76%

의 정확도를 보였습니다.

물론 가장 기본적인 모형만 만들었기 때문에 parameter를 튜닝하거나 모델을 변형해서 더 나은 accuracy를 만들수 있을 것입니다.

반응형
반응형

저번 시간의 Naive Bayes Classifier에 이어서 Doc2Vec에 대해 알아보겠습니다.

 

Doc2Vec은 벡터 공간 모형의 일종으로 간단한 신경망 모형을 통해 만들어지는 모델입니다.

Word2Vec과 유사하지만 단어가 아닌 문장, 문단, 문서를 통해 벡터를 만들기 때문에 단어간의 유사성 뿐만 아니라 문서의 구성요소간의 유사성을 정의하고 계산할 수 있습니다.

즉, 문맥을 고려한 분석이 가능하다는 것이 가장 큰 장점입니다.

 

Doc2Vec을 활용한 감성 분석을 하기 위해 먼저 gensim 외 필요한 패키지를 불러옵니다.

 

Doc2Vec 모델 생성을 위한 gensim 패키지!

 

Text preprocessing 을 위한 nltk패키지

 

Naive Bayes와 같은 방식을 통해 전처리와 train/test data셋을 생성합니다.

 

리뷰 리스트와 레이블 리스트를 합쳐서 LabeledLineSentence 객체를 생성합니다.

그 다음 Doc2Vec모델을 다음과 같이 학습시킵니다.

size 는 feature 벡터의 차원입니다.

window는 문서의 내에서의 예측을 위한 예측된 단어와 문맥의 단어들 사이의 최대 거리입니다.

dm은 트레이닝 알고리즘으로 distributed memory가 default 값입니다.

alpha값은 초기 학습률(learning rate)이고 min_alpha는 alpha값이 학습과정에서 선형으로 줄어들어서 도달하는 최소 값입니다.

min_count 이하의 total frequency를 가진 단어들은 모두 무시됩니다.

workers는 cpu의 코어 수에 따라 multi-threads를 지원해서 병렬처리하는 옵션입니다.

그 밖의 옵션에 대한 설명은 https://radimrehurek.com/gensim/models/doc2vec.html을 참고해 주세요.

 

 

이제 생성된 모델을 통해서 앞서 NBC와 같이 train 셋에 대해 감성 분석을 시행하겠습니다.

logistic regression classifier를 이용해서 긍, 부정으로 분류하였습니다.

 

test 셋에서 출력해본 결과 약 91.65%의 정확도를 보였습니다. NBC보다 높은 정확도를 보였지만 전체데이터(25000)를 대상으로 한 경우 정확도가 79%대로 떨어지는 모습을 보였습니다.

 

특정 분석법이 절대 우위에 있는 것이 아니라 다양한 분석방법을 시행해보고 결과를 비교해 보는 것이 좋은 방법 같습니다.

다음 시간에는 감성 분석의 마지막 시간으로 인공신경망(ANN)을 활용한 감성분석을 해보도록 하겠습니다.

반응형
반응형

저번 포스팅에서는 Sentiment Analysis(SA)의 방법 중 하나인 knowledge based technique에 대해서 살펴봤습니다.

이번에는 Statistical methods을 통해서 감성 분석을 해보겠습니다.

 

  1. Naive Bayes classifier

나이브 베이즈 분류기를 다루기 전에 먼저 TF-IDF(Term Frequency – Inverse Document Frequency)와 같은 벡터 공간 모형에 대해서 알아보겠습니다.

TF_IDF는 이와 같은 형태로 문장에서 해당하는 단어의 등장 여부에 따라서 다음과 같이 나타냅니다. 이 때 단어의 등장여부를 T/F로 보는 boolean model, 단어의 등장 횟수로 보는 frequency model 둘다 가능합니다.

여기서 자주 등장하는 단어를 통해 하나하나의 dimension을 생성하는 것이 벡터 공간 모형의 기초적인 형태입니다. 그리고 이러한 feature에 기계학습 알고리즘을 통해서 train을 진행합니다.

먼저 긍정 학습 데이터에서의 모든 토큰을 words라는 리스트에 저장합니다.  이때  Stopwords도 함께 제거해줍니다

 

같은 방식으로 부정 학습 데이터에서 Stopwords 를 제거하고 words리스트에 붙여 넣습니다.

 

words리스트를 이용해서 3000 dimension의 word feature를 생성합니다.

 

word feature를 통해서 분석할 데이터의 feature를 생성하는 함수도 만들어 줍니다.

이제 첫번째 리뷰 데이터의 문장들을 이용해서 feature를 생성해보겠습니다.

 

 

첫 번째 리뷰 데이터의 feature생성 결과입니다.

 

같은 방법으로 1000개의 리뷰 데이터에 대해 feature set을 생성해서 리스트에 저장합니다.

 

본격적으로 나이브 베이즈 분류기(Naive Bayes Classifier)를 통해 감성분석을 시작하겠습니다. 분류기는 nltk에 내장되어 있기 때문에 따로 설치할 것은 없습니다.

가장 먼저 2000개의 영화 리뷰를 긍/부정으로 분류해보겠습니다.

training,test set을 위한 feature set을 만들어준다음

 

순서대로 앞의 2000 set은 training 나머지는 test set으로 분류합니다

 

그 다음 nltk의 나이브 베이즈 분류기를 실행해주면 됩니다. 전처리에 비해 정말 간단합니다…

 

분류기를 training한 뒤에는 test set에서 얼마나 잘 작동하는지 살펴봅니다.

 

82%의 분류율이 나옵니다. 샘플 데이터라서 그런지 생각보다 높은 분류율을 보여주는 것 같네요. 같은 방법으로 전체 데이터(train 25000, test 25000)으로 한 결과 82.756의 정확도로 2000개의 train 데이터의 경우와 비교해 크게 향상되진 않았습니다.

 

다음 포스팅에서는 나머지 감성 분석 방법들에 대해 알아보겠습니다.

반응형
반응형

책, 영화등과 같은 매체를 보고나면 그에 대한 피드백이 발생합니다. 어떤 사람들은 주위 지인들에게 평가를 공유하기도 하고 어떤 사람들은 어떤 인상을 받았는지 인터넷 상에 글을 남깁니다. 이번 포스팅에서는 이러한 리뷰들의 단어에 긍/부정을 평가하여 그 리뷰, 더나아가 그 영화에 대한 감정이 긍정적인지 부정적인지 살펴보려고 합니다.

Sentiment Analysis(SA) 에는 크게 세 가지 방식이 있습니다.

  • knowledge-based techniques
  • statistical methods
  • hybrid approaches

먼저 knowledge-based technique은 단어들의 감정의 정도를 평가하는 사전을 만들고 이를 활용해서 단어 또는 글의 감정 상태를 평가하는 방법입니다.

예를 들어 SentiWordnet은 단어 마다의 긍/부정 척도를 더한 대표적인 db로 Python의 nltk에 포함되어 있습니다.

 

다음으로 statistical method는 SVM(support vector machine)이나 ‘bag of words’등 다양한 ML 방법론을 활용해서 단어의 감정을 분류하는 방법입니다. Python의 nltk 패키지에 naive bayes classifier도 통계적 방법 중 하나입니다.

 

마지막으로 hybrid approaches의 경우 두 방법을 적절하게 혼합하여 활용하는 방법입니다.

 

SA에서 가장 힘든 부분 중 하나는 비정형인 자연어의 노이즈들이 많다는 점입니다. 인간의 경우 다양한 문맥에 따라서 흐름을 쉽게 파악할 수 있지만 기계는 어렵습니다.

예를 들어 ‘내가 치킨을 싫어하는 것은 아니야.’ 라는 문장을 보았을 때 사람들은 이 문장이 postive 또는  neutral 하다고 쉽게 판단하지만 긍/부정 단어에 점수를 매기는 knowledge-based technique에서는 negative 문장으로 해석될 수 있습니다. 이처럼 이중 부정문을 다루거나 비속어, 은유, 비유 또는 풍자 등과 같은 skill을 학습하기 힘들기 때문에 현재까지는 한계점이 뚜렸하다고 생각합니다.

 

네이버 평점 9점대에 빛나는 바로 그 영화! – sarcastic review의 대표적인 사례

 

이제 본격적으로 샘플 데이터를 활용해서 SA를 해보겠습니다.

http://ai.stanford.edu/~amaas/data/sentiment/

에서 데이터셋을 받습니다. 이 데이터는 IMDb라는 미국 영화 사이트의 리뷰를 50,000개 수집하여 리뷰마다 각각 긍/부정 분류로 나눈 데이터 셋입니다.

os 패키지는 operating system의 약자로 운영체제에서 쓰는 여러 기능들을 python에서도 쓸수 있게 해주는 패키지입니다.

먼저 긍정 리뷰중 하나를 review에 담았습니다.

 

제대로 리뷰가 담긴것을 확인하시면 for문을 이용해서 전체 긍정 리뷰를 pos_train_list에 저장합니다.

 

train positive 리뷰를 모두 저장했다면 이제 knowledge-based techniques 중 하나인 SentiWordNet에 대해 알아보겠습니다.

[SentiSynset(‘hate.n.01’), SentiSynset(‘hate.v.01’)]

 

sentiwordnet을 쓰기 위해 nltk 패키지를 import하고 ‘hate’라는 단어를 불러왔습니다. filter object 타입을 list로 바꾸면 n, v로 각각 명사 동사의 유의어가 나온것을 볼 수 있습니다.

0.0

0.75

hate라는 동사의 긍/부정 스코어를 확인하면 각각 0, 0.75점 입니다.

 

단어마다 여러 유의어가 존재하는 경우가 있습니다. 그래서 편의를 위해 특정 단어의 모든 유의어의 긍/부정 스코어를 각각 평균한 것으로 쓰기 위해 함수를 정의합니다.

(0.625, 0.03125)

love의 동사 유의어 점수 평균은 0.625이다.

 

2.5/4=0.625이므로 잘 동작하는 것을 알 수 있습니다.

 

원리를 그대로 사용해서 for문을 이용하면 문장에서의 긍/부정 지수도 쉽게 구할 수 있습니다.

 

이제 본격적으로 아까 다운받은 영화 리뷰 자료에서 postive train 리뷰 10가지와 negative train 리뷰 10가지를 불러와 SA를 해보겠습니다.

긍정적 리뷰와 부정적 리뷰를 순서대로 10개씩 불러왔기 때문에 actual에는 1(긍정)을 10번 반복하고 0(부정)을 10번 반복했습니다. 그 다음 과정은 앞과 마찬가지로 리뷰를 10개씩 불러와서 tokenize를 하고 pos_tag를 붙여서 리스트로 만들어주는 작업입니다. 합산된 긍/부정 스코어를 if문을 통해 대소비교를 하는데, 긍정 수치가 높으면 1을 반환하고 그렇지 않다면 0을 predicted에 반환합니다.

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1]
Number of correct instance: 12
Number of incorrect instance: 8

코드를 실행하면 다음과 같은 결과가 출력됩니다. 60%의 확률로 긍/부정 리뷰를 맞추는 성능을 보이고 있습니다.

 

다음 포스팅에서는 statistical methods중 하나인 Naive Bayes를 통한 감성 분석에 대해 알아보겠습니다.

반응형
반응형

첫번째 글에 이어서 이번에는 단어들간의 관계를 살펴보도록 하겠습니다.

similarity를 구해 리뷰의 단어 중 문맥상 서로 유사한 단어를 살펴보겠습니다.

film movies superhero seen action movie actors character heath performance modern villain second goodbetter aniconic amore batpod iconic watched ------------------------------------------------------------jack time sequel dramas

 

리뷰 텍스트 문맥상 ‘Batman’,’Joker’와 각각 유사하다고 판단된 단어들입니다.

 

텍스트의 연어(Collocation)을 출력해보면 아래와 같습니다.

dark knight; heath ledger; christian bale; harvey dent; comic book; christopher nolan; brucewayne; aaron eckhart; gary oldman; two face; maggie gyllenhaal; morgan freeman; rachel dawes;special effects; batman begins; gotham city; 've seen; district attorney; michael caine; superhero

빈번하게 같이 사용되는 단어들이 출력된 것을 확인할 수 있습니다.

 

마지막으로 연관 단어 그래프를 만들어 보도록 하겠습니다.

리뷰의 텍스트를 바탕으로 같은 문장에서 등장하는 명사들끼리 연결된 그래프를 만드려고 합니다. 먼저 unique 명사들을 추출해서 uniqueNouns 리스트에 추가하는 과정입니다.

위와 같이 행렬을 생성한 다음

이중 for문을 이용해 각 원소에 명사가 들어온 경우 1의 값을 반환하게 합니다.

그리고 이 행렬의 전치와 곱하여 co-occurrence matrix(동시 발생 행렬)를 생성합니다

행렬에서 값이 30이상은 결과 값을 이용해 노드를 만들었습니다.

그래프를 출력하면 위와 같이 명사들 간의 연관 관계를 표현한 그래프가 그려집니다.


반응형
반응형

http://www.imdb.com/title/tt0110912/?ref_=nv_sr_1

영어로 된 영화 평점 사이트에서 리뷰를 크롤링하고 간단한 텍스트 마이닝을 해보겠습니다.

 

먼저 토큰화(tokenize), 기본형 형태로 만들기(lemmatize)와 stopwords를 제거하는 작업을 했습니다.

Dark knight라는 영화의 리뷰 100개를 크롤링하여 만든 텍스트 파일을 불러와서 위의 순서로 코딩했습니다. 참고로 stopwords의 경우 소문자의 형태만 인식하기 때문에 .lower()를 통해 토큰을 전부 소문자로 변환했습니다.

 

결과를 다시 텍스트 파일로 저장하고…

이제 이 리뷰에서 가장 많이 등장하는 명사를 추출해보겠습니다.

혹시 중간에 nltk패키지의 오류로 실행이 안되면 nltk.download()를 입력해서 필요한 것을 받을 수 있습니다.

[('movie', 340), ('batman', 249), ('film', 247), ('joker', 177), ('dark', 113), ('ledger', 100),('knight', 95), ('heath', 88), ('time', 83), ('action', 67)]

결과를 출력하면 다음과 같이 리뷰에서 가장 많이 등장한 10개의 단어가 빈도 순으로 출력된 것을 볼 수 있습니다.

동사와 형용사도 동일한 방법으로 추출하시면 됩니다.

 

전체 리뷰의 토큰 개수를 구할 수도 있습니다.

20575 6069

그래프를 이용해서 토큰의 등장 횟수를 시각화 할 수도 있습니다

 

‘이나 ‘s와 같은 보통의 상황에서는 불필요한 단어들이 많이 등장하는데 stopwords에서 추가적으로 옵션을 두어 필터링이 되도록 하는 방법도 있습니다.


반응형
반응형

시각화에서 데이터가 어떤 특징을 지니고 있는지 확인했으니 이번에는 영화 리뷰 간 유사성을 계산해보겠습니다. 이와 같은 분석 말고도 영화 평에 대한 감성 분석과 같은 여러 분석 방법들이 있습니다.

먼저 분석하려는 영화의 리뷰들을 String 형식의 데이터로 받아보겠습니다.

review_list=[]
for n in range(30):
url = ‘http://movie.daum.net/moviedb/grade?movieId=97728&type=netizen&page={}’.format(n+1)
webpage = urlopen(url)
source = BeautifulSoup(webpage,’html.parser’,from_encoding=’utf-8′)
reviews = source.findAll(‘p’,{‘class’: ‘desc_review’})
for review in reviews:
review_list.append(review.get_text().strip().replace(‘\n’,”).replace(‘\t’,”).replace(‘\r’,”))

file = open(‘okja.txt’,’w’,encoding=’utf-8′)

for review in review_list:
file.write(review+’\n’)

file.close()

doc1 = ”
file = open(‘okja.txt‘,’r’,encoding=’utf-8′)
lines = file.readlines()
for line in lines:
doc1 += line
file.close()

영화 ‘옥자’의 리뷰를 받아와서 doc1에 저장하였습니다. 이처럼 나머지 영화들도 doc2, doc3 과 같은 형식으로 저장했습니다.

 

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
corpus = [doc1, doc2, doc3]
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(corpus)
X=X.todense()

sklearn 모듈을 이용해서 TF-IDF로 벡터화를 하고 코사인 유사도를 사용합니다. 세 리뷰의 명사들 간의 코사인 유사도를 활용해서 유사성이 얼마나 있는지 알아보겠습니다.

print(“similarity between ‘okja’ and ‘monster’:”,cosine_similarity(X[0],X[1]))
print(“similarity between ‘okja’ and ‘real’:”,cosine_similarity(X[0],X[2]))
print(“similarity between ‘monster’ and ‘real’:”,cosine_similarity(X[1],X[2]))

 

 

>similarity between ‘okja’ and ‘monster’: [[ 0.26134023]]

>similarity between ‘okja’ and ‘real’: [[ 0.3445885]]

>similarity between ‘monster’ and ‘real’: [[ 0.27664574]]

구한 코사인 유사도를 프린트하면 다음과 같은 결과가 나오게 됩니다.

 

반응형
반응형

이번에는 저번 포스팅에서 처리한 데이터를 이용해서 시각화를 해보겠습니다.

먼저 예전에 설치한 pytagcloud 모듈을 불러옵니다. 이 때 pytagcloud는 기본적으로 한국어 폰트를 지원하지 않기 때문에 사용자가 직접 한국어 폰트를 추가해야 합니다.

https://www.google.com/get/noto/

위 사이트에 접속하셔서 Noto Sans CJK KR을 검색하셔서 파일을 아래의 경로로 받아주시면 됩니다.

C:\Users\[사용자 id]\Anaconda3\Lib\site-packages\pytagcloud\fonts

받은 다음 압축을 풀고 fonts 폴더에 otf파일을 붙여넣은 뒤, fonts.json 파일을 텍스트 에디터를 이용해서 열어줍니다.

(저는 Notepad++을 사용합니다.)

{
“name”: “Noto Sans CJK”,
“ttf”: “NotoSansCJKkr-Medium.otf”,
“web”: “http://fonts.googleapis.com/css?family=Nobile”
}

그리고 위의 문장을 추가하시면 됩니다. 혹시라도 중괄호 사이에 쉼표가 빠진다면 정상적으로 동작하지 않기 때문에 주의하셔야 합니다.

import pytagcloud, random, webbrowser
from konlpy.tag import Twitter
from collections import Counter

다시 notebook으로 돌아와서 위와 같이 입력합니다.

 

def get_tags(text,ntags=100,multiplier=1):
t = Twitter()
nouns = []
for sentence in text:
for noun in t.nouns(sentence):

nouns.append(noun)
count = Counter(nouns)
return [{‘color’:color(),’tag’:n,’size’:2*c*multiplier} for n,c in count.most_common(ntags)]

get_tags라는 함수를 만들어서 워드클라우드에 포함될 명사를 추출했습니다. return에서 텍스트의 color와 size를 조절할 수 있습니다.

def draw_cloud(tags, filename, fontname = ‘Noto Sans CJK’,size = (1100,660)):
pytagcloud.create_tag_image(tags,filename,fontname=fontname,size=size)
webbrowser.open(filename)

다음으로 워드클라우드가 출력되는 space를 만들겠습니다. font와 size를 각각 조절할 수 있습니다.

r = lambda: random.randint(0,255)
color = lambda: (r(),r(),r())

입력되는 단어를 구분하기 위해 색을 추출하는 과정입니다. rgb값(0~255)에 랜덤 값을 각각 부여해서 새로운 색상이 나타나도록 했습니다.

okjak = []
file=open(‘okja.txt’,’r’,encoding=’utf-8′)
lines = file .readlines()
for line in lines:
okjak.append(line)
file.close()

tags = get_tags(okjak)
draw_cloud(tags,’wc.png’)

이제 okja.txt를 python으로 불러와서 앞서 작성한 2개의 function을 이용해서 워드클라우드를 작성합니다.


위와 같이 워드클라우드가 생성된 것을 확인 할 수 있습니다.

반응형
반응형

okja = []
file = open(‘okja.txt’,’r’,encoding=’utf-8′)
lines=file.readlines()

for line in lines:
okja.append(line)
file.close()

먼저 저번 포스팅에서 수집한 데이터를 불러와서 옥자라는 객체에 저장합니다.

from konlpy.tag import Twitter

twitter = Twitter()

sentences_tag=[]
for sentence in okja:
sentences_tag.append(twitter.pos(sentence))

그다음 저번에 설치한 KoNLPy 모듈을 불러와서 sentences_tag라는 리스트 형태로 저장합니다.

print(sentences_tag)
print(‘-‘*30)
print(len(sentences_tag))

를 입력하시면,

 

이와 같은 형태로 추출된 token과 token의 형태소 분석 결과를 출력합니다. 300개의 token이 출력된 것을 볼 수 있습니다.

 

이제 자주 사용되는 단어를 추출하겠습니다.

noun_adj_list = []

for sentence in sentences_tag:
for word, tag in sentence:
if tag in [‘Noun’,’Adjective’]:
noun_adj_list.append(word)

먼저 분석에 중요한 품사로 판단되는 명사와 형용사 token만 추출하여 noun_adj_list에 담았습니다.

from collections import Counter
counts = Counter(noun_adj_list)
print(counts.most_common(10))

collections모듈을 이용해서 most frequent word 10가지를 출력한 결과입니다.

 

 

 

반응형
반응형

텍스트를 분석 역시 일반적인 데이터 분석처럼 데이터 수집 -> 데이터 전처리 -> EDA -> 데이터 분석 -> knowledge 도출의 단계를 거칩니다. 하지만 unstructured data인 관계로 데이터의 수집과 전처리 과정에서 큰 차이가 나타납니다.

이번 포스팅에서는 movie.daum.net의 영화 리뷰 댓글을 이용해서 데이터를 수집하려고 합니다.

Jupyter notebook에서 새로운 스크립트를 만들고 제일 윗 부분에

#-*- coding: utf-8 -*-

를 입력합니다 기본적으로 윈도우 환경의 Python은 ANSI 형식을 따르기 때문에 한글이 깨지는 경우가 발생합니다. 그래서 기본 인코딩 형식을 utf-8로 바꾸셔야 텍스트를 불러왔을때 깨지지 않게 됩니다.

 

from urllib.request import urlopen

from bs4 import BeautifulSoup

전에 설치한 크롤링 모듈을 불러옵니다.

 

먼저 한 페이지의 리뷰들을 불러오는 과정을 살펴보겠습니다.

url=’http://movie.daum.net/moviedb/grade?movieId=97728&type=netizen&page=2′

webpage=urlopen(url)

먼저 urlopen을 이용해 url주소의 리뷰 데이터를 객체에 저장합니다.

source = BeautifulSoup(webpage,’html.parser’,from_encoding=’utf-8′)

HTML형식으로 된 데이터를 parsing하여 텍스트 형태로 변환합니다.

reviews = source.findAll(‘p’,{‘class’: ‘desc_review’})

for review in reviews:

print(review.get_text().strip())

HTML 소스 코드를 보면 <p class=”desc_review”>내용</p> 형태로 데이터가 저장되어 있습니다. 그래서 reviews라는 객체에 이와 같은 양식을 모두 찾아 list형식으로 변환했습니다. 또한 get_text()를 이용해 텍스트 데이터만 추출하고 strip()을 통해 공백을 제거했습니다.

2페이지의 리뷰들을 모두 추출했는데 for문을 통해 url을 리스트 형식으로 만들어 여러 페이지의 데이터를 추출할 수 있습니다.

review_list=[]
for n in range(10):
url = ‘http://movie.daum.net/moviedb/grade?movieId=97728&type=netizen&page={}’.format(n+1)
webpage = urlopen(url)
source = BeautifulSoup(webpage,’html.parser’,from_encoding=’utf-8′)
reviews = source.findAll(‘p’,{‘class’: ‘desc_review’})
for review in reviews:
review_list.append(review.get_text().strip().replace(‘\n’,”).replace(‘\t’,”).replace(‘\r’,”))

그 다음 추출한 텍스트 데이터를 txt파일로 저장합니다.

file = open(‘okja.txt’,’w’,encoding=’utf-8′)

for review in review_list:
file.write(review+’\n’)
file.close()

 

반응형
반응형

이번 포스팅에서는 Python을 활용하여 웹페이지에서 텍스트를 추출하고 이를 워드클라우드로 나타내고 텍스트들간의 유사도를 활용해 관련성을 구해보겠습니다.

 

파이썬 텍스트 마이닝은 간단한 편이지만 몇 가지 모듈을 설치해야 합니다.

웹 크롤링을 위해 Beautifulsoup4, 한국어 텍스트 분석을 위한 KoNLPy,워드 클라우드를 위한 pygame,simplejson,pytagcloud 가 필요합니다.

 

>pip install beautifulsoup4

cmd라인에서 다음과 같이 입력하여 먼저 beautifulsoup4 모듈을 설치하시고

 

http://www.lfd.uci.edu/~gohlke/pythonlibs/#jpype 로 가셔서 자신의 세팅에 맞는 JPype1-0.x.x-cp36-…whl 파일을 받으시면 됩니다. 글 작성 기준 버전은 0.6.2인데 최신버전으로 받으시면 될겁니다. 참고로 KoNLPy를 설치하시기 전에 Java가 설치되어있는지 확인하시고 없다면 설치해주시면 됩니다.

>pip install JPype1 …… .whl

cmd라인에서 파일을 받은 경로로 이동하신 뒤(cd 폴더명), 받은 파일의 이름을 install 뒤에 입력하시면 모듈이 설치됩니다.

 

>pip install pygame

>pip install simplejson

>pip install pytagcloud

cmd라인에 3개의 모듈을 설치하시면 됩니다.


pip install konlpy

다음 포스팅에서는 설치된 모듈을 통해 웹페이지에서 크롤링한 텍스트를 시각화 해보겠습니다.

반응형
반응형

명사 분리 추출 후, 단어 사용 빈도 계산하기


이번에는 동아일보, 한겨레신문에서 '사드'관련 기사를 크롤링해 저장한 텍스트파일을 이용해 단어 사용 빈도를 계산하는 프로그램을 만들어보겠습니다.




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
36
37
38
39
40
41
42
""" 형태소 분석기
    명사 추출 및 빈도수 체크
    python [모듈 이름] [텍스트 파일명.txt] [결과파일명.txt]
"""
 
import sys
from konlpy.tag import Twitter
from collections import Counter
 
 
def get_tags(text, ntags=50):
    spliter = Twitter()
    nouns = spliter.nouns(text)
    count = Counter(nouns)
    return_list = []
    for n, c in count.most_common(ntags):
        temp = {'tag': n, 'count': c}
        return_list.append(temp)
    return return_list
 
 
def main(argv):
    if len(argv) != 4:
        print('python [모듈 이름] [텍스트 파일명.txt] [단어 개수] [결과파일명.txt]')
        return
    text_file_name = argv[1]
    noun_count = int(argv[2])
    output_file_name = argv[3]
    open_text_file = open(text_file_name, 'r')
    text = open_text_file.read()
    tags = get_tags(text, noun_count)
    open_text_file.close()
    open_output_file = open(output_file_name, 'w')
    for tag in tags:
        noun = tag['tag']
        count = tag['count']
        open_output_file.write('{} {}\n'.format(noun, count))
    open_output_file.close()
 
 
if __name__ == '__main__':
    main(sys.argv)
cs

위 코드는 사용자로부터 텍스트를 입력받아 명사를 분리/추출해 단어사용 빈도를 계산하는 모듈입니다.

코드 한줄한줄 자세히 살펴보겠습니다.




6
7
8
import sys
from konlpy.tag import Twitter
from collections import Counter
cs

이전 글의 크롤러 모듈에서와 마찬가지로 해당 모듈을 실행할 때, 터미널에서 사용자로부터 인자를 받기 위해 'sys.argv'를 사용하려고 'sys'를 임포트했습니다. 그리고 명사를 분리/추출하기 위해 한국어 형태소 분석기인 'konlpy'를 임포트했습니다.  특히, 저는 여기서 'konlpy'의 여러 품사 태깅 클래스 중 'Twitter'를 사용했습니다.


(konlpy.tag 안에는 Kkma, Komoran, Hannanum, Twitter, Mecab 등의 여러 품사 태깅 클래스가 있습니다. 여기서 저는 Twitter를 사용했습니다. Hannanum 클래스의 경우, 정확한 이유는 모르겠지만 텍스트의 길이가 길어져 사용된 단어가 많아지면 올바르게 실행이 안되는 문제가 있는 것 같습니다. 참고하시기 바랍니다. konlpy의 설치법과 더 많은 정보를 원하시는 분들은 'konlpy 공식 홈페이지' 를 참조하시기 바랍니다. )


collections의 Counter객체는 counting hashable container 객체로서 간단히 말해, 빈도수 계산을 위한 사전형태의 데이터타입입니다. 'konlpy'를 통해 품사 태깅을 한 후, 명사만 따로 모아 빈도수를 계산하기 위해 임포트했습니다.




11
12
13
14
15
16
17
18
19
def get_tags(text, ntags=50):
    spliter = Twitter()
    nouns = spliter.nouns(text)
    count = Counter(nouns)
    return_list = []
    for n, c in count.most_common(ntags):
        temp = {'tag': n, 'count': c}
        return_list.append(temp)
    return return_list
cs

위 코드는 텍스트에서 명사를 분리/추출한 후, 빈도 계산을 하는 함수입니다.

해당 함수의 인자 중, 'text'는 분석에 사용할 텍스트이며, 'ntags'는 분리한 명사 중 결과를 출력할 명사의 갯수입니다. 'ntags'는 키워드 인자로 디폴트값으로 50을 설정했습니다. 즉, 사용자로부터 아무 입력이 없으면 해당 함수는 빈도수 기준으로 상위 50개의 명사의 결과만 출력합니다. 


함수가 실행되면 우선 'Twitter'객체를 생성합니다. 이후, 'Twitter'객체의 'nouns'메소드를 이용해 'text'에서 명사만 분리/추출합니다. 'Twitter'객체의 'nouns'메소드는 결과 값으로 순환 가능한 객체를 반환합니다. 다시 말해, 참조변수 'nouns'에는 'Twitter'객체의 'nouns'메소드에 의해 분리/추출된 명사들이 순환가능한 객체로 저장되어 있습니다.

('Twitter'객체를 통해 'nouns'품사말고도 'Josa', 'Verb', 'Eomi' 등의 다른 품사도 추출할 수 있습니다. 공식 홈페이지를 참조세요.)


이후, 이 객체를 이용해 'Counter'객체를 생성하고, 이를 참조변수 'count'에 할당합니다. 위에서 설명했듯이 'Counter'객체는 빈도 계산을 위한 사전 객체로 명사의 사용 빈도를 계산하기 위해 생성했습니다. 그 후, 명사별 빈도수를 저장하기 위한 리스트인 'return_list'를 선언합니다.


그 다음 for문에서는 다음과 같은 작업이 수행됩니다. 'Counter'객체의 'most_common'메소드는 정수를 입력받아 객체 안의 명사 중, 빈도수가 큰 명사부터 순서대로 입력받은 정수갯수만큼 저장되어 있는 객체를 반환합니다. 따라서 for문을 통해 해당 객체에 접근을 한 후, 참조변수 'n'에는 명사를, 참조변수 'c'에는 해당 명사의 빈도수를 할당받아 'tag'키값에는 명사(n)를, 'count'키값에는 해당 명사의 빈도를 갖는 딕셔너리를 만들어 저장하고 이를 결과 리스트인 'return_list'에 순서대로 삽입합니다. 해당 함수의 'ntags'는 디폴트값으로 50이 설정되어 있으므로 별도의 설정이 없다면 해당 함수는 빈도수 기준 상위 50개의 명사만 결과값으로 반환할 것입니다.


for문이 종료된 후에는 'return_list'를 반환합니다. 'return_list'의 예시 형태는 다음과 같습니다.


[{'tags': '명사1', 'count': N1}, {'tags': '명사2', 'count': N2}, ... ,{'tags': '명사50', 'count': N50}]

(N1 >= N2 >= N3 >= .... >= N50)


만약 반환된 결과가 'ntags'수만큼 나오지 않았다면, 사용된 단어가 그 수보다 적은 것 입니다.


 


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def main(argv):
    if len(argv) != 4:
        print('python [모듈 이름] [텍스트 파일명.txt] [단어 개수] [결과파일명.txt]')
        return
    text_file_name = argv[1]
    noun_count = int(argv[2])
    output_file_name = argv[3]
    open_text_file = open(text_file_name, 'r')
    text = open_text_file.read()
    tags = get_tags(text, noun_count)
    open_text_file.close()
    open_output_file = open(output_file_name, 'w')
    for tag in tags:
        noun = tag['tag']
        count = tag['count']
        open_output_file.write('{} {}\n'.format(noun, count))
    open_output_file.close()
 
 
if __name__ == '__main__':
    main(sys.argv)
cs

위 코드는 메인 함수입니다. 사용자로부터 읽어 올 파일명, 뽑아올 명사 갯수, 결과를 저장할 파일명을 입력받아 각각 'text_file_name', 'noun_count', 'output_file_name'에 할당합니다. 'noun_count'의 경우 정수형으로 캐스팅했습니다.


이후, 분석할 텍스트를 읽어와 'text'에 할당하고, 위에서 설명한 'get_tags'함수를 통해 명사 분석 결과를 참조변수 'tags'에 할당합니다. 그 다음 결과파일명을 통해 결과파일을 생성하고 for문을 통해 'tags'에 저장된 분리된 명사와 해당 명사의 사용 빈도수를 결과 파일에 콤마(,)로 구분해 write합니다.


결과 파일 형식은 다음과 같습니다.


명사1, N1

명사2, N2

명사3, N3

        .

        .

        .


(N1 >= N2 >= N3 >= ...)




(좌 동아일보, 우 한겨레신문)


위 모듈을 통해 동아일보와 한겨레신문에서 사드관련 기사를 크롤링해와 사용된 명사의 빈도수를 추출한 결과입니다. 두 신문사의 기사 모두 총 50개의 명사를 추출했습니다. 보시다시피 두 신문사 모두 '사드'와 '배치'를 제일 많이 사용한 것을 알 수 있습니다. 그 이후에 사용된 단어와 빈도수 순위는 신문사별로 조금씩 달랐습니다. 


중간중간 '수', '등', '그', '며' 등의 무의미한 명사와 명사형태가 아닌 단어도 추출된 것이 보이는데, 이는 'Twitter' 품사 태깅 클래스에서 걸러주지 못한 것이므로 수작업으로 제거한 후, 시각화 또는 분석에 사용하시면 됩니다.




다음 마지막 글에서는 계산한 명사의 빈도수를 바탕으로 워드 클라우드로 시각화하는 프로그램을 만들어보도록 하겠습니다.




연관된 글

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 2 (네이버 뉴스 크롤링하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 3 (동아일보, 한겨레 '사드'관련 기사 크롤링하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 4 (단어 사용 빈도 체크하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 5 (워드클라우드 만들기) 미작성



출처: http://yoonpunk.tistory.com/7 [윤빵꾸의 공부노트]

반응형
반응형

동아일보, 한겨레 신문에서 '사드'관련 신문기사 크롤링하기


이번 글에서는 지난 글에서 연습한 네이버 신문기사 크롤링 모듈을 응용 확장해, 동아일보와 한겨레 신문에서 특정 키워드와 관련된 신문기사의 본문 내용을 대량으로 긁어오는 크롤러를 만들어보도록 하겠습니다. 


 
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
""" 동아일보 특정 키워드를 포함하는, 특정 날짜 이전 기사 내용 크롤러(정확도순 검색)
    python [모듈 이름] [키워드] [가져올 페이지 숫자] [결과 파일명]
    한 페이지에 기사 15개
"""
 
import sys
from bs4 import BeautifulSoup
import urllib.request
from urllib.parse import quote
 
TARGET_URL_BEFORE_PAGE_NUM = "http://news.donga.com/search?p="
TARGET_URL_BEFORE_KEWORD = '&query='
TARGET_URL_REST = '&check_news=1&more=1&sorting=3&search_date=1&v1=&v2=&range=3'
 
 
# 기사 검색 페이지에서 기사 제목에 링크된 기사 본문 주소 받아오기
def get_link_from_news_title(page_num, URL, output_file):
    for i in range(page_num):
        current_page_num = 1 + i*15
        position = URL.index('=')
        URL_with_page_num = URL[: position+1+ str(current_page_num) \
                            + URL[position+1 :]
        source_code_from_URL = urllib.request.urlopen(URL_with_page_num)
        soup = BeautifulSoup(source_code_from_URL, 'lxml',
                             from_encoding='utf-8')
        for title in soup.find_all('p''tit'):
            title_link = title.select('a')
            article_URL = title_link[0]['href']
            get_text(article_URL, output_file)
 
 
# 기사 본문 내용 긁어오기 (위 함수 내부에서 기사 본문 주소 받아 사용되는 함수)
def get_text(URL, output_file):
    source_code_from_url = urllib.request.urlopen(URL)
    soup = BeautifulSoup(source_code_from_url, 'lxml', from_encoding='utf-8')
    content_of_article = soup.select('div.article_txt')
    for item in content_of_article:
        string_item = str(item.find_all(text=True))
        output_file.write(string_item)
 
 
# 메인함수
def main(argv):
    if len(argv) != 4:
        print("python [모듈이름] [키워드] [가져올 페이지 숫자] [결과 파일명]")
        return
    keyword = argv[1]
    page_num = int(argv[2])
    output_file_name = argv[3]
    target_URL = TARGET_URL_BEFORE_PAGE_NUM + TARGET_URL_BEFORE_KEWORD \
                 + quote(keyword) + TARGET_URL_REST
    output_file = open(output_file_name, 'w')
    get_link_from_news_title(page_num, target_URL, output_file)
    output_file.close()
 
 
if __name__ == '__main__':
    main(sys.argv)
 
 
cs

위 코드는 동아일보에서 특정 키워드를 가지는 신문기사를 대량으로 크롤링하기 위한 크롤러 모듈입니다.  

전체적인 모듈의 동작 과정은 다음과 같습니다.


첫째, 특정 키워드를 통해 신문 기사를 검색해 관련된 기사 목록을 얻어 옴

둘째, 신문사의 기사 목록 페이지의 URL주소 패턴을 분석해, 반복문으로 여러 목록 페이지를 돌며 올라와 있는 기사 제목에 연결된 모든 링크 주소(기사 본문 URL 주소)를 얻어 옴

셋째, 얻어낸 기사의 링크 주소 하나하나에 접근해, 기사 본문 내용이 담긴 HTML 요소를 찾아 해당 요소만 긁어와 본문 내용을 하나의 파일에 저장


그럼 아래에서 한줄한줄 코드를 살펴보며 자세히 설명하도록 하겠습니다.




1
2
3
4
import sys
from bs4 import BeautifulSoup
import urllib.request
from urllib.parse import quote
cs

해당 모듈에서 크롤링을 위해 사용할 라이브러리를 임포트한 부분입니다.

해당 모듈을 실행할 때, 터미널에서 사용자로부터 인자를 받기 위해 'sys.argv'를 사용하려고 'sys'를 임포트했고, 타겟 URL에 요청을 보내고 응답을 받기 위해 'urllib'을, 받은 응답(HTML 코드)를 파싱하기 위해 'BeautifulSoup'을 임포트했습니다.

이전 예제와는 다르게 'urllib.parse'에서 'quote'함수를 임포트했는데, 'quote'는 'urlopen'에서 인자로 사용되는 URL주소(이하 타겟 주소)에 한글(UTF_8)이 포함되었을 때, 이를 아스키(ASCII)형식으로 바꿔주기 위한 함수 입니다.

이 내용은 해당 함수를 사용할 때, 다시 한번 자세히 설명하겠습니다.




1
2
3
TARGET_URL_BEFORE_PAGE_NUM = "http://news.donga.com/search?p="
TARGET_URL_BEFORE_KEWORD = '&query='
TARGET_URL_REST = '&check_news=1&more=1&sorting=3&search_date=1&v1=&v2=&range=3'
cs

이 부분은 'urlopen'으로 요청을 보낼 타겟 주소를 세 부분으로 나누어 상수에 할당한 부분입니다.

타겟 주소를 왜 세 부분으로 나누어놓았는지 알기 위해선 'urlopen'으로 처음 접근할 동아일보의 기사 검색 페이지(이하 타겟 페이지)를 찾아 해당 페이지의 URL패턴을 찾아내야 합니다. 




위는 동아일보 메인 페이지입니다. 

URL 패턴을 찾기 위해 검색 키워드로 '사드'를 사용해 우측 상단 검색 공간에서 검색해보겠습니다.




사드로 검색한 결과 페이지입니다. 우리는 정확히 사드와 관련된 신문 기사만 필요하기 때문에 본 화면에서 주황색으로 칠해진 카테고리 탭을 뉴스로 한정 짓고, 카테고리 탭 바로 밑의 검색필터에서 정렬은 정확도순으로 범위는 동아일보로 기간은 전체로 다시 한번 검색을 설정하도록 하겠습니다.




위의 설정을 마친 화면입니다. 

웹동아일보에서 총 823건이 검색되었으나 첫 화면에 보이는 기사의 갯수는 총 5개 입니다. 따라서 본 페이지는 아직 크롤링의 타겟 페이지로는 부족합니다.

더 많은 기사를 보기 위해 아래의 주황색으로 링크된 더보기를 클릭하도록 하겠습니다.




위 화면은 더보기를 누른 화면입니다. 

이전과는 다르게 많은 기사들이 한 페이지당 15개씩 게시되어 나타나는 것을 알 수 있습니다.

(한 사진에 해당 웹페이지 화면은 다 담을 수 없어 하단 부분만 사진으로 담았습니다.)

우리는 이 화면 뿐 아니라 원하는 갯수 만큼의 기사 목록 페이지(2페이지, 3페이지 ...)에 있는 각각의 기사에 접근해 본문 내용을 수집해야 합니다. 

사 내용을 수집하기 위해선 실제 기사 페이지로 접근해야 하기 때문에 파란색으로 링크처리가 된 기사의 제목에서 연결된 기사 본문 URL주소를 뽑아와야 합니다. 

그럼 먼저 이 페이지의 URL주소를 한번 살펴보겠습니다.


http://news.donga.com/search?check_news=1&more=1&sorting=3&range=3&query=사드

사진 속 URL주소창을 보게 되면 위 화면의 URL주소는 위와 같습니다.

'check_news=1'은 뉴스로 한정지은 것을, 'more=1'은 더보기를 누른 상태임을, 'sorting=3'과 'range=3'은 각각 정확도순 정렬이며, 전체기간검색임을, 마지막으로 'query=사드'는 우리가 검색한 키워드임을 지레짐작 할 수 있습니다.

 

하지만 아직도 이 페이지가 우리가 크롤링할 타겟 페이지가 되기엔 부족합니다.

우리는 1번 페이지의 15개의 기사만 가져올 것이 아니라 2번 페이지, 3번 페이지 등등 원하는 페이지 수 만큼 기사를 크롤링해야하기 때문입니다. 

그러나 이상하게도 위 URL주소에는 페이지에 해당 하는 내용이 있어 보이진 않습니다.


사실 페이지에 관한 내용은 숨겨져 있습니다. 

하단의 페이지 숫자를 눌러 2번째 페이지로 들어가 보겠습니다.  




위 화면은 두번째 페이지로 넘어 온 화면입니다.

첫번째 화면의 URL 주소와는 달리 두번째 페이지의 URL주소가 달라진 것을 확인할 수 있습니다.


http://news.donga.com/search?p=16&query=사드&check_news=1&more=1&sorting=3&search_date=1&v1=&v2=&range=3


기존의 'query=사드'위치가 앞쪽으로 이동해 왔으며 페이지숫자로 추정되는 'p=16'이 나타났습니다.

16의 숫자가 페이지를 어떤 식으로 정의하는지 알아보기 위해 다시 여러 페이지를 눌러 URL 주소를 찾아 본 결과 아래와 같이 변했습니다.


1번 페이지: http://news.donga.com/search?p=1&query=사드&check_news=1&more=1&sorting=3&search_date=1&v1=&v2=&range=3

2번 페이지: http://news.donga.com/search?p=16&query=사드&check_news=1&more=1&sorting=3&search_date=1&v1=&v2=&range=3

3번 페이지: http://news.donga.com/search?p=31&query=사드&check_news=1&more=1&sorting=3&search_date=1&v1=&v2=&range=3

4번 페이지: http://news.donga.com/search?p=46&query=사드&check_news=1&more=1&sorting=3&search_date=1&v1=&v2=&range=3


URL 주소가 변화하는 것을 살펴보면 페이지가 증가할 때마다 'p'의 값이 15씩 더해지는 것을 알 수 있습니다.

한 페이지에 나타나는 기사의 수가 15개인 것으로 보아 한 페이지가 증가할 때마다 p의 값을 15씩 늘려 페이지를 구분한 것을 알아낼 수 있습니다. 

그럼 다시 한번 타겟 주소를 상수화한 부분을 살펴보겠습니다.




11
12
13
14
TARGET_URL_BEFORE_PAGE_NUM = "http://news.donga.com/search?p="
TARGET_URL_BEFORE_KEWORD = '&query='
TARGET_URL_REST = '&check_news=1&more=1&sorting=3&search_date=' \
                  '1&v1=&v2=&range=1'
cs

'사드'를 통해 검색된 기사 목록 전체를 둘러보기 위해선 'query=사드'로 설정되어야 하며 페이지 번호를 'p=1', 'p=16', 'p=31'... 처럼 원하는 페이지를 나타내는 수 만큼 변경시켜가며 전부 둘러봐야 하는 것을 알 수 있습니다. 

따라서 위 처럼 페이지수를 나타내는 'p='부분과 검색 키워드를 나타내는 '&query='부분의 정보를 사용자로 부터 입력받아 하나의 타겟 주소로 결합하기 위해서 타겟 주소를 세 부분으로 나눈 것입니다.

그럼 타겟 주소가 어떻게 결합되어 사용되는지 알아보기 위해 크롤러 모듈에서 메인함수를 먼저 살펴보겠습니다.




42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 메인함수
def main(argv):
    if len(argv) != 4:
        print("python [모듈이름] [키워드] [가져올 페이지 숫자] [결과 파일명]")
        return
    keyword = argv[1]
    page_num = int(argv[2])
    output_file_name = argv[3]
    target_URL = TARGET_URL_BEFORE_PAGE_NUM + TARGET_URL_BEFORE_KEWORD \
                 + quote(keyword) + TARGET_URL_REST
    output_file = open(output_file_name, 'w')
    get_link_from_news_title(page_num, target_URL, output_file)
    output_file.close()
 
 
if __name__ == '__main__':
    main(sys.argv)
cs

위 코드는 메인함수 부분입니다.

'sys.argv'를 통해 사용자로부터 검색 키워드, 페이지 숫자, 결과 파일명을 각각 'keyword', 'page_num', 'output_file_name'에  입력받습니다.

'page_num'은 정수형으로 쓰여야 하므로 int형으로 변환해서 사용합니다.


이후, target_URL 변수에 모듈 위에서 정의해놓은 URL상수들을 사용자로부터 입력받은 정보와 결합시킵니다.

이때, 'TARGET_URL_BEFORE_PAGE_NUM'뒤에 사용자로부터 입력받은 페이지 숫자를 결합하지는 않고 검색 키워드인 'keword'만 'TARGET_URL_BEFORE_KEWORD'뒤에 'quote'(urllib.parse의 메소드)메소드만 사용해 결합합니다.

(페이지 숫자를 결합하지 않는 이유는 아래에서 설명하겠습니다.)

'quote' 메소드를 'keword'에 사용하는 이유는 우리가 검색할 때 사용하는 언어는 한글('UTF-8')이기 때문입니다.

URL주소에는 'ASCII' 표현 방식 이외의 문자표기법은 사용될 수 없기 때문에 '사드'라는 'UTF-8' 방식의 문자를 'ASCII' 방식으로 변환해야하기 위해 'quute'메소드를 'keword'에 사용했습니다.


그렇게 완성된 타겟 주소(사실 페이지 숫자가 아직 포함되지 않았으므로 완전한 형태의 타겟 주소는 아닙니다.)를 'get_link_from_news_title'함수를 이용해 기사를 크롤링해와 파일로 저장합니다.




16
17
18
19
20
21
22
23
24
25
26
27
28
39
# 기사 검색 페이지에서 기사 제목에 링크된 기사 본문 주소 받아오기
def get_link_from_news_title(page_num, URL, output_file):
    for i in range(page_num):
        current_page_num = 1 + i*15
        position = URL.index('=')
        URL_with_page_num = URL[: position+1+ str(current_page_num) \
                            + URL[position+1 :]
        source_code_from_URL = urllib.request.urlopen(URL_with_page_num)
        soup = BeautifulSoup(source_code_from_URL, 'lxml',
                             from_encoding='utf-8')
        for title in soup.find_all('p''tit'):
            title_link = title.select('a')
            article_URL = title_link[0]['href']
            get_text(article_URL, output_file)
cs

위 함수는 메인함수에서 크롤링하기 위한 함수인 'get_link_from_news_title'함수 입니다.

인자로는 크롤링해 올 기사 목록 수인 page_num과, 타겟 주소인 URL, 그리고 본문 내용을 저장할 결과파일명인 'output_file'을 인자로 받습니다.


함수 안 첫번째 반복문은 여러 기사 목록을 돌며 기사 제목에 링크된 기사 본문 주소(URL주소)를 얻어 오는 부분입니다.

이 반복문에서는 기사를 긁어올 페이지 수만큼 코드를 반복합니다.

'current_page_num'에 'i'를 이용해서 긁어올 페이지 수만큼의 현재 페이지를 만듭니다.

(i=0 일때 current_page_num=1, i=1 일때 current_page_num=16, i=2 일때 current_page_num=31 ...)


그 후, 인자로 받은 URL에서 첫번째로 '='문자가 나오는 위치를 'position'에 할당한 후, 해당 'position'뒤에 'current_page_num'을 스트링으로 변환한 후 삽입합니다. 

(21~22번째 줄 코드가 이해가 가지 않는다면 문자열 슬라이스를 검색해 살펴보시길 바랍니다.)


URL에서 처음으로 '='문자가 나오는 위치는 아까 위에서 URL 주소에 마저 결합하지 않았던 페이지 숫자를 나타내는 부분이기 때문에 위와 같은 방법으로 완전한 타겟 주소를 만들수 있으며 반복문을 통해 1번 페이지부터, 원하는 페이지(예를 들어 원하는 페이지 수가 10이면 i의 최대값은 9가 되고, 그때의 'current_page_num'은 136이 됩니다.)까지의 모든 URL주소를 만들어 낼 수 있습니다. 


이렇게 만들어진 'URL_with_page_num'을 이용해 'urlopen'으로 요청을 하고 응답을 받아 'BeaurifulSoup'객체인 'soup'을 만듭니다.


이제부터는 모든 기사 목록에 올라온 기사 제목에서 기사 본문 주소(URL주소)를 추출해올 수가 있습니다.

하단의 26번째 줄 부터 시작되는 반복문은 기사 제목에 링크된 기사 본문 주소(URL주소)를 얻어오기 위한 부분입니다.

이를 설명하기 앞서 다시 한번 동아일보 기사 검색 페이지 화면을 보도록 하겠습니다.




'사드'로 검색한 기사 목록 첫번째 페이지입니다.

크롬 개발자 도구를 통해 첫번째 기사의 제목(링크)부분이 어딘지 찾아봤습니다.




class가 'tit'인 p태그 안의 첫번째 a태그에 연결된 URL주소가 해당 기사 본문 URL가 포함된 것을 알 수 있습니다.

직접 더 살펴본 결과, 2번째 기사, 3번째 기사 .... 15번째 기사 제목 모두  연결된 기사 본문 URL주소가 위와 같은 형식이었습니다.

따라서 우리는 위에서 만든 'soup'객체에서 class='tit인 p태그를 모두 뽑아와 그 안에 있는 첫번째 a태그의 'href'의 내용을  가져온다면 모든 기사의 내용을 크롤링할 수 있습니다.

그럼 'get_link_from_news_title'함수의 하단 반복문을 다시 보도록 하겠습니다.




26
27
28
29
        for title in soup.find_all('p''tit'):
            title_link = title.select('a')
            article_URL = title_link[0]['href']
            get_text(article_URL, output_file)
cs

위에서 생성한 'soup'객체에서 'find_all' 메소드를 통해 class가 'tit'인 p태그를 모두 가져와 반복문을 통해 'title'에 하나씩 할당했으며 'select'메소드를 통해 모든 a태그를 'title_link'에 저장했습니다.

('select' 메소드는 인자로 주어지는 태그를 모두 가지고 오며, 해당 객체는 인덱스로 태그 하나하나에 접근할 수 있습니다. 자세한 사항은 BeautifulSoup 문서를 참조하시기 바랍니다.) 


기사 본문 URL 주소는 p태그 안 첫번째 a태그에 저장되어 있으므로 'title_link'의 0번 인덱스로 첫번째 a태그에 접근했으며 키 값으로 'href'를 이용해 'href'로 연결된 URL주소를 'article_URL'에 저장했습니다. 

(역시나 BeutifulSoup 객체의 자세한 사용법은  BeautifulSoup 문서를 참조하시기 바랍니다.) 


2개의 반복문을 통해 최종적으로 얻어진 기사 본문 URL주소를 'get_text'함수에 결과 파일명과 함께 전달합니다.

'get_text'메소드는 실제적으로 각 기사의 본문 내용을 크롤링해오는 함수입니다.


 


32
33
34
35
36
37
38
39
40
# 기사 본문 내용 긁어오기 (위 함수 내부에서 기사 본문 주소 받아 사용되는 함수)
def get_text(URL, output_file):
    source_code_from_url = urllib.request.urlopen(URL)
    soup = BeautifulSoup(source_code_from_url, 'lxml', from_encoding='utf-8')
    content_of_article = soup.select('div.article_txt')
    for item in content_of_article:
        string_item = str(item.find_all(text=True))
        output_file.write(string_item)
 
cs

위는 기사 본문 주소를 받아 본문 내용을 긁어오는 'get_text'함수 입니다.

이전 글에서 네이버뉴스를 긁어오기 위해 사용했던 함수와 큰 차이는 없습니다.

이 함수는 기사 URL 주소를 통해 응답을 받아 'BeautifulSoup'객체를 만들고, 본문을 추출해 결과 파일에 쓰는 동작을 합니다.

다만 이 함수에서 다른 점이 있다면, 동아일보에서는 기사의 본문 내용이 class가 'article_txt'인 div태그안에 포함되어 있었기 때문에 'select'메소드를 통해  class가 'article_txt'인 div태그안 텍스트 요소만 뽑아온 점입니다.


다시 한번 두 함수를 정리해보면 다음과 같습니다.

'get_link_from_title'함수를 통해 특정 키워드로 검색한 기사 목록의 모든 기사 주소들을 추출해오며

해당 함수 내부에서 추출한 기사 주소들을 이용해 'get_text'함수를 호출에 결과 파일에  기사 본문 내용을 누적해 저장하는 방식으로 두 함수는 동작하게 됩니다. 




터미널을 통해 크롤러 모듈을 실행시켜 사드 관련 기사를 10페이지(150개)를 크롤링해 결과 파일을 'result_articles.txt'에 저장했습니다.

그 저장 결과는 다음과 같습니다.




기사가 잘 크롤링 된 것을 알 수 있습니다. 

텍스트 중간 자바스크립트 코드도 포함된 것이 보이는데, 아마 본문 내용이 포함된 태그에 같이 포함된 코드인 것으로 추정됩니다.

해당 코드 뿐 아니라 불필요한 문자는 이전글에서 설명한 텍스트 정제 모듈로 제거해서 분석에 사용하시면 됩니다.


한겨레신문에서 기사를 크롤링해 오는 방법 또한 동아일보에서 크롤링해 온 방법과 동일합니다.

다만 한겨레신문에서 사용하는 별도의 URL패턴이 있을 것이며 기사 본문 내용을 구성하는 HTML코드 또한 동아일보와 다를 것입니다.

위 크롤러 모듈에서 해당 사항만 한겨레신문에 맞게 변경하여 사용한다면 동일하게 '사드'와 관련된 신문 기사를 크롤링해 오실 수 있을겁니다.


한겨레신문을 위한 크롤러 모듈은 아래의 코드를 참조하시기 바랍니다.




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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
""" 한겨레 신문 특정 키워드를 포함하는, 특정 날짜 이전 기사 내용 크롤러(정확도순 검색)
    python [모듈이름] [키워드] [가져올 페이지 숫자] [가져올 기사의 최근 날짜]
    [결과 파일명.txt]
    한페이지에 10개
"""
 
import sys
from bs4 import BeautifulSoup
import urllib.request
from urllib.parse import quote
 
TARGET_URL_BEFORE_KEWORD = 'http://search.hani.co.kr/Search?command=query&' \
                          'keyword='
TARGET_URL_BEFORE_UNTIL_DATE = '&media=news&sort=s&period=all&datefrom=' \
                               '2000.01.01&dateto='
TARGET_URL_REST = '&pageseq='
 
 
def get_link_from_news_title(page_num, URL, output_file):
    for i in range(page_num):
        URL_with_page_num = URL + str(i)
        source_code_from_URL = urllib.request.urlopen(URL_with_page_num)
        soup = BeautifulSoup(source_code_from_URL, 'lxml',
                             from_encoding='utf-8')
        for item in soup.select('dt > a'):
            article_URL = item['href']
            get_text(article_URL, output_file)
 
 
def get_text(URL, output_file):
    source_code_from_url = urllib.request.urlopen(URL)
    soup = BeautifulSoup(source_code_from_url, 'lxml', from_encoding='utf-8')
    content_of_article = soup.select('div.text')
    for item in content_of_article:
        string_item = str(item.find_all(text=True))
        output_file.write(string_item)
 
 
def main(argv):
    if len(sys.argv) != 5:
        print("python [모듈이름] [키워드] [가져올 페이지 숫자] "
              "[가져올 기사의 최근 날짜] [결과 파일명.txt]")
        return
    keyword = argv[1]
    page_num = int(argv[2])
    until_date = argv[3]
    output_file_name = argv[4]
    target_URL = TARGET_URL_BEFORE_KEWORD + quote(keyword) \
                 + TARGET_URL_BEFORE_UNTIL_DATE + until_date + TARGET_URL_REST
    output_file = open(output_file_name, 'w')
    get_link_from_news_title(page_num, target_URL, output_file)
    output_file.close()
 
 
if __name__ == '__main__':
    main(sys.argv)
 
 
cs




다음 글에선 크롤링한 기사 본문 내용을 바탕으로 단어 사용 빈도를 체크하는 프로그램을 만들어보도록 하겠습니다.



연관된 글

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 2 (네이버 뉴스 크롤링하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 3 (동아일보, 한겨레 '사드'관련 기사 크롤링하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 4 (단어 사용 빈도 체크하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 5 (워드클라우드 만들기) 미작성



출처: http://yoonpunk.tistory.com/6 [윤빵꾸의 공부노트]

반응형
반응형

네이버 뉴스 크롤링하기


이번 글에서는 동아일보와 한겨레신문에서 특정 키워드를 포함하는 기사를 긁어오기 전 예제로 네이버포털의 뉴스기사를 긁어 오는 것을 먼저 연습하도록 하겠습니다.



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
"""네이버 뉴스 기사 웹 크롤러 모듈"""
 
from bs4 import BeautifulSoup
import urllib.request
 
# 출력 파일 명
OUTPUT_FILE_NAME = 'output.txt'
# 긁어 올 URL
URL = 'http://news.naver.com/main/read.nhn?mode=LSD&mid=shm&sid1=103&oid=055'\
      '&aid=0000445667'
 
 
# 크롤링 함수
def get_text(URL):
    source_code_from_URL = urllib.request.urlopen(URL)
    soup = BeautifulSoup(source_code_from_URL, 'lxml', from_encoding='utf-8')
    text = ''
    for item in soup.find_all('div', id='articleBodyContents'):
        text = text + str(item.find_all(text=True))
    return text
 
 
# 메인 함수
def main():
    open_output_file = open(OUTPUT_FILE_NAME, 'w')
    result_text = get_text(URL)
    open_output_file.write(result_text)
    open_output_file.close()
    
 
if __name__ == '__main__':
    main()
cs

위 코드는 네이버뉴스를 긁어오기 위한 크롤러 모듈 입니다.

그럼 코드를 하나하나 살펴보도록 하겠습니다.



1
2
3
4
5
"""네이버 뉴스 기사 웹 크롤러 모듈"""
 
from bs4 import BeautifulSoup
import urllib.request
 
cs
크롤링을 하기 위해 두 가지 라이브러리를 임포트했습니다.

HTTP REQUEST를 보내고 HTTP RESPONSE를 받기 위한 urllib과 

urllib을 통해 받은 RESPONSE(HTML)를 파싱하기 위한 Beautiful Soup 입니다.

urllib, Beautiful Soup이 임포트되지 않는다면 해당 라이브러리를 먼저 설치하시기 바랍니다.



6
7
8
9
10
11
# 출력 파일 명
OUTPUT_FILE_NAME = 'output.txt'
# 긁어 올 URL
URL = 'http://news.naver.com/main/read.nhn?mode=LSD&mid=shm&sid1=103&oid=055'\
      '&aid=0000445667'
 
cs
그 후에 기사내용을 긁어와 저장할 파일명과 긁어 올 기사의 URL주소를 상수로 할당했습니다.

URL주소를 백슬래시를 사용해 두 줄로 나누었습니다. 이는 한 행이 80자가 넘어가는 것을 방지하기 위해서 입니다.

신문기사 URL주소는 본인이 크롤링하길 원하는 뉴스기사의 URL주소를 복사해 오시면 됩니다. 



저는 네이버 뉴스에서 동영상이 포함된 일기예보 기사를 골라 복사해왔습니다.

동영상이 포함된 기사였지만 일기예보 내용이 텍스트로 본문에 포함되어 있으므로 해당 기사의 본문 내용(텍스트)만 긁어오도록 하겠습니다.



13
14
15
16
17
18
19
20
# 크롤링 함수
def get_text(URL):
    source_code_from_URL = urllib.request.urlopen(URL)
    soup = BeautifulSoup(source_code_from_URL, 'lxml', from_encoding='utf-8')
    text = ''
    for item in soup.find_all('div', id='articleBodyContents'):
        text = text + str(item.find_all(text=True))
    return text
cs

위는 해당 URL주소로 요청을 보내고 받아 기사 내용을 파싱해 하나의 문자열로 저장하는 함수입니다.



14
15
16
def get_text(URL):
    source_code_from_URL = urllib.request.urlopen(URL)
    sou= BeautifulSoup(source_code_from_URL, 'lxml', from_encoding='utf-8')
cs

우선 15번째 줄에서 urlopen메소드를 통해 URL주소에 대한 요청을 보내 source_code_from_URL 변수에 그 결과를 저장했습니다. 

그 후, HTML코드를 파싱하기 위해 16번째 줄에서 source_code_from_URL을 이용, BeautifulSoup객체를 생성해 soup에 할당했습니다. 

BeautifulSoup객체 생성자의 2번째 인자로 'lxml'을 사용해 기존 'html'방식 대신 'lxml'방식으로 파싱을 했고, 한글 내용이 포함된 기사이기 때문에 from_encoding 키워드 인자로 'utf-8'을 넣어 'UTF-8'방식으로 인코딩을 했습니다. 

'lxml'방식으로 BeautifulSoup 객체를 생성할때 오류가 난다면 'lxml' 라이브러리 또한 설치해주시길 바랍니다.


이 후, soup객체에서 원하는 부분(HTML요소)만 가지고 와야 합니다. 우선 우리가 가지고 올 기사의 본문 내용이 포함된 태그(HTML 요소)가 무엇인지를 찾아야합니다.



위 사진은 신문기사 페이지에서 크롬 개발자도구를 이용해 본문 내용이 포함된 태그를 찾은 사진입니다.



자세히 살펴보면 본문 내용은 id가 'articleBodyContents'인 'div'클래스 안에 담겨 있음을 알 수 있습니다.

따라서 우리가 생성한 객체 soup에서 위에서의 'div'클래스를 가져오기만 하면 됩니다.



17
18
19
20
21
    text = ''
    for item in soup.find_all('div', id='articleBodyContents'):
        text = text + str(item.find_all(text=True))
    return text
 
cs

우선 본문 내용을 저장하기 위해 text에 빈 문자열을 할당합니다.

이 후, soup객체의 find_all 메소드를 이용해 id가 'articleBodyContents'인 'div'클래스를 모두 뽑아내고, for문을 통해 뽑힌 요소 하나하나에 다시 한번 직접 접근합니다.

for문으로 뽑혀진 각 요소 item에 find_all 메소드를 사용, text 키워드 인자에 True를 넣어 텍스트 요소만 뽑아 문자열로 치환한 후, text 문자열에 이어 붙였습니다. 

그 후, text를 반환했습니다.

좀 더 자세 BeautifulSoup객체의 사용법은 공식 문서를 참조하시기 바랍니다.


본문 내용이 포함된 요소가 'div'클래스 하나인데 for문을 쓴 이유는 첫번째로는 find_all 메소드의 반환 객체가 인자로 주어진 조건을 만족하는 모든 요소들을 순환가능한 객체(그 타입이 리스트인지 아닌지는 잘 모르겠네요...)로 주어지기 때문에 사용했으며, 두번째로는 다른 신문사의 경우, 본문 내용이 동일한 id를 갖는 동일한 여러개의 태그에 나뉘어 할당되어있는 경우도 있기 때문입니다.



23
24
25
26
27
28
29
30
31
32
# 메인 함수
def main():
    open_output_file = open(OUTPUT_FILE_NAME, 'w')
    result_text = get_text(URL)
    open_output_file.write(result_text)
    open_output_file.close()
    
 
if __name__ == '__main__':
    main()
cs

위는 그 이후의 코드입니다. 

메인 함수를 별도로 만들고 __name__을 이용해 main함수를 실행시켰습니다.

메인함수에서는 위에서 할당한 OUTPUT_FILE_NAME상수를 통해 txt파일을 생성하고, get_text함수를 사용해 기사내용을 result_text에 할당했습니다. 

이 후, 오픈한 output_file에 기사를 쓰고 닫았습니다.



터미널에서 해당 크롤러모듈을 실행한 결과 'output.txt' 파일이 생성되었으며



'output.txt' 파일의 내용 또한 우리가 긁어오고자 한 신문기사의 내용이 성공적으로 긁어와졌음을 확인할 수 있습니다.

다만 '\n', '\r'등의 문자와 워드클라우드 시각화에 필요 없는 특수문자의 제거가 필요해 보입니다. 

따라서 텍스트 정제 모듈을 따로 만들어 추출된 기사에서 필요없는 문자를 제거하였습니다.



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
""" 텍스트 정제 모듈
    영어, 특수기호 모두 제거
"""
 
import re
 
# 입,출력 파일명
INPUT_FILE_NAME = 'output.txt'
OUTPUT_FILE_NAME = 'output_cleand.txt'
 
 
# 클리닝 함수
def clean_text(text):
    cleaned_text = re.sub('[a-zA-Z]''', text)
    cleaned_text = re.sub('[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]',
                          '', cleaned_text)
    return cleaned_text
    
 
# 메인 함수
def main():
    read_file = open(INPUT_FILE_NAME, 'r')
    write_file = open(OUTPUT_FILE_NAME, 'w')
    text = read_file.read()
    text = clean_text(text)
    write_file.write(text)
    read_file.close()
    write_file.close()
 
 
if __name__ == "__main__":
    main()
cs

위 코드는 텍스트 정제 모듈입니다.



1
2
3
4
5
6
7
8
9
10
""" 텍스트 정제 모듈
    영어, 특수기호 모두 제거
"""
 
import re
 
# 입,출력 파일명
INPUT_FILE_NAME = 'output.txt'
OUTPUT_FILE_NAME = 'output_cleand.txt'
 
cs

정규표현식을 활용하기 위해 're' 라이브러리를 임포트하고, 정제할 파일의 이름을 INPUT_FILE_NAME으로, 결과 파일 이름을 OUTPUT_FILE_NAME으로 할당했습니다.



12
13
14
15
16
17
# 클리닝 함수
def clean_text(text):
    cleaned_text = re.sub('[a-zA-Z]''', text)
    cleaned_text = re.sub('[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]',
                          '', cleaned_text)
    return cleaned_text
cs

이 후, 문자열을 입력 받아 영어와 특수문자를 제거하는 클리닝 함수를 정의했습니다.

14번째 줄은 대소문자 영어를 제거하는 코드, 15번째 줄은 특수문자를 제거하는 코드입니다.

're'라이브러리의 자세한 사용법은 공식 문서를 참조하시기 바랍니다.



20
21
22
23
24
25
26
27
28
29
30
31
32
# 메인 함수
def main():
    read_file = open(INPUT_FILE_NAME, 'r')
    write_file = open(OUTPUT_FILE_NAME, 'w')
    text = read_file.read()
    text = clean_text(text)
    write_file.write(text)
    read_file.close()
    write_file.close()
 
 
if __name__ == "__main__":
    main()
cs

이번 모듈도 마찬가지로 main를 정의하고 __name__을 이용해 main함수를 실행했습니다.

main함수에서 위에서 상수로 정의한 입력파일명을 이용해 입력파일에서 문자열을 읽고, clean_text함수를 이용해 본문 내용을 정제했습니다.

이후 출력파일명을 이용해 출력파일을 열고,  출력파일에 정제한 본문 내용을 썼습니다.



터미널을 통해 해당 정제모듈을 실행한 결과 'output_cleand.txt'이 생성되었고,



'output_cleand.txt' 파일을 열어 본 결과, 영어와 불필요한 문자들이 제거되었음을 확인할 수 있습니다.



위에서 설명한 크롤러모듈과 정제모듈을 저처럼 따로 나눌 필요는 없습니다. 한 개의 모듈에 모두 구현해 사용해도 무방합니다. 

다음 글에서는 이번에 다룬 크롤러 모듈을 수정, 확장해 동아일보와 한겨레일보에서 '사드' 키워드를 갖는 신문기사를 대량으로  크롤링해보도록 하겠습니다.



연관된 글

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 2 (네이버 뉴스 크롤링하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 3 (동아일보, 한겨레 '사드'관련 기사 크롤링하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 4 (단어 사용 빈도 체크하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 5 (워드클라우드 만들기) 미작성



출처: http://yoonpunk.tistory.com/4 [윤빵꾸의 공부노트]

반응형
반응형

article_crawling.py

article_crawling1.py

cleaner.py

crawling.py

output_cleand.txt

output.txt

result_acticls1.txt

result_articles.txt

words_result.txt

words_result1.txt

words.py

동일한 키워드를 가지는 신문 기사를 논조가 다른 신문사에서 긁어와 사용된 단어를 워드클라우드로 시각화해 분석해보는 예제입니다.

키워드는 '사드'를 사용했고, 신문사는 동아일보, 한겨레신문 두 신문사에서 각각 150개씩 기사를 긁어왔습니다.

결과는 아래와 같습니다. (왼쪽이 동아일보, 오른쪽이 한겨레신문 입니다.)




키워드가 '사드'인 만큼, 워드클라우드 모양을 미사일로 만들어봤습니다.

워드클라우드를 살펴보면 동아일보는 '중국'을 한겨레신문에서는 '미국'을 가장 많이 사용했음을 알 수 있습니다.

분석환경은 우분투 16.04에서 파이썬 3.5.2버전을 사용했습니다.

다음 글 부터 사용된 라이브러리와 코드를 차근차근 살펴보도록 하겠습니다.



연관된 글

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 1 (개요)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 2 (네이버 뉴스 크롤링하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 3 (동아일보, 한겨레 '사드'관련 기사 크롤링하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 4 (단어 사용 빈도 체크하기)

특정 키워드를 포함하는 신문기사 웹크롤링 & 워드클라우드 시각화 분석 - 5 (워드클라우드 만들기) 미작성



출처: http://yoonpunk.tistory.com/3 [윤빵꾸의 공부노트]

반응형
반응형

설치하고 돌려 보면 에러 발생


ImportError: No module named 'jpype'


해결방안 - 직접 설치해서 해결함 (python3.5)


순서

1. https://pypi.python.org/pypi/JPype1

2. 최신 패키지 다운로드

3. 압축 풀고 

4. 해당 디렉토리로 이동

5. 설치 

   % python3.5 setup.py build 

   % python3.5 setup.py install



출처: http://sfixer.tistory.com/entry/Python3-konlpy-설치시-jpype-관련-실패-해결-방안 [Block Busting]

반응형
반응형

R에서 파이썬까지…데이터과학 학습 사이트 8

 

1. 데이터과학 입문

코세라 / 워싱턴대 데이터과학 입문

 

 

2. R, Python 프로그래밍

데이터캠프 / R 입문

 

코드카데미 파이썬

구글 파이썬 클래스

 


 

3. 기계 학습

코세라 / 스탠포드 머신 러닝 / 앤드류 응

 

4. 통계학

유튜브 / 프린스턴대 통계 1

 

5. 데이터 시각화

D3 튜토리얼 / 스콧 머레이 블로그

 

 

 

보다 심도 있는 강좌들 및 기타 데이터 과학 오픈소스 강좌가 알고 싶다면, 다음의 웹페이지를 참고하자.

깃허브 : 데이터 사이언스 마스터즈

 

출처 : http://www.bloter.net/archives/237013



출처: http://extraman.tistory.com/400 [맨땅에 헤딩하기]

반응형
반응형

Python

Using the Python DB API, don't do this:

# Do NOT do it this way.
cmd = "update people set name='%s' where id='%s'" % (name, id)
curs.execute(cmd)

Instead, do this:

cmd = "update people set name=%s where id=%s"
curs.execute(cmd, (name, id))

Note that the placeholder syntax depends on the database you are using.

'qmark'         Question mark style,
                e.g. '...WHERE name=?'
'numeric'       Numeric, positional style,
                e.g. '...WHERE name=:1'
'named'         Named style,
                e.g. '...WHERE name=:name'
'format'        ANSI C printf format codes,
                e.g. '...WHERE name=%s'
'pyformat'      Python extended format codes,
                e.g. '...WHERE name=%(name)s'

The values for the most common databases are:

>>> import MySQLdb; print MySQLdb.paramstyle
format
>>> import psycopg2; print psycopg2.paramstyle
pyformat
>>> import sqlite3; print sqlite3.paramstyle
qmark

So if you are using MySQL or PostgreSQL, use %s (even for numbers and other non-string values!) and if you are using SQLite use ?



반응형
반응형

ImportError: No module named 'MySQLdb'


파이썬 3.5에서는 아직 지원하지 않는다.

mysqlclient를 사용하자


apt-get install libmysqlclient-dev

pip3 install mysqlclient

반응형
반응형

ImportError: libSM.so.6: cannot open shared object file: No such file or directory


apt-get install -y python-qt4


반응형

'Program > Python' 카테고리의 다른 글

Python SQL Injection 피하기  (0) 2016.09.28
mysqldb  (0) 2016.09.28
MIT 6.00 컴퓨터 공학과 프로그래밍(Python) 오픈 코스  (0) 2016.05.15
python mysql mssql  (0) 2016.05.13
파이썬 라이브러리를 활용한 데이터 분석  (0) 2016.05.09
반응형


MIT 공대에서 컴퓨터 공학과 프로그래밍 소개에 관한 강의가 유튜브에 올라왔다. 최근에 업로드된 강의가 있음에도 이 강의들을 정리한 이유는 아무래도 한글 자막이 잘 되어 있기 때문이다.

출처 : MIT OpenCourseWare YouTube

교수 : Eric Grimson, John Guttag

제 01강 - 연산이란 - 데이터 타입, 연산자 및 변수 소개

제 02강 - 연산자와 피연산자 - 분기문, 조건문 그리고 반복문

제 03강 - 공통 코드 패턴, 반복 프로그램

제 04강 - 기능을 통한 분해 및 추상화, 재귀 소개

제 05강 - 부동 소수점, 계통적 명세화, 루트 찾기

제 06강 - 이분법, 뉴턴/랩슨, 그리고 리스트 소개

제 07강 - 리스트와 가변성, 딕셔너리, 의사코드, 그리고 효율성 소개

제 08강 - 복잡성 - 로그, 선형, 이차 방정식, 지수 연산 알고리즘

제 09강 - 이진 탐색, 버블 그리고 분류 선택

제 10강 - 분할 정복 방법, 합병 정렬, 예외

제 11강 - 테스트와 디버깅

제 12강 - 디버깅 추가 강의, 배낭 문제, 동적 프로그래밍 소개

제 13강 - 동적 프로그래밍 - Overlapping subproblems, Optimal substructure

제 14강 - 배낭 문제 분석, 객체 지향 프로그래밍 소개

제 15강 - 추상 데이터 타입, 클래스와 메소드

제 16강 - 캡슐화, 상속, 쉐도잉

제 17강 - 연산 모델 - 랜덤워크 시뮬레이션

제 18강 - 시물레이션 결과 제시, Pylab, Plotting

제 19강 - 편향된 랜덤워크, 배포

제 20강 - 몬테카를로(Monte Carlo) 시뮬레이션, 추정 파이

제 21강 - 시뮬레이션 결과 검증, 곡선 적합, 선형 회귀

제 22강 - 일반, 균등 그리고 지수 분포 - 통계의 오류

제 23강 - 주식 시장 시뮬레이션

제 24강 - 과정 개요 - 컴퓨터 과학자들은 무엇을 하나요?


반응형

'Program > Python' 카테고리의 다른 글

mysqldb  (0) 2016.09.28
libSM.so.6  (0) 2016.05.22
python mysql mssql  (0) 2016.05.13
파이썬 라이브러리를 활용한 데이터 분석  (0) 2016.05.09
ipython ubuntu Anaconda  (0) 2016.05.03
반응형



apt-get install python-pip


pip

pip --upgrade

apt-get update

apt-get install python-pip

pip show MySQL-python

pip install MySQL-python

pip install --upgrade

pip install --upgrade pip

pip install MySQL-python

apt-get install libmysqlclient-dev python-dev

pip install MySQL-python


apt-get purge python-pymssql

apt-get install freetds-dev

pip install pymssql



반응형

'Program > Python' 카테고리의 다른 글

libSM.so.6  (0) 2016.05.22
MIT 6.00 컴퓨터 공학과 프로그래밍(Python) 오픈 코스  (0) 2016.05.15
파이썬 라이브러리를 활용한 데이터 분석  (0) 2016.05.09
ipython ubuntu Anaconda  (0) 2016.05.03
[python] MySQL 연결  (0) 2014.11.05
반응형

CHAPTER 1 시작하기 전에
1.1 이 책은?
1.2 왜 데이터 분석을 위한 파이썬인가?
1.2.1 접착제처럼 사용하는 파이썬
1.2.2 한 가지 언어만 사용
1.2.3 파이썬을 사용하면 안 되는 경우
1.3 필수 파이썬 라이브러리
1.3.1 NumPy
1.3.2 pandas
1.3.3 matplotlib
1.3.4 IPython
1.3.5 SciPy
1.4 설치와 설정
1.4.1 윈도우
1.4.2 애플 OS X
1.4.3 리눅스
1.4.4 파이썬 2.x와 파이썬 3.x
1.4.5 통합 개발 환경
1.5 커뮤니티와 컨퍼런스
1.6 이 책을 살펴보는 방법
1.6.1 예제 코드
1.6.2 예제에 사용된 데이터
1.6.3 import 컨벤션
1.6.4 용어
1.7 감사의 말

CHAPTER 2 사례 소개
2.1 bit.ly의 1.usa.gov 데이터
2.1.1 순수 파이썬으로 표준시간대 세어보기
2.1.2 pandas로 표준시간대 세어보기
2.2 MovieLens의 영화 평점 데이터
2.2.1 평점 차이 구하기
2.3 신생아 이름
2.3.1 이름 유행 분석
2.4 맺음말

CHAPTER 3 IPython 소개
3.1 IPython 기본
3.1.1 탭 자동 완성
3.1.2 자기관찰
3.1.3 %run 명령어
3.1.4 클립보드에 있는 코드 실행하기
3.1.5 키보드 단축키
3.1.6 예외와 트레이스백
3.1.7 매직 명령어
3.1.8 Qt 기반의 GUI 콘솔
3.1.9 Pylab 모드와 Matplolib 통합
3.2 명령어 히스토리 사용하기
3.2.1 명령어 검색과 재사용
3.2.2 입middot;출력 변수
3.2.3 입middot;출력 기록하기
3.3 운영체제와 함께 사용하기
3.3.1 셸 명령어와 별칭
3.3.2 디렉터리 북마크 시스템
3.4 소프트웨어 개발 도구
3.4.1 인터랙티브 디버거
3.4.2 코드 시간 측정: %time과 %timeit
3.4.3 기본적인 프로파일링: %prun과 %run -p
3.4.4 함수의 각 줄마다 프로파일링하기
3.5 IPython HTML 노트북
3.6 IPython을 사용한 제품 개발을 위한 팁
3.6.1 모듈 의존성 리로딩하기
3.6.2 코드 설계 팁
3.7 IPython 고급 기능
3.7.1 IPython 친화적인 클래스 만들기
3.7.2 프로파일과 설정
3.8 감사의 글

CHAPTER 4 NumPy 기본: 배열과 벡터 계산
4.1 NumPy ndarray: 다차원 배열 객체
4.1.1 ndarray 생성
4.1.2 ndarray의 자료형
4.1.3 배열과 스칼라 간의 연산
4.1.4 색인과 슬라이싱 기초
4.1.5 불리언 색인
4.1.6 팬시 색인
4.1.7 배열 전치와 축 바꾸기
4.2 유니버설 함수
4.3 배열을 사용한 데이터 처리
4.3.1 배열연산으로 조건절 표현하기
4.3.2 수학 메서드와 통계 메서드
4.3.3 불리언 배열을 위한 메서드
4.3.4 정렬
4.3.5 집합 함수
4.4 배열의 파일 입middot;출력
4.4.1 배열을 바이너리 형식으로 디스크에 저장하기
4.4.2 텍스트 파일 불러오기와 저장하기
4.5 선형대수
4.6 난수 생성
4.7 계단 오르내리기 예제
4.7.1 한 번에 계단 오르내리기 시뮬레이션하기

CHAPTER 5 pandas 시작하기
5.1 pandas 자료 구조 소개
5.1.1 Series
5.1.2 DataFrame
5.1.3 색인 객체
5.2 핵심 기능
5.2.1 재색인
5.2.2 하나의 로우 또는 칼럼 삭제하기
5.2.3 색인하기, 선택하기, 거르기
5.2.4 산술연산과 데이터 정렬
5.2.5 함수 적용과 매핑
5.2.6 정렬과 순위
5.2.7 중복 색인
5.3 기술통계 계산과 요약
5.3.1 상관관계와 공분산
5.3.2 유일 값, 값 세기, 멤버십
5.4 누락된 데이터 처리하기
5.4.1 누락된 데이터 골라내기
5.4.2 누락된 값 채우기
5.5 계층적 색인
5.5.1 계층 순서 바꾸고 정렬하기
5.5.2 단계별 요약통계
5.5.3 DataFrame의 칼럼 사용하기
5.6 pandas와 관련된 기타 주제
5.6.1 정수 색인
5.6.2 Panel 데이터

CHAPTER 6 데이터 로딩, 저장, 파일 형식
6.1 텍스트 파일 이용하는 방법
6.1.1 텍스트 파일 조금씩 읽어오기
6.1.2 데이터를 텍스트 형식으로 기록하기
6.1.3 수동으로 구분 형식 처리하기
6.1.4 JSON 데이터
6.1.5 XML과 HTML: 웹 내용 긁어오기
6.2 이진 데이터 형식
6.2.1 HDF5 형식 사용하기
6.2.2 마이크로소프트 엑셀 파일에서 데이터 읽어오기
6.3 HTML, 웹 API와 함께 사용하기
6.4 데이터베이스와 함께 사용하기
6.4.1 MongoDB에 데이터 저장하고 불러오기

CHAPTER 7 데이터 준비하기: 다듬기, 변형, 병합
7.1 데이터 합치기
7.1.1 데이터베이스 스타일로 DataFrame 합치기
7.1.2 색인 머지하기
7.1.3 축 따라 이어붙이기
7.1.4 겹치는 데이터 합치기
7.2 재형성과 피벗
7.2.1 계층적 색인으로 재형성하기
7.2.2 피버팅으로 데이터 나열 방식 바꾸기
7.3 데이터 변형
7.3.1 중복 제거하기
7.3.2 함수나 매핑 이용해 데이터 변형하기
7.3.3 값 치환하기
7.3.4 축 색인 이름 바꾸기
7.3.5 개별화와 양자화
7.3.6 특이값 찾아내고 제외하기
7.3.7 치환과 임의 샘플링
7.3.8 표시자/더미 변수
7.4 문자열 다루기
7.4.1 문자열 객체 메서드
7.4.2 정규표현식
7.4.3 pandas의 벡터화된 문자열 함수
7.5 예제: 미국 농무부 음식 데이터베이스

CHAPTER 8 도식화와 시각화
8.1 matplotlib API 간략하게 살펴보기
8.1.1 Figure와 서브플롯
8.1.2 색상, 마커, 선 스타일
8.1.3 눈금, 라벨, 범례
8.1.4 주석과 그림 추가
8.1.5 그래프를 파일로 저장
8.1.6 matplotlib 설정
8.2 pandas에서 그래프 그리기
8.2.1 선 그래프
8.2.2 막대 그래프
8.2.3 히스토그램과 밀도 그래프
8.2.4 산포도
8.3 지도 그리기: 아이티 지진 데이터 시각화하기
8.4 파이썬 시각화 도구 생태계
8.4.1 Chaco
8.4.2 mayavi
8.4.3 기타 패키지
8.4.4 시각화 도구의 미래

CHAPTER 9 데이터 수집과 그룹 연산
9.1 GroupBy 메카닉
9.1.1 그룹 간 순회하기
9.1.2 칼럼 또는 칼럼의 일부만 선택하기
9.1.3 사전과 Series에서 묶기
9.1.4 함수로 묶기
9.1.5 색인 단계로 묶기
9.2 데이터 수집
9.2.1 칼럼에 여러 가지 함수 적용하기
9.2.2 색인되지 않은 형태로 집계된 데이터 반환하기
9.3 그룹별 연산과 변형
9.3.1 apply: 분리-적용-병합
9.3.2 변위치 분석과 버킷 분석
9.3.3 예제: 그룹에 국한된 값으로 누락된 값 채우기
9.3.4 예제: 랜덤 표본과 순열
9.3.5 예제: 그룹 가중 평균과 상관관계
9.3.6 예제: 그룹 상의 선형 회귀
9.4 피벗 테이블과 교차일람표
9.4.1 교차일람표
9.5 예제: 2012년 연방 선거관리위원회 데이터베이스
9.5.1 직장 및 피고용별 기부 통계
9.5.2 기부금액
9.5.3 주별 기부 통계

CHAPTER 10 시계열
10.1 날짜, 시간 자료형, 도구
10.1.1 문자열을 datetime으로 변환하기
10.2 시계열 기초
10.2.1 인덱싱, 선택, 부분 선택
10.2.2 중복된 색인을 갖는 시계열
10.3 날짜 범위, 빈도, 이동
10.3.1 날짜 범위 생성하기
10.3.2 빈도와 날짜 오프셋
10.3.3 데이터 시프트
10.4 시간대 다루기
10.4.1 지역화와 변환
10.4.2 시간대 고려해 Timestamp 객체 다루기
10.4.3 다른 시간대 간의 연산
10.5 기간과 기간 연산
10.5.1 Period의 빈도 변환
10.5.2 분기 빈도
10.5.3 타임스탬프와 기간 서로 변환하기
10.5.4 배열을 이용해 PeriodIndex 생성하기
10.6 리샘플링과 빈도 변환
10.6.1 다운샘플링
10.6.2 업샘플링과 보간
10.6.3 기간 리샘플링
10.7 시계열 그래프
10.8 이동창 기능
10.8.1 지수 가중 함수
10.8.2 이진 이동창 함수
10.8.3 사용자 정의 이동창 함수
10.9 성능과 메모리 사용량에 대한 노트

CHAPTER 11 금융, 경제 데이터 애플리케이션
11.1 데이터 준비
11.1.1 시계열과 크로스 섹션 정렬
11.1.2 다른 빈도를 가지는 시계열 연산
11.1.3 일별 시간과 현재 최신 데이터 선택하기
11.1.4 데이터와 함께 나누기
11.1.5 수익 지수와 누적 수익
11.2 그룹 변환과 분석
11.2.1 그룹 요인 밝히기
11.2.2 십분위와 사분위 분석
11.3 추가 예제 애플리케이션
11.3.1 신호 경계 분석
11.3.2 선물 계약 롤링
11.3.3 롤링 상관관계와 선형 회귀

CHAPTER 12 고급 NumPy
12.1 ndarray 객체 내부 알아보기
12.1.1 NumPy dtype 구조
12.2 고급 배열 조작 기법
12.2.1 배열 재형성하기
12.2.2 C와 포트란 순서
12.2.3 배열 이어붙이고 나누기
12.2.4 원소 반복시키기: repeat과 tile
12.2.5 팬시 색인: take와 put
12.3 브로드캐스팅
12.3.1 다른 축에 대해 브로드캐스팅하기
12.3.2 브로드캐스팅 이용해 배열에 값 대입하기
12.4 고급 ufunc 사용법
12.4.1 ufunc 인스턴스 메서드
12.4.2 사용자 ufunc
12.5 구조화된 배열과 레코드 배열
12.5.1 중첩된 dtype과 다차원 필드
12.5.2 구조화된 배열을 사용해야 하는 이유
12.5.3 구조화된 배열 다루기: numpy.lib.recfunctions
12.6 정렬에 관하여
12.6.1 간접 정렬: argsort와 lexsort
12.6.2 다른 정렬 알고리즘
12.6.3 numpy.searchsorted: 정렬된 배열에서 원소 찾기
12.7 NumPy matrix 클래스
12.8 고급 배열 입middot;출력
12.8.1 메모리 맵 파일
12.8.2 HDF5 및 기타 배열 저장 옵션
12.9 성능 팁
12.9.1 인접 메모리의 중요성
12.9.2 기타 성능 옵션: Cython, f2py, C

부록 파이썬 언어의 기본
A.1 파이썬 인터프리터
A.2 파이썬 기초
A.2.1 시멘틱
A.2.2 스칼라형
A.2.3 흐름 제어
A.3 자료 구조와 순차 자료형
A.3.1 튜플
A.3.2 리스트
A.3.3 내장 순차 자료형 함수
A.3.4 사전
A.3.5 세트
A.3.6 리스트 내포, 사전 내포, 세트 내포
A.4 함수
A.4.1 네임스페이스, 스코프, 지역 함수
A.4.2 여러 값 반환하기
A.4.3 함수도 객체다
A.4.4 익명 함수
A.4.5 클로저: 함수를 반환하는 함수
A.4.6 *args와 **kwargs를 사용해서 호출 문법 확장하기
A.4.7 커링: 일부 인자만 취하기
A.4.8 제너레이터
A.5 파일과 운영체제

반응형

'Program > Python' 카테고리의 다른 글

MIT 6.00 컴퓨터 공학과 프로그래밍(Python) 오픈 코스  (0) 2016.05.15
python mysql mssql  (0) 2016.05.13
ipython ubuntu Anaconda  (0) 2016.05.03
[python] MySQL 연결  (0) 2014.11.05
python - mysql MySQLdb  (0) 2014.11.05
반응형

Updates:
7th January, 2016 - changes made according to new Anaconda distribution (v2.4.1) which contains Jupyter Notebook.
Note: The update to the video tutorial is still in progress so please don't refer it for now. Once, I have updated it, I'll remove this note from here.


I hope everyone is familiar with the AWS (Amazon Web Services) and how to use iPython (Now Jupyter) Notebooks. If you are not familiar with Jupyter Notebook and you work with Python, then you are definitely missing a very important tool in you work. Please go through this video which is a short tutorial on iPython (Jupyter) Notebook.

OK, to begin with, I'll list all the steps to create an Jupyter Notebook Server on an EC2 Instance in a step-wise fashion. I have also created a Youtube Video for this post, which you can check it out here.(update in progress to the video. please don't refer it for now)

The reason for deploying Jupyter Notebook Server on AWS is to access all my Notebooks from anywhere in the World, just using my browser and also be able to work with them.

Enough of talking, let's begin:

1. Login to your Amazon Management Console. If you don't have an account yet, you can create one for it. You get 1 yr of free access to some of the services, which you can check out at this link

2. Create a new EC2 Instance with Ubuntu. If you are not familiar with how to create an EC2 instance, you can check out the video of this blog, in which I go through the steps from the beginning.

3. The important thing to remember while creating the instance is to assign the security group settings as mentioned in the image below


4. Launch your instance and ssh into it to perform the following operations
  • First of all we are going to use Anaconda Python Distribution for installing all the required Python libraries. This is a free distribution and we are going to use the Linux version of it. Remember to verify the latest version of the distribution from the site. This blog is updated to reflect the changes in the latest Anaconda distribution - 2.4.1.
    $ wget https://3230d63b5fc54e62148e-c95ac804525aac4b6dba79b00b39d1d3.ssl.cf1.rackcdn.com/Anaconda2-2.4.1-Linux-x86_64.sh
       
  • Next we will bash to run this .sh file. You need to accept the license terms and set the installation directory for the anaconda distribution. I use the default one only, which is "/home/ubuntu/anaconda2/". Also, it asks you to add the default path of anaconda python to your .bashrc profile. You can accept to do it or add it manually.
    $ bash Anaconda2-2.4.1-Linux-x86_64.sh
       
  • Now you need to check, which python version you are using, just to confirm if we are using the one from Anaconda Distribution or not. You can do this by using
    $ which python
       
    This will list the python your system is currently using. If it does not mentions the one from ".../anaconda2/..." folder, then you can use the following command to re-load your.bashrc, so as to set the correct python
    $ source .bashrc
       
  • Open the iPython Terminal to get an encrypted password so as to use it for logging into our iPython Notebook Server. Remember to copy and save the output of this command, which will be an encrypted password, something like "sha1..."
    $ ipython
    In [1]:from IPython.lib import passwd
    In [2]:passwd()
    and exit out of ipython terminal using "exit" command. [ I'm not gonna use this password(shown in the pic below), so don't waste your time trying to copy and use it. ;) ]
  • Now we're going to create the configuration profile for our Jupyter Notebook server
    $ jupyter notebook --generate-config
       
  • The next thing is going to be to create a self-signed certificate for accessing our Notebooks through HTTPS
    $ mkdir certs
    $ cd certs
    $ sudo openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem
       
    it will ask some questions, please answer them to the best of your knowledge as some of them are required to successfully create the certificate.

  • It's time to change the config settings of our server
    $ cd ~/.jupyter/
    $ vi jupyter_notebook_config.py
       
    You will see a long list of configuration settings. You can go through each one of them and uncomment them as you like, but for me I know what I want, so I'll add the following settings to the top of the file and leave the rest commented as it is.
    c = get_config()
    
    # Kernel config
    c.IPKernelApp.pylab = 'inline'  # if you want plotting support always in your notebook
    
    # Notebook config
    c.NotebookApp.certfile = u'/home/ubuntu/certs/mycert.pem' #location of your certificate file
    c.NotebookApp.ip = '*'
    c.NotebookApp.open_browser = False  #so that the ipython notebook does not opens up a browser by default
    c.NotebookApp.password = u'sha1:68c136a5b064...'  #the encrypted password we generated above
    # It is a good idea to put it on a known, fixed port
    c.NotebookApp.port = 8888
       
  • We are almost done. Now its time to start our Jupyter notebook server. For this, first I'll create a new folder which will store all my notebooks
    $ cd ~
    $ mkdir Notebooks
    $ cd Notebooks
       
    and now I'll start my notebook server
    $ jupyter notebook
       
5. And that is all. Now you can access your Notebook from anywhere through your browser. Just navigate to the DNS name, or Public IP of your instance, along with the port number. (By default, the browser adds "http" to the url. Please remember to update it to "https")
You will be asked by your browser to trust the certificate, as we have signed it on our own, so we know we can trust it. See images for reference below:





6. Login, using the password you specified when you used the iPython Terminal to create an encrypted version of it and you are good to go.


7. One thing to note is that if you close the ssh access to your instance, your notebook server will not work. To keep it working, even when you close the ssh access to the server you can use the following command
$ nohup jupyter notebook
  

This will put your server process as no-hangup and will keep it running even if you close the ssh access to the instance 

8. Later, if you decide you want to stop this process, you have to find the PID of this process. you can do so by going into your notebooks folder and using the command
$ lsof nohup.out
  

which will list the PID of the nohup process which is running(if any). 
Then you can use the kill command to kill this process and stop your ipython notebook server. 

$ kill -9 "PID"
  

replace "PID" with the ID of the process you get from the "lsof" command. 

So, that is all you need to run an iPython Notebook on AWS EC2 instance. Please leave your comments about this blog post and do remember to check out its video.(update in progress to the video. please don't refer it for now)

Until next time... :)

반응형
반응형

어제 python 관련 글 올린 김에 많은 분들이 찾으시는 MySQL 데이터베이스 이용과 관련된 내용을 정리해드립니다.

예전에 제가 프로그래밍으로 밥 벌어먹기 시작했을 때(!)에는 여러가지 DBMS가 많았습니다만, 이제는 LAMP니 뭐니 할 정도로 MySQL이 정석이 되었습니다. 실제로 제가 있었던 회사는 MySQL로 회원 수 천만이 넘는 사이트를 무리 없이 운영하니까요. 옛날에는 MySQL로 그 정도 규모의 상업적인 서비스를 한다 그러면 주위에서는 그거로 돌리면 불안해서 밤에 잠이 오겠냐, M$나 오라클 써라 했었는데요. ㅋㅋ

다 오픈소스의 힘이라고 생각합니다. 그만큼 보완이 많이 되고, 서비스를 하면서 경험이 축적되면서 개선이 되니까요. 개인적으로는 MongoDB로 개발을 하고 있는 부분이 있는데 Mongo가 더 재미있긴 하지만요.

각설하고, python에서 MySQL 이용하는 것과 관련해서 간단하게 정리해 보겠습니다.

많이 이용되고 있는 모듈은 

_mysql, MySQLdb

입니다.

_mysql은 C API를 이용하는 raw-level 모듈이고, MySQLdb는 _mysql 모듈을 포장한 wrapper입니다. 

연결은 

con = MySQLdb.connect('localhost', 'testuser', 'mypassword', 'mydbname')


이런 식으로 해주시면 되고, 연결 후에는 cursor를 생성하여 이용하시면 됩니다.

간단하게 예를 들어보면 아래와 같습니다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/python
# -*- coding: utf-8 -*-
import MySQLdb as mdb
import sys
 
try:
    con = mdb.connect('localhost', 'dbuser', 'mypassword', 'mydb')
     
    cur = con.cursor()
    sql = "SELECT ID FROM articles ORDER BY ID DESC LIMIT 10"
    cur.execute(sql)
    for i in range(cur.rowcount):
        row = cur.fetchone()
        print row[0]
 
except mdb.Error, e:
    print "Error %d: %s" % (e.args[0], e.args[1])
    sys.exit(1)


python의 dictionary type cursor를 이용하시려면 cursor 생성시에 다음과 같이 하시면 됩니다.

1
cur = con.cursor(mdb.cursors.DictCursor)


반응형
반응형
python 에 mysql 을 물리기 위해서 많은 자료를 찾아보았는데
다들 제대로 되어있지 않은 자료가 많아서 여기에 정리를 해둔다.

일단 기본적으로 http://sourceforge.net/projects/mysql-python 에서 압축 파일을 받아서 설치해라고 한다. 하지만 나같은 경우에는 받아서 설치를 할 경우에 mysql_config 파일이 존재하지 않는다고 떠서 꽤나 고생을 먹었다.

mysql_config 파일의 파일명은 my.cnf 이다. 찾을려면
find / -name my.cnf 로 찾아라. mysql_config 백날 찾아도 나오지 않는다.
그러하다 ! 그랬던 것이었다 !!!

근데 이렇게 해도 제대로 설치가 안되서 더 삽질을 가한 결과

apt-cache search python-mysql 을 검색해본 결과

python-mysqldb 라는 것이 보였다. 이것을 설치해본 결과 에러없이 내가 원하던 기능을 작동하는 것을 보았다.... 아 할레루야 한시간동안 삽질했다. 


http://noveljava.tistory.com/49


반응형

+ Recent posts

반응형