Belli bir div bloğundaki divleri parse etme

Delphi'de kod yazma ile ilgili sorularınızı bu foruma yazabilirsiniz.
Cevapla
Lord_Ares
Üye
Mesajlar: 1070
Kayıt: 15 Eki 2006 04:33
Konum: Çorlu

Belli bir div bloğundaki divleri parse etme

Mesaj gönderen Lord_Ares »

Merhaba. Html parse yaparken kolaylaştırmak adına aşağıdaki prosedürü kullanıyorum. Yaptığı iş, sadece verdiğim bir class adına sahip taglerin içindeki bilgiyi getirmek.

Kod: Tümünü seç

procedure Getir(const classname: string);
var
  Doc:IDispatch;
  Document: IHTMLDocument2;
  Body: IHTMLElement2;
  Tags: IHTMLElementCollection;
  Tag: IHTMLElement;
  I: Integer;
 
 begin
                      Doc:=Form1.WebBrowser1.Document;
                      Form1.Memo2.Clear;
                      if not Supports(Doc, IHTMLDocument2, Document) then exit;
                      if not Supports(Document.body, IHTMLElement2, Body) then exit;
                      Tags := Body.getElementsByTagName('*');
                      for I := 0 to Pred(Tags.length) do
                      begin
                        Tag := Tags.item(I, EmptyParam) as IHTMLElement;
                        if AnsiSameText(Tag._className, classname) then
                        begin
                                Form1.Memo2.Lines.Add(Tag.innerHTML);
                        end;
   end;


Sorunum ise , web saysafında, sadece belirlediğim bir ana div içindeki divlerde arama yapsın istiyorum. Aşağıda kısaca gösterim. Class adı Anadiv adında bir divin içerisinde sağ ve sol class adına sahip iki divimiz var. Benim yapmak istediğim sadece Soldiv class adınla oluşturulmuş divin içindeki Deneme adlı divdeki bilgileri çekmek. Yukarıdaki kodu kullandığımda hem sağ hem sol div içindeki bilgileri getiriyor. Normal de pos ile bu istediklerimi alabiliyorum fakat bu işlemler bazı sayfalarda başlangıç ve bitiş noktası bulmak,temizlemek gibi işlemler epey zor oluyor. Dolayısı ile yukarıdaki kod parçasının mantığı ile sayfadaki istediğim bloğa odaklanabilir ve sadece buradaki şu class adına sahip divlere bu işlemi yap diyebilirsem çok kolay olucak. Fikriniz ve yardımlarınız için şimdiden teşekkürler.
Örneğin şöyle;

Kod: Tümünü seç

<div class="Anadiv">

             <div class="Soldiv">                             <div class="Sağdiv">
                <div class="Deneme">55</div>                     <div class="Deneme">55</div>  
                <div class="Deneme">88</div>                     <div class="Deneme">88</div>
            </div>                                            </div>    

</div>
Kullanıcı avatarı
mrmarman
Üye
Mesajlar: 4741
Kayıt: 09 Ara 2003 08:13
Konum: İstanbul
İletişim:

Re: Belli bir div bloğundaki divleri parse etme

Mesaj gönderen mrmarman »

Hocam daha önceki paylaştığım fonksiyonu unuttuk mu ? bkz. bu link

Ayrıca verdiğin örnek bence hatalı olmuş. DIV ile ayrılmış iki farklı blok varsa bunlar matematikteki parantez gibidir. İlgilendiğin sayfada teknik bir hata olduğunu düşünüyorum.

Daha aşağıdaki verdiğim fonksiyonun senin örnekteki şekildeki kullanımı :

Kod: Tümünü seç

procedure TForm1.BitBtn1Click(Sender: TObject);
Const
  IstedigimDIV = '<div class="Soldiv"';
begin
  Memo1.Lines.Text := FaceKonusma( IstedigimDIV, WebBrowser1.Oleobject.Document.Body.InnerHTML );
end;

Kod: Tümünü seç

Function FaceKonusma( strDIV, strIcerik:String ):String;
Var
  strBlok, strUrun: String;
  iDIVsay, iAc, iKapa: Integer;
begin
 // Yapılacak işlem strDIV parametresinde verileni bulursak, bulunan <DIV>'e konumlanmak
 // sonra da alt <DIV Class oluşumlarını (+) / (-) yönde sayarak istediğimiz DIV'in
 // yani tüm ana DIV bloğunu yakalamak.
  Result := '';
  if Pos( strDIV, strIcerik ) <= 0 then EXIT;
  strBlok := strIcerik;
  System.Delete( strBlok, 1, Pos( strDIV, strBlok )-1 );
  iDIVSay := 1; // bizim ana <DIV> bunu (0) olana kadar gidip gelicez...
  strUrun := '';
  // Başlıyoruz...
  // Önce aradığımız ana strDIV'i ekleyip, strBloktakini imha ediyoruz. Ayağımıza dolaşmasın :)
  strUrun := strUrun + Copy( strBlok, 1, Pos('strDIV', strBlok) + Length(strDIV)-1 );
    System.Delete( strBlok, 1, Pos('strDIV', strBlok) + Length(strDIV)-1 );
  while iDIVSay > 0 do
  begin
    iAc   := Pos( '<DIV',   UpperCase( strBlok ) );
    iKapa := Pos( '</DIV>', UpperCase( strBlok ) );
    // bir '<div' başlangıcı ve bir '</div' sonlandırıcı bulduk.
    // Şimdi (iAc ve iKapa) hangi pozisyon büyük / küçük onu sorgulayarak
    // aradığımız master <DIV> 'i kapsıyor mu onu öğrenicez...
    if iAc < iKapa then
    begin // kurallı bir blok ama bizim için yeterli değil...
      strUrun := strUrun + Copy( strBlok, 1, iAc + 3 ); // en yakındaki = '<div'
        System.Delete( strBlok, 1, iAc + 3 ); // yakındakine kadar (dahil) sil...
      inc(iDIVSay); // bir iç blok ekledik...
    end else
    begin // kırık kural bulduk...
      strUrun := strUrun + Copy( strBlok, 1, iKapa + 5 ); // en yakındaki = '</div'
        System.Delete( strBlok, 1, iKapa + 5 ); // yakındakine kadar (dahil) sil...
      dec(iDIVSay); // bir iç blok düştük...
    end;
  end; // while
  Result := strUrun;
end;
Resim
Resim ....Resim
Lord_Ares
Üye
Mesajlar: 1070
Kayıt: 15 Eki 2006 04:33
Konum: Çorlu

Re: Belli bir div bloğundaki divleri parse etme

Mesaj gönderen Lord_Ares »

MrMarman hocam unuturmuyum o koda bayılıyorum. Fakat bazen doğru yakalamıyor oda sanırım açtığım site ile alakalı oluyor. Örneğin o kod ile 12 sitede başarılı bir şekilde çalıştım. Fakat öyle siteler denk geldki onlarda div başını buldu, fakat sonunu yanlış aldı tamamını alamadı. Bende hatasını bulamadığım için gözüme çarpan bu kodu denemek istedim o sitelerde. Aslında bu koddaki amacım madem böyle hazır bir yapı var bunu tüm siteye değilde sitenin belli bir noktasına odaklanıp kullanabilecekmiyim diye bakmak. Paylaştığım site kodunu açık anlaşılsın diye elle oluşturdum. Gerçekte çekmeye çalıştığım yerhttp://www.tahmin102.com/kuponlar/ bu sayfanın kaynak koduna bakarsanız sonuçlanmış kuponlar ve kazançlı kuponlar başlığında iki bölüm var, bunlar bir ana divin (kuponlar_blok) içerisinde left_blok ve right_blok ikiye ayrılmış. Bende dedim ki tüm sayfadaki classları tek tek gezeceğine left_blok divine odaklansın sadece buradaki class adı t102-kupon olanlara baksın. Bu işi pos ile yaparsam ana div bloğu için başlangıç ve bitiş noktası ver , gelen bilgide aradığın div içindekileri için başlangıç bitiş noktası ver gibi işlemlere gerek kalmaz diye düşündüm.

Bana bunu düşündüren şu oldu aslında , Document.body diyerek madem html yapının body kısmına odaklanıp o kısımda arama yaptırabilyorsak yukarıdaki yapı ile ozaman istediğimiz dive odaklanır o divde arama yaptırır böylece tüm sayfadaki divleri gezmemiş olur işlem hızlı olurum. Ayrıca silme, pos ile tekrar odaklan gibi işlemlere gerek kalmaz. Onun için bu yapı dikkatimi çekti deneyeyim dedim :D
Kullanıcı avatarı
mrmarman
Üye
Mesajlar: 4741
Kayıt: 09 Ara 2003 08:13
Konum: İstanbul
İletişim:

Re: Belli bir div bloğundaki divleri parse etme

Mesaj gönderen mrmarman »

Hocam bence bir kullanım hatası var...

Aşağıdaki gibi denedim, sorunsuz geldi... Tam kaynak kodu ve projesini de ekliyorum.

Kod: Tümünü seç

Function DIV_Oku( strDIV, strIcerik:String ):String;
Var
  strBlok, strUrun: String;
  iDIVsay, iAc, iKapa: Integer;
begin
 // Yapılacak işlem strDIV parametresinde verileni bulursak, bulunan <DIV>'e konumlanmak
 // sonra da alt <DIV Class oluşumlarını (+) / (-) yönde sayarak istediğimiz DIV'in
 // yani tüm ana DIV bloğunu yakalamak.
  Result := '';
  if Pos( strDIV, strIcerik ) <= 0 then EXIT;
  strBlok := strIcerik;
  System.Delete( strBlok, 1, Pos( strDIV, strBlok )-1 );
  iDIVSay := 1; // bizim ana <DIV> bunu (0) olana kadar gidip gelicez...
  strUrun := '';
  // Başlıyoruz...
  // Önce aradığımız ana strDIV'i ekleyip, strBloktakini imha ediyoruz. Ayağımıza dolaşmasın :)
  strUrun := strUrun + Copy( strBlok, 1, Pos(strDIV, strBlok) + Length(strDIV)-1 );
    System.Delete( strBlok, 1, Pos(strDIV, strBlok) + Length(strDIV)-1 );
  while iDIVSay > 0 do
  begin
    iAc   := Pos( '<DIV',   UpperCase( strBlok ) );
    iKapa := Pos( '</DIV>', UpperCase( strBlok ) );
    // bir '<div' başlangıcı ve bir '</div' sonlandırıcı bulduk.
    // Şimdi (iAc ve iKapa) hangi pozisyon büyük / küçük onu sorgulayarak
    // aradığımız master <DIV> 'i kapsıyor mu onu öğrenicez...
    if iAc < iKapa then
    begin // kurallı bir blok ama bizim için yeterli değil...
      strUrun := strUrun + Copy( strBlok, 1, iAc + 3 ); // en yakındaki = '<div'
        System.Delete( strBlok, 1, iAc + 3 ); // yakındakine kadar (dahil) sil...
      inc(iDIVSay); // bir iç blok ekledik...
    end else
    begin // kırık kural bulduk...
      strUrun := strUrun + Copy( strBlok, 1, iKapa + 5 ); // en yakındaki = '</div'
        System.Delete( strBlok, 1, iKapa + 5 ); // yakındakine kadar (dahil) sil...
      dec(iDIVSay); // bir iç blok düştük...
    end;
  end; // while
  Result := strUrun;
end;
Var
  xIslemTamam : Boolean = False;

procedure TForm1.BitBtn1Click(Sender: TObject);
Const
  strUrl = 'http://www.tahmin102.com/kuponlar/';
  strLeftBlokClass  = '<DIV class=left_blok';
  strRightBlokClass = '<DIV class=right_blok';
Var
  strIcerik : String;
begin
  WebBrowser1.Navigate(strURL);
  while WebBrowser1.ReadyState <> READYSTATE_COMPLETE do
  begin
    Sleep(1);
    Application.ProcessMessages;
  end;
  xIslemTamam := False;
  WebBrowser1.OleObject.Document.GetElementByID('inputUname').Value    := 'loginadi'; // login name
  WebBrowser1.OleObject.Document.GetElementByID('inputPassword').Value := 'parola';   // password
  WebBrowser1.OleObject.Document.GetElementByID('submit').Click;
  while NOT xIslemTamam do
  begin
    Sleep(1);
    Application.ProcessMessages;
  end;
  strIcerik := WebBrowser1.OleObject.Document.Body.InnerHTML;

  With TStringList.Create do
  begin
    Text := DIV_Oku( strLeftBlokClass, strIcerik );
    SaveToFile( ChangeFileExt( Application.Exename, '_LeftBlock.HTML') );

    Text := DIV_Oku( strRightBlokClass, strIcerik );;
    SaveToFile( ChangeFileExt( Application.Exename, '_RightBlock.HTML') );
    Free;
  end;
end;

procedure TForm1.WebBrowser1DocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
Const
  strLoginSonrasiURL = 'http://www.tahmin102.com/kuponlar/';
begin
  if URL = strLoginSonrasiURL then xIslemTamam := True;
end;
Dosya ekleri
WEB_Kuponlar_DIV.rar
HTML'den ilgilenilen DIV bloğunu alma örneği (Proje)
(17.64 KiB) 74 kere indirildi
Resim
Resim ....Resim
Lord_Ares
Üye
Mesajlar: 1070
Kayıt: 15 Eki 2006 04:33
Konum: Çorlu

Re: Belli bir div bloğundaki divleri parse etme

Mesaj gönderen Lord_Ares »

Hocam evet kodunuz bunda çalışıyor. Amacım şuydu kendimi ifade edemedim özür diliyorum.
Html parse işlemlerinde genelde çalışma şeklimiz şöyle

1)Pos ile ana blok başını ve sonunu bul ve bu bloğu değişkene al.
2)Tekrar bir pos kullanarak aldığın bu blok içinde arama gerçekleştirerek aradığımız bilginin olduğu kısmı al.
3)Gereksiz yerleri temizle.

Oysa diğer yapıya bakarsak, ne silme işlemi gerektiriyor nede pos ile tekrar nereye konumlanacağımızı bulmaya. Bende şöyle birşey denedim, ilk paylaştığım kod ile sadece left_div bloğunu aldım ve html sayfa olarak kaydettim. Sonra bunu açarak aynı kod ile class adı t102 olanları al dedim istediğim oldu. Hani diyorum ki, böyle iki işlem yapmaktansa (sayfayı aç istedin yeri al html olarak kaydet) sayfayı ilk açtığımda kaydetmeden direk o dive odaklanıp bu işi yapayım. İşte orada kalıyorum :)))) Sizce aşağıdaki düşüncem yanlış mı fikriniz nedir ?

Kod: Tümünü seç

           Tag := Tags.item(I, EmptyParam) as IHTMLElement;/// Burada left div bloğuna ulaştım artık tag değişkenimde
           if AnsiSameText(Tag._className, classname) then
           begin
 ///// burada prosedüre kendini tekrar çağır işlemi birde burada yap diyebilirsem pos ve delete işlemleri ile hiç uğraşmadan parse yapabilen bir kalıp bulmuş yapmış olurum diye düşündüm

           end;
   end;


Kullanıcı avatarı
mrmarman
Üye
Mesajlar: 4741
Kayıt: 09 Ara 2003 08:13
Konum: İstanbul
İletişim:

Re: Belli bir div bloğundaki divleri parse etme

Mesaj gönderen mrmarman »

Merhaba.

Seni çok iyi anlıyorum ama bu kolaylıklara alışıp PARSE işlemini ikinci plana atmamak lazım derim, neden biliyor musun ? MultiPlatform uyumu / dostu için.

Şöyle açayım. Bugün bir proje geliştirirken kendi düşüncemdir ve altını çiziyorum bilgi (birikim demiyorum) kültürü olarak %50, Windows %30 WEB (PHP,JAVA) , %20 mobil kod yazabilmek gerekiyor. Buna ulaştığımızda gündelik işleri yapabilir durumdayız demektir diyorum.

%50 + %30'luk toplam %80 native işlemler ve Windows API kullanımı demektir. Bunların Delphi native (FireMonkey framework'de yer bulanlar) dışındakiler mobil dünyada çalışmayacaktır.

Şimdi bu mobil olan %20 içindeki işlemlerin %80'i de -adı üzerinde mobil platform- WEB üzerinde gerçekleştiğine göre %20 + %30 olarak oldu sana %50. Buna alışmamız lazım diye düşünüyorum.

Konuya dönecek olursak; DOM destekli ( MSHTML koyuyoruz ya USES'a ) yarın mobil platforma geçtiğimizde bunları bir değeri yoktur, kendi DOM altyapımızı kurmamız gerek. İşte PARSE fonksiyonlarının geliştirilmesi bu noktada işimizi görüyor. PARSE ile yapılan fonksiyonlar tüm platformlarda iş görürken, DOM yapısı sadece Windows tabanlı olarak iş görür. Bir de ek bilgi, Bu DOM yapıyı JAVASCRIPT ile web üzerine yıkabiliyoruz ama biraz sancılı bir işlem tahmin edersin.

Kendimce Windows veya Mobile farketmez; XML dahi olsa XMLDocument ikinci plana bırakıp PARSE ile yaklaşmaya gayret ediyorum. Gelen verideki yapıda bozukluk olsa dahi sağlam olan yapı kadarlık veriyi yine elde edebiliyoruz. XML / DOM gibi yapılar eğer gelen veri ağacında bozukluk varsa sorgulama çöküyor.
Resim
Resim ....Resim
Lord_Ares
Üye
Mesajlar: 1070
Kayıt: 15 Eki 2006 04:33
Konum: Çorlu

Re: Belli bir div bloğundaki divleri parse etme

Mesaj gönderen Lord_Ares »

Hımm anladım hocam evet çok doğru söylediniz aslında işin kolayına kaçarken, bir yandan diğer platformlar için kendimi köreltmiş sayılıyorum. Dediğinizi düşününce biraz, bu kalıbı oluşturmaya çalışmak bu platform için geçerli olabilir ama diğer platformlarda bana ileride kullanmak istersem bir yarar sağlamaz. Madem bir kalıp yakalamaya çalışıyorum dediğiniz gibi çoklu platformlu bir ufukta düşünmeliyim. Fikriniz için çok teşekkürler.
Cevapla