R İle Sayfa Verilerinin Derlenmesi

R programlama dili sahip olduğu dplyr gibi kapsamlı paketler aracılığı ile veri temizleme ve analiz süreçlerini oldukça pratik hale getirmekte. R İle Temel Veri Düzenleme İşlemleri başlıklı yazıda WordPress ve Google Analytics verilerini birleştirip görselleşirmeye hazır hale getirmiştik.

AA

R programlama dili aracılığı ile gerçekleştirdiğimiz site haritası kontrolü, web sitesi tarama ve site haritası oluşturma gibi işlemleri daha önce yayınladığım R İle Google Analytics Reporting API Erişimi yazısı ile de ilişkilendirip konu kapsamını biraz daha genişleceğim. Bir sonraki ilgili yazıda ise edindiğimiz verileri içerikleri puanlanmak ve kendimize bir içerik gelişirme yol haritası oluşturmak amacıyla nasıl kullanabileceğimize değinmeye çalışacağım.

Adım Adım Sayfa Verilerinin Derlenmesi

Konu akışını kaybetmeme adına, mümkün olduğunca önceki yazılarda yer alan kodları kullanmaya ve/veya bu kodlar üzerine ekleme yapmaya devam edeceğim. Kodların tamamını dosyalar halinde görüntülemek, üzerinde değişiklikler yapmak ve geliştirmek isterseniz GitHub reposuna göz atabilirsiniz.

Crawling

Aşağıdaki örneği 3 bölüm halinde ele alacağım. İlk aşamda web sitesini tarayıp sayfalardan belirli bir amaç doğrultusunda bazı verileri toplayacağız; canonical, meta content, meta robots, title, h1 ve içerik alanını (bu örnek için .entry-content) tanımı.

İlk bölümün amacı arama motoru optimizasyon süreçlerindeki temel unsurladan olan içerikteki belli alanların kontrolünü gerçekleştirmek. Heading tags okuyuculara ilgili paragrafın ne hakkında olduğunu kısaca ifade eden bir rehber görevi görmesinin yanı sıra arama motorları için de yine içeriğin vurgu noktalarını temsil ederler1. Bu nedenle bir hiyerarşi temelinde ele alıırlar ve özellikle h1 ve mümkünse h2 etiketinin benzersizliği vurgulanır.

Birden fazla editörün içerik ürettiği web sitelerinde görüntüde benzer görünümlere sahip başlıkların span, big gibi etiketlerle oluşturulduğu sıklıkla karşılaşılan durumlardan biridir ve kaynak kodlara bakılmadan bu tür durumların anlaşılması güçtür.

Bunun yanı sıra, içerik yoğunluğunun da kontrol edilip yazının yoğunluğu da ayrıca değerlendirilmelidir.

R İle Web Sitesi Tarama ve Veri Kazıma İşlemi başlıklı yazıda bu sürece dair bir örnek kod paylaşmıştım. İlgili kod içerisinde Rcrawler fonksiyonuna ait CustomXPaths ve CustomLabels değerlerini şu şekilde güncelleyebiliriz.

CustomXPaths <- c("//link[@rel='canonical']/@href",
                  "//meta[@name='robots']/@content",
                  "//meta[@name='description']/@content",
                  "//title",
                  "//h1",
                  "//div[@class='entry-content']"
)

CustomLabels <- c("link_canonical",
                  "meta_robots",
                  "meta_description",
                  "title",
                  "h1",
                  "content"
)

Bu tanımlar çerçevesinde gerçekleşecek taramanın ardından (INDEX ve DATA'nın birleştirilmesi ise) sayfa ve içeriğe dair pek çok bilgi edinmiş oluruz. İlgili kod için Github reposundaki Crawling.R dosyasına göz atabilirsiniz.

Google Analytics

İkinci bölümde son 1 yıla ait Google Analytics verilerini temel alacağım. Bunun nedeni kullanıcılar tarafında sayfaların nasıl değerlendirildiğini, ne kadar ilgi çektiğini anlamaya çalışmak. Bu konu ile ilgili R İle Google Analytics Reporting API Erişimi başlıklı bir yazım olmuştu. Bu yazı bağlamında erişimin nasıl sağlanabileceğine değinmiş ve sonrasında R İle Temel Veri Düzenleme İşlemleri başlıklı yazı ile örnek bir durum içerisinde kullanmıştık.

Şimdi, Google Analyics API erişimine dair adımı biraz daha sadeleştirelim2 3.

library(googleAuthR)
library(googleAnalyticsR)

email <- "user@gmail.com"
site <- "https://domain.com"
JSONfile <- '/Users/user/Desktop/client-id.json'

gar_set_client(JSONfile)
gar_auth(email = email, scopes = 'https://www.googleapis.com/auth/analytics.readonly')

GA_accounts <- ga_account_list()

selectedGA <- GA_accounts[which(grepl('domain.com', GA_accounts$websiteUrl)),]
getIDs <- ifelse(any(selectedGA$starred == "TRUE"),
                 selectedGA[which(selectedGA$starred == "TRUE"),]$viewId,
                 selectedGA$viewId[1])
GA_id <- ifelse((!is.na(getIDs) && length(getIDs) == 1 &&
                   typeof(getIDs) == 'character'),
                paste('ga:', getIDs, sep = ''),
                stop("error message"))

start_date <- as.Date("2020-01-01")
end_date <- as.Date("2020-12-31")

gaPageData <- google_analytics(
  GA_id,
  date_range = c(start_date, end_date),
  metrics = c("ga:sessions",
              "ga:bounceRate",
              "ga:pageviews",
              "ga:avgSessionDuration",
              "ga:avgPageLoadTime"),
  dimensions = c("ga:hostname",
                 "ga:pagePath",
                 "ga:pageTitle"),
  anti_sample =  TRUE,
  anti_sample_batches = 30,
  segments = segment_ga4(name = "Organic Traffic",
                         segment_id = "gaid::-5"),
  max = -1)

gar_deauth()

gaPageData <- tibble(gaPageData)

write.csv2(gaPageData, file = "gaPageData.csv") 

Elbette isteğin belirli bir amaç doğrultusunda gerçekleştirilmesi önemli. İçerik-kullanıcı ilgisi bağlamında organik trafik segmenti dahilinde sessions, bounceRate, pageviews, avgSessionDuration, avgPageLoadTime metrik ve hostname, pagePath ve pageTitle boyutları yeterli olacaktır. Ancak, ihtiyaçlar doğrultusunda farklı segmentler, özel metrik ve boyutlar, kullanıcı ID'leri, etkinlikler ve/veya e-ticaret verileri de sürece dahil edilebilir.

Google Analytics ile ilgili yazılarımı takip ediyorsanız sıklıkla filtresiz ve kullanım amacıyla farklı görünümler oluşturulmasını önermekteyim. Bu tür mülk kurulumuna sahipseniz birden fazla viewId seçeneği karşınıza çıkacaktır. Dolayısıyla, analiz için kullanacağınız görünümü ayrıca belirtmeniz faydalı olacaktır4.

getIDs <- ifelse(any(selectedGA$starred == "TRUE"),
                 selectedGA[which(selectedGA$starred == "TRUE"),]$viewId,
                 selectedGA$viewId[1])

Search Console

Edindiğimiz bu verileri bir önceki adımda elde ettiğimiz sayfa verileri ile birleştirerek sayfa/içeriklere genel bir perspektiften bakabilir ve çeşitli hipotezler üretmeye başlayabiliriz. Ancak, ayrıca Search Console aracılığı ile elde edeceğimiz cihaz, kelime sorguları ve ortalama pozisyon verilerini dahil etmemiz daha isabetli fikriler üretebilmemizi sağlacaktır. Bu işlem için searchConsoleR paketini kullanabiliriz5 2.

library(googleAuthR)
library(searchConsoleR)

email <- "user@gmail.com"
site <- "https://domain.com"
JSONfile <- '/Users/user/Desktop/client-id.json'

start_date <- as.Date("2020-01-01")
end_date <- as.Date("2020-12-31")

gar_set_client(JSONfile)
gar_auth(email = email, scopes = 'https://www.googleapis.com/auth/webmasters.readonly')

SC_sites <- list_websites()
site <- SC_sites[which(grepl('domain', SC_sites$siteUrl)),]$siteUrl[2]

gbr_desktop_queries <- 
  search_analytics(site, 
    start_date, end_date, 
    c("query", "page"), 
    dimensionFilterExp = c("device==DESKTOP"), 
    searchType="web")

write.csv2(gbr_desktop_queries, file = "gbr_desktop_queries.csv")

gar_deauth()

Evet, görüldüğü üzere googleAuthR paketi başta olmak üzere Google Analytics için oluşturduğumuz kod ile pek çok ortak tanım aynı şekilde değerlendirilmiş durumda6. İlgili istek sonucunda masaüstü (desktop) cihaz kırılımı dahilinde veriler edinilmeke. Mobile (mobil) cihaz için dimensionFilterExp'a değer olarak c("device==MOBILE") vermek yeterli olacaktır7.

Şayet web sitesi doğrulama işlemini alan adı mülkü olarak gerçekleştiyseniz alan adı mülkü ve alan adı tanımı olmak üzere karşınıza 2 seçenek çıkacaktır. Alan adı tanımı ile devam edebilirsiniz8 9.

site <- SC_sites[which(grepl('domain', SC_sites$siteUrl)),]$siteUrl[2]

Son durumda, elimizde sorgular (queries) ve sayfa (page) bağlamında tıklama (clicks), görüntüleme (impressions), tıklama ortalaması (ctr) ve sıralama (position) verileri de yer almakta10.

Verilerin Birleştirilmesi

Tüm bu verileri birleştirdiğimizde bir geniş (wide/unstacked) veri tablosu elde ederiz11. Elbette tüm sütunlara ihtiyacımız olmayacak ve bir sonraki aşamada sütunları ve kayıtları filtrelemeye başlayacağız.

library(dplyr)
library(lubridate)
library(urltools)
library(stringr)

GA_PageData <- read.csv2(file = 'gaPageData.csv')
CR_Contents <- read.csv2(file = 'Contents.csv')
SC_Desktop_Queries <- read.csv2(file = 'gbr_desktop_queries.csv')
SC_Mobile_Queries <- read.csv2(file = 'gbr_mobile_queries.csv')

GA_PageData <- tibble(GA_PageData)
CR_Contents <- tibble(CR_Contents)
SC_Desktop_Queries <- tibble(SC_Desktop_Queries)
SC_Mobile_Queries <- tibble(SC_Mobile_Queries)

Google Analytics aracılığı ile elde ettiğimiz verilerde sayfalar path olarak (örn. /page) yer alacaklardır. Ancak, tarama ve Search Console aracılığı ile elde ettiğimiz sayfa bilgisi alan adını da içerir. Bunun yanı sıra, Google Analytics reklamlar ve/veya site içerisindeki bağlantı seçeneklerine bağlı olarak çeşitli sorgu parametreleri (UTM, preview, q, p, vb.) içerebilir. Dolayısıyla, tekrarlanan kayıtlarla karşılaşmamız mümkün. Dolayısıyla, ilk işlemimiz bu tür sayfa tanımlarını temizlemek olmalı.

pageFilter <- paste("preview",
                    "search_keywords",
                    "wp-",
                    "login",
                    "page",
                    "null",
                    "iframe",
                    "s=",
                    "p=",
                    "post_type",
                    sep = "|")

cleangaPageData <- GA_PageData %>%
  filter(hostname == "domain.com",
         !str_detect(pagePath, pageFilter)) %>%
  mutate(bounceRate = round(bounceRate),
         avgPageLoadTime = round(avgPageLoadTime),
         ID = rownames(.)
         ) %>%
  select(ID, pagePath, pageTitle, sessions, bounceRate, pageviews, avgSessionDuration, avgPageLoadTime)

Bu işlem sonunda az önce de belirttiğim gibi karşımıza tekrar eden sayfa adresleri çıkabilir. Bu tür durumlarda eldeki veri miltarına bağlı olarak incelemelerde bulunmak vakit alacaktır.

Bu tür durumlarda benzersiz bir tanıma ihtiyacımız var. En başından bu yana bu görevi sayfa adresi görmekteydi. Ancak, son durumda bu özelliğini kaybettiğini düşünebiliriz. O halde yeni bir tamıma ihtiyacımız olacak. Ben bu amaçla ID sütununu oluşturdum.

Ardından, tekrar eden sayfalar arasında yakaldığım örüntü ile ilişkili olarak sayfa görüntüleme (pageview) değerlerini kullanmaya geçtim.

duplicateIDs <- cleangaPageData %>%
  group_by(pagePath) %>% 
  filter(n() > 1) %>%
  mutate(duplicate = ifelse(pageviews > mean(pageviews), FALSE, TRUE)) %>%
  filter(duplicate == TRUE) %>%
  ungroup() %>%
  select(ID)

Elbette filtrelemek yerine mutate ile ilgili sayfalara ait metrik değerlerini toplamak, ortalamalarını almak ve/veya min/max değerlerini de kullanmak mümkün.

Elimizde 2 tablomuz var; cleangaPageData ve duplicateIDs. O halde ID üzerinden ana tablomuz olan cleangaPageData içerisindeki mükerrer ve gereksiz kayıtları filtreleyebiliriz.

notFound <- cleanedPageData %>%
  filter(grepl('Sayfa bulunamadı', pageTitle)) %>%
  select(ID)

cleanedPageData <- cleangaPageData %>%
  filter(!ID %in% c(duplicateIDs$ID, notFound$ID))

Elbette bu filtreleme işlemini çok daha genişletebiliriz.

Şimdi tarama işlemi ile birlikte elde ettiğimiz tablomuzu da sürece dahil edelim. İlk olarak URL içerisinden alan adını kaldıralım ve tabloyu birleştirme işlemine uygun hale getirelim.

CR_ContentsNew <- CR_Contents %>%
  mutate(pagePath = str_remove(Url, 'https://domain.com')) %>%
  select(-X, -Url) %>%
  tibble()

mergedData <- cleanedPageData %>% inner_join(CR_ContentsNew, by =  c("pagePath" = "pagePath"))
mergedData$Content_type <- as.factor(mergedData$Content_type)

summary(mergedData)

Birleştirme işlemi için inner_join fonksiyonunu ve her iki tablodaki pagePath sütununu kullandım12 13. Artık elimde mergedData adında sayfa içeriği ve sayfa-kullanıcı etkileşimine dair sorular sorabileceğim geniş bir tablo var. Bu sayede, sayfalar arasında çeşitli karşılaştırmalar yapabilir, grafikler ile genel bir bakış oluşturabilir, öne çıkan veya geliştirilmesi gereken sayfalar hakkında işlemler gerçekleştirebilirim.

Bir sonraki yazıda bu bilgileri kullanarak içerikleri gruplandıracak, görselleştirecek ve geliştirme planlarını bu puanlar çerçevesinde ele alacağım.