본문 바로가기
2. Data Science Basics/Python

간단한 NLP 모델로 WSJ 부정적인 기사만 crawling 하기

by Mojito 2021. 8. 23.

일상에서 우리가 데이터를 가장 쉽게 접할 수 있는 곳은 인터넷일것이다. 인터넷에는 수많은 데이터가 있지만, 각각 하나하나의 데이터는 큰 힘을 갖지 못하고 효율적으로 데이터를 다루기가 어렵다. 많은 데이터를 가공하고 특징을 찾아내기 위해서 가장 흔하게 쓰이는 방법은 크롤링(crawling)일 것이다. 오늘은 간단한 NLP 모델을 만들어 부정적인 단어가 들어가 있는 기사만 crawling 해 볼것이다. 

 

데이터 출저: https://data.world/crowdflower/sentiment-analysis-single-word

 

Sentiment Analysis Single Word - dataset by crowdflower

Sentiment analysis of single words or short phrases

data.world

 

Wall Street Journer (유료 구독 필요) 에서 부정적인 기사만 가져오기위해서 필요한 과정은 아래와 같다.

1. 데이터 전처리

2. 데이터 토크나이즈 (단어추출)

3. 불필요한 단어 지우기, Stemming, Lemmatization

4. Vectorize. Train and Test model

5. Selenium 을 이용해 WSJ headline 기사 crawling 하기

6. 크롤링한 기사 예측하기

 

 

1. 데이터 전처리

pandas 를 통해 데이터를 불러오게 되면 아래와 같은 데이터를 확인 할 수있다.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

data = pd.read_csv("../sentiment-words-DFE-785960.csv")
data.head()

 

data.columns 를 사용하여 확인해보면 7번째, 9번째 column 에 각각 positive / negative 의미를 담고있는 단어들이 정리되어있다. 새로운 데이터프레임을 만들어, 각각의 단어들을 한 column에 나열하고 다음 column 에 score 값을 정해 주겠다. score 은 값이 0이면 긍정적, 1이면 부정적인 단어를 뜻한다.

 

data['positive'] = data[data.columns[7]]
data['negative'] = data[data.columns[9]]

df = pd.DataFrame()
df['text'] = pd.concat([data['positive'], data['negative']], ignore_index=True)
df['score'] = 0
df.iloc[int(7544/2):]['score'] = 1    #처음 반은 positive 나머지 반은 negative words

위와같이 supervised learning 을 위해 label 을 해주었다. 하지만 3772번을 보면 부정적 단어로 쓰인 failure 앞에 # 이 붙어있는것을 확인 할 수 있다. 위와같이 punctuation 이나 stopwords 들은 찾아서 없애주는것이 학습에 더 도움이 된다.

 

import string

def str_only(text):
  str_only = "".join([i for i in text if i not in string.punctuation])
  return str_only
  
df['text'] = df['text'].apply(lambda x: str_only(x))

기호들을 삭제한 단어

 

2. Tokenize

문장안에 있는 단어들을 각각 쪼개주기위해 Tokenize 를 해야한다. Tokenize 하기위해 많이 사용되고있는 nltk 라이브러리를 사용하겠다.

 

from nltk.tokenize import word_tokenize
df['text']= df['text'].apply(lambda x: word_tokenize(x))

3. 불필요한 단어 지우기, Stemming, Lemmatization

import nltk
from nltk.stem.porter import PorterStemmer
from nltk.stem import WordNetLemmatizer

#stopwords 지우기
def remove_stopwords(text):
    output= [i for i in text if i not in nltk.corpus.stopwords.words('english')]
    return output

#Stemming
def stemming(text):    
    stem_text = [PorterStemmer().stem(word) for word in text]
    return stem_text

#Lemmatization
def lemmatizer(text):
    lemm_text = [WordNetLemmatizer().lemmatize(word) for word in text]
    return lemm_text
    
df['text']=df['text'].apply(lambda x: remove_stopwords(x))
df['text']=df['text'].apply(lambda x: stemming(x))
df['text']=df['text'].apply(lambda x: lemmatizer(x))

4. Vectorize. Train and Test model

word = []

for i in df['text'].values:
  word = word + i
  
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

df['text_strings'] = df['text'].apply(lambda x: ' '.join([str(elem) for elem in x]))

vectorizer = CountVectorizer()
X = vectorizer.fit_transform(df['text_strings'])
x_train = X.toarray()
y_train = df['score']

clf = LogisticRegression(random_state=42)
clf.fit(x_train,y_train)
pred = clf.predict(x_train)
print('Accurcay of model is {}'.format(accuracy_score(y_train, pred)))

약 91퍼센트의 정확도가 나왔고 이 모델을 기반으로 WSJ 에서 Selenium 을 이용해 부정적인 기사만 크롤링 해보겠다.

 

5. Selenium 을 이용해 WSJ headline 기사 crawling 하기

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from time import sleep
from bs4 import BeautifulSoup

path = "../chromedriver.exe"
driver = webdriver.Chrome(path)
action = ActionChains(driver)
url = 'https://www.wsj.com'
driver.get(url)
time.sleep(4)

## 로그인하기
driver.find_element_by_link_text('Sign In').click()
time.sleep(10)
email = 아이디
input_id = driver.find_element_by_id('username')
input_id.clear()
input_id.send_keys(email)

password = 비밀번호
input_pw = driver.find_element_by_id('password')
input_pw.clear()
input_pw.send_keys(password)
input_pw.submit()

time.sleep(5)

WSJ 은 유료 구독이 필요한 서비스입니다. 위 모델을 사용해 간단한 구글, cnn 뉴스 기사 크롤링도 가능합니다.

위와 같이 아이디와 비밀번호를 입력해서 접속한 후 헤드라인을 crawling 합니다.

 

a = driver.find_elements_by_class_name("title")

wsj = []
df_test = pd.DataFrame(columns=['text'])
for i in range(len(a)):
    wsj.append(driver.find_elements_by_class_name("title")[i].text)
df_test['text'] = wsj

 

기사 헤드라인을 데이터프레임에 넣어준 후에 위 2-4 번을 다시 반복해줍니다

df_test['text'] = df_test['text'].apply(lambda x: x.lower())
df_test['text'] = df_test['text'].apply(lambda x: word_tokenize(x))
df_test['text'] = df_test['text'].apply(lambda x: remove_stopwords(x))
df_test['text'] = df_test['text'].apply(lambda x: stemming(x))
df_test['text_strings'] = df_test['text'].apply(lambda x: ' '.join([str(elem) for elem in x]))
x_test = vectorizer.transform(df_test['text_strings'])
x_test = x_test.toarray()
x_test = np.array(x_test)
y_test_pred = clf.predict(x_test)

neg_article = np.where(y_test_pred ==1)[0]   # 부정적 기사 배열 찾기
for count, ele in enumerate(neg_article):
    print(count, wsj[ele], '\n')

 

[결론]

간단한 NLP 를 이용해서 부정적인 기사를 crawling 해보았다.

총 7개의 기사중 3가지 기사가 나왔으며 정확하게 찾아낸 것 같다. 모델로 찾아낸 후 직접 기사 본문을 읽어보니 찾아낸 3가지 기사는 부정적인 기사가 맞았다. 더 많은 데이터에 직접 실험을 해봤더니, 긍정적이거나 중립적인 기사보다는 부정적 의미를 가지는 기사를 더 잘 찾아내는것 같다.

 

반응형

댓글