Python İle SiteMap Kontrolü

Ultimate Sitemap Parser, Requests, Pandas, CSV ve Requests paketleri ile işlemler

Site Haritası (sitemap) ile ilgili daha önce hem site haritasının ne olduğu hem de R programlama dili aracılığı ile bir web sitesi taranarak nasıl site haritası oluşturulabileceği ve var olan bir site haritası içeriğinin nasıl çözümlenebileceği değinmeye çalışmıştım. Bu yazıda okuma sürecini bir de örnek çeşitliliği sağlamak adına Python programlama dilini kullanarak gerçekleştirmeye çalışacağım.

AA

Python sunduğu paket çeşitliliği sayesinde pek çok konuda hızlı çözümler üretmeyi ve bu çözümler arasıdan da en uygun olanına (amaca yönelik olarak) karar vermemizi mümkün kılmakta. Elbette sağlanan bu esneklik süreçlerin de daha geniş bağlamlarda ele alınabilmesini sağlamakta. Örneğin, daha önce yayınladığım Python ve FTP Dosya İşlemleri başlıklı yazıda, gist olarak bulundurulan kod parçacıklarını edinmiş ve FTP ile bir sunucuya yüklemiştik. Ardından, Python İle Klasör İzleme ve Görsel Formatları Arasında Dönüştürme İşlem başlıklı yazıda bir klasör içeriğini izlemiş ve buna yönelik dosya format dönüştürme işlemleri yapmıştık. Bu yazıyı da yine sadece örnek olarak ele alınan kapsam ile düşünmemek gerekiyor. Örneğin, bir web sitesindeki tüm sayfalara sitemap aracılığı ile ulaşabilir, bu sayfaları dizin yapılarına göre klasörlere bölebilir, içeriğindeki görselleri sıkıştırabilir ve statik bir web sayfası olarak FTP aracılığı ile bir sunucuya yükleyebilirsiniz.

Python - Ultimate Sitemap Parser

Python, site haritası okuma ve oluşturma işlemlerine dair pek çok pakete sahip. Öne çıkan seçeneklerden biri olan ultimate-sitemap-parser paketi doğrudan belirtilen web sitesi üzerinden kullanılması muhtemel sie haritasyı yollarının kontrolü ve bulunan sita haritası yolu üzerinden içeriğin okunmasını sağlamakta1. Diğer popüler seçeneklerden biri olan advertools ise şimdilik bir başka yazının konusu olarak kenarda dursun2.

#!pip install ultimate_sitemap_parser

from usp.tree import sitemap_tree_for_homepage

tree = sitemap_tree_for_homepage('https://www.google.com/')
print(tree)

Yukarıdaki kod parçacığı çalıştırıldığında eğer site haritası ve içeriğinde sayfalar bulunmuş ise tree ile ayrı ayrı döndürülecektir. tree değişkeni yazdırıldığında görüntüleyeceğimiz çıktı şu şekilde olacaktır:

SitemapPage(url=https://www.google.com/finance/quote/510880:SHA, priority=0.5, last_modified=None, change_frequency=None, news_story=None),
SitemapPage(url=https://www.google.com/finance/quote/JLGZX:MUTF, priority=0.5, last_modified=None, change_frequency=None, news_story=None),
SitemapPage(url=https://www.google.com/finance/quote/002423:SHE, priority=0.5, last_modified=None, change_frequency=None, news_story=None)
...

O halde ilk işlemimize geçelim ve edindiğimiz bu sayfa bilgilerini analiz için uygun bir biçime sokup csv dosyası olarak kaydedelim.

from usp.tree import sitemap_tree_for_homepage
import csv

tree = sitemap_tree_for_homepage('https://domain.com')

pageDetails = [[
    page.url,
    page.last_modified.isoformat('#','hours').split('#')[0] if page.last_modified else None,
    float(page.priority) if page.priority else None] for page in tree.all_pages()]

with open('pages.csv', 'w+', newline='') as fl:
    write = csv.writer(fl)
    write.writerow(['URL', 'LastModified', 'Priority'])
    write.writerows(pageDetails)

Bu adım sonrasında os.getcwd() ile ulaşabileceğimiz dizinde pages.csv dosyasının oluşturulduğu görülebilir. Son olarak, ilgili dosya içeriğinde yer alan kayıtların ilk 10 satırını kabaca görüntüleyelim.

with open('pages.csv') as csvFile:
    csvReader = csv.reader(csvFile, delimiter=',')
    print([row[0] for row in csvReader][1:10])

Python - Pandas ve Requests İle URL Kontrolleri

Yukarıdaki adımlarla birlikte, temel bir biçimde site haritası içeriğine ulaştık ve bu içeriği daha detaylı analizlerlerde kullanmak üzere bir dosya olarak kaydettik. Şimdi, yukarıdaki örneği biraz daha genişletelim ve ulaştığımız her URL için bir istek gönderip isteğe karşılık dönen yanıtların bir kısmını işleme alalım. Bu işlem için çalışma dosyamıza pandas veri analiz3 ve requests HTTP kütüphanesini4 de dahil edeceğim.

import pandas, requests

df = pd.read_csv('pages.csv')

İlgili dosya içeriğini tekrar pandas ile okumanın nedeni içeriği data frame olarak görüntülemek istemem. Elbette bir önceki adım üzerinde de status kontrolleri ve list içeriğine yeni bilgilerin eklenmesi gibi işlemleri gerçekleştirebilirdik. Ancak, bu yazının devamında da pandas kütüphanesinden sıklıkla faydalanacağım için hızlı bir şekilde dahil etmek istedim.

İlk kayıtlarımıza bir göz atalım.

df.head()
URL LastModified Priority
0 https://domain.com/tr/posts/2020/05/2019-en-p... 2021-03-18 0.8
1 https://domain.com/tr/about 2021-06-01 0.8
2 https://domain.com/tr/posts/2018/12/absolute-... 2021-04-23 0.8
3 https://domain.com/tr/posts/2021/02/acuity-sc... 2021-03-27 0.8
4 https://domain.com/tr/posts/2019/04/ads-hesap... 2021-04-23 0.8

Bir sonraki adımda kayıt sayısı, sütun adları ve veri özeti gibi bilgilere göz atabiliriz5.

print(df.shape)
print(df.columns)
print(df.describe)

Şu aşamada ilgili URL'lerin durum kodları (status), yönledirme bilgileri (is_redirect, is_permanent_redirect) ve varsa bir problem tespiti (reason) gibi bazı detaylarını da veri tablosuna dahil etmek istiyorum.

for url in df['URL']:
    response = requests.head(url, verify=True, timeout=25, allow_redirects=True) # response = requests.get(url)
    df['Status'] = response.status_code
    df['Redirected'] = response.is_redirect
    df['PermanentRedirection'] = response.is_permanent_redirect
    df['IssueReason'] = response.reason if response.reason != 'OK' else None

Bu işlemle birlikte veri tablom aşağıdaki biçimi alacakır.

URL LastModified Priority Status Redirected PermanentRedirection IssueReason
0 https://domain.com/tr/posts/2020/05/2019-en-p... 2021-03-18 0.8 200 False False None
1 https://domain.com/tr/about 2021-06-01 0.8 200 False False None
2 https://domain.com/tr/posts/2018/12/absolute-... 2021-04-23 0.8 200 False False None
3 https://domain.com/tr/posts/2021/02/acuity-sc... 2021-03-27 0.8 200 False False None
4 https://domain.com/tr/posts/2019/04/ads-hesap... 2021-04-23 0.8 200 False False None

Birkaç sütun ile ilgili dönen yanıtlara odaklanmak istiyorum. Örneğin, 200 dışında aldığım bir durum kodu var mı? Herhangi bir yönlendirme işlemi söz konusu oldu mu? Kayıtlı herhangi bir problem kaydı var mı?

set ile ilgili sütunlardaki benzersiz değerleri hızlı bir şekilde görüntüleyebilirim.

# print(df.columns)
print(list(set(df['Priority'])))
print(list(set(df['Status'])))
print(list(set(df['Redirected'])))
print(list(set(df['PermanentRedirection'])))
print(list(set(df['IssueReason'])))

df['Priority'], df['Status'], df['Redirected'] ve df['PermanentRedirection'] sadece birkaç değer alabilmekte. Bu nedenle aslında kategorik değer olarak da nitelendirilebilirler. Örneğin, df['Status'] 1xx (bilgilendirme) , 2xx (başarılı işlem) , 3xx (yönlendirme) , 4xx (kullanıcı kaynaklı hata ve 5xx (sunucu kaynaklı hata gibi kodlar alabilir. df['Priority'] ise float veri tipinde 0.0 ile 1.0 arasında önceliklendirme değeri alabilir ve bu değerler 0.9 > 0.0 şeklinde göz önünde bulundurulur. Diğer sütunlar ise TRUE ya da FALSE değeri alabilirler ancak bu değerlerde bir sıralama yoktur. O halde, bu bilgiler doğrultusunda ilgili veri tiplerini düzenleyelim.

df['Status'] = (pd
    .Categorical(
        df['Status'],
        categories=list(set(df['Status'])),
        ordered=False))

df['Priority'] = (pd
    .Categorical(
        df['Priority'],
        categories=[float(i/10) for i in range(0,10,1)],
        ordered=True))

df['Redirected'] = (pd
    .Categorical(
        df['Redirected'],
        categories=list(set(df['Redirected'])),
        ordered=False))

df['PermanentRedirection'] = (pd
    .Categorical(
        df['PermanentRedirection'],
        categories=list(set(df['PermanentRedirection'])),
        ordered=False))

Artık elimizde daha detaylı analizler ve görselleştirme süreçleri için kaynak teşkil edecek bir veri yığını mevcut. Hızlı bir şekilde verilere göz atalım ve veri tablosunu yeni bir dosya olarak kaydedelim.

# 0.8 ve üzeri önceliklendirilen sayfalar neler?
df.loc[df.Priority > 0.5]
# son 10 sayfa adresi ve status kodu nedir?
df[['URL', 'Status']].tail(10)
# rastgele seçilmiş 10 sayfaya ait sayfa, durum ve önceliklendirme tanımları nelerdir?
df[['URL', 'Status', 'Priority']].sample(10)

df.to_csv('sitemap.zip', index=False, compression=dict(method='zip',archive_name='pages-new.csv'))

Evet, bu yazı kapsamındaki işlemler tamamlandı. İlgili kod parçacığını bütü olarak Google Colab aracılığı ile görüntüleyebilir ve düzenlemeler yapabilirsiniz. Bir sonraki yazıda, edinilen sayfa bilgilerini kullarak sayfa içeriklerine ulaşacak ve içerik temelinde işlemler gerçekleştireceğim.