R İle Web Sitesi Tarama ve Veri Kazıma İşlemi

Rcrawler Paketi ve Kullanımı

R ile ilgili bir önceki yazıda curl ile çekilen bir XML dosyası içeriğini okumuştuk. İlgili XML sürecini özellikle sitemap olarak ifade edilen, websitesi içeriğini (sayfalar, medyalar, vb.) arama motorlarına sunduğumuz protokol üzerinden örneklendirmiştim. Bu yazıda ise bir web sitesinin veya sadece belirli sayfaların nasıl taranabileceğine ve içeriğinden veri(leri) kazıyabileceğimizden bahsetmek istiyorum.

AA

Crawling işlemi bir websitesi içeriğinin belirli bir amaç doğrultusunda bağlantılar aracılığı ile taranması işlemini ifade ediyor. Bu tarama işlemi meta data başta olmak üzere pek çok bilgiye erişim için gerçekleştirilebilir. Bu işlemi gerçekleştiren yazılımlar / botlar crawler ve/veya spider olarak ifade edilir. Arama motorlarının web siteleriyle ilgili bilgileri toplaması örnek olarak ele alınabilir. Veri Kazıma (Data Scraping) başlıklı yazıda da değindiğim üzere, scraping ise bir bilgi yığını içerisinden (sadece web sitesi olmak zorunda değil) ihtiyaç duyduğumuz bilginin ayıklanması sürecini ifade eder1.

R ve Crawling İşlemi

R programlama dili Rvest2, RCrawler3 ve scrapeR4 gibi popüler tarama ve kazıma paketlerini barındırmaktadır5. Bu paketlerin yanı sıra XML6 ve/veya XML27, jsonlite8, RJSONIO9, Selectr10, Httr11, RCurl12 ile de spesifik işlemler gerçekleştirmek mümkün. RSelenium13 ise tarama ve kazıma dışında sunduğu test özellikleri ile biraz farklı bir noktada değerlendirilebilir.

Örnek işlemler boyunca kullanacağım paket RCrawler olacak.

graph TD A[Website] B[About] C[Products] D[Product Categories] J[Blog] E[Campaigns] F[Product - 1] G[Product - 2] H[Post - 1] I[Post - 2] K[Cart] L[Checkout] M[Purchase] A --> B & C & D A --> F & G C --> F & G A --> J J --> F & G F & G --> K K --> L L --> M subgraph s1 [lvl 0] A end subgraph s11 [lvl 1] B & C & J & K & L & M end subgraph st2 [lvl 2] B-->D & E C-->E & D end subgraph st3 [lvl 3] D-->F & G E-->G & F end subgraph st4 [lvl 3] J --> H & I end

Rcrawler

Rcrawler, web sitelerinin / uygulamalarının taranması ve kurallar çerçevesinde belirtilmiş verilerin web, metin, içerik madenciliği amacıyla verilerin kazınması işlemlerinde kullanılan, popüler R paketlerinden biridir14 15 16. Yukarıda bahsi geçen örnekler arasında öne çıkan seçeneklerden biri olan Rcrawler ile rvest arasındaki farklılığa da kısaca değinmek gerekirse; rvest verinin spesifik bir sayfadan ve/veya seçiciler (selectors) aracılığı ile kazırken, Rcrawler bir kural belirlenmemişse bir web sitesinin tüm sayfalarını otomatik olarak dolaşıp (traverse) ayrıştırabilir (parse) ve ihtiyaç duyulan tüm verileri tek seferde çıkarabilir (extract)17 18 3.

install.packages("Rcrawler")

library(Rcrawler)

# Eğer "Error in makePSOCKcluster(names = spec, ...): Cluster setup failed." hatası alırsanız aşağıdaki satırı çalıştırabilirsiniz:
# parallel:::setDefaultClusterOptions(setup_strategy = "sequential")
## https://stackoverflow.com/questions/17966055/makecluster-function-in-r-snow-hangs-indefinitely

setwd("/Users/user/Desktop/scanning")

Rcrawler(Website = "https://domain.com")

Yukarıdaki satırlar itibariyle Website aranmaya başlayacaktır. Herhangi bir URL, seçici ve/veya derinlik sınırı olmadığı için erişebildiği tüm bağlantılar üzerinden ilerleyecektir. Taramanın tamamlanmasının ardından tarama sonucu INDEX adında bir değişkene list tipinde eklenecektir. Ayrıca, setwd() ile belirtilen dizine listedeki satırların karşılığı olan sayfaları HTML formatında yerleştirir. Yapılan taramalar sonucunda oluşturulan bu dizinlere ListProjects() fonksiyonu ile ulaşmak mümkün.

[1] "domain.com-231149" "domain.com-231423" "domain.com-231727" "domain.com-232051" "domain.com-232102" "domain.com-232130" "domain.com-232131" "domain.com-232132"

Bu liste içeriğindeki HTML dosyalarına ise LoadHTMLFiles() ile erişebiliriz.

pageData <- LoadHTMLFiles("domain.com-232132", type = "vector")
summary(pageData)
head(pageData, n = 1)

pageData içeriğinin tamamını listelemenizi önermem. Ancak, fikir sahibi olmak için head veya tail ile bir kısmını inceleyebilirsiniz. Örneğin, bu site dahilinde yapılan tarama summary ile de görüntülendiğinde 4320 kayıt olduğu görülüyor. Özellikle listeleme sayfalarının bu sayıda büyük bir etkisi mevcut.

Length Class Mode
4320 character character

Rcrawler tarama sürecinin tamamlanmasının ardından oluştulan INDEX içeriğine baktığımızda ise aşağıdaki gibi bir tablo ile karşılaşırız.

View(head(INDEX, n = 2))
Id Url Stats Level OUT IN Http Resp Content Type Encoding Accuracy
1 1 https://domain.com/ finished 0 28 1 200 text/html UTF-8
2 2 https://domain.com/about finished 1 13 1 200 text/html UTF-8

Edindiğimiz bu veri üzerinden temel bazı veri tipi dönüştürme ve ardından veri kontrol işlemleri gerçekleştirelim.

INDEX$Id <- as.integer(INDEX$Id)
INDEX$Stats <- as.factor(INDEX$Stats)
INDEX$IN <- as.integer(INDEX$IN)
INDEX$OUT <- as.integer(INDEX$OUT)
INDEX$`Content Type`<-as.factor(INDEX$`Content Type`)
INDEX$`Http Resp`<-as.factor(INDEX$`Http Resp`)
INDEX$Encoding<-as.factor(INDEX$`Encoding`)

any(INDEX$`Http Resp` == "200")
any(is.na(INDEX$`Http Resp`))

Görüldüğü üzere, ilgili sayfa derinliği ve HTTP Status Code içerikleri gibi bilgiler bize sunulmakta. Elbette Rcrawler ile XPath tanımları yaparak taranan sayfalardan heading etiketleri, meta verileri, içerik gibi çeşitli bilgileri de ayıklayabiliriz.

Rcrawler(
  Website = "https://domain.com",
  no_cores = 4,
  no_conn = 4,
  ExtractXpathPat = c(
    "//title",
    "//h1",
    "//link[@rel='canonical']/@href",
    "/meta[@rel='robots']/@content",
    "/link[@rel='alternate']/@hreflang",
    "//body/@class"),
  PatternsNames = c(
    "title",
    "h1",
    "canonical",
    "metarobots",
    "hreflang",
    "bodyclass"))

crawledData <- as.data.frame(do.call(rbind, DATA))
View(head(crawledData, n = 1))

Evet, görüldüğü üzere sayfa içeriğine dair istediğimiz bilgilere sahibiz. Bu sayede, heading ve/veya meta verileri eksik olan, içeriği yetersiz olan sayfaları ayıklayabilir ve bu içeriklerin geliştirilmesini sağlayabiliriz. Eğer heading etiketleri içerisinde span, br ve benzeri etiketler kullanıyorsanız büyük ihtimalle boşluklar da görüntüleyeceksiniz. Bu tür durumlarda, ilgili veri parçasını trimws() ile düzenleyebilirsiniz.

Yukarıdaki örnek temelinde çeşitli iyileştirmeler yapıldığını varsayalım. Bu durumda tekrar sitenin tamamını taramak yerine ContentScraper() ile sayfa temelinde işlemler gerçekleştirebilir ve vakitten tasarruf edebiliriz.

updatedPosts <- ContentScraper(
  Url = "https://domain.com/post-1",
  XpathPatterns = c("//title",
                    "//h1",
                    "//h2",
                    "//h3",
                    "//link[@rel='canonical']/@href",
                    "//meta[@name='description']/@content",
                    "//meta[@name='keywords']/@content",
                    "//link[@rel='stylesheet']/@hreflang"),
  ManyPerPattern = TRUE)

Şimdilik işlemlerimiz bu kadar. Bir sonraki yazıda bir web sitesi içeriğini tarayıp elde ettiğimiz verileri sitemap oluşturmak amacıyla kullanacağız.