-- C#

Veri Çıkarma – C# ile Web’den Veri Çıkarma

C# ile Web’den Veri Çıkarma (Data Extraction from Web)

Web ve yazılım teknolojilerinin gelişimiyle birlikte uygulama ihtiyaçları da arttı. Mesela artık bir web uygulamasını otomatik yazılımlarla analiz edip içerisindeki veriden anlamlı veriler bulma ya da anlam çıkarma gibi ihtiyaçlar söz konusu olabiliyor. Ya da bir sisteme otomatik bir yazılımla login olmak gibi… Bu bahsettiğim konulara Web Scraping ya da Web Crawling diyoruz. Aslında bir arama motoru da bu mantıkta çalışır. Sizin web uygulamalarınızın erişilebilir alanlarındaki HTML-CSS gibi Front-End kodlarını alır ve içerisindeki veriyi çıkararak bunlar arasında yapay zeka algoritmaları çalıştırır. Bu işlem sonucunda da artık BigData ve Yapay Zeka tekniklerine geçilerek bir arama motoru halini alır. Yani bir arama motorunun işçi sınıfı aslında veri çıkarma işini yapan örümcek yazılımlardır.

Tabi biz bu makalede bir scraping ya da crawling yazılımını JSON veri üreten New York Times API’si üzerinde çalıştıracağız. Yani bir JSON API tüketen yazılım geliştireceğiz.

Kullanacağımız API; https://developer.nytimes.com/

Bu sayfaya girdikten sonra sağ üstteki Get NYT API Key butonuna tıklayarak API Key alma ekranına gidin. Burada gerekli bilgileri girdikten sonra bir API Key alacaksınız. Bunu kaydedin. Çünkü tüm sorguları bu Key ile gerçekleştireceğiz.

Genel hazırlık bu kadardı. Şimdi uygulamayı geliştirmeye başlayalım.

Bitmiş uygulamanın genel görünümü;

Siz de projenizi oluşturup yukarıdaki görselde bulunan klasör yapısını oluşturun. Sonrasında adım adım sınıfları gösterip kullandıkça klasörleri doldurursunuz.

Projede ilk işimiz genel ayarları ve yapıyı kurmaktır. Burada ilk yapmamız gereken WebParser sınıfı olacak.

WebParser sınıfında kullanacağım namespace’ler;

using Newtonsoft.Json;
using RestSharp;
using System;
WebParser sınıfı;
public class WebParser : IDisposable
{
    protected RestClient WebClient = null;
    protected string response = String.Empty;
    protected bool disposed;

    public string Response
    {
        get { return response; }
    }

    public string Request(string resource, Method type, string param, string value)
    {
        string res = String.Empty;

        if (WebClient != null)
        {
            var request = new RestRequest(resource, type);
            request.AddParameter(param, value);
            IRestResponse htmlRes = WebClient.Execute(request);
            res = htmlRes.Content;

            if (res != String.Empty)
                response = res;
        }
        return res;
    }

    public WebParser()
    {
        WebClient = null;
        response = String.Empty;
    }

    public WebParser(string baseUrl)
    {
        WebClient = new RestClient(baseUrl);
    }

    ~WebParser()
    {
        this.Dispose(false);
    }

    public virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing) WebClient = null;
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Serialization işlemi için...
    public T DeserializeJson(string res)
    {
        return JsonConvert.DeserializeObject(res);
    }
}

Bu hazırlıktan sonra genel ayarların ve API path’lerinin bulunduğu APIOptions sınıfını oluşturalım. APIOptions sınıfı Options klasörü içerisinde yer alıyor.

public static class APIOptions
{
     // GENERAL
     public const string NYTBooksBaseUrl = "http://api.nytimes.com/svc/books/v3/";
     public const string NYTSearchBaseUrl = "http://api.nytimes.com/svc/search/v2/";
     public const string NYTApiKeyStr = "api-key";
     public const string NYTApiKeyVal = "e9656f17525f48a.............";

     // APIs
     public const string NYTBooksResource = "lists/names.json";
     public const string NYTBooksReviews = "reviews.json?";
     public const string NYTArticleSearch = "articlesearch.json?";
}

Üstteki alanda API sorgusu için kullanacağımız API-Key String bilgisi ve API-Key Value bilgisi bulunuyor. Siz de kendi API Key bilginizi o alana yapıştırın. Ben kendi Key bilgimin yarısını noktalarda doldurdum. Herkes kendi API’sini sömürsün. 🙂

Üstteki kodlarda APIs alanında bulunan kısım ise işimizi daha kolaylaştırmak için API Path bilgilerini yazdığımız sabit değişkenlerdir.

Dikkat ederseniz yukarıdaki kodlarda, en üstte iki adet BaseUrl tanımlaması var. Çünkü sorgularımızın yapısı sorgu alanlarına göre değişiyor. Mesela NYTimes kitaplarını sorgulamak için Books API’si, ama NYTimes’da arama yapmak için Search API’sini kullanacağız.

Şimdi de genel olarak API’de kullanacağımız Enum sabitlerinin bulunduğu Enums sınıfını oluşturalım. Bu sabitler uygulama genelinde bize yardımcı olacak.

public enum EnumReviews
{
    isbn,
    title,
    author
}

public enum EnumArticles
{
    q,
    fq,
    begin_date,
    end_date,
    sort,
    fl,
    hl,
    page,
    facet_field,
    facet_filter
}

ÖNEMLİ : Enums dosyasını bir sınıf olarak oluşturdum ama içerisindeki “public class enums” gibi sınıf tanımlama alanını sildim. Yani doğrudan namespace içerisine tanımlanan Enum’lar var.

Artık Callers klasöründeki statik sınıf içeren BestSellerCallers.cs dosyasını hazırlamaya geçebiliriz. Bu sınıfın hazırlık aşaması olarak Models klasörü içerisinde iki adet sınıf oluşturacağız.

  • BestSellerListNamesItem
public class BestSellerListNamesItem
{
     public string list_name;
     public string display_name;
     public string list_name_encoded;
     public DateTime oldest_published_date;
     public DateTime newest_published_date;
     public string updated;
}
  • BestSellersListNames
public class BestSellersListNames
{
     public string status;
     public string copyright;
     public int num_results;
     public BestSellerListNamesItem[] results;
}

Dikkat ederseniz sınıflardan biri(BestSellerListNamesItem) ‘item’, yani bir listenin içerisindeki parçaları oluşturmak için kullanılıyor. Diğeri ise(BestSellersListNames) ana nesnemiz…

Artık BestSellerCallers sınıfı içerisini hazırlayabiliriz;

   
// Bu sınıf içerisinde iki adet metot bulunuyor. 
// Aslında iki metot da temelde aynı işi yapıyor. 
// Sadece, biri NYTimes'dan aldığı JSON veriyi ham haliyle ekranda gösterecekken, diğeri ise birazdan oluşturacağımız Models klasörü içerisindeki BestSeller... yapısı ile bize .NET objesi olarak geri dönüş yapacak.
public static class BestSellerCallers
{
    // Ham JSON veri dönen metot.
    public static string GetRawNYTBestSellersListNames()
    {
        string res = String.Empty;

        using (WebParser wp = new WebParser(APIOptions.NYTBooksBaseUrl))
        {
            res = wp.Request(APIOptions.NYTBooksResource, Method.GET, APIOptions.NYTApiKeyStr, APIOptions.NYTApiKeyVal);
            Console.WriteLine(res);
        }
        return res;
    }

    // BestSellersListNames sınıfı türünde geri dönüş yapan metot.
    public static BestSellersListNames GetNYTBestSellersListNames()
    {
        BestSellersListNames data = null;

        using (WebParser wp = new WebParser(APIOptions.NYTBooksBaseUrl))
        {
            string res = wp.Request(APIOptions.NYTBooksResource, Method.GET, APIOptions.NYTApiKeyStr, APIOptions.NYTApiKeyVal);
            data = wp.DeserializeJson(res);
        }
        return data;
    }
}

Şimdi ilk testimizi yapmak için Program.cs dosyasındaki Main metodumuza geliyoruz.

// 1.
Console.WriteLine(BestSellerCallers.GetRawNYTBestSellersListNames());

Bu kodun çalıştırılmasından sonra NYTimes’dan elde edilen En Çok Satanlar listesinin JSON formatında ham haliyle listelendiğini görebileceksiniz.

Peki, şimdi de bunun .NET Object’e dönüştürülerek elde edilişini uygulayalım.

// 2.
var data = BestSellerCallers.GetNYTBestSellersListNames();
Console.WriteLine(data.copyright);
Console.WriteLine("Status : " + data.status);
Console.WriteLine("**********");
Console.WriteLine("Number Results : " + data.num_results.ToString());
foreach (var model in data.results)
{
     Console.WriteLine(model.display_name);
}

Bu işlem sonunda, artık uygulamanız gerçek anlamda bir .NET uygulaması gibi JSON veriyi alıp(WebParser sınıfı ile), dönüştürüp(WebParser sınıfı ile) kullanıma sunuyor demektir.

İlk örnek yapımızı test ettiğimize göre diğer işlemleri uzun uzun anlatmadan doğrudan kodları vererek çalışmanızı sağlayabiliriz.

Review Bilgilerini Almak

Şimdiki işlemlerde de NYTimes API’sinde bulunan Review API örneklerini uygulayacağız.

Review Item sınıfını oluşturun;

public class ReviewItem
{
     public string url { get; set; }
     public string publication_dt { get; set; }
     public string byline { get; set; }
     public string book_title { get; set; }
     public string book_author { get; set; }
     public string summary { get; set; }
     public string[] isbn13 { get; set; }
}

Review sınıfını oluşturun;

public class Review
{
     public string status;
     public string copyright;
     public int num_results;
     public ReviewItem[] results { get; set; }
}

ReviewCallers sınıfını oluşturun;

public static class ReviewCallers
{
     //title=SALVAGE THE BONES
     public static Review GetNYTReviewItems(EnumReviews type, string parameter)
     {
         Review data = null;

         using (WebParser wp = new WebParser(APIOptions.NYTBooksBaseUrl))
         {
             string res = wp.Request(APIOptions.NYTBooksReviews + type + "=" + parameter, Method.GET, APIOptions.NYTApiKeyStr, APIOptions.NYTApiKeyVal);
             data = wp.DeserializeJson(res);
         }
         return data;
     }
}

ReviewCallers sınıfının Program.cs içerisinde kullanımı;

Örnek 1:

var data = ReviewCallers.GetNYTReviewItems(EnumReviews.title, "SALVAGE THE BONES");
foreach (var item in data.results)
{
   Console.WriteLine("Author : " + item.book_author);
   Console.WriteLine("-> Book : " + item.book_title);
}

Metoda parametre olarak EnumReviews isimli Enum’u verdiğimize dikkat edin!

Örnek 2:

var data = ReviewCallers.GetNYTReviewItems(EnumReviews.author, "Jesmyn Ward");
foreach (var item in data.results)
{
   Console.WriteLine("Author : " + item.book_author);
   Console.WriteLine("-> Book : " + item.book_title);
}

Bu işlemlerden sona epey pratik yapmışsınız demektir. Şimdi son noktayı da API’nin arama işlemlerini gerçekleştirerek yapalım. En karışık kısımlarından biri burasıdır. Çünkü JSON serialization işlemleri sırasında bir çok parse hatası meydana gelebilmektedir.

NYTimes’da Makale Araması Yapmak

Bu işlemde geri dönen JSON veri çok karmaşık! Bu da dönen JSON’ı .NET’e dönüştürürken bolca sınıf oluşturmanız ve olası hatalara hazır olmanız gerektiği anlamına geliyor.

Sınıfları oluşturalım…

ArticleMeta sınıfı;

public class ArticleMeta
{
    public int hits { get; set; }
    public int time { get; set; }
    public int offset { get; set; }
}

ArticleDoc, ArticleDocHeadline, ArticleDocKeyword, ArticleDocByLine ve ArticleDocMultimedia sınıfları;

public class ArticleDoc
{
    public string web_url { get; set; }
    public string snippet { get; set; }
    public string lead_paragraph { get; set; }
    public string print_page { get; set; }
    public Dictionary<string,string> blog { get; set; }
    public string source { get; set; }
    public ArticleDocHeadline headline { get; set; }
    public ArticleDocKeyword[] keywords { get; set; }
    public string pub_date { get; set; }
    public string document_type { get; set; }
    public string news_desk { get; set; }
    public string section_name { get; set; }
    public string subsection_name { get; set; }
    public ArticleDocByLine byline { get; set; }
    public string type_of_material { get; set; }
    public string _id { get; set; }
    public string word_count { get; set; }
    public string slideshow_credits { get; set; }
    public ArticleDocMultimedia[] multimedia { get; set; }
}

public class ArticleDocHeadline
{
    public string main { get; set; }
    public string kicker { get; set; }
    public string print_headline { get; set; }
}

public class ArticleDocKeyword
{
    public string isMajor { get; set; }
    public int rank { get; set; }
    public string name { get; set; }
    public string value { get; set; }
}

public class ArticleDocByLine
{
    public string organization { get; set; }
    public string original { get; set; }
    public string[] person { get; set; }
}

public class ArticleDocMultimedia
{
    public string url { get; set; }
    public string format { get; set; }
    public int height { get; set; }
    public int width { get; set; }
    public string type { get; set; }
    public string subtype { get; set; }
    public string caption { get; set; }
    public string copyright { get; set; }
}

ArticleItem sınıfı;

public class ArticleItem
{
    public ArticleDoc[] docs { get; set; }
    public ArticleMeta meta { get; set; }
}

Article sınıfı;

public class Article
{
    public string status;
    public string copyright;
    public ArticleItem response;
}

Sınıflara dikkat ederseniz hepsi birbiriyle ilişkili ve OOP modeline göre planlanmıştır. Aslında hepsi tek bir Article sınıfını üretmek için oluşturulmuştur.

Artık makale arama işlemini son adımlarını gerçekleştirebiliriz.

ArticleSearchCallers sınıfını oluşturalım;

public static class ArticleSearchCallers
{
    public static Article GetNYTArticleSearch(EnumArticles type, string parameter)
    {
        Article data = null;
        using (WebParser wp = new WebParser(APIOptions.NYTSearchBaseUrl))
        {
            string qr = APIOptions.NYTArticleSearch + type + "=" + parameter;
            string res = wp.Request(qr, Method.GET, APIOptions.NYTApiKeyStr, APIOptions.NYTApiKeyVal);                
            data = wp.DeserializeJson<Article>(res);
        }
        return data;
    }
}

Artık arama API’sini de kullanabiliriz.

Örnek 1:

var data = ArticleSearchCallers.GetNYTArticleSearch(EnumArticles.q, "Machine Learning");
foreach (var d in data.response.docs)
{
    Console.WriteLine(d.web_url);
}

Örnek 2:

var data = ArticleSearchCallers.GetNYTArticleSearch(EnumArticles.page, "2");
foreach (var d in data.response.docs)
{
    Console.WriteLine(d.web_url);
}

Uzun bir makale oldu. Umarım faydalı olmuştur.

Başarılar.
Cihan Özhan

Yorumla

Yorum