Python ile Veri Kazıma(Web Scraping) Çalışması: Dünya Halleri Verisinin Kazınıp, Analiz Edilmesi ve Modellenmesi

Yunus Emre Gündoğmuş
KaVe

--

Merhaba Sevgili okurlar, uzun bir aradan sonra sizlerle beraber olmak mutluluk verici. Umarım iyisinizdir. Bugün sizlere dunyahalleri.com’dan veri kazıma yöntemleri ile kazıyıp farklı kütüphaneler ile analiz ettiğim çalışmadan bahsedeceğim. Yazımızın içeriği şu şekilde olacak;

  1. Veri Kazıma Nedir?
  • Neden Veri Kazırız?
  • Veri Kazıma Nasıl Çalışır?
  • Veri Kazıma Yöntemleri
  • Python ile Veri Kazıma

2. Python ile DunyaHalleri.com’un verisinin kazınması

  • Veri Kazıma Stratejisi
  • BeautifulSoup ile Kategori Altındaki Haber Linklerinin Toplanması
  • Haber Metninin Seçilmesi
  • Haber Özetinin Seçilmesi
  • Haber Başlığının ve Zamanının Seçilmesi

3. Verinin Analizi ve İçeriye Alınması

  • Verinin Görselleştirilmesi ve Analizi
  • Haber Metinlerinin Kategorilerine Göre Sınıflandırılması
  • Word2Vec ile Modelleme
  • TSNE ile Word2Vec Görselleştirilmesi
  • TextScatter ile Kategorilerin Derin Analizi

4. Sonuç

5. Kodlar ve Kaynakça

Öncelikle bu çalışma 5.sini düzenlediğimiz “Araştırma ve Geliştirme Odaklı Yapay Zeka Eğitimi” için bir başvuru sorusuydu. Veri kazıma izni için Serdar Kuzuloğlu’na ayrıca teşekkürler.

1. Veri Kazıma Nedir?

Veri kazıma diğer bir adıyla Data Scraping, Web sitelerinden veri çıkarma işlemidir.

Neden Veri Kazırız?

Web üzerinde birçok veri bulunuyor. Veri kazıma’da bu veri toplama işlemini otomatikleştirir. Dağıtık halde olan verileri daha düzgün şekilde sunar. İnternet’ten kazınan verilerle birçok çalışma yapılabilir. Bunların en yaygınlarından biri fiyat karşılaştırmasıdır. Elinizdeki ürünün diğer mağazalardaki satış fiyatına bakarak, fiyat stratejinizi belirleyebilirsiniz.

Veri Kazıma Nasıl Çalışır?

Veri Kazımanın Çalışma Şeması

Web scraping işlemlerini kavramak için, öncelikle web sayfalarının metin tabanlı biçimlendirme dilleri ile oluşturulduğunu anlamak önemlidir. (en yaygın olanı HTML’dir.)

Bir biçimlendirme dili, bir web sitesinin içeriğinin yapısını tanımlar. Evrensel bileşenlerin ve işaretleme dillerinin etiketleri bulunduğundan, web kesicilerinin ihtiyaç duyduğu bilgileri almaları çok daha kolay olur.

HTML ile ayrıştırma web scraping yalnızca yarısıdır. Bundan sonra scraper daha sonra gerekli verileri alır ve saklar.[1]

Veri Kazıma Yöntemleri

Veri kazımanın birçok yöntemi vardır. Bunlardan birincisi, gerçek kullanıcı gibi davranan bir bot oluşturup, web sitesinden verinin kazınması, diğeri ise web sitesinin html içeriğinin indirilip, istenilen kısımların ayrıştırılması … bugünkü çalışmada biz html içeriği indirip ayrıştırmayı tercih edeceğiz. Ama kullanıcı girişi, butonlara tıklama gibi kullanıcı işlemleri gerektiren çalışmalarda, bir bot oluşturup işlemleri ona yaptırabiliriz.

Python ile Veri Kazıma

Bugünkü çalışmada Python’un BeautifulSoup adlı kütüphanesinden yararlanacağız. BeautifulSoup, HTML veya XML dosyalarını işlemek için oluşturulmuş güçlü ve hızlı bir kütüphanedir.[2] Bu kütüphane ile öncelikle sayfaların html kodlarını indirip, bu kodları parçalayarak verileri kazıyacağız. Daha önce BeautifulSoup ile hiç veri kazıma çalışması yapmadıysanız lütfen Bu makaleye göz atın.

2. Python ile DunyaHalleri.com’un Verisinin Kazınması

Sitedeki bütün veriye ihtiyacımız var. Öncelikle bir veri kazıma stratejisi belirlemeliyiz.

Veri Kazıma Stratejisi

Veri kazıma stratejisinin bu tip çalışmalar için çok önemli olduğunu düşünüyorum, bir sitedeki bütün veriyi otomatik şekilde kazımak istiyorsak bir plana ihtiyacımız var. Burada kısaca veriyi hangi yollarla nasıl bir Pipeline’da kazıyacağımızı belirlemeliyiz.

Bu çalışmada biz öncelikle bütün kategorilerdeki linkleri toplayacağız. Ardından tek tek bu linklerin içerisine girip, istediğimiz veriyi kazıyacağız. Stratejimizin şemasını da aşağıdaki görselde görebilirsiniz.

Veri Kazıma Stratejisi

BeautifulSoup ile Kategori Altındaki Haber Linklerinin Toplanması

#Kütüphanelerimizi içeriye alalım.#Veri İşlemleri
import pandas as pd
import numpy as np
#Veri Kazıma
from bs4 import BeautifulSoup as bs
import requests
import datetime
#Konsol Güzelleştirme
from progressbar import *
from IPython.display import clear_output

Dunyahalleri.com’un 6 farklı ana kategorisi var, öncelikle bu kategorilere gireceğiz. Ardından sayfa sayfa bütün makalelerin linklerini toplayacağız.

#Kategoriler
sections = ["https://www.dunyahalleri.com/category/haftanin-ozeti/page/",
"https://www.dunyahalleri.com/category/genel-gundem/page/",
"https://www.dunyahalleri.com/category/teknoloji-bilim/page/",
"https://www.dunyahalleri.com/category/internet-girisimler/page/",
"https://www.dunyahalleri.com/category/tasarim-inovasyon/page/",
"https://www.dunyahalleri.com/category/kultur-sanat/page/"]

Şimdi ise for döngüleri ile öncelikle kategorileri, ardından kategorinin içerisindeki sayfaları, ve sayfanın içerisindeki yazı linklerini alacağız. Şemamız şu şekilde olacak;

Not: Sağ tarafta gördüğünüz kısım Google Chrome’da f12 tuşuna basarak açılabiliyor. Bu kısımda Html elementlerinin isimlerini, Xpathlerini görebiliyorsunuz.

Link Toplama Şeması
urls = []
#Öncelikle bir Kategori seçiyoruz.
for section in sections:
#Kategorinin içerisinde sırayla 100 sayfa gezineceğiz.
for i in range(1,100):
try:
#Öncelikle URL'imizi oluşturuyoruz. Örneğin;
#https://www.dunyahalleri.com/category/kultur-sanat/page/25
newurl = section+str(i)
print(newurl)

#Url'nin içerisindeki bütün html dosyasını indiriyoruz.
html = requests.get(newurl).text
soup = bs(html, "lxml")

#Yukarıdaki şemadada görüldüğü gibi bütün makaleler bu element'in içerisinde yer alıyor.
#Bizde bütün makaleleri buradan tags adında bir değişkene topluyoruz.
tags = soup.findAll("div", class_="row row-eq-height herald-posts")[0]

#Sırayla bütün makalelere girip, href'in içerisindeki linki urls adlı listemize append ediyoruz.
for a in tags.find_all('a', href=True):
urls.append((section.split("/")[4],a['href']))
except IndexError:
break

Ardından topladığım bütün bu linkleri Pandas kütüphanesi yardımıyla bir “DataFrame” objesine dönüştürüp, yine “DataFrame” objesinin bir özelliği olan csv çıktı alma özelliğini kullanarak “urldata.csv” olarak çıktı alıyorum.

urldata = pd.DataFrame(urls)
urldata.columns = ["Kategori","Link"]
urldata.head()
Urldata DataFrame’i
#Bazı linkler çoklamışlar, onlardan kurtulmak için drop_duplicates() #Fonksiyonunu kullanacağım.
urldata = urldata.drop_duplicates()
urldata.to_csv('urldata.csv')

Evet şimdi 2. kısım olan haber içi kazıma kısmına geçiyoruz.

Bu kısımda öncelikle haber metinlerini toplayacağız. Alttaki gif’te element isimlerine nasıl erişebileceğinizi görebilirsiniz, sağdaki panelin açılması için F12'ye basmanız yeterli.

Haber Metninin Seçilmesi

Element ismini belirledikten sonra şu şekilde bir kod ile haber metnini kazıyabilir hale geliyoruz.

#Url içerisindeki html'i indiriyoruz.
html = requests.get(url).text
soup = bs(html, "lxml")
#Belirlediğimiz element'in altındaki bütün p'leri seçiyoruz.
body_text = soup.findAll("div", class_="tldr-post-content")[0].findAll('p')
#Body_text adındaki metni tek bir string üzerinde topluyoruz.
body_text_big = ""
for i in body_text:
body_text_big = body_text_big +i.text

Haber Özetinin Seçilmesi

Haber özetini bulmak için sayfada F12'deki seçme tuşunu seçtikten sonra gezmeniz yeterli, zaten tldr-post-summary adlı elementin aşağısında, hem haber bilgileri hemde özet bulunuyor.

Haber özetinin bulunduğu element

Şu şekilde bir kod ile haber özetini kazıyabiliriz.

#Url içerisindeki html'i indiriyoruz.
html = requests.get(url).text
soup = bs(html, "lxml")
#Özetin bulunduğu element'in metin kısmını alıyoruz.
summarized = soup.find("div", class_="tldr-sumamry").text

Haber Başlığının ve Zamanının Seçilmesi

Aynı şekilde haber başlığını ve zamanı bulmak için de sayfada F12'deki seçme tuşunu seçtikten sonra gezmeniz yeterli.

Kodu da şu şekilde olacak;

#Url içerisindeki html'i indiriyoruz.
html = requests.get(url).text
soup = bs(html, "lxml")
#Başlığı ve zamanı'da element isimlerinden bu şekilde seçip, metinlerini alıyoruz.
header = soup.find("h1", class_="entry-title h1").text
timestamp = soup.find("span", class_="updated").text

Bütün bu parça parça çekim işlemlerini bitirdikten sonra, kodu tamamlayıp bir fonksiyon haline getirdiğimizde ise şunu elde ediyoruz.

def GetData(url):
try:
#Url içerisindeki html'i indiriyoruz.
html = requests.get(url).text
soup = bs(html, "lxml")

#Belirlediğimiz element'in altındaki bütün p'leri seçiyoruz.
body_text = soup.findAll("div", class_="tldr-post-content")[0].findAll('p')

#Body_text adındaki metni tek bir string üzerinde topluyoruz.
body_text_big = ""
for i in body_text:
body_text_big = body_text_big +i.text

#Başlığı ve zamanı'da element isimlerinden bu şekilde seçip, metinlerini alıyoruz.
header = soup.find("h1", class_="entry-title h1").text
timestamp = soup.find("span", class_="updated").text

#Özetin bulunduğu element'in metin kısmını alıyoruz.
summarized = soup.find("div", class_="tldr-sumamry").text
return ((url,header,body_text_big,summarized,timestamp))

#Link boş ise verilen hata üzerine Boş Data mesajını dönüyor.
except IndexError:
return ("Boş Data")

#Eğer link haftalık özet ise özet kısmı olmadığından oraya haftalık özet yazıp, sonuçlar o şekilde dönüyor.
except AttributeError:
return ((url,header,body_text_big,"Haftalık Özet",timestamp))

Ardından bütün veriyi kazıdıktan sonra bir Dataframe’e kaydedip, analiz için hazır hale getiriyoruz.

#Veri kazıma, Urldatasındaki linkleri tek tek fonksiyon ile çalıştırıp, sonuçları bigdata listesine kaydediyor.
bigdata = []
k = 0
for i in urldata.Link:
clear_output(wait=True)
print(k)
bigdata.append(GetData(i))
k = k + 1
#Verileri DataFrame olarak kaydetme
bigdatax = pd.DataFrame(bigdata)
bigdatax.drop([5,6,7],axis=1,inplace=True)
bigdatax.drop(bigdatax[bigdatax[0]=="B"].index,axis=0,inplace=True)
bigdatax.columns = ["Link","Başlık","Body_text","Summarized_Text","TimeStamp"]
bigdatax = bigdatax.loc[bigdatax.Link.drop_duplicates().index]
bigdatax.index = range(0,len(bigdatax))
bigdatax.head()

Bütün işlemlerin ardından şöyle bir çıktı elde edeceğiz.

3. Verinin Analizi ve Modellenmesi

Kütüphanelerin İçeri Alınması

Farklı birçok çalışma yapacağımız için bir sürü kütüphane kullanacağız.

#Gerekli Kütüphaneler
import pandas as pd
import numpy as np
import re, io
import os, pkgutil, json, urllib
from urllib.request import urlopen
import warnings
warnings.filterwarnings(action = 'ignore')
import multiprocessing
#Görselleştirme
from IPython.display import display,HTML,IFrame
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import matplotlib.cm as cm
import base64
%matplotlib inline
import plotly.express as px
sns.set()
#NLP ve Modelleme
from collections import Counter
import gensim
from gensim.models import Word2Vec
from nltk.corpus import stopwords
from nltk.tokenize import sent_tokenize, word_tokenize
from scattertext import CorpusFromPandas, produce_scattertext_explorer
import scattertext as st
from textblob import TextBlob
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
from pprint import pprint
from scipy.stats import rankdata, hmean, norm
import spacy

Verinin Hazırlanması

Elimizde Scraper’dan gelen 2 farklı dosya bulunuyor. Birisi Kategori ve Linkleri barındırıyor. Diğeri ise Haber linki, metni, özeti, başlığı ve zamanını barındırıyor. Bu verileri link üzerinden birleştirerek haber metnini kategorisi ile eşleştireceğiz.

# Haber içeriği excel'inin içeri alınması
data = pd.read_excel('DunyaHalleri.xlsx')
data.head()
# Url verisinin içeri alınması
urldata = pd.read_csv('urldata.csv')
urldata.drop('Unnamed: 0',axis=1, inplace=True)
urldata.head()
#Veriler link üzerinden eşleşecekler, Eşleşmeyen linkler için kategori boş olarak gelecek.
data = pd.merge(data, urldata, on='Link', how='left')
data.head()

Verinin işlenmesi

Web sitelerinden veri kazıma yöntemleri ile veri elde ettiğimizde, verimiz de harf olmayan(link,hashtag vs.) birçok karakter olabiliyor. Bunlardan kurtulmamız gerekiyor.

def preprocess(ReviewText):
#Verideki <br> taglarını kaldır.
ReviewText = ReviewText.str.replace("(<br/>)", "")
ReviewText = ReviewText.str.replace('(<a).*(>).*(</a>)', '')
ReviewText = ReviewText.str.replace('(&amp)', '')
ReviewText = ReviewText.str.replace('(&gt)', '')
ReviewText = ReviewText.str.replace('(&lt)', '')
ReviewText = ReviewText.str.replace('(\xa0)', ' ')
#Verideki Linkleri Kaldır.
ReviewText = ReviewText.str.replace(r'http[s]?://(?:[a-z]|[0-9]|[$-_@.&amp;+]|[!*\(\),]|(?:%[0-9a-f][0-9a-f]))+', ' ')
return ReviewText
data['Body_text'] = preprocess(data['Body_text'])

Verinin Görselleştirilmesi ve Analizi

Verimizi de temizlediğimize göre şimdi bazı analizler yapabiliriz, ilk olarak haber bazlı olarak kelime sayısı, harf sayısı, yoğunluk gibi birkaç değişkene bakacağız. Tabii bunları öncelikle oluşturmamız gerekiyor.

data['yogunlasma'] = data['Body_text'].map(lambda text: TextBlob(text).sentiment.polarity)
data['harf_uzunlugu'] = data['Body_text'].astype(str).apply(len)
data['kelime_sayisi'] = data['Body_text'].apply(lambda x: len(str(x).split()))

Bunları da oluşturduktan sonra Plotly kütüphanesini kullanarak, çeşitli görselleştirmeler yapacağız. Öncelikle verideki kelime sayısı dağılımına bakalım.

fig = px.histogram(data, x="kelime_sayisi", nbins=200, title='Kelime Sayısı')
fig.show()
Kelime Sayısı Dağılımı

Ardından, harf sayısı ve yoğunlaşma dağılımlarına bakalım.

fig = px.histogram(data, x="harf_uzunlugu", nbins=200, title='Metin Uzunluğu')
fig.show()
Harf Sayısı Dağılımı
fig = px.histogram(data, x="yogunlasma", nbins=20, title='Metin Yoğunlaşması')
fig.show()
Metin Yoğunlaşması Dağılımı

Evet şimdi kategori bazlı olarak kutu grafiği yardımıyla, Metin Yoğunlaşması, Harf Sayıları ve Kelime Sayılarına göz atalım.

fig = px.box(data, x="Kategori",y='yogunlasma', color="Kategori",
title="Kategori bazlı Yogunlasma",
hover_data=["yogunlasma"])
fig.show()
Kategori bazlı yoğunlaşma
fig = px.box(data, x="Kategori",y='kelime_sayisi', color="Kategori",
title="Kategori bazlı kelime sayısı",
hover_data=["yogunlasma"])
fig.show()
Kategori bazlı kelime sayısı
fig = px.box(data, x="Kategori",y='harf_uzunlugu', color="Kategori",
title="Kategori bazlı Harf Sayısı",
hover_data=["yogunlasma"])
fig.show()
Kategori bazlı harf sayısı

Son olarak kelimelerin frekanslarına göz atalım. İlk analizimizi Stopwordsleri kaldırmadan yapacağız.

def get_top_n_words(corpus, n=None):
vec = CountVectorizer().fit(corpus)
bag_of_words = vec.transform(corpus)
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]
words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)
return words_freq[:n]
common_words = get_top_n_words(data['Body_text'], 25)
df1 = pd.DataFrame(common_words, columns = ['kelime' , 'geçiş frekansı'])
fig = px.bar(df1, x='kelime', y='geçiş frekansı',
hover_data=['kelime', 'geçiş frekansı'], color='geçiş frekansı',
title='Stopwordsleri kaldırmadan en çok geçen 25 kelime',
height=400)
fig.show()
Stopwordleri kaldırmadan en çok geçen 25 kelime

Gördüğünüz gibi listede bir sürü etkisiz kelime(Stopwords) bulunuyor. Şimdi bunları kaldırarak grafiği çizdirelim.

#Buradaki turkce-stop-words.txt dosyasına   https://github.com/ncarkaci/tr-preprocessing buradan erişebilirsiniz. stop = stopwords.words('Turkish')
with open('turkce-stop-words.txt', encoding='utf-8') as file:
stw = file.read()
stw = stw.split()
stw = [s.lower() for s in stw]
stop += stw
def get_top_n_words(corpus, n=None):
vec = CountVectorizer(stop_words = stop).fit(corpus)
bag_of_words = vec.transform(corpus)
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]
words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)
return words_freq[:n]
common_words = get_top_n_words(data['Body_text'], 25)
df2 = pd.DataFrame(common_words, columns = ['kelime' , 'geçiş frekansı'])
fig = px.bar(df2, x='kelime', y='geçiş frekansı',
hover_data=['kelime', 'geçiş frekansı'], color='geçiş frekansı',
title='Stopwordsleri kaldırdıktan sonra en çok geçen 25 kelime',
height=400)
fig.show()
Stopwordleri kaldırınca en çok geçen 25 kelime

Evet şimdi ise en çok yan yana gelmiş ikili kelimelere bakalım.

def get_top_n_bigram(corpus, n=None):
vec = CountVectorizer(ngram_range=(2, 2), stop_words=stop).fit(corpus)
bag_of_words = vec.transform(corpus)
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]
words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)
return words_freq[:n]
common_words = get_top_n_bigram(data['Body_text'], 25)
df3 = pd.DataFrame(common_words, columns = ['kelime' , 'geçiş frekansı'])
fig = px.bar(df3, x='kelime', y='geçiş frekansı',
hover_data=['kelime', 'geçiş frekansı'], color='geçiş frekansı',
title='Stopwordsleri kaldırdıktan sonra en çok yan yana gelen ikili kelimeler',
height=400)
fig.show()

Bu kısımda direkt stopwordsleri kaldırılmış hali üzerinden gittim.

Haber Metinlerinin Kategorilerine Göre Sınıflandırılması

Şimdi ise biraz modelleme yapalım. Elimizdekilere baktığımızda, bizde haber metinleri ve kategorileri bulunuyor. Burada rahatlıkla, haber metinlerini kategorize edecek bir model kurabiliriz. Şimdi bu modellemeyi nasıl yapacağımıza göz atalım.

#Öncelikle text_data adında yeni bir dataframe oluşturup, bunda sadece haber metinleri ve kategorilerini tutuyorum.
text_data = data[['Body_text','Kategori']]
text_data.head()

Burada önemli bir nokta var. Kategoriye göre haber sayıları eşit olmadığı için veri setimiz dengesiz halde oluyor. O yüzden bunu dengeli hale getireceğiz.

ax = sns.barplot(data.Kategori.value_counts().index, y=data.Kategori.value_counts(), data=data, palette="rainbow")
print("Data'nın Bölünmeden Önceki Boyutu", len(text_data))
data_tekno = text_data[text_data.Kategori == 'teknoloji-bilim']
data_internet = text_data[text_data.Kategori == 'internet-girisimler']
data_inovasyon = text_data[text_data.Kategori == 'tasarim-inovasyon']
data_genel_gundem = text_data[text_data.Kategori == 'genel-gundem']
data_kultur_sanat = text_data[text_data.Kategori == 'kultur-sanat']
data_hafta = text_data[text_data.Kategori == 'haftanin-ozeti']
text_data = pd.concat([data_tekno.sample(len(data_kultur_sanat), random_state=39), data_internet.sample(len(data_kultur_sanat), random_state=39), data_inovasyon.sample(len(data_kultur_sanat), random_state=39), data_genel_gundem.sample(len(data_kultur_sanat), random_state=39) ,data_kultur_sanat,data_hafta])
print("Data'nın Yeni Boyutu: ", len(text_data))

Verisetimizi dengeli hale getirdikten sonra, haber metni değişkenimiz temiz olduğu için ekstra bir işlem yapmayacağım. Burada direkt TF-IDF yöntemini kullanarak metin verisetimizi modellemeye hazır hale getireceğim. TF-IDF’in ne olduğu hakkında bir fikriniz yok ise şu makaleye göz atabilirsiniz.

#Öncelikle TF-IDF algoritmasını fit etmek için bütün metinleri kapsayan bir corpus oluşturuyoruz. 
corpus = []
for i in range(len(data)):
corpus.append(text_data.iloc[i,0])

Corpus’u oluşturduktan sonra TF-IDF’i fit edip ardından bütün verimi de fit ettiğim TF-IDF modeline göre değiştirip verimi hazır hale getireceğim.

# Corpus ile TF-IDF modelini fit ediyoruz.
vectorizer = TfidfVectorizer()
vectorizer.fit(corpus)

Evet şimdi bütün verisetimi modele göre güncelleyip, train ve test olmak üzere 2'ye ayıracağım.

#Burada transform ediyorum. 
X = vectorizer.transform(corpus).toarray()
y = text_data.Kategori
#Burada %25-%75 olmak üzere 2'ye ayırıyorum. Stratify=y'de train ve testteki y oranının aynı olmasını sağlıyor.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, random_state=39, stratify=y)

Evet şimdide RandomForest algoritmasıyla çoklu sınıflandırma problemine çözüm üreteceğiz.

rf = RandomForestClassifier(class_weight='balanced_subsample')
rf.fit(X_train,y_train)
y_pred_rf = rf.predict(X_test)
print(classification_report(y_test, y_pred_rf))
print(accuracy_score(y_test,y_pred_rf))
cm = confusion_matrix(y_test, y_pred_rf)
print(cm)

Çıktı olarak şöyle bir çıktıyla karşılaşıyoruz.

Burada baktığımızda ortalama %50 ile tahminleme yapılabiliyor. Ve sanırım haftanin-ozeti’ni diğerlerinden ayıran belirleyici bir özellik var, yazınının ilerleyen kısımlarında bu özelliklere derinden bakacağız. RandomForest algoritmasını importance score adında, hangi değişkenin modellemede önemli olduğunu belirten bir çıktı veriyor. Şimdi bizde bunu kullanıp, hangi kelimelerin modellemede bizim için önemli olduğuna bakalım.

questions = pd.DataFrame({'features': vectorizer.get_feature_names(),'importance': rf.feature_importances_})
questions = questions.sort_values(by='importance', ascending=False)
questions.head(50)
Değişken isimleri ve önem skorları

Bütün bu modelleme işlemlerinin ardından, elimizdeki bütün metin verisini kullanarak birde Word2Vec Modellemesi yapıp, bazı anahtar kelimeler için ağımızı TSNE algoritması ile görselleştirelim.

Word2Vec Modellemesi

Word2Vec , kelimeleri vektör uzayında ifade etmeye çalışan unsupervised (no labels) ve tahmin temelli(prediction-based) bir modeldir .[8] Daha fazla bilgiye şu makaleden ulaşabilirsiniz. Evet şimdi öncelikle metinleri büyük bir string üzerinde toplayıp, bunları kelime kelime parçalayacağız.

#Haber Metinlerini Büyük Bir String üzerinde toplayalım.
big = " "
for text in data.Body_text:
big = big + text
#Şimdide bu string'i parçalayalım edelim.
big_data = []
for i in sent_tokenize(big):
temp = []

# tokenize the sentence into words
for j in word_tokenize(i):
temp.append(j.lower())

big_data.append(temp)

Şimdi verimiz hazır olduğuna göre Gensim kütüphanesinden yararlanarak, 300 değişkenli bir Word2Vec modeli ortaya çıkartacağız.

model = Word2Vec(big_data, size=300, window=5, min_count=5, workers=multiprocessing.cpu_count())

Evet modelimizi ortaya çıkardık, şimdi belirli anahtar kelimeler için TSNE kullanarak etrafındaki kelimeleri görselleştirelim.

TSNE ile Word2Vec Görselleştirmesi

Manifold learning algoritmaları, asıl olarak veri görselleştirme için kullanılır. t-SNE (t-Distributed Stochastic Neighbor Embedding), en kullanışlı manifold learning algoritmalarından biridir. t-SNE algoritmasının ana fikri, noktalar arasındaki uzaklıkları olabildiğince koruyacak bir şekilde düşük boyutlu bir temsil bulmaktır. t-SNE, her bir veri noktası için rastgele bir düşük boyutlu temsil ile başlar ve, orijinal uzayda yakın olan noktaları birbirine yakın, uzak olanları ise birbirinden uzak tutmaya çalışır. t-SNE, birbirine uzak noktaların arasındaki uzaklığı korumaktansa birbirine yakın noktalara daha çok önem verir. [6]

Evet şimdi Word2vec modelimizi kurduktan sonra belirlediğimiz anahtar kelimeler için TSNE algoritmasını kullanarak etrafındaki kelimeleri görselleştirelim.

keys = ['teknoloji','bilişim','finans','dünya','yapay','blockchain','abd','google','facebook','apple']embedding_clusters = []
word_clusters = []
for word in keys:
embeddings = []
words = []
for similar_word, _ in model.most_similar(word, topn=30):
words.append(similar_word)
embeddings.append(model[similar_word])
embedding_clusters.append(embeddings)
word_clusters.append(words)
tsne_model_en_2d = TSNE(perplexity=15, n_components=2, init='pca', n_iter=3500, random_state=32)
embedding_clusters = np.array(embedding_clusters)
n, m, k = embedding_clusters.shape
embeddings_en_2d = np.array(tsne_model_en_2d.fit_transform(embedding_clusters.reshape(n * m, k))).reshape(n, m, 2)

Bizim için önemli kelimeleri belirledik, şimdi bunları görselleştirelim.

def tsne_plot_similar_words(labels, embedding_clusters, word_clusters, a=0.7):
plt.figure(figsize=(20, 12))
colors = cm.rainbow(np.linspace(0, 1, len(labels)))
for label, embeddings, words, color in zip(labels, embedding_clusters, word_clusters, colors):
x = embeddings[:,0]
y = embeddings[:,1]
plt.scatter(x, y, c=color, alpha=a, label=label)
for i, word in enumerate(words):
plt.annotate(word, alpha=0.5, xy=(x[i], y[i]), xytext=(5, 2),
textcoords='offset points', ha='right', va='bottom', size=8)
plt.legend(loc=4)
plt.grid(True)
plt.show()
tsne_plot_similar_words(keys, embeddings_en_2d, word_clusters)
Kelimelerin Görselleştirilmesi

TextScatter ile Kategorilerin Derin Analizi

Evet Kategori modellemesinde bahsettiğim Derin analiz kısmında TextScatter adlı kütüphaneden yararlanacağız. TextScatter, hangi kelimelerin ve ifadelerin bir kategori için karakteristik olduğunu görselleştirmeyi amaçlayan bir araçtır.

#Kurmak için;
pip install scattertext

Bu kısımda da yine Modellemede kullandığımız Text_data’yı kullanacağız. Öncelikle “parsed” adında bir değişken oluşturacağız. Bu değişkenin içerisinde Haber metinlerinin kelime kelime parçalanmış hali olacak.

Not: Aşağıdaki kodun çalışması için Spacy’nin modellerini indirmiş olmanız gerekiyor. Şuraya tıklayarak kuruluma bakabilirsiniz.

#Spacy'nin türkçe modeli olmadığı için parçalayıcı olarak ingilizce modelini kullanıyorum.
nlp = spacy.load('en_core_web_sm')
text_data['parsed'] = text_data.Body_text.apply(nlp)

Evet “Parsed” adlı değişkenimizi oluşturduk, şimdi ise parçalanan metinlerden Corpus’umuzu oluşturacağız.

corpus = st.CorpusFromParsedDocuments(text_data, category_col='Kategori', parsed_col='parsed').build()

Evet şimdi ise ilk örneğimizi modellemede karşımıza çıkan haftanın özeti kategorisinde yapalım.

#Category: Bu kısıma derin analizini yapmak istediğimiz kategoriyi yazıyoruz.
#not_category_name: Buraya diğer kategorileri yazıyoruz.
#minimum_term_frequency: Kelimeler için en az kaç gere corpus'ta geçtiğini yazıyoruz. NOT: 5-10 Gibi sayıları yazarsanız çok büyük olacağından bilgisiyarınız açmayabilir.
html = produce_scattertext_explorer(corpus,
category='haftanin-ozeti',
category_name='Haftanın Özeti',
not_category_name=['kultur-sanat','genel-gundem','teknoloji-bilim','internet-girisimler',
'tasarim-inovasyon'],
width_in_pixels=1000,
minimum_term_frequency=100,
transform=st.Scalers.percentile,
metadata=text_data['Kategori'])
file_name = 'haftanin-ozeti-100.html' #Kaydetmek istediğimiz dosya ismi
open(file_name, 'wb').write(html.encode('utf-8'))
IFrame(src=file_name, width = 1200, height=700) #Dosyayı Jupyter-Notebook'ta Göstermek için kullanıyoruz.

Bu kodun çıktısı olarak şöyle bir görüntü gelecek;

Haftanın Özeti Kategorisi için Derin Analiz

Siyah Kutuyla işaretlediğim kısım bu kategori için karakteristiği belirleyen kelimeleri kapsıyor.

Bunun dışında bir Filename kısmında belirttiğiniz isimde bir Html dosyası kodları yazdığınız dizinde olacaktır. Onu açtığınızda da şu şekilde bir görüntü karşınıza gelecek;

Aynı zamanda Search for term kısmına herhangi bir kelime yazdığınızda onun nerelerde geçtiğini ve o metnin kategorisini de gösteriyor.

Şimdi birde bu işlemi internet-girişimler kategorisi için yapalım.

html = produce_scattertext_explorer(corpus,
category='internet-girisimler',
category_name='İnternet Girişimler',
not_category_name=['kultur-sanat','genel-gundem','haftanin-ozeti','teknoloji-bilim',
'tasarim-inovasyon'],
width_in_pixels=1000,
minimum_term_frequency=100,
transform=st.Scalers.percentile,
metadata=data['Kategori'])
file_name = 'internet-girisimler-100.html'
open(file_name, 'wb').write(html.encode('utf-8'))
IFrame(src=file_name, width = 1200, height=700)

Ve çıktımızda şu şekilde oluyor; Karakteristiği belirleyen kelimeler sağ tarafta yer alıyor.

İnternet Girişimler Kategorisi için Derin Analiz

4. Sonuç

Bu çalışmada öncelikle Veri Kazımanın ne olduğundan, nasıl ve neden yapıldığından bahsedip, dunyahalleri.com için bir veri kazıma algoritması geliştirdik. Ardından elde ettiğimiz verileri ilk önce Kelime Sayısı, Metin uzunluğu, Metin yoğunluğu, en çok geçen kelimeler, en çok geçen ikililer gibi değişkenler üzerinden analiz ettik. Ardından Haber Metinlerini kategorileri üzerinden RandomForest ile sınıflandırdık. Sonrasında bütün haber metinlerini Word2Vec ile modelledik ve TSNE ile belli anahtar kelimeler için görselleştirmeler yaptık. Son olarakta TextScatter kütüphanesini kullanarak kategori bazlı derin ve detaylı kelime analizi yaptık.

Buraya kadar okuduğunuz için çok teşekkürler, merak ettiklerinizi yorumlarda yazmayı unutmayın. Kendinize iyi bakın, görüşmek üzere :)

Kodlar

Bütün kodlara https://github.com/yemregundogmus/dunya-halleri adresinden erişebilirsiniz.

Kaynakça

[1] Web Scraping nedir? (Nasıl çalışır ve neden önemlidir?)

[2] Python BeautifulSoup Modülü

[3] A Complete Exploratory Data Analysis and Visualization for Text Data

[4] Text Data Visualization in Python

[5] Google News and Leo Tolstoy: visualizing Word2Vec word embeddings using t-SNE

[6] t-SNE ile Manifold Learning

[7] Scattertext: a Browser-Based Tool for Visualizing how Corpora Differ

[8] Word2Vec Nedir ?

--

--