R İle Site Haritası (Sitemap) Oluşturma
R ile ilgili yakın zamanda yayınladığım yazılarda sitemap içeriğinin kontrolü ve web sitesi tarama işlemlerine değinmiştim. Bu yazıda ise, önceki yazıları birleştirip farklı bir açıdan yeniden ele alacağım.
Sitemap (site haritası), ile ilgili yayınladığım Google Search Console İçin Site Haritası başlıklı yazıda da belirttiğim üzere, site haritası tüm site içi bağlantıları içereceği gibi içerik türleri ve/veya görseller, videolar gibi belirli bağlamlarda da hazırlanabilir.
Aşağıda yer alan örnekte site içi bağlantılarda yer alan tanımlara göre farklı site haritaları oluşturulacak. Akıl karışıklığı olmaması adına önden bilgilendirmek istedim.
Rcrawler İle Site Tarama İşlemi
R İle Web Sitesi Tarama ve Veri Kazıma İşlemi başlıklı yazıda Rcrawler paketinden detaylıca bahsetmeye çalışmış ve örnek olarak bir tarama işlemi gerçekleştirmiştim1. Bu yazıda yine Rcrawler paketini kullanacak ve benzer şekilde bir web sitesini tarayacağım. Ardından, edindiğim URL bilgilerini kullanarak site haritaları oluşturacağım. Örnek işlem için doparank tarafından paylaşılmış How to create XML Sitemap with R başlıklı yazıyı temel aldım2.
Örnek işlem sürecinde, genel olarak Rcrawler, dplyr ve stringr paketlerini kullanacağım.
library(Rcrawler)
library(dplyr)
library(stringr)
CustomXPaths <- c("//link[@rel='canonical']/@href",
"//meta[@name='robots']/@content")
CustomLabels <- c("link_canonical",
"meta_robots")
CustomXPaths
kazıyacağımız alanları belirtip CustomLabels
ile bu alanlara başlık vermiş oluyoruz. Eğer sayfa tipleriniz CMS aracılığı ile bir class olarak body
etiketine iliştiriliyor ya da meta veri olarak sunuluyorsa elbete bu alanları da CustomXPaths
içerisine dahil etmek oldukça faydalı olacaktır. Aksi durumda, URL içerisinde yer alan ifadelere göre ayrıştırmak gerekecek. Eğer içerik türlerini (post, page, listing, product, vb.) ayrıştıran tanımlar URL içerisinde yer almıyorsa işler biraz daha zorlaşacaktır.
https://domain.com/product/product-name
https://domain.com/post/post-name
https://domain.com/product-name
https://domain.com/post-name
Yukarıdaki örnekte de görüldüğü üzere ilk 2 URL tanımını ayrışırmak çok daha kolay olacaktır. Artık çalışma dizinimizi de belirtip tarama işlemini başlatabiliriz.
setwd("~/Desktop")
Rcrawler(Website = "https://domain.com/",
ExtractXpathPat = CustomXPaths,
PatternsNames = CustomLabels)
saveRDS(DATA, file="DATA.rds")
saveRDS(INDEX, file="INDEX.rds")
URL Sınıflandırma ve Sitemap Oluşturma İşlemi
Tarama işleminin sonuçlanması web sitesinde yer alan sayfa sayısına göre farklılık gösterecektir. Bu işlem sonucunda elde edilecek DATA
ve INDEX
listelerini farkı bir zaman içerisinde yeniden kullanmak üzere kayıt altına alabilirsiniz.
Artık bu 2 tabloyu listeyi birleştirip veri tiplerini düzenleyerek bir sonraki aşamaya geçebiliriz.
mergedCrawl <- cbind(INDEX, data.frame(do.call(rbind, DATA)))
mergedCrawl$Id <- as.integer(mergedCrawl$Id)
Indexable_pages <- mergedCrawl %>%
mutate(Canonical_Indexability = ifelse(Url == link_canonical | is.na(mergedCrawl$link_canonical), TRUE, FALSE)) %>%
mutate(Indexation = ifelse(grepl("NOINDEX|noindex", mergedCrawl$meta_robots), FALSE, TRUE)) %>%
filter(Canonical_Indexability == TRUE & Indexation == TRUE)
Yukarıdaki işlem ile birlikte yeni 2 sütun oluşturduk. Url
ile link_canonical
alanlarını karşılaştırıp ilgili sayfanın canonical tanımına sahip ve index için uygun olup olmadığını kontrol ettik. Her iki sütunun da TRUE
değerine sahip olması ilgili URL'in sitemap içerisinde yer alması için yeterli.
Artık URL içeriğine bağlı olarak URL'leri ayrıştırabiliriz3.
Sitemaps <- Indexable_pages %>%
filter(`Http Resp` == '200' & `Content Type` == 'text/html') %>%
select(Url) %>%
mutate(Content_type =
ifelse(str_detect(Indexable_pages$Url, "/caegory|tag/"), "Taxonomy",
ifelse(str_detect(Indexable_pages$Url, "/list|listings/"), "Listing",
ifelse(str_detect(Indexable_pages$Url, "/shop|contact/"), "Pages",
ifelse(str_detect(Indexable_pages$Url, "/locations"), "Locations",
ifelse(str_detect(Indexable_pages$Url, "/product"), "Products", "Posts")))))) %>%
group_by(Content_type) %>%
unique() %>%
arrange(Content_type)
Sitemaps$Content_type <- as.factor(Sitemaps$Content_type)
Bu işlemin sonunda Content_type
adında belirlediğimiz içerik tiplerini içeren bir sütun daha oluşturmuş olduk. İlgili sütuna ait veri tipini factor olarak belirleyebiliriz.
Sıradaki işlemimiz belirlediğimiz Content_type
içeriğine bağlı olarak sayfaları ayrıştırmak.
Sitemap_taxonomy <- Sitemaps %>% filter(Content_type == "Taxonomy")
Sitemap_listing <- Sitemaps %>% filter(Content_type == "Listing")
Sitemap_pages <- Sitemaps %>% filter(Content_type == "Pages")
Sitemap_places <- Sitemaps %>% filter(Content_type == "Places")
Sitemap_products <- Sitemaps %>% filter(Content_type == "Products")
Sitemap_posts <- Sitemaps %>% filter(Content_type == "Posts")
Hangi içerik tipinde kaçar adet URL'in yer aldığına bir bakalım.
Sitemaps %>%
group_by(Content_type) %>%
summarise(no_rows = n())
Bu aşamadan sonrası çok farklı biçimlerde ele alınabilir. Eğer ilgili URL'leri tekrar sorgulamak ve last-modified
gibi HEADER içeriklerini kontrol etmek isterseni httr4 ya da curl gibi paketlerle istekler gerçekleştirip devam edebilirsiniz ya da herhangi bir ek istek gerçekleştirmeden istediğiniz herhangi bir tarihi lastmod
değeri olarak atayabilirsiniz.
createSitemap <- function (links = list(), fileName = format(Sys.time(), "%y-%m-%d_%H-%M-%S")) {
require(whisker)
require(httr)
cat("Please wait...", "\n")
cat("Total Link: ", length(links), "\n")
template <- '<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{{#links}}
<url>
<loc>{{{loc}}}</loc>
<lastmod>{{{lastmod}}}</lastmod>
<changefreq>{{{changefreq}}}</changefreq>
<priority>{{{priority}}}</priority>
</url>
{{/links}}
</urlset>'
map_links <- function(url) {
tmp <- GET(url)
# https://www.stat.berkeley.edu/~s133/dates.html
date <- format(as.Date(strptime(tmp$headers$date, format = '%a, %d %b %Y %H:%M:%S', tz = "UTC")), "%Y-%m-%d")
sys_date <- format(Sys.time(), "%Y-%m-%d")
list(loc = url,
lastmod = ifelse(!is.na(date), date, sys_date),
changefreq = "monthly",
priority = "0.8")
}
links <- lapply(links, map_links)
cat(whisker.render(template, partials = links), file = paste(fileName, ".xml", sep = ""))
}
Görüldüğü üzere, createSitemap
fonksiyonu httr ve whisker5 paketlerine ait çeşitli fonksiyonlar barındırmakta. Whisker ile ilgili daha önce yayınladığım R Dilinde Mustache Template Sistemi Kullanımı başlıklı yazıma da göz atabilirsiniz5. GET
ile istekleri yönetirken, whisker.render
ile XML şablonu içerisinde yer alan yer tutuculara ilgili veriler aktarılmakta ve cat
ile dosya olarak kayıt edilmekte.
GET
aşamasını es geçip doğrudan URL'leri yazdırabilirsiniz.
Son aşamada artık istediğimiz site haritalarını az önce oluşturduğumuz createSitemap
fonksiyonu aracılığı ile dosya haline getirebiliriz.
createSitemap(links = Sitemap_listing$Url, file = "listing")
createSitemap(links = Sitemap_pages$Url, file = "pages")
createSitemap(links = Sitemap_places$Url, file = "places")
createSitemap(links = Sitemap_products$Url, file = "products")
createSitemap(links = Sitemap_posts$Url, file = "posts")
createSitemap(links = Sitemap_taxonomy$Url, file = "taxonomy")
İşlemlerimiz bu kadar. Oluşturulan site haritalarını uygun bir dizine ekleyip Search Console üzerinden dizine eklemeniz yeterli. Örnekte bölümler halinde açıkladığım R kod parçacığını bütün olarak görüntülemek için ceaksan/R_createXMLSitemap.R başlıklı gist'e göz atabilirsiniz.