연습문제) 하기 사이트를 파싱해서 텍스트를 뽑아보기

조건)

- 사이트주소 : http://new-collar.kr/data_set/sample_text.html 

결과)

더보기

A society is a group of individuals involved in persistent social interaction or a large social group sharing the same spatial or social territory, typically subject to the same political authority and dominant cultural expectations. Societies are characterized by patterns of relationships (social relations) between individuals who share a distinctive culture and institutions; a given society may be described as the sum total of such relationships among its constituent members. In the social sciences, a larger society often exhibits stratification or dominance patterns in subgroups.

코드)

import requests as req
from bs4 import BeautifulSoup as bs

url = 'http://new-collar.kr/data_set/sample_text.html'

page = req.get(url)  

soup = bs(page.text, 'html.parser')  

info = soup.select('p.pb-3.mb-0.small.lh-sm.border-bottom') 

text = info[0].get_text()  # list 형태로 되어있으면 for문을 돌려야할 수도 있다. 확인해보자.
text

 

 

 

문제) 네이버뉴스에서 아래 조건에 맞는 기사리스트를 출력하기

조건)

- 사용모듈: requests, beautifulsoup

- 검색어 : 손흥민

결과)

[('\'인종차별\' 재차 사과한 벤탄쿠르 "손흥민과 해결…오해였다"',
  'https://www.hankyung.com/article/2024062243047'),
 ('\'손흥민 인종차별\' 동료 두번째 사과문 "손과 대화, 함께 해결"',
  'https://www.joongang.co.kr/article/25258211'),
 ('“손흥민만 언급했다” 인종차별 논란 벤탄쿠르 재차 사과',
  'https://www.seoul.co.kr/news/international/europe/2024/06/22/20240622500004?wlog_tag3=naver'),
 ('손흥민은 용서했지만…인종차별 발언한 벤탄쿠르, FA 징계 받을 듯',
  'https://www.news1.kr/articles/5454537'),
 ('“우린 형제”…‘인종차별’ 발언 팀 동료 사과 받아들인 대인배 손흥민',
  'https://www.mk.co.kr/article/11047621'),
 ('토트넘 댓글창에 "No Korean"? 피해자는 손흥민인데‥',
  'https://imnews.imbc.com/news/2024/world/article/6609667_36445.html'),
 ('손흥민 열혈팬 할머니, 손 꼭 잡고 "왜 이리 말랐어"(영상)',
  'https://www.newsis.com/view/NISX20240620_0002779953'),
 ('‘인종차별’ 벤탄쿠르, 손흥민 용서에도 FA 징계 전망',
  'https://biz.chosun.com/sports/2024/06/21/YQYF5HPIHJE5LEYHVRESAEIR5Q/?utm_source=naver&utm_medium=original&utm_campaign=biz'),
 ('손흥민, 前에이전트와 계약서 분쟁…2심서도 사실상 승소',
  'https://www.yna.co.kr/view/AKR20240619120000004?input=1195m'),
 ('수지·송혜교, 친분샷 또 떴다…손흥민 모자 쓰고 미소',
  'http://starin.edaily.co.kr/news/newspath.asp?newsid=01689206638924344')]

 

코드)

import requests
from bs4 import BeautifulSoup as bs

h = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'} 

soups=[]
n_news = 10

for i in range(0,n_news):
    url_i = 'https://s.search.naver.com/p/newssearch/search.naver?cluster_rank=1&de=&ds=&eid=&field=0&force_original=&is_dts=0&is_sug_officeid=0&mynews=0&news_office_checked=&nlu_query=&nqx_theme=%7B%22theme%22%3A%7B%22main%22%3A%7B%22name%22%3A%22corporation_hq%22%7D%2C%22sub%22%3A%5B%7B%22name%22%3A%22corporation_list%22%7D%2C%7B%22name%22%3A%22stock%22%7D%5D%7D%7D&nso=%26nso%3Dso%3Ar%2Cp%3Aall%2Ca%3Aall&nx_and_query=&nx_search_hlquery=&nx_search_query=&nx_sub_query=&office_category=0&office_section_code=0&office_type=0&pd=0&photo=0&query=%ED%8F%AC%EC%8A%A4%EC%BD%94&query_original=&service_area=0&sort=0&spq=0&start='
    num = str(1+i*10)
    url_e = '&where=news_tab_api&nso=so:r,p:all,a:all'
    url = url_i+num+url_e
    page = requests.get(url,headers=h)
    soup = bs(page.text, 'html.parser')
    soups.append(soup)

news = []

for i in range(0,n_news):
    articles = soups[i].select('li') #뉴스 리스트 가져오기 '#'은 id명 '.'은 class명
    for article in articles:
        try:
            a1 = article.select('div > div > div')[4].select('a')[1].text
            a2 = article.select('div > div > div')[4].select('a')[1]['href'].replace('\\"','')
            news.append((a1,a2))
        except:
            continue
news

Afinn 감정 어휘 사전을 이용하여 뉴스의 감정 상태를 분류하기 

 

예제) Scikit-learn에 내장되어 있는 뉴스그룹 데이터를 이용해서 뉴스를 '긍정, 중립, 부정'으로 분류하기

조건)

- 데이터셋 : data_set/newsdata.txt

- 뉴스는 100개만 분석하기

- 사용패키지: matplotlib.pyplot, tqdm (100개만 분석할때)

결과)

코드)

# 데이터셋 불러오기

f = open('data_set/newsdata.txt', 'r', encoding ='utf 8')
raw = f.read()

raw = eval(raw) # 개별 뉴스단위로 구분하기위해 스트링을 ','를 기준으로 리스트로 변환


#감정 사전을 구성하고 감정 스코어를 측정
#afinn 라이브러리는 감정 사전과 더불어 편리하게 감정가를 계산할 수 있는 함수를 제공
#afinn 객체 생성

from afinn import Afinn
afinn = Afinn()

for i in range(10):
    print(afinn.score(raw[i]))
    
# 'fabulous'단어의 감정점수 측정 : 'afinn.score('fabulous') -> 4.0  

# plot 스타일 설정

import matplotlib.pyplot as plt

plt.style.use('default')
plt.rc('font', family='Gulim',size=13)

# 긍정, 중립, 부정 기사수 세기 

from tqdm import tqdm

positive =0
neutral =0
negative =0

for i in tqdm(raw[:100]):
    score = afinn.score(i)
    if score >0:
        positive +=1
    elif score ==0:
        neutral +=1
    elif score <0:
        negative +=1

# 데이터 시각화
plt.figure(figsize=(4.8,3 ))
x_pos = ['긍정','중립','부정']
y_pos = [positive, neutral,negative]
plt.bar(x_pos, y_pos)
plt.show();

데이터 군집화의 순서

1) 데이터 전처리

2) Word Embedding (Word2Vec 연관어 모델 생성)

 2-1) 어휘간 유사도 계산

 2-2) 시각화 

3) Scikit-learn을 이용한 비계층적 군집화

 - 대표적인 비계층적 군집화 알고리즘인 kMeans 를 사용해 실습

 

 

 

예제1) 네이버 영화 리뷰 데이터를 불러와서 불용어 처리 후, 토큰화 하기

조건)

- 사용패키지: Kiwi, Stopwords, tqdm (리뷰별 토큰화할때)

- 데이터 셋 : data_set\\ratings.txt

- 데이터 개수 : 1000개 리뷰만

- 지정 불용어 사전 만들기 : 불용어사전 = ['저','노라노','거','듯','서','위']

- 3개 문장의 토큰만 추출해보기 

결과)

[[], ['디자인', '학생', '외국', '디자이너', '전통', '발전', '문화', '산업', '나라', '시절', '끝', '열정', '전통', '꿈', '감사'], ['폴리스 스토리', '시리즈', '뉴', '하나', '최고']]

코드)

#텍스트 불러오기
f = open('data_set\\ratings.txt', 'r', encoding ='utf 8')
raw = f.readlines()

reviews = []
for i in range(1, len(raw)):
    reviews.append(raw[i].split('\t')[1])

print(reviews[:3])

#형태소 분석

from kiwipiepy import Kiwi
from kiwipiepy.utils import Stopwords
from tqdm import tqdm

kiwi = Kiwi()
stopwords = Stopwords()
stopwords_add = ['저','노라노','거','듯','서','위'] #불용어 사전 추가 

reviews_tokens=[]
for review in tqdm(reviews[:1000]):
    token = kiwi.tokenize(review, stopwords=stopwords)
    review_token = []
    for t in token:
        if (t.tag[0]=='N') & (t.form not in stopwords_add):
            review_token.append(t.form)
    reviews_tokens.append(review_token)

print(reviews_tokens[:100])

 

 

예제2-1) 1)에서 만들어진 토큰화된 리뷰를 활용하여 연관어 검색 모델을 만들고 (Word Embedding : Word2Vec) , 벡터 시각화 모듈은 TSNE를 이용하여  (x,y) tuple 데이터를 데이터프레임 형태로 표현하기(단, x축 기준 오름차순으로)  

조건)

- 토큰화된 리뷰 변수명 : reviews_tokens

- 사용 함수: Word2Vec 모듈의 gensim.models 함수, TSNE 모듈의 sklearn.manifold  함수

- 연관어 검색결과 추출하기 : '영화' 와 관련도가 높은 10개의 단어 추출

결과)

# 영화와 관련도가 높은 10개 단어 
['연출', '굿', '감독', '코믹', '평점', '결말', '노래', '사랑', '주인공', '시리즈']

 

# TSNE를 이용하여 벡터시각화한 결과를 데이터프레임으로 나타내기

 

코드)

#Word2Vec (Word Embedding Model) 생성

from gensim.models import Word2Vec
from sklearn.manifold import TSNE 
 

#토큰화된 리뷰 불러오기
word2vec = Word2Vec(reviews_tokens, min_count=5) #min_count: 단어의 최소 빈도수 (5개 이상 나오는 토큰들만 학습)

# 특정 단어와 유사한 단어 추출
sim = word2vec.wv.most_similar('영화', topn=10)
topn_sim = []
for s in sim:
    topn_sim.append(s[0])
print(topn_sim)


# 토큰화된 단어간 유사도를 배열로 표시  
# 결과값 : similarity[:1] -> array([[ 6.2310090e-04,  7.6949596e-04,  5.3994791e-03, ..., 6.4751715e-03]], dtype=float32)
# 차원확인: similarity.ndim  -> 2
# 행수 확인(행수, 열수)  similarity.shape -> tuple (141, 100)

vocab =word2vec.wv.index_to_key
similarity= word2vec.wv[vocab]


#시각화 모듈 tsne실행, 높은 차원의 복잡한 데이터를 저차원으로 축소하는 패키지
# [6.2, 7.6, 5.3][3.4,2.4,3.4]....[1.4,2.4,3.4] 등의 복잡한유사도를 고려, 저차원으로 바꾸어 표현가능

tsne = TSNE(n_components=2)  # 2차원 (x,y)으로 바꾸기 
transform_similarity = tsne.fit_transform(similarity


#데이터프레임으로 전환
import pandas as pd

df = pd.DataFrame(transform_similarity, index=vocab, columns=['x', 'y'])
df.sort_values(by='x', ascending=True, inplace=True) # x축을 기준으로 오름차순 정렬
df[0:10]

 

예제2-2) 벡터 시각화하기

조건) 

- 사용 모듈 : seaborn, font_manager 및 rc 모듈의 matplotlib함수, matplotlib.pyplot 모듈

결과)

코드)

#시각화

import seaborn as sns
from matplotlib import font_manager as fm
from matplotlib import rc
import matplotlib.pyplot as plt

plt.style.use('default')

map = sns.lmplot(x='x', y ='y', data=df, fit_reg=False)
map.figure.set_size_inches(4.8,3)
plt.show();

 

 

예제3-1) Scikit-learn 과 KMeans를 이용한 비계층적 군집화

조건) KMeans를 이용해 비계층적 군집화한 데이터프레임 만들기

- 비계층적 군집화 조건 - 클러스터 수 : 6개, 각 centeroid 별 계산 수 : 10번 

결과)

코드)

from sklearn.cluster import KMeans

# 군집 6개, n_init sets the number of initializations to perform. 
kmeans = KMeans(n_clusters=6, n_init=10) 

#각 값들이 각각 어떤 클러스트에 속하는지 클러스터 인덱스 번호(0~5로 6개) 출력
predict = kmeans.fit_predict(df)
#len(predict) -> 141개 
# print(predict)

results = df
results['predict'] = predict
results[:10]

 

 

예제3-2) 3-1)의 데이터프레임 결과를 활용하여 가시화하기

조건) 사용패키지 : seaborn 

결과)

코드)

import seaborn as sns

map = sns.lmplot(x='x', y ='y', data = results, fit_reg = False, hue ='predict')
map.figure.set_size_inches(4.8,3)
plt.show();

 

+ Recent posts