Web Scraping: Sonsuz Kaydırmalı Sayfalardan Metin Kazıma
JavaScript ve Python Kullanarak Veri Kazıma İşlemi
Son dönemde veri kazıma ile ilgili oldukça çok mesaj almaya başladım. Gelen mesajlar gruplandığında haber siteleri ve para piyasaları ile ilgili kaynaklar öne çıkmakta. Bunun yanı sıra çok uzun zamandır sonsuz kaydırmalı (infinite scroll) bir web sitesi ile ilgili işlem yapmak istiyordum. Aldığım mesajlardan birinde yer alan istek benim için de güzel bir çalışma bahanesi oldu1.
Aşağıda yer alan örnek T24 haber sitesi üzerinden veri kazıma işlemlerini içermekte. Birkaç aşamadan oluşan bu veri kazıma sürecinin ilk aşamasında tarayıcı üzerinde tutarsızlık gösteren ve istekler üzerinden çözüm üretilemeyen sayfadan köşe yazarı tarafından yayınlanmış yazıları liste olarak edineceğim. Ardından, edinilen bu listede yer alan yazıları hem HTML hem de temizlenmiş olarak Markdown formatında kaydedeceğim.
Bu tür bir çalışmanın sonucunda edinilen liste arşivleme ve köşe yazarı veya gündem ile ilgili analizler yapma gibi amaçlar için sıklıkla kullanılmaktadır. İlerleyen günlerde bu tür süreçlerde kullanılabilecek no-code araçlar, otomasyon çözümleri ve farklı Python paketlerinden de ayrıca bahsedeceğim.
R ile örnek veri kazıma işlemi için R İle Web Sitesi Tarama ve Veri Kazıma İşlemi başlıklı yazıma göz atabilirsiniz.
Sayfa Yapısı
Örnek sayfa olarak Tan Oral yazı & çizimlerini alabiliriz2. İlgili sayfayı görüntülediğinizde içeriklerin sonsuz kaydırmalı (infinite scroll) olarak her scroll akışında 12 içerik getirdiğini görebilirsiniz.
Yazı sayısı az ise doğrudan scroll-down işlemini yapmak ve console üzerinden ilgili başlık ve bağlantıları edinmek mümkün. Bu aşamada, tüm yazılar görüntülendiğinde (artık #MoreButton
görüntülenmediğinde) aşağıdaki kod ile yazı bilgileri edinilebilir.
// Get all elements with class '_2Mepd' and '_1fE_V'
var elements = document.querySelectorAll('._2Mepd ._1fE_V');
var results = [];
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
// Get the 'h3' element inside the '_31Tbh' element and extract the text content
var h3 = element.querySelector('._31Tbh h3');
var h3Text = h3 ? h3.textContent : '';
// Get the 'a' element inside the '_31Tbh' element and extract the 'href' attribute
var a = element.querySelector('._31Tbh a');
var href = a ? a.getAttribute('href') : '';
// Get the 'p' element inside the '_31Tbh' element and extract the text content
var p = element.querySelector('._31Tbh p');
var pText = p ? p.textContent : '';
// Get the second 'p' element inside the '_2J9OF' element and extract the text content
var secondP = element.querySelector('._2J9OF p:nth-of-type(2)');
var secondPText = secondP ? secondP.textContent : '';
// Add the extracted information to an object and push it to the 'results' array
var result = {
title: h3Text,
path: 'https://t24.com.tr' + href,
description: pText,
date: secondPText
};
results.push(result);
}
console.log('Results:', results);
Daha önce, benzer bir mantığı JavaScript Kullanarak Liste İçeriklerini Seperatör İle Yazdırmak yazısı kapsamında da kullanmıştım.
Bu yöntemin dezavantajı, yazı sayısı arttığında pratikliğini kaybetmesi. Bu durumda scroll hareketlerinin tekrarlanması, belirlenen elementlerin görüntülenmesi ve/veya değişimlerinin izlenmesi gibi ek kontroller de koda dahil edilebilir.
Data Scraping repo'su, her biri ayrı bir scraping projesi içeren alt dizinlerin bir koleksiyonudur. Projelerimiz hakkında daha fazla bilgi edinebileceğiniz depo ana sayfasını burada bulabilirsiniz.
Veri Kazıma
Python veya JavaScript ile doğrudan scroll davranışı kurgulandığında maalesef birkaç işlemden sonra içerikler gelmeyebiliyor. Bu durumda ya #MoreButton
ya da document.body.scrollHeight
yerine document.documentElement.scrollTop += 500
gibi bir numeric değer verilerek ilerlenebilir. Elbette bu içeriklerin edinilmesi sürecini biraz yavaşlatacaktır.
İlk çözümlemede Python kullanarak içerikleri edinmeye çalıştım, ancak Selenium ve BeautifulSoup ile istediğim kararlı sonuca ulaşamadım. Bu nedenle console üzerinden JavaScript ile ilerlemeye karar verdim. İlgili kod parçacıkları T24 Columnists repo'sunda yer almakta.
Repo'da yer alan JavaScript ve Python kodları benzer amaca farklı şekillerde ulaşmaktalar.
[
{
"title": "Tan Oral Çiziyor",
"path": "https://t24.com.tr/yazarlar/tan-oral/tan-oral-ciziyor,39208",
"description": "Türkiye'nin önde gelen çizerlerinden Tan Oral, çizgileriyle Türkiye ve dünya gündemini yorumluyor",
"date": "18 Mart 2023"
},
{
"title": "Tan Oral Çiziyor",
"path": "https://t24.com.tr/yazarlar/hasan-gogus/devekusu,39096",
"description": "Türkiye'nin önde gelen çizerlerinden Tan Oral, çizgileriyle Türkiye ve dünya gündemini yorumluyor",
"date": "17 Mart 2023"
},
{
"title": "Tan Oral Çiziyor",
"path": "https://t24.com.tr/yazarlar/hasan-gogus/deprem-diplomasisi-mi-diplomaside-deprem-mi,38976",
"description": "Türkiye'nin önde gelen çizerlerinden Tan Oral, çizgileriyle Türkiye ve dünya gündemini yorumluyor",
"date": "14 Mart 2023"
},
{
"title": "'Birlik ve beraberlik' tekerlemesi…",
"path": "https://t24.com.tr/yazarlar/tan-oral/birlik-ve-beraberlik-tekerlemesi,39119",
"description": "Tek tek anlamlı olan bu iki kavram yada önerinin aynı anda, aynı yerde var olmaları imkânsızdır. İlk kez 22.07.2015 tarihinde T24'te yayımlanan bu yazının, bol bol seçim konuşmaları dinlemeye hazırlandığımız şu günlerde düşünceli bir pazar okumasına vesile olması dileği ile…",
"date": "12 Mart 2023"
},
//...
]
Bu işlemin ardından elde edilecek JSON dosyası elbette pek çok amaç doğrultusunda kullanılabilir. Ancak, bu yazı kapsamında görevi ikiye böldüğümüz için, sonraki aşamada ilgili dosyayı yazıların HTML ve Markdown dosyaları olarak derlenmesi için kullanacağız. İlgili kod parçacığı yukarıda belirtilen repo'da yer almakta. Kod çalıştırıldıktan sonra elde edilecek klasör ve dosya yapısı şu şekilde olacaktır.
t24_scrapped_pages
└── tan-oral
├── 39208
│ ├── page.html
│ └── page.md
├── 39192
│ ├── page.html
│ └── page.md
├── 39152
│ ├── page.html
│ └── page.md
└── 39119
├── page.html
└── page.md
6 directories, 8 files
Yukarıdaki örnek akışı kodlar aracılığı ile test edebilirsiniz.