R İle SiteMap İçeriğinin Derlenmesi ve URL Kontrolü
XML ve curl Paketleri İle XML-URL İşlemleri
R programlama dili ile ilgili son yazıyı Eylül, 2020'de yayınlamışım. İş yoğunluğu ve yine bununla ilintili olarak, ağırlıkla JavaScript ile çözüm üretmeye başlamam sebebiyle R ile aramdaki mesafe açılmış gibi hissediyorum. Bunu engellemek adına, hafta sonu birkaç işlemi yeniden oluşturmaya ve farklı paketlerle benzer eylemleri yinelemeye karar verdim.
Daha önce R İle Sitemap (XML) Çözümlemesi başlıklı yazıda da değindiğim xsitemapGet yerine bir XML dosyasını indirmek, içeriğini okumak ve içeriğine bağlı olarak URL'leri nasıl kontrol edebileceğimize değinmek istiyorum. İlgili işlem özellikle farklı türlerdeki içerikleri kapsayan sitemapindex dosyaları için kolaylaştırıcı olacaktır.
<sitemapindex>
<sitemap>
<loc>https://domain.com/pages.xml</loc>
</sitemap>
<sitemap>
<loc>https://domain.com/posts.xml</loc>
</sitemap>
<sitemap>
<loc>https://domain.com/taxonomy.xml</loc>
</sitemap>
<sitemap>
<loc>https://domain.com/images.xml</loc>
</sitemap>
<sitemap>
<loc>https://domain.com/products.xml</loc>
</sitemap>
<sitemap>
<loc>https://domain.com/videos.xml</loc>
</sitemap>
</sitemapindex>
domain.com/sitemap.xml
içeriğinin yukarıdaki gibi olduğunu düşünelim. Bu durumda ilgili loc
kayıtları için ayrı ayrı işlemler yapmamız gerekir. Ancak, biz elimizdeki sitemap.xml
adreslerini bir tablo içerisinde tutarak, içeriklerine bakmadan hepsi için ayrı tarama işlemleri gerçekleştirilmesini isteyebiliriz.
XML ve curl İşlemleri
İlgili işlemleri XML ve curl paketleri aracılığı ile gerçekleştireceğim. curl paketi url()
ve download.file()
ile kıyaslandığında daha iyi performans göstermekte1. Bunun yanı sıra, https ve ftps desteği, gzip sıkıştırma, kimlik doğrulama gibi özellikler nedeniyle çoğu zaman önceliğim oluyor. Diğer yandan, curl paketi ile ilgili bilmediğim pek çok konu bulunmakta. Bu nedenle olabildiğince farklı şekilde ilgili paket içeriğinden faydalanmaya çalışıyorum.
İşlemleri 2 parça halinde ele alacağım. İlk bölüm XML dosyasına erişilmesi, içeriğinin okunması ve belirtilen formatta kaydedilmesini kapsarken, ikinci bölümde bir dosya içerisindeki URL'lerin okunması ve status testlerinin kontrolleri yer alacak.
any(grepl("curl", installed.packages()))
any(grepl("XML", installed.packages()))
library(curl)
library(XML)
İlgili paketlerin kontrol edilmesi ve yüklenmesi ile işlemlere başlayabiliriz. Vikram'ın paylaştığı kod parçacığı gibi çözümlerle de ilgili kontrol ve yükleme işlemlerini gerçekleştirebilirsiniz2.
Gönderilen URL'in uzantısını kontrol ederek temel bir doğrulama işlemi gerçekleştirebiliriz. Bu işlem için tools paketine ait file_ext()
fonksiyonu kullanılabilirsiniz.
# url <- "https://domain.com/sitemap.xml"
getExtension <- tools::file_ext(sub("\\?.+", "", url))
Paket kullanmadan strsplit()
veya benzer fonksiyonlarla işlem gerçekleştirebilirsiniz.
# url <- "https://domain.com/sitemap.xml"
getExtension <- function(url){
ex <- strsplit(basename(url), split="\\.")[[1]]
return(ex[-1])
}
curl paketinde bulunan curl_fetch_memory()
bir URL'den belleğe, diske veya bir callback fonksiyonuna veri yazmak amacıyla kullanılabilir. Önceki kod örneğinde httr paketini kullanmıştım.
HTTP Status Kontrolü
İlk fonksiyon XML dosyasının çekilmesi HTTP Status, Content-type gibi temel doğrulamaları ve ardından XML içeriğinin xmlToDataFrame()
ile tabloya (data frame) dönüştürülmesi işlemlerini kapsamakta.
getURL <- function(url) {
getExtension <- function(url){
ex <- strsplit(basename(url), split="\\.")[[1]]
return(ex[-1])
}
if(!is.na(url)
&& !is.na(getExtension(url))
&& getExtension(url) == 'xml') {
get <- curl_fetch_memory(url)
if(get$status_code == '200' &&
!is.null(get$content) &&
grepl("xml", get$type)) {
xmlData <- xmlParse(rawToChar(get$content), encoding = "UTF-8")
# rootNodeList <- xmlToList(xmlData)
rootNodeDF <- xmlToDataFrame(xmlData)
rootNodeDF$xmlFileName <- parseURI(url)$path
cat("...")
return(rootNodeDF)
}
}
}
İkinci fonksiyon ise tabloya dönüştürdüğümüz verinin bir dosya olarak kaydedilmesi görevine sahip.
saveXMLFile <- function(
url,
extension = ".csv",
fileName = format(Sys.time(), "%y-%m-%d_%H-%M-%S"),
workDir = "/Users/user/Desktop/",
preX = "sitemap_",
fileDir = "sitemaps") {
res <- getURL(url)
pageData <- lapply(res$loc, getURL)
pages <- do.call(rbind.data.frame, pageData)
dataXML <- if(any(ncol(pages) > 2)) pages else res
fullPath <- paste0(workDir, fileDir)
if(!dir.exists(fullPath)) dir.create(file.path(fullPath))
setwd(file.path(fullPath))
file <- paste0(preX, fileName, extension)
switch(
extension,
".csv" = {
write.csv(dataXML, file)
}, ".xlsx" = {
library(xlsx)
write.xlsx(dataXML, file, sheetName = "Links")
}, {
saveRDS(dataXML, file)
}
)
}
Evet, fonksiyonlar da hazır olduğuna göre belirlediğimiz bir XML'in URL ile ilgili işlemimizi bağlatabiliriz.
siteMapURL <- "https://domain.com/sitemap.xml"
saveXMLFile(siteMapURL)
XML dosyalarının sayısına ve içeriklerine bağlı olarak işlemin tamamlanma süresi elbette değişkenlik gösterecektir3. İlgili dosyanın oluşturulması ile birlikte aşağıdaki örneğe benzer bir CSV (veya belirttiğimiz diğer uzantıya göre) dosya oluşturulacaktır4.
"","loc","priority","lastmod","changefreq","xmlFileName"
"1","https://domain.com","1.00","2021-03-19T10:53:46+03:00","always","/pages.xml"
"2","https://domain.com/about","0.50","2021-03-19T10:53:46+03:00","monthly","/pages.xml"
Peki, sitemap dosyalarından elde ettiğimiz bu URL'lerin HTTP durum kodlarını nasıl kontrol edebiliriz?
getURL()
fonksiyonuna URL kontrolünü ekleyebiliriz.
getURL <- function(url) {
getExtension <- function(url){
ex <- strsplit(basename(url), split="\\.")[[1]]
return(ex[-1])
}
if(!is.na(url)
&& !is.na(getExtension(url))
&& getExtension(url) == 'xml') {
get <- curl_fetch_memory(url)
if(get$status_code == '200' &&
!is.null(get$content) &&
grepl("xml", get$type)) {
xmlData <- xmlParse(rawToChar(get$content), encoding = "UTF-8")
# rootNodeList <- xmlToList(xmlData)
rootNodeDF <- xmlToDataFrame(xmlData)
rootNodeDF$status <- NA
rootNodeDFwStatus <- rootNodeDF
data <- list()
success <- function(res){
# cat(res$url, res$status, "\n")
data <<- c(data, list(res))
}
failure <- function(msg){
cat("Request failed!", msg, "\n")
}
cat("Please wait...","\n")
getURIs <- function(URIs) {
pool <- new_pool()
for(i in URIs) {
cat("> ", i, "\n")
curl_fetch_multi(i, done = success, fail = failure, pool = pool)
}
multi_run(pool = pool)
}
getStatus <- lapply(rootNodeDFwStatus$loc, getURIs)
for (j in 1:length(data)){
w <- which(rootNodeDFwStatus$loc == data[[j]]$url)
if(any(w)){
rootNodeDFwStatus[w,]$status = data[[j]]$status_code
}
}
rootNodeDFwStatus$xmlFileName <- parseURI(url)$path
return(rootNodeDFwStatus)
}
}
}
URL kontrolleri için de yine curl paketi içerisinde bulunan curl_fetch_multi()
isteklere dönen yanıtları callback ile işleyebilmemizi sağlar5. Elbette bu işlemi curl()
ya da curl_fetch_stream()
ile de gerçekleştirebilirdik6. Ancak, yazının başında da belirttiğim gibi özellikle farklı fonksiyonlar kullanmaya özen göstermeye çalıştım7.
Yukarıda yer alan kod parçacığının tamamını ayrıca GitHub üzerinden görüntüleyebilir ve indirebilirsiniz.
- curl: A Modern and Flexible Web Client for R ↩
- Vikram B. Baliga. (2019). Check if packages are installed (and install if not) in R ↩
- XML: Tools for Parsing and Generating XML Within R and S-Plus ↩
- Karlijn Willems. (2018). This R Data Import Tutorial Is Everything You Need ↩
- Working with XML Data in R ↩
- The curl package: a modern R interface to libcurl (2019) ↩
- Multiple Concurrent Downloads using RCurl ↩