Blog Full Notice
back to main page

6 분 소요

motivation: 네이버 블로그

#Ch02. [텍스트] 뉴스 기사 3줄 요약하기 : 네이버 블로그

Ch02. [텍스트] 뉴스 기사 3줄 요약하기

프로파일 무지성 2023. 2. 25. 15:40
URL 복사 이웃추가

1. 프로젝트 개요

base64, textwrap, re, collections, gensim

open close 사용하는 방법 공부해보자.

2. 바이너리 파일을 문자열로 변경 - base64

다른 ascii 인코딩은 프로토콜, 시스템마다 다르게 해석되어 데이터가 왜곡될 여지가 있어서 적합하지 않다. 그래서 ascii encoding을 하고 base64 인코딩을 하면 XML, HTTP 프로토콜에서도 특수문자 파싱 문제를 해결할 수 있는 수단이라고 한다.

import base64

string= 'any string'

encoded = base64.b64endcode(string)

하면 에러가 난다. 왜냐하면 base64는 byte형식의 객체만 가능하기 때문이다. string은 binary file이 아니다. 그래서 ascii 인코딩을 거치고, base64 인코딩을 거쳐야 한다.

bstring = string.encode('ascii')

encoded = base64.b64encode(bstring)

decoded = base64.decodebytes(encoded)

string = decoded.decode('ascii')

하면 된다.

사실 이미지 인코딩/디코딩의 목적이 훨씬 크다.

path = "./img/anyimage.jpg"

conda activate fastcampus

pip install pillow #이미지 확인하는 패키지

# 이미지 확인

from PIL import Image

img = Image.open(path)

img

그럼 오류없이 사진이 나온다.

with open(path, 'rb') as img:

image = img.read()

image

하면 byte 형태의 바이너리파일이다. 그러면 b'\xff\x44' 이런 식으로 나온다.

#바이트 정보 확인 (pip install bitstring)

from bitstring import BitArray

input_str = '0xff' #(0x는 16진수라는 것.)

c = BitArray(hex=input_str)

c.bin

하면 11111111 이렇게 나온다. bitsring 모듈은 그냥 bit가 어떻게 나오는지 보여주기 위한 모듈이다.

인코딩을 본격적으로 해보자.

# base64 인코딩

with open(path, 'rb') as img:

data = img.read()

encoded = base64.b64encode(data)

print(encoded) # 이 encoded 형태가 이메일로 전송할 수 있는 형태가 된 것이다.

이렇게 인코딩된 형태로 받았다고 하면,

decoded = base64.b64.decodebytes(encoded)

# 이미지 파일로 저장

file = "img/decoded.png"

with open(file, 'wb') as file:

file.write(decoded)

이렇게 하면 열어볼 수 있는 이미지 파일로 저장이 된다.

3-1. 문자열 다루기 - txtwrap

문자열을 보기 좋은 형태로 정렬, 줄바꿈해준다.

  • 원하는 길이에 맞게 줄이기 : textwrap.shorten()

  • 긴 문장 자르기 : textwrap.wrap()

  • 긴 문장 줄바꿈 : textwrap.fill()

text = """ 매우 긴 문자열 """

textwrap.shorten(text, width=200) #처리하고자 하는 문자열, 단어수

SQL과 파이썬은 전 세계에서 가장 인기 있는 프로그래밍 언어이자 미래에도 수요가 높을 것으로 예상된다. 파이썬과 SQL은 활용 범위가 겹치기만, 보통 SQL은 직접 데이터베이스를 다룰 때 이용하며 파이썬은 보다 범용적으로 프로그래밍에 활용한다. SQL은 데이터에 대한 신속한 분석이 필요하거나 기록을 불러오고 결론을 내릴 때 가장 많이 이용되는 [...]'

이렇게 나온다

textwrap.shorten(text, width=100, placeholder='...[이하줄임]')

'SQL과 파이썬은 전 세계에서 가장 인기 있는 프로그래밍 언어이자 미래에도 수요가 높을 것으로 예상된다. 파이썬과 SQL은 활용 범위가 겹치기만, 보통 SQL은...[이하줄임]'

textwrap.wrap(text, width=40)

하면 40글자씩 리스트에 들어간다.

print("\n".join(wrapped_text))

위에 것과 똑같은 함수가 있다.

filled_text = textwrap.fill(text, width=40)

print(filled_text)

결과가 똑같다.

3-2. re

2-3-2. re

  • 정규표현식(regular expressions)은 복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬뿐 아니라 C, 자바, 심지어 문서 작성 프로그램 등 문자열을 처리해야 하는 다양한 곳에서 활용중

  • 특정 문자열 추출, 변환 등에 사용

  • 공식문서

re.findall()

  • 단어 추출

import re

words = re.findall(r'\w+', text)

findall의 첫번째 인자는 단어를 뽑아와라라는 정규표현식이다. 정규표현식에 대한 자세한 정보는 위 공식문서를 보면 된다. 이렇게 하면 words라는 리스트가 만들어지게 된다. 리스트에는 단어가 인덱스마다 하나씩 있다.

다음으로는 전화번호를 추출하자.

이름 나이 전번 이메일. 여기에 있는 것에서, 전화번호만 뽑아오고 싶다. 그럼 전화번호에 해당하는 정규표현식을 적어주면 된다.

# 연락처 목록

contact = '''김미키 21 010-3344-5566 Mike@google.com

김소은 20 010-5032-1111 Soeun@naver.com

유한슬 34 010-2789-1476 Lyu@school.ac.com

박민철 40 010 4040 1313 Zoe@school.ac.com

이민아 23 010-7777-2222 Kate@google.com'''

# 전화번호 추출

regex = r'0\d{1,2}[ -]?\d{3,4}[ -]?\d{3,4}'

phone = re.findall(regex, contact)

print("\n".join(phone))

010-3344-5566

010-5032-1111

010-2789-1476

010 4040 1313

010-7777-2222

전화번호만 뽑아왔다.

# 변환(마스킹)

pat = re.compile(r'0\d{1,2}[ -]?\d{3,4}[ -]?\d{3,4}')

print(pat.sub("***-****-****", contact))

변환도 할 수 있다. 이 같은 양식을 이용해서, 전화번호를 별표 모양으로, 마스킹하고 싶다. 하면, 컨택도, 신상정보 보호도 가능하다.

4. 단어 개수 구하기 - collections.Counter

import collections

import textwrap

import re

words = re.findall(r'\w+', text)

print(words)

counter = collections.Counter(words)

print(counter)

Counter({'SQL은': 8, '데이터': 8, 'SQL을': 6, '데이터를’:6,}

# 덧셈

a = collections.Counter(['a', 'b', 'c', 'b', 'd', 'a'])

b = collections.Counter(['e', 'f', 'f', 'b', 'a', 'd'])

print(a)

Counter({'a': 2, 'b': 2, 'c': 1, 'd': 1})

print(b)

Counter({'f': 2, 'e': 1, 'b': 1, 'a': 1, 'd': 1})

print(a+b)

Counter({'a': 3, 'b': 3, 'd': 2, 'f': 2, 'c': 1, 'e': 1})

더하면, 이렇게 된다. 각각을 더해서 알려준다.

# 뺄셈

print(a-b)

Counter({'a': 1, 'b': 1, 'c': 1})

뺄셈에서는, 차집합 개념이다.

# 교집합

print(a & b)

Counter({'a': 1, 'b': 1, 'd': 1})

# 합집합

print(a | b)

Counter({'a': 2, 'b': 2, 'f': 2, 'c': 1, 'd': 1, 'e': 1})

# 상위 빈도수 단어 추출

print(counter.most_common(5)) #상위 5개 가져오겠다는 것.

[('SQL은', 8), ('데이터', 8), ('SQL을', 6), ('데이터를', 6), ('수', 6)]

5. 문서 요약하기 - gensim

gensim은 자연어 처리, 토픽 모델링에 활발히 사용되느 파이썬 머신러닝 라이브러리다.

Word2Vec 알고리즘을 통한 자연어의 벡터화가 대표적이다. 어떤 자연어를 매개변수의 벡터로 가지고 있는 것.

summarization 내장 모듈로 긴 문장을 요약할 수 있는데, gensim 4.x 버젼부터 요약기능이 지원되지 않아서, gensim 3.7.3 버젼을 사용해야 한다.

conda activate fastcampus

pip install gensim==3.7.3

Word2Vec 예제

여기서 한국어 학습된 모델을 다운받아라. ko라는 폴더, ko.bin, ko.tsv 두개 있으면 된다.

import gensim

# 모델 불러오기

model = gensim.models.Word2Vec.load('ko/ko.bin') #워닝 메세지는 무시

model

<gensim.models.word2vec.Word2Vec at 0x23e2549ab80> #이걸 가지고, 유사한 단어 검색.

# 유사한 단어 검색

model.wv.most_similar("뉴스")

[('리포트', 0.6553797721862793),

('언론사', 0.6399534940719604),

('앵커', 0.637794017791748),

('데일리', 0.6316319704055786),

('일간지', 0.6260204315185547),

('투데이', 0.623687744140625),

('토크쇼', 0.5938538312911987),

('중앙일보', 0.5876286625862122),

('데스크', 0.5873063802719116),

('한겨레', 0.584434986114502)]

# 유사도 검색

model.wv.similarity('자동차', '강아지')

-0.019872822

# 유사도 검색

model.wv.similarity('자동차', '버스')

0.42055488

from gensim.summarization.summarizer import summarize

import pandas as pd

import numpy as np

# 데이터 불러오기

df = pd.read_csv('Book_test.csv') #pandas를 이용해서, read_csv 하면은 csv읽어올 수 있다.

df = df.iloc[0:100] # 이중에서, 너무 많아서, 위에서 100줄만 사용하겠다.

df.reset_index(inplace=True)

판다스는 어떻게 사용하는지는 다루지 않겠다. 외부 라이브러리. 손쉽게 csv 파일을 조작할 수 있기 때문에 활용하고 있다.

df.head() # 상위 5개 출력

# 첫번째 데이터 요약

summarize(df.loc[0,'passage'])

# 첫번째 데이터 요약

summarize(df.loc[0,'passage'], ratio=0.4)

이 요약 길이도 조절할 수 있다. ratio 비율 설정, 0-1사이의 값. 전체 문자열에서 어느정도까지, 40%까지 줄이겠다고 하는 것.

# 전체 데이터 적용

df['extract'] = df.passage.apply(lambda x : summarize(x, ratio=0.4))

6. 텍스트 파일 저장-open, close

파일 쓰기

# 텍스트 파일 쓰기

f = open("새파일.txt", 'w')

for i in range(1, 11):

data = "%d번째 줄입니다.\n" % i

f.write(data)

f.close()

파일 쓰기

# 텍스트 파일 쓰기

f = open("새파일.txt", 'w')

for i in range(1, 11):

data = "%d번째 줄입니다.\n" % i

f.write(data)

f.close()

# 모든 줄 읽기

f = open("새파일.txt", 'r')

lines = f.readlines()

for line in lines:

print(line)

f.close()

# 파이썬 % 서식 기호: (https://blockdmask.tistory.com/428)

%s -> 문자열

%d -> 정수

%f -> 실수

%o -> 8진수

%x -> 16진수

%% -> 문자 % 표현

print("%s, %d" %('any_string', 3))

이렇게 문자열 뒤에 % (튜플)

형태로 사용해주면 된다.

# a 모드 사용 시

f = open("새파일.txt",'a')

for i in range(11, 21):

data = "%d번째 줄입니다.\n" % i

f.write(data)

f.close()

with 문 사용

# close() 사용 불필요

with open("새파일.txt", "w") as f:

for i in range(1, 11):

data = "%d번째 줄입니다.\n" % i

f.write(data)

7. 프로젝트 실습

진행 순서

  1. 뉴스 기사 (base64 파일) 디코딩 - 뉴스 기사를, base64 파일로, 이메일로 받았다는 가정에서 시작한다. 그러면 사람이 알아볼 수 없다.

  2. 문서 요약 / 키워드 추출 - 기사를 요약만 해서는, 중요한 내용을 놓칠 수 있어서, 키워드 추출. 그리고, 이 디코딩된 뉴스기사를 요약해서 리포트를 작성해 볼 꺼고, 그리고 html 파일로 export를 하자.

  3. 요약 리포트 작성

  4. html 파일로 Export

2-7-1. 뉴스 기사 (base64 파일) 디코딩

원본 기사

바이너리 파일 읽기

원본 기사에서, 바이너리 파일 형태로, 강의자료에, news라는 폴더가 있다. 이 폴더 안에, 기사원문 article, image 파일이 있다. 이 파일을 텍스트 파일로 열면, 우리가 알 수 없는 형태로, base64로 인코딩되어 있다.

이 상황에서, python에서 읽어온다.

# 기사 이미지

f = open("news/image", 'rb')

image = f.readlines()

f.close()

print(image) # base64 인코딩 파일

# 기사 본문

f = open("news/article", 'rb')

article = f.readlines()

f.close()

print(article) # base64 인코딩 파일

base64 디코딩

# 기사 이미지 디코딩

import base64

file_base64 = image[0]

path = "news/image.jpg"

with open(path, 'wb') as f:

decoded_data = base64.decodebytes(file_base64)

f.write(decoded_data)

이렇게 되면, jpg로, 사진이 잘 복구가 되었다. 탐색기에서 확인 가능.

# 이미지 확인

from PIL import Image

img = Image.open(path)

img

이미지 확인은, 외부 라이브러리에서 확인을 할 수 있다. 에러가 나온다는 것은, 라이브러리가 설치가 안되어있다는 것.

# 기사 본문 디코딩

file_base64 = article[0]

decoded_data = base64.decodebytes(file_base64)

decoded_data

base64로 디코딩하면, byte 형식의 문자열로 디코딩이 된다. 이거를 한번 더 utf-8 , ascii, 등의 형식으로 디코딩을 해줘야 한다. 문자열은 이런 형식으로 디코딩을 한번 더 해야 한다. 우리는 utf-8로 인코딩이 되었다는 가정을 가지고 있다. 그래서 디코딩도 한번 더 해준다.

article = decoded_data.decode('utf-8')

print(article)

디코딩이 제대로 되었다.

2-7-2. 문서 요약 / 키워드 추출

from gensim.summarization.summarizer import summarize

from gensim.summarization.textcleaner import split_sentences

지난 시간에 배웠던 summarizer를 사용하고, 그 요약 문서에서, 텍스트를 clean하게 만들어주는 textcleaner를 같이 보도록 하자.

gensim은 외부 라이브러리인데, 지난번에 설치를 같이 했다.

summarize 함수

# 단어수 기반 요약 (word_count)

print(summarize(article, word_count=50))

50자로 요약해준 것.

# 비율 기반 요약 (ratio)

print(summarize(article, ratio=0.1))

전체 내용에서 10프로로 요약한 것.

# 요약 텍스트 저장

article_summarized = summarize(article, ratio=0.1)

키워드 추출

키워드 추출하면, collection를 하면, 빈번하게 등장한 단어를 구할 수 있다.

import collections

import textwrap

import re

그 전에, 기사를 정렬하자.

# 줄바꿈 정렬

article_align = textwrap.fill(article, width=50)

print(article_align)

# 단어 추출 #정규표현식을 이용해서 단어만 추출하자.

words = re.findall(r'\w+', article_align)

print(words)

# 빈도수 산출

counter = collections.Counter(words)

print(counter)

# 키워드 추출

print(counter.most_common(5))

[('있다', 8), ('달', 7), ('발사', 6), ('아르테미스', 6), ('29일', 5)]

keywords = counter.most_common(5)[1:] #있다 빼고 사용하자.

2-7-2. 요약 리포트 작성

from IPython.display import Image

Image(filename=path, width=300)

이 이미지가 있고,

print(article_summarized)

keys = ['# ' + elem[0] for elem in keywords]

keys = ' '.join(keys)

print(keys)

# 달 # 발사 # 아르테미스 # 29일

2-7-4. html 파일로 저장

# html 파일 저장

htmlfile = open("news/summary.html", "w")

htmlfile.write("<html>\n")

htmlfile.write ("<h1>"+ '카운트다운 들어간 아르테미스 계획…"달의 여신"은 미소지을까' + "</h2>\n")

htmlfile.write ("<img src='image.jpg'/>\n")

htmlfile.write ("<h2>"+ article_summarized + "</h2>\n")

htmlfile.write ("<h2 style='background-color:powderblue;''>"+ keys + "</h2>\n")

htmlfile.write("</html>\n")

htmlfile.close()

이렇게 실행을 하면, news 폴더 안에, summary.html

이 만들어졌다. 그럼 키워드가 잘 나타난다. 요약 리포트까지 만들었다.

같이 들은 강의자료에, py 파일로도, 프로그램을 실행할 수 있는 형태로 되어 있다. 파이참에서, run 왼쪽에 있는 거 클릭, 구성편집 가서, 프로젝트 설정하고, 실행하면 html 파일이 나오게 된다.

댓글남기기