Python İle Web Crawler Yapımı

11 Kasım 2019 tarihinde Emre Can ÖZTAŞ tarafından paylaşıldı.

Daha önceki bir yazımızda; web crawling konusuna ve bir başka yazımızda web scraping konusuna değinmiştik. Bu yazımızda; bahsettiğimiz bu iki kavramı kullanarak bir web crawler, böcek veya bot (adına ne derseniz artık) yazacağız. Bu işlemi de Python ile gerçekleştireceğiz. İlerleyen zamanlar da bir de PHP ile yazacağız.

Bu geliştireceğimiz uygulamada herhangi bir framework kullanmayacağız. Bunu kendimiz sıfırdan ve core Python ile oluşturacağız.

Ayrıca; canlı bir test ortamı oluşturacağız ve diğer işlemleriniz için bu kuracağımız yapıyı özelleştirebileceksiniz.

Kurulumlar

Python ile web crawler yapmak için; çeşitli modüllere ihtiyacımız olacak. Bu modüllerin kurulumlarını sırası ile gerçekleştirelim.

Parsel Modülü

İlk adım olarak gerçekleştireceğimiz işlem; Parsel modülünü kurmak.

Parsel modülü, Python’a ait olan muazzam bir yapı. Bu yapı sayesinde; HTML veya XML dokümanından, XPath ve CSS Selector’lerini kullanarak veri alma işlemini gerçekleştirebiliyoruz. Biz de Parsel’i kullanarak web sayfalarından verileri alacağız veya daha doğrusu o verileri koparacağız.

Sisteminizde; Python ve pip’in kurulu olduğunu varsayarsak, komut ekranından aşağıdaki şekilde kurulumu gerçekleştirebiliriz.

$ pip install parsel

Yukarıdaki komuttan sonra; herhangi bir hata mesajıyla karşılaşmamış isek modül kurulumu tamamlanmış demektir.

Request Modülü

Request modülü, HTTP istekleri gerçekleştirmek için geliştirilmiş bir modüldür. Biz de bu modül ile hedef site veya sitelere HTTP istekleri atacağız. Bildiğiniz gibi HTTP istekleri, default olarak GET metodu ile yapılır. HTTP hakkında detaylara buradan ulaşabilirsiniz.

Aşağıdaki komut ile Request modulünün kurulumunu yapalım.

$ pip install requests

Ön Bilgilendirme

Web crawling konusunda bahsettiğimiz gibi; hedef sitenin veya sitelerin sizi engellememesi için proxy kullanabilirsiniz veya bir proxy sunucusu kullanabilirsiniz.

Ücretli proxy hizmeti veren çeşitli kurum ve kuruluşlardan destek alabilirsiniz. Ücretli proxy hizmeti veren firmalardan birisi de proxyorbit.com. Ücretleri gayet makul seviyede. Bunun dışında ücretsiz proxy hizmetlerine, Google’da kısa bir araştırma ile ulaşabilirsiniz. Ama bilinmelidir ki; proxy kullanarak, hedef sitenin sizi saf dışı bırakılması engellenemez. Bu işin bazı riskleri var ve bu riskleri göze alarak yola çıkmanız gerekiyor. Aksi halde; herhangi bir yere gidemezsiniz!

Bağlantının Kurulması

Hedef site olarak; amazon.com adresini kullanalım. Amazon’u seçmemizin nedeni; hem geniş bir içeriğe sahip olması hem de bizim gibi basit işlemleri yapan kişileri fazla kâle almayacaklardır.

Şimdi ilk olarak, bir Python projesi oluşturalım ve bir de proje dosyası ekleyelim. Ben kısaca; py-web-crawler adında bir proje dosyası oluşturdum. Bu proje dosyasının içerisine de crawler.py adında bir dosya oluşturdum.

Daha sonra yüklediğimiz modülleri sayfamıza ekleyelim. Sayfamızın ilk hali aşağıdaki gibi olacaktır.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import requests
from parsel import Selector

Şimdi de hedef sayfamızı belirleyelim. Ben basit olarak; O’REILLY’e ait olan muazzam Python kitabının olduğu sayfayı seçtim. Bu sayfanın linkini de kodlarımız arasına ekleyelim.

target_url = 'https://www.amazon.com/Learning-Python-5th-Mark-Lutz/dp/1449355730/ref=sr_1_2?crid=NW6UD7OY2O06&keywords=python+book&qid=1573405217&sprefix=python+book%2Caps%2C338&sr=8-2'

Şimdi sıra geldi; hedef sitemize istek yapmaya. Bu işlemi de aşağıdaki gibi gerçekleştireceğiz.

response = requests.get(target_url)

Yukarıdaki kodlarımızdan da anlaşılacağı üzere; hedef sitemize bir GET isteği atıyoruz.

Kodlarımızı denemeden önce; istek yaptığımız sayfadan bize dönen HTTP kodunu kontrol etmemiz gerekiyor. Bunun için de basit bir kontrol denetimi ekleyelim. Bunun için de aşağıdaki kodlarımızı yazmamız yeterli olacaktır.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import requests
from parsel import Selector

target_url = 'https://www.amazon.com/Learning-Python-5th-Mark-Lutz/dp/1449355730/ref=sr_1_2?crid=NW6UD7OY2O06&keywords=python+book&qid=1573405217&sprefix=python+book%2Caps%2C338&sr=8-2'

response = requests.get(target_url)
if response.status_code == 200:
    print('Bağlantı kuruldu!')
else:
    print('Bağlantı kurulamadı! HTTP Kodu: ', response.status_code)

Yukarıdaki kodlarımızda şunu yaptık: belirtilen siteye bir GET isteği at, eğer bu istek 200 ise yani Ok! ise ‘Bağlantı kuruldu!’ yaz değilse; ‘Bağlantı kurulamadı! HTTP Kodu: xxx’ yani dönen HTTP konu yaz dedik.

Şimdi yukarıdaki kodlarımızı deneyelim. Ekran çıktımız aşağıdaki gibi olacaktır.

Yukarıdaki ekran alıntısından da anlaşılacağı üzere; HTTP 503 hatası aldık. Bunun nedeni; bizden header bilgisini istemesi. Böyle durumlarda panik yapmaya hiç gerek yok. Hemen bir header bilgisi oluşturalım.

headers = {
    'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
}

Şimdi bu header bilgisini de ekleyerek tekrar istek yapalım.

Kodlarımızın tamamı aşağıdaki gibi olacaktır.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import requests
from parsel import Selector

target_url = 'https://www.amazon.com/Learning-Python-5th-Mark-Lutz/dp/1449355730/ref=sr_1_2?crid=NW6UD7OY2O06&keywords=python+book&qid=1573405217&sprefix=python+book%2Caps%2C338&sr=8-2'
headers = {
    'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
}

response = requests.get(target_url, headers=headers)
if response.status_code == 200:
    print('Bağlantı kuruldu!')
else:
    print('Bağlantı kurulamadı! HTTP Kodu: ', response.status_code)

Tekrar istek yapalım.

Görüldüğü gibi hedef site ile bağlantıyı sağladık. Bundan sonraki adım ise hedefteki verileri almak olacaktır. Tabiki bu işlemi de Parsel modülü ile gerçekleştireceğiz.

Dilerseniz; birden fazla header bilgisi belirleyebilirsiniz. Her istekte farklı bir header bilgisi gönderebilirsiniz. Örneğin; bir dizi oluşturup bu diziye ekleyeceğiniz header bilgilerini her istekte sırasıyla gönderebilir veya rastgele birisini seçip gönderebilirsiniz. Açıkcası bu daha mantıklı bir yaklaşım olacaktır.

Hedef Verilerin Koparılması

Hedef verilerin koparılması işlemini 2 farklı şekilde Parsel modülü ile gerçekleştirebiliriz. Bunlar; xpath ve CSS Selector’leri ile. Şimdi bunları sırasıyla görelim.

Xpath Değerine Göre Veri Koparılması

Hedefteki sayfa üzerindeki verileri xpath değerine göre alalım. Öncelikle; belirlediğimiz sayfanın başlığını xpath değerini alalım.

Xpath değerini nasıl alırım? Diye düşünüyorsanız; web scraping konusunu incelemenizde yarar var.

Sayfa başlığının xpath değeri aşağıdaki gibidir.

//*[@id="productTitle"]

Öncelikle; bağlantı kurtuğumuz sayfa üzerinde bir selector tanımlayacağız. Daha sonra da xpath değerine göre seçim işlemi yapacağız. Yani şu şekilde olacak:

selector = Selector(response.text)

Sayfa üzerinde bir selector tanımladık. response değişkeninin .text değerini atadık. Bunu yapmamızın amacı; bu sayfayı belli bir formata sokmak istememiz. Bunun sebebi de işlemlerimizi hızlı bir şekilde gerçekleştirmek.

Şimdi ise; bu selector üzerinden xpath değerine göre verileri almamız gerekiyor, tabi eğer değer sayfa üzerinde varsa.

Kodlarımızı düzenleyelim, son hali aşağıdaki gibidir.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import requests
from parsel import Selector

target_url = 'https://www.amazon.com/Learning-Python-5th-Mark-Lutz/dp/1449355730/ref=sr_1_2?crid=NW6UD7OY2O06&keywords=python+book&qid=1573405217&sprefix=python+book%2Caps%2C338&sr=8-2'
headers = {
    'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
}

response = requests.get(target_url, headers=headers)
if response.status_code == 200:
    print('Bağlantı kuruldu!')
    selector = Selector(response.text)
    page_title = selector.xpath('//*[@id="productTitle"]').get()
    print(page_title)
else:
    print('Bağlantı kurulamadı! HTTP Kodu: ', response.status_code)

Yukarıdaki kodlarımızı komut ekranında çalıştıralım.

Yukarıdaki ekran alıntısından da görüldüğü üzere; belirlediğimiz sayfanın başlığını aldık. Burada dikkatinizi; HTML etiketleri çekmiştir. İstediğimiz veri, bu etiketler ile geldi. Bu etiketlerden kurtulmanın bir çok yolu var. Bunlardan birisi de; re modülünü kullanmak. re modülü, Regular Expressions (Düzenli İfadeler) seçeneklerini belirlememize yardımcı olur. Aynı zamanda bu modül, default olarak kurulu gelir.

selector.xpath(‘//*[@id=”productTitle”]’) ifadesi, daha önce oluşturmuş olduğumuz selector’umuza verileri xpath olarak belirteceğimiz anlamına gelir. get() fonksiyonu ise; veriyi almak için kullanıldı. Şayet alacağınız değer bir veri kümesi ise yani birden fazla değer ihtiva ediyorsa; getall() fonksiyonunu kullanmanız gerekir.

CSS Değerine Göre Veri Koparılması

Xpath dışında bir alternatifte CSS selector’lerini kullanarak verileri almak olacaktır.

Yine aynı şekilde başlığı almak istersek; bu başlığın CSS değeri aşağıdaki gibi olacaktır.

#productTitle

Xpath konusunda; oluşturmuş olduğumuz Selector yapısından bu sefer de .css() fonksiyonu ile değerleri isteyeceğiz.

page_title = selector.css('#productTitle').get()

şeklinde olacaktır. Burada yine belirtmek isterim; eğer alacağınız değer birden fazlaysa o zaman getall() fonksiyonunu kullanmanız gerekmektedir.

Kodlarımızın tamamı aşağıdaki gibi olacaktır.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import requests
from parsel import Selector

target_url = 'https://www.amazon.com/Learning-Python-5th-Mark-Lutz/dp/1449355730/ref=sr_1_2?crid=NW6UD7OY2O06&keywords=python+book&qid=1573405217&sprefix=python+book%2Caps%2C338&sr=8-2'
headers = {
    'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
}

response = requests.get(target_url, headers=headers)
if response.status_code == 200:
    print('Bağlantı kuruldu!')
    selector = Selector(response.text)
    page_title = selector.css('#productTitle').get()
    print(page_title)
else:
    print('Bağlantı kurulamadı! HTTP Kodu: ', response.status_code)

Yukarıdaki kodlarımızı çalıştırdığımızda, xpath başlığındaki sonuç ile karşılaşırız.

Daha Fazla Veri

Buraya kadar olan kısmı iyi takip ettiyseniz; burdan sonraki adımlar da aynı şekilde olacaktır.

Hedef sayfamızdan, örneğin; kitap adı, yazarı ve fiyatını alalım. Ben xpath değerini kullanmak istiyorum. Siz dilerseniz; CSS selector’lerini de kullanabilirsiniz.

Daha fazla veri almak için kullanacağımız kodlarımız da aşağıdaki gibi olacaktır.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import requests
from parsel import Selector

target_url = 'https://www.amazon.com/Learning-Python-5th-Mark-Lutz/dp/1449355730/ref=sr_1_2?crid=NW6UD7OY2O06&keywords=python+book&qid=1573405217&sprefix=python+book%2Caps%2C338&sr=8-2'
headers = {
    'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
}

response = requests.get(target_url, headers=headers)
if response.status_code == 200:
    print('Bağlantı kuruldu!')
    selector = Selector(response.text)
    title = selector.xpath('//*[@id="productTitle"]').get()
    author = selector.xpath('//*[@id="bylineInfo"]/span/span[1]/a[1]').get()
    price = selector.xpath('//*[@id="rentPrice"]').get()

    print('KİTAP DETAYLARI:')
    print('Kitap Adı: ', title)
    print('Yazarı: ', author)
    print('Fiyatı: ', price)
else:
    print('Bağlantı kurulamadı! HTTP Kodu: ', response.status_code)

Yukarıdaki kodlarımızın ekran çıktısı da aşağıdaki gibi olacaktır.

Görüldüğü gibi belirlediğimiz tüm değerleri aldık.

Düzenli Veriler

HTML etiketleri ile gelen verileri, re modülü yardımıyla temizleyebileceğimizi belirtmiştik. Şimdi de HTML etiketlerini temizleyelim ve istediğimiz verilere ulaşalım.

İlk olarak re modülünü import edelim.

import re

HTML etiketlerini temizlemek için bir regex’e ihtiyacımız var. Bunun için de aşağıdaki satır işimizi görecektir.

<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});

Bu regex’i kullanmak için aşağıdaki gibi bir kullanım sağlamamız gerekiyor.

REGEX = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});')

Görüldüğü gibi yazdığımız bu regex’e göre metinlerimiz temizlenecek ve saf bir halde bize iletilecektir.

Kodlarımızın tamamı üzerinden konuşalım.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import requests
from parsel import Selector
import re

target_url = 'https://www.amazon.com/Learning-Python-5th-Mark-Lutz/dp/1449355730/ref=sr_1_2?crid=NW6UD7OY2O06&keywords=python+book&qid=1573405217&sprefix=python+book%2Caps%2C338&sr=8-2'
headers = {
    'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
}

response = requests.get(target_url, headers=headers)
if response.status_code == 200:
    print('Bağlantı kuruldu!')
    selector = Selector(response.text)
    REGEX = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});')
    title = selector.xpath('//*[@id="productTitle"]').get()
    author = selector.xpath('//*[@id="bylineInfo"]/span/span[1]/a[1]').get()
    price = selector.xpath('//*[@id="rentPrice"]').get()

    print('KİTAP DETAYLARI:')
    print('Kitap Adı: ', REGEX.sub('', title).strip())
    print('Yazarı: ', REGEX.sub('', author).strip())
    print('Fiyatı: ', REGEX.sub('', price).strip())

else:
    print('Bağlantı kurulamadı! HTTP Kodu: ', response.status_code)

Yukarıdaki kodlarımızda; HTML ile gelen verileri, REGEX.sub() ile temizledik bir de strip() fonksiyonunu ekledik. strip() fonksiyonu, bir ifadenin başındaki ve sonundaki boşlukları temizler.

Ekran çıktımız aşağıdaki gibi olacaktır.

Yukarıdaki ekran alıntısında da görüldüğü üzere; tüm verilerimizi temizleyerek aldık.

Bu verileri bir veri tabanına kaydedebilir veya bir sitede gösterebiliriz. Başka sayfalarda aynı türden verileri alacaksanız; bu sayfaları önce crawling etmeli daha sonra da scraping yapmalısınız. Bu yazıda sadece bir sayfadan nasıl veri alınır üzerinde durduk. Böyle bir durumla karşılaşırsanız; öncelikle ziyaret ettiğiniz sayfadaki tüm linkleri veya ilgili linkleri toplamalı daha sonra da bu linkleri tek tek ziyaret edip istediğiniz verileri aramalısınız.

Şimdiden bol şans!

Kategoriler: [Python] Etiketler: #parsel #python #re #regex #requests #web crawling #web-scraping

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*

*
*

This site uses Akismet to reduce spam. Learn how your comment data is processed.