본문 바로가기

문돌이 존버/코딩연습

웹스크래핑 + Tokenization + Lemmatization feat.파이썬

반응형

안녕하세요~ 이번엔 제가 지난 학기 학교 과제로 수행했던 CNN 기사를 웹스크래핑했던 코드를 공유하고자 합니다. 다음이 전반적인 과제 내용입니다. 

import requests
from bs4 import BeautifulSoup
import pandas as pd
import os
import natsort
import re

# 폴더 안에 있는 cnn html파일 읽어들이기
# html 파일 구조가 동일하지 않고 총 2가지로 분류되기 때문에 try와 except 활용
path = './cnn/'
file_list = os.listdir(path) 
file_list = natsort.natsorted(file_list,reverse=False) 
df = pd.DataFrame(columns=['filename', 'date', 'headline', 'text'])

for i in file_list:
    filename = i[:5]+i[5:].split('.')[0].zfill(3)
    url = path + i
    try:
        with open(url, 'rt', encoding='UTF-8') as fp:
            soup = BeautifulSoup(fp, 'html.parser')
            title = soup.find(class_ = 'pg-headline').get_text() #headline
            
            inputstr = ""
            for i in soup.findAll(class_="zn-body__paragraph"):
                if len(i['class']) == 2:
                    if i['class'][1] !='zn-body__footer':
                        inputstr += (i.get_text() + " ")
                else:
                    inputstr += (i.get_text() + " ")
            text = inputstr    
            date_or = soup.find(class_ = 'update-time').get_text()
            date = str(date_or)[24:].strip()

    except Exception:
        with open(url, 'rt', encoding='UTF-8') as fp:
            soup = BeautifulSoup(fp, 'html.parser')
            title = soup.find(class_ = 'article-title speakable').get_text() #headline
             
            date_to = soup.find(class_ = 'cnnDateStamp').get_text()
            pattern = ':'
            date_ch = re.split(pattern, date_to) 
            date = date_ch[0].strip()
            bodies = soup.findAll('h2', 'speakable')
            bodies += soup.select('#storytext > p')

            inputstr = ""
            for body in bodies:
                if body.get_text().find('--') != 1:
                    inputstr += (body.get_text() + " ")
                text = inputstr   
    
    df.loc[len(df)] = [filename, date, title, text]

# 순서대로 정리 및 csv 파일로 변환
df = df.sort_values('filename').reset_index(drop=True)
df.to_csv('201419191_cnn.csv', sep=',', index=True, encoding='UTF-8')
df

# 데이터 정제(소문자, 각종 기호 및 의미 없는 반복 구조 제거, 문장 변환)
import re
from nltk.tokenize import sent_tokenize
def data_cleaning():
    df = pd.read_csv('201419191_cnn.csv')
    df['text'] = df.text.apply(lambda x: x.split('(CNN)')[1] if len(x.split('(CNN)'))==2 else x.split('(CNN)')[0])
    df['text'] = df['text'].str.lower()
    df['text'] = df['text'].apply(lambda x: re.sub('[!|"|#|$|%|\'|&|(|)|*|+|,|\-|/|:|;|<|=|>|?|@|\[|\]|^|_|`|{|\||}|~]', '', x))
    df['text'] = df.text.apply(lambda x: sent_tokenize(x))
    df.to_csv('201419191_cnn_cleaned.csv')
    return df

df = data_cleaning()
df

# 문장 수 출력
from ast import literal_eval
def sent_count():
    df = pd.read_csv('201419191_cnn_cleaned.csv')
    df.text = df.text.apply(literal_eval)
    df['sent_count'] = df.text.apply(lambda x: len(x))
    return df
    
df = sent_count()
df

# 단어 토크나이즈
import nltk
from nltk import RegexpTokenizer
def content_token_count():
    tokenizer = RegexpTokenizer("\s+", gaps=True)
    df['content'] = df.text.apply(lambda x: " ".join(x))
    df['word'] = df.content.apply(lambda x: tokenizer.tokenize(x))
    df['word_count'] = df['word'].apply(lambda x: len(x))
    return df
    
df = content_token_count()
df

# lemmatization 실행
from nltk import pos_tag
from nltk.stem import WordNetLemmatizer
import numpy as np
def lemma_type_count(x):
    keys, values = np.unique(x, return_counts=True)
    return list(zip(keys, values))

def lemmatize():
    wnl = WordNetLemmatizer()
    df['lemma'] = df.word.apply(lambda x: pos_tag([wnl.lemmatize(i) for i in x])) # pos_tag가 아니라 lemma 자체 전체 갯수를 구해야함..
    df['lemma(type)'] = df.lemma.apply(lambda x: [t[1] for t in x])
    df['lemma_count'] = df['lemma(type)'].apply(lemma_type_count)
    return df

df = lemmatize()
df

위의 출력값이 최종 lemmatization한 결과입니다! 약 반년이 지난 지금 다시 정리해보려니 또 어렵네요... 역시 코드는 계속 공부해야 하나 봅니다 ㅠㅠ

728x90
반응형