Ne yapmalı? Normal ifade deseni dizenin hiçbir yerinde eşleşmiyor mu?


182

<input>Bu kalıbı kullanarak tip "gizli" alanları eşleştirmeye çalışıyorum :

/<input type="hidden" name="([^"]*?)" value="([^"]*?)" />/

Bu örnek form verileridir:

<input type="hidden" name="SaveRequired" value="False" /><input type="hidden" name="__VIEWSTATE1" value="1H4sIAAtzrkX7QfL5VEGj6nGi+nP" /><input type="hidden" name="__VIEWSTATE2" value="0351118MK" /><input type="hidden" name="__VIEWSTATE3" value="ZVVV91yjY" /><input type="hidden" name="__VIEWSTATE0" value="3" /><input type="hidden" name="__VIEWSTATE" value="" /><input type="hidden" name="__VIEWSTATE" value="" />

Ama ben emin değilim type, nameve valuenitelikleri hep aynı sırada görünür. Eğer typenitelik geçen geliyor benim desende o başında olduğu için, maç başarısız olur.

Soru: Etiketteki
özelliklerin konumundan bağımsız olarak desenimi eşleştirmek için desenimi nasıl değiştirebilirim <input>?

Not: Bu arada düzenli ifadeleri test etmek için Adobe Air tabanlı RegEx Masaüstü Aracı'nı kullanıyorum .


4
Düzenli vs tartışmaya karşı düzenli olduğu için oluşturulan html üzerinde kontrole sahipseniz regex mükemmel bir çözümdür, ama benim durumum html gelecekte regex yerine ayrıştırıcı kullanmak için nasıl değişeceğini bilmiyorum, kullandım üzerinde kontrol sahibi olduğum parçalar projemde regex
Salman

Yanıtlar:


108

Tüm cevapların aksine, regex yapmaya çalıştığınız şey mükemmel geçerli bir çözümdür. Bunun nedeni, dengeli etiketleri eşleştirmeye çalışmamanızdır; regex ile bu mümkün olmaz! Ancak yalnızca bir etikette bulunanlarla eşleşiyorsunuz ve bu tamamen düzenli.

Sorun burada. Tek bir regex ile yapamazsınız ... bir <input>etiketi yakalamak için bir eşleşme yapmanız ve ardından daha fazla işlem yapmanız gerekir . Bunun yalnızca özellik değerlerinin hiçbirinde bir >karakter yoksa işe yarayacağını unutmayın, bu yüzden mükemmel değildir, ancak aklı başında girişler için yeterli olmalıdır.

İşte size ne demek istediğimi göstermek için bazı Perl (sözde) kodu:

my $html = readLargeInputFile();

my @input_tags = $html =~ m/
    (
        <input                      # Starts with "<input"
        (?=[^>]*?type="hidden")     # Use lookahead to make sure that type="hidden"
        [^>]+                       # Grab the rest of the tag...
        \/>                         # ...except for the />, which is grabbed here
    )/xgm;

# Now each member of @input_tags is something like <input type="hidden" name="SaveRequired" value="False" />

foreach my $input_tag (@input_tags)
{
  my $hash_ref = {};
  # Now extract each of the fields one at a time.

  ($hash_ref->{"name"}) = $input_tag =~ /name="([^"]*)"/;
  ($hash_ref->{"value"}) = $input_tag =~ /value="([^"]*)"/;

  # Put $hash_ref in a list or something, or otherwise process it
}

Buradaki temel ilke, tek bir düzenli ifade ile çok fazla şey yapmaya çalışmayın. Fark ettiğiniz gibi, düzenli ifadeler belirli bir düzen düzenler. Bunun yerine yapmanız gereken, önce ayıklamaya çalıştığınız içeriğin CONTEXT ile eşleşmek, sonra istediğiniz verilere boyun eğmektir.

EDIT: Ancak, genel olarak, bir HTML ayrıştırıcı kullanmak muhtemelen daha kolay ve daha iyi ve gerçekten kodunuzu yeniden tasarlamayı veya hedeflerinizi yeniden incelemek düşünmelisiniz. :-) Ama bu cevabı, HTML'nin herhangi bir alt kümesini ayrıştırmanın imkansız olduğu diz sarsıntısı tepkisine bir sayaç olarak göndermek zorunda kaldım: HTML ve XML, tüm spesifikasyonu düşündüğünüzde düzensiz, ancak bir etiketin spesifikasyonu düzenli , kesinlikle PCRE'nin gücü dahilindedir.


14
Buradaki tüm cevapların aksine değil . :)
tchrist

6
@tchrist: Benimkini gönderdiğimde cevabın burada değildi. ;-)
Platinum Azure

7
yah iyi - nedense yazmak seninkinden daha uzun sürdü. Bence klavyemin yağlanması gerekiyor. :)
tchrist

6
Bu geçersiz HTML - değer = "olmalı & lt; Bundan gerçekten emin misiniz? & Gt;" Kazıma yeri böyle bir şeyden kaçan kötü bir iş çıkarırsa, daha sofistike bir çözüme ihtiyaç duyar - ama eğer doğru yaparlarsa (ve üzerinde kontrolü varsa, doğru olduğundan emin olmalıdır) o zaman iyi olur.
Ross Snyder

14
Konuyla ilgili en iyi SO cevabına zorunlu bağlantı (muhtemelen en iyi SO cevap süresi): stackoverflow.com/questions/1732348/…
Daniel Ribeiro

682

Oh Evet HTML Ayrıştırma Regexes kullanabilirsiniz!

Yapmaya çalıştığınız görev için normal ifadeler gayet iyi!

İse çoğu kişi normal ifadeler ile ayrıştırma HTML zorluğunu hafife ve bu nedenle çok kötü yapmak doğrudur.

Ancak bu, hesaplama teorisiyle ilgili bazı temel kusur değildir. Bu saçmalık burada çok papağan , ama onlara inanmıyorsun.

Bu yüzden kesinlikle yapılabilir olsa da (bu ilan bu tartışılmaz gerçeğin bir varoluş kanıtıdır), bu olması gerektiği anlamına gelmez   .

Normal ifadelerden özel, özel amaçlı bir HTML ayrıştırıcısının ne anlama geldiğini yazma görevine bağlı olup olmadığınıza kendiniz karar vermelisiniz. Çoğu insan değildir.

Ama ben değilim. ☻


Genel Normal İfade Tabanlı HTML Ayrıştırma Çözümleri

Önce rastgele HTML ile rastgele HTML ayrıştırmanın ne kadar kolay olduğunu göstereceğim . Programın tamamı bu yayının sonunda, ancak ayrıştırıcının kalbi:

for (;;) {
  given ($html) {
    last                    when (pos || 0) >= length;
    printf "\@%d=",              (pos || 0);
    print  "doctype "   when / \G (?&doctype)  $RX_SUBS  /xgc;
    print  "cdata "     when / \G (?&cdata)    $RX_SUBS  /xgc;
    print  "xml "       when / \G (?&xml)      $RX_SUBS  /xgc;
    print  "xhook "     when / \G (?&xhook)    $RX_SUBS  /xgc;
    print  "script "    when / \G (?&script)   $RX_SUBS  /xgc;
    print  "style "     when / \G (?&style)    $RX_SUBS  /xgc;
    print  "comment "   when / \G (?&comment)  $RX_SUBS  /xgc;
    print  "tag "       when / \G (?&tag)      $RX_SUBS  /xgc;
    print  "untag "     when / \G (?&untag)    $RX_SUBS  /xgc;
    print  "nasty "     when / \G (?&nasty)    $RX_SUBS  /xgc;
    print  "text "      when / \G (?&nontag)   $RX_SUBS  /xgc;
    default {
      die "UNCLASSIFIED: " .
        substr($_, pos || 0, (length > 65) ? 65 : length);
    }
  }
}

Okumanın ne kadar kolay olduğunu görüyor musunuz?

Yazıldığı gibi, her HTML parçasını tanımlar ve parçayı nerede bulduğunu söyler. Herhangi bir parça türüyle veya bunlardan daha belirli türler için istediğinizi yapmak için kolayca değiştirebilirsiniz.

Başarısız test durumlarım yok (sol :): Bu kodu 100.000'den fazla HTML dosyasında başarıyla çalıştırdım - hızlı ve kolay bir şekilde ellerimi alabildiğim her biri. Bunların ötesinde, saf ayrıştırıcıları kırmak için özel olarak oluşturulmuş dosyalar üzerinde de çalıştırıyorum .

Bu saf bir ayrıştırıcı değil .

Oh, eminim mükemmel değil, ama henüz kırmayı başaramadım. Bir şey yapsa bile, programın net yapısı nedeniyle düzeltmenin kolayca sığacağını düşünüyorum. Regex-heavy programlarında bile yapı bulunmalıdır.

Şimdi bu yoldan çekildiğine göre, OP'nin sorusunu ele alalım.

Regexes Kullanarak OP'nin Görevini Çözme Demosu

html_input_rxAşağıda eklediğim küçük program aşağıdaki çıktıyı üretir, böylece HTML'yi regexes ile ayrıştırma yapmak istediğiniz şey için iyi çalışır:

% html_input_rx Amazon.com-_Online_Shopping_for_Electronics,_Apparel,_Computers,_Books,_DVDs_\&_more.htm 
input tag #1 at character 9955:
       class => "searchSelect"
          id => "twotabsearchtextbox"
        name => "field-keywords"
        size => "50"
       style => "width:100%; background-color: #FFF;"
       title => "Search for"
        type => "text"
       value => ""

input tag #2 at character 10335:
         alt => "Go"
         src => "http://g-ecx.images-amazon.com/images/G/01/x-locale/common/transparent-pixel._V192234675_.gif"
        type => "image"

Giriş Etiketlerini Ayrıştırın, Bkz. Kötü Giriş Yok

Yukarıdaki çıktıyı üreten programın kaynağı.

#!/usr/bin/env perl
#
# html_input_rx - pull out all <input> tags from (X)HTML src
#                  via simple regex processing
#
# Tom Christiansen <tchrist@perl.com>
# Sat Nov 20 10:17:31 MST 2010
#
################################################################

use 5.012;

use strict;
use autodie;
use warnings FATAL => "all";    
use subs qw{
    see_no_evil
    parse_input_tags
    input descape dequote
    load_patterns
};    
use open        ":std",
          IN => ":bytes",
         OUT => ":utf8";    
use Encode qw< encode decode >;

    ###########################################################

                        parse_input_tags 
                           see_no_evil 
                              input  

    ###########################################################

until eof(); sub parse_input_tags {
    my $_ = shift();
    our($Input_Tag_Rx, $Pull_Attr_Rx);
    my $count = 0;
    while (/$Input_Tag_Rx/pig) {
        my $input_tag = $+{TAG};
        my $place     = pos() - length ${^MATCH};
        printf "input tag #%d at character %d:\n", ++$count, $place;
        my %attr = ();
        while ($input_tag =~ /$Pull_Attr_Rx/g) {
            my ($name, $value) = @+{ qw< NAME VALUE > };
            $value = dequote($value);
            if (exists $attr{$name}) {
                printf "Discarding dup attr value '%s' on %s attr\n",
                    $attr{$name} // "<undef>", $name;
            } 
            $attr{$name} = $value;
        } 
        for my $name (sort keys %attr) {
            printf "  %10s => ", $name;
            my $value = descape $attr{$name};
            my  @Q; given ($value) {
                @Q = qw[  " "  ]  when !/'/ && !/"/;
                @Q = qw[  " "  ]  when  /'/ && !/"/;
                @Q = qw[  ' '  ]  when !/'/ &&  /"/;
                @Q = qw[ q( )  ]  when  /'/ &&  /"/;
                default { die "NOTREACHED" }
            } 
            say $Q[0], $value, $Q[1];
        } 
        print "\n";
    } 

}

sub dequote {
    my $_ = $_[0];
    s{
        (?<quote>   ["']      )
        (?<BODY>    
          (?s: (?! \k<quote> ) . ) * 
        )
        \k<quote> 
    }{$+{BODY}}six;
    return $_;
} 

sub descape {
    my $string = $_[0];
    for my $_ ($string) {
        s{
            (?<! % )
            % ( \p{Hex_Digit} {2} )
        }{
            chr hex $1;
        }gsex;
        s{
            & \043 
            ( [0-9]+ )
            (?: ; 
              | (?= [^0-9] )
            )
        }{
            chr     $1;
        }gsex;
        s{
            & \043 x
            ( \p{ASCII_HexDigit} + )
            (?: ; 
              | (?= \P{ASCII_HexDigit} )
            )
        }{
            chr hex $1;
        }gsex;

    }
    return $string;
} 

sub input { 
    our ($RX_SUBS, $Meta_Tag_Rx);
    my $_ = do { local $/; <> };  
    my $encoding = "iso-8859-1";  # web default; wish we had the HTTP headers :(
    while (/$Meta_Tag_Rx/gi) {
        my $meta = $+{META};
        next unless $meta =~ m{             $RX_SUBS
            (?= http-equiv ) 
            (?&name) 
            (?&equals) 
            (?= (?&quote)? content-type )
            (?&value)    
        }six;
        next unless $meta =~ m{             $RX_SUBS
            (?= content ) (?&name) 
                          (?&equals) 
            (?<CONTENT>   (?&value)    )
        }six;
        next unless $+{CONTENT} =~ m{       $RX_SUBS
            (?= charset ) (?&name) 
                          (?&equals) 
            (?<CHARSET>   (?&value)    )
        }six;
        if (lc $encoding ne lc $+{CHARSET}) {
            say "[RESETTING ENCODING $encoding => $+{CHARSET}]";
            $encoding = $+{CHARSET};
        }
    } 
    return decode($encoding, $_);
}

sub see_no_evil {
    my $_ = shift();

    s{ <!    DOCTYPE  .*?         > }{}sx; 
    s{ <! \[ CDATA \[ .*?    \]\] > }{}gsx; 

    s{ <script> .*?  </script> }{}gsix; 
    s{ <!--     .*?        --> }{}gsx;

    return $_;
}

sub load_patterns { 

    our $RX_SUBS = qr{ (?(DEFINE)
        (?<nv_pair>         (?&name) (?&equals) (?&value)         ) 
        (?<name>            \b (?=  \pL ) [\w\-] + (?<= \pL ) \b  )
        (?<equals>          (?&might_white)  = (?&might_white)    )
        (?<value>           (?&quoted_value) | (?&unquoted_value) )
        (?<unwhite_chunk>   (?: (?! > ) \S ) +                    )
        (?<unquoted_value>  [\w\-] *                              )
        (?<might_white>     \s *                                  )
        (?<quoted_value>
            (?<quote>   ["']      )
            (?: (?! \k<quote> ) . ) *
            \k<quote> 
        )
        (?<start_tag>  < (?&might_white) )
        (?<end_tag>          
            (?&might_white)
            (?: (?&html_end_tag) 
              | (?&xhtml_end_tag) 
             )
        )
        (?<html_end_tag>       >  )
        (?<xhtml_end_tag>    / >  )
    ) }six; 

    our $Meta_Tag_Rx = qr{                          $RX_SUBS 
        (?<META> 
            (?&start_tag) meta \b
            (?:
                (?&might_white) (?&nv_pair) 
            ) +
            (?&end_tag)
        )
    }six;

    our $Pull_Attr_Rx = qr{                         $RX_SUBS
        (?<NAME>  (?&name)      )
                  (?&equals) 
        (?<VALUE> (?&value)     )
    }six;

    our $Input_Tag_Rx = qr{                         $RX_SUBS 

        (?<TAG> (?&input_tag) )

        (?(DEFINE)

            (?<input_tag>
                (?&start_tag)
                input
                (?&might_white) 
                (?&attributes) 
                (?&might_white) 
                (?&end_tag)
            )

            (?<attributes>
                (?: 
                    (?&might_white) 
                    (?&one_attribute) 
                ) *
            )

            (?<one_attribute>
                \b
                (?&legal_attribute)
                (?&might_white) = (?&might_white) 
                (?:
                    (?&quoted_value)
                  | (?&unquoted_value)
                )
            )

            (?<legal_attribute> 
                (?: (?&optional_attribute)
                  | (?&standard_attribute)
                  | (?&event_attribute)
            # for LEGAL parse only, comment out next line 
                  | (?&illegal_attribute)
                )
            )

            (?<illegal_attribute>  (?&name) )

            (?<required_attribute> (?#no required attributes) )

            (?<optional_attribute>
                (?&permitted_attribute)
              | (?&deprecated_attribute)
            )

            # NB: The white space in string literals 
            #     below DOES NOT COUNT!   It's just 
            #     there for legibility.

            (?<permitted_attribute>
                  accept
                | alt
                | bottom
                | check box
                | checked
                | disabled
                | file
                | hidden
                | image
                | max length
                | middle
                | name
                | password
                | radio
                | read only
                | reset
                | right
                | size
                | src
                | submit
                | text
                | top
                | type
                | value
            )

            (?<deprecated_attribute>
                  align
            )

            (?<standard_attribute>
                  access key
                | class
                | dir
                | ltr
                | id
                | lang
                | style
                | tab index
                | title
                | xml:lang
            )

            (?<event_attribute>
                  on blur
                | on change
                | on click
                | on dbl   click
                | on focus
                | on mouse down
                | on mouse move
                | on mouse out
                | on mouse over
                | on mouse up
                | on key   down
                | on key   press
                | on key   up
                | on select
            )
        )
    }six;

}

UNITCHECK {
    load_patterns();
} 

END {
    close(STDOUT) 
        || die "can't close stdout: $!";
} 

İşte böyle! Hiçbir şey! :)

Regexes ile olan yeteneğinizin belirli bir ayrıştırma görevine bağlı olup olmadığını sadece siz değerlendirebilirsiniz. Herkesin beceri düzeyi farklıdır ve her yeni görev farklıdır. İyi tanımlanmış bir giriş kümesine sahip olduğunuz işler için, normal ifadeler açık bir şekilde doğru seçimdir, çünkü başa çıkmak için kısıtlanmış bir HTML alt kümeniz olduğunda bazılarını bir araya getirmek önemsizdir. Regex yeni başlayanlar bile bu işleri regexes ile ele almalıdır. Başka her şey aşırı.

Bununla birlikte , HTML daha az çivilenmeye başladığında, tahmin edemeyeceğiniz, ancak tamamen yasal olan yollarla çarpmaya başladıktan sonra, daha farklı şeyler veya daha karmaşık bağımlılıklar ile eşleşmeniz gerektiğinde, sonunda bir noktaya ulaşacaksınız. regexes kullanan bir çözümü uygulamak için ayrıştırıcı sınıf kullanmak zorunda olduğunuzdan daha fazla çalışmanız gerekir. Başabaş noktasının nereye düştüğü, regexes ile kendi konfor seviyenize tekrar bağlıdır.

Peki ne yapmalıyım?

Sana ne diyeceğim gitmiyorum gerekir yapmak ya ne edemez yapmak. Bence bu yanlış. Size sadece olasılıklar sunmak, gözlerinizi biraz açmak istiyorum. Ne yapmak istediğinizi ve nasıl yapmak istediğinizi seçebilirsiniz. Mutlaklık yoktur - ve hiç kimse kendi durumunuzu kendiniz kadar iyi bilmez. Bir şey çok fazla iş gibi görünüyorsa, belki de öyle. Programlama eğlenceli olmalı . Değilse, yanlış yapıyor olabilirsiniz.

html_input_rxProgramıma herhangi bir sayıda geçerli şekilde bakılabilir . Bir şekilde sen gerçekten de can düzenli ifadeler ile ayrıştırma HTML. Ama bir diğeri, neredeyse herkesin sandığından çok, çok, çok daha zor olmasıdır. Bu kolayca benim program gereken bir kanıtıdır olduğu sonucuna yol açabilir değil gerçekten çok zor, çünkü yok.

Buna katılmayacağım. Kesinlikle programımda yaptığım her şey bir çalışmadan sonra sizin için bir anlam ifade etmiyorsa, bu tür bir görev için normal ifadeleri kullanmaya çalışmamalısınız. Belirli HTML için normal ifadeler mükemmeldir, ancak genel HTML için delilikle eşdeğerdir. Her zaman ayrıştırma sınıfları kullanıyorum, özellikle HTML ise kendimi oluşturmadım.

Küçük HTML ayrıştırma sorunları için en uygun regexes, büyük olanlar için soluk

Benim program neden açıklayıcı olarak alınmıştır bile değil ☺ Ben biraz geliyordu çünkü o olmak için, Tamam - - Genel HTML ayrıştırma için Regexes kullanmak daha fazla insan korkunç ortak kırmak yüzden yine de bir göz açıcı olmalıdır ve okunamayan, yapılandırılmamış ve sürdürülemez kalıplar yazma gibi kötü ve kötü alışkanlıklar

Desenler çirkin olmak zorunda değildir ve zor olmak zorunda değildir. Eğer çirkin desenler yaratırsanız, bu onlar üzerinde değil, size bir yansımadır.

Olağanüstü Mükemmel Regex Dili

Soruna yönelik güçlü çözümümün Perl'de yazıldığını belirtmem istendi. Şaşırdın mı? Fark etmedin mi? Bu vahiy bomba mı?

Diğer tüm araçların ve programlama dillerinin, Perl gibi regexes söz konusu olduğunda oldukça kullanışlı, etkileyici ve güçlü olmadığı doğrudur. Orada büyük bir spektrum var, bazıları diğerlerinden daha uygun. Genel olarak, kütüphane yerine temel dilin bir parçası olarak normal ifadeleri ifade eden dillerle çalışmak daha kolaydır. PCRE'de yapamayacağınız regexes ile hiçbir şey yapmadım, ancak C'yi kullanıyorsanız programı farklı yapılandıracaksınız.

Sonunda diğer diller, Perl'in şu anki regexes olduğu yere yetişecek. Bunu söylüyorum çünkü Perl başladığında Perl'in ifadelerine benzeyen başka kimse yoktu. Beğendiğiniz bir şey söyleyin, ancak Perl açıkça kazandı: herkes Perl'in regex'lerini gelişimlerinin çeşitli aşamalarında da kopyaladı. Perl, hangi aracı veya dili kullanırsanız kullanın, bugün modern desenlere güvendiğiniz neredeyse her şeye (neredeyse hepsi değil, neredeyse) her şeye öncülük etti. Yani sonuçta başkaları olacaktır yakalamak.

Ancak Perl'in geçmişte olduğu gibi, şimdi olduğu gibi yetişecekler. Her şey ilerler. Regexes'te Perl'in yol açtığı başka bir şey yoksa diğerleri izler. Herkes sonunda Perl'in nerede olduğunu yakaladığında Perl nerede olacak? Hiçbir fikrim yok, ama biz de taşınacağımızı biliyorum. Muhtemelen Perl₆'nun işçiliği modellerine daha yakın olacağız .

Bu tür bir şeyden hoşlanıyorsanız, ancak Perl₅'de kullanmak istiyorsanız, Damian Conway'in harika Regexp :: Grammars modülüyle ilgilenebilirsiniz . Tamamen harika ve programımda burada yaptığım şey, benimki gibi insanların beyaz boşluk veya alfabetik tanımlayıcılar olmadan bir araya getirdikleri desenleri yaptığı gibi ilkel görünüyor. Bunu kontrol et!


Basit HTML Parçalayıcı

İşte bu yazının başlangıcından itibaren merkez parçasını gösterdiğim ayrıştırıcıya tam kaynak.

Bunu , titizlikle test edilmiş bir ayrıştırma sınıfında kullanmanız gerektiğini söylemiyorum . Ama sırf regexes o kimse can ayrıştırma HTML büründüğü yorgun am onlar olamaz. Açıkça yapabilirsiniz ve bu program bu iddianın kanıtıdır.

Tabii, bu kolay değil, ama o olduğunu mümkün!

Ve bunu yapmaya çalışmak korkunç bir zaman kaybıdır, çünkü bu görev için kullanmanız gereken iyi ayrıştırma sınıfları vardır . Ayrıştırma çalışıyorum insanlara doğru cevap keyfi HTML değil imkansız olduğunu. Bu kolay ve gösterişsiz bir cevap. Doğru ve dürüst cevap, bunu denememeleri gerektiğidir, çünkü sıfırdan anlamak çok zahmetlidir; mükemmel çalışan bir tekerleği yeniden icat etmek için çaba sarf etmemeliler.

Öte yandan, öngörülebilir bir alt kümeye giren HTML'nin normal ifadelerle ayrıştırılması son derece kolaydır. İnsanların bunları kullanmaya çalışması şaşırtıcı değil, çünkü küçük problemler için oyuncak problemleri belki de hiçbir şey daha kolay olamazdı. Bu nedenle, aynı yaklaşımı zorunlu kılmadıkları için iki görevi - spesifik ve genel - birbirinden ayırmak çok önemlidir.

Gelecekte HTML ve normal ifadelerle ilgili soruların daha adil ve dürüst bir şekilde ele alınmasını umuyorum.

İşte benim HTML lexer'ım. Doğrulayıcı bir ayrıştırma yapmaya çalışmaz; sadece sözcüksel unsurları tanımlar. Bir HTML ayrıştırıcısından daha çok bir HTML parçası olarak düşünebilirsiniz . Bu yönde çok küçük izinler almasına rağmen, bozuk HTML'yi affetmez.

Tam HTML'yi kendiniz ayrıştırmasanız bile (ve neden çözmelisiniz? Bu çözülmüş bir sorundur!), Bu programın birçok insanın çok şey öğrenebileceğine inanıyorum. Zevk almak!

#!/usr/bin/env perl
#
# chunk_HTML - a regex-based HTML chunker
#
# Tom Christiansen <tchrist@perl.com
#   Sun Nov 21 19:16:02 MST 2010
########################################

use 5.012;

use strict;
use autodie;
use warnings qw< FATAL all >;
use open     qw< IN :bytes OUT :utf8 :std >;

MAIN: {
  $| = 1;
  lex_html(my $page = slurpy());
  exit();
}

########################################################################
sub lex_html {
    our $RX_SUBS;                                        ###############
    my  $html = shift();                                 # Am I...     #
    for (;;) {                                           # forgiven? :)#
        given ($html) {                                  ###############
            last                when (pos || 0) >= length;
            printf "\@%d=",          (pos || 0);
            print  "doctype "   when / \G (?&doctype)  $RX_SUBS  /xgc;
            print  "cdata "     when / \G (?&cdata)    $RX_SUBS  /xgc;
            print  "xml "       when / \G (?&xml)      $RX_SUBS  /xgc;
            print  "xhook "     when / \G (?&xhook)    $RX_SUBS  /xgc;
            print  "script "    when / \G (?&script)   $RX_SUBS  /xgc;
            print  "style "     when / \G (?&style)    $RX_SUBS  /xgc;
            print  "comment "   when / \G (?&comment)  $RX_SUBS  /xgc;
            print  "tag "       when / \G (?&tag)      $RX_SUBS  /xgc;
            print  "untag "     when / \G (?&untag)    $RX_SUBS  /xgc;
            print  "nasty "     when / \G (?&nasty)    $RX_SUBS  /xgc;
            print  "text "      when / \G (?&nontag)   $RX_SUBS  /xgc;
            default {
                die "UNCLASSIFIED: " .
                  substr($_, pos || 0, (length > 65) ? 65 : length);
            }
        }
    }
    say ".";
}
#####################
# Return correctly decoded contents of next complete
# file slurped in from the <ARGV> stream.
#
sub slurpy {
    our ($RX_SUBS, $Meta_Tag_Rx);
    my $_ = do { local $/; <ARGV> };   # read all input

    return unless length;

    use Encode   qw< decode >;

    my $bom = "";
    given ($_) {
        $bom = "UTF-32LE" when / ^ \xFf \xFe \0   \0   /x;  # LE
        $bom = "UTF-32BE" when / ^ \0   \0   \xFe \xFf /x;  #   BE
        $bom = "UTF-16LE" when / ^ \xFf \xFe           /x;  # le
        $bom = "UTF-16BE" when / ^ \xFe \xFf           /x;  #   be
        $bom = "UTF-8"    when / ^ \xEF \xBB \xBF      /x;  # st00pid
    }
    if ($bom) {
        say "[BOM $bom]";
        s/^...// if $bom eq "UTF-8";                        # st00pid

        # Must use UTF-(16|32) w/o -[BL]E to strip BOM.
        $bom =~ s/-[LB]E//;

        return decode($bom, $_);

        # if BOM found, don't fall through to look
        #  for embedded encoding spec
    }

    # Latin1 is web default if not otherwise specified.
    # No way to do this correctly if it was overridden
    # in the HTTP header, since we assume stream contains
    # HTML only, not also the HTTP header.
    my $encoding = "iso-8859-1";
    while (/ (?&xml) $RX_SUBS /pgx) {
        my $xml = ${^MATCH};
        next unless $xml =~ m{              $RX_SUBS
            (?= encoding )  (?&name)
                            (?&equals)
                            (?&quote) ?
            (?<ENCODING>    (?&value)       )
        }sx;
        if (lc $encoding ne lc $+{ENCODING}) {
            say "[XML ENCODING $encoding => $+{ENCODING}]";
            $encoding = $+{ENCODING};
        }
    }

    while (/$Meta_Tag_Rx/gi) {
        my $meta = $+{META};

        next unless $meta =~ m{             $RX_SUBS
            (?= http-equiv )    (?&name)
                                (?&equals)
            (?= (?&quote)? content-type )
                                (?&value)
        }six;

        next unless $meta =~ m{             $RX_SUBS
            (?= content )       (?&name)
                                (?&equals)
            (?<CONTENT>         (?&value)    )
        }six;

        next unless $+{CONTENT} =~ m{       $RX_SUBS
            (?= charset )       (?&name)
                                (?&equals)
            (?<CHARSET>         (?&value)    )
        }six;

        if (lc $encoding ne lc $+{CHARSET}) {
            say "[HTTP-EQUIV ENCODING $encoding => $+{CHARSET}]";
            $encoding = $+{CHARSET};
        }
    }

    return decode($encoding, $_);
}
########################################################################
# Make sure to this function is called
# as soon as source unit has been compiled.
UNITCHECK { load_rxsubs() }

# useful regex subroutines for HTML parsing
sub load_rxsubs {

    our $RX_SUBS = qr{
      (?(DEFINE)

        (?<WS> \s *  )

        (?<any_nv_pair>     (?&name) (?&equals) (?&value)         )
        (?<name>            \b (?=  \pL ) [\w:\-] +  \b           )
        (?<equals>          (?&WS)  = (?&WS)    )
        (?<value>           (?&quoted_value) | (?&unquoted_value) )
        (?<unwhite_chunk>   (?: (?! > ) \S ) +                    )

        (?<unquoted_value>  [\w:\-] *                             )

        (?<any_quote>  ["']      )

        (?<quoted_value>
            (?<quote>   (?&any_quote)  )
            (?: (?! \k<quote> ) . ) *
            \k<quote>
        )

        (?<start_tag>       < (?&WS)      )
        (?<html_end_tag>      >           )
        (?<xhtml_end_tag>   / >           )
        (?<end_tag>
            (?&WS)
            (?: (?&html_end_tag)
              | (?&xhtml_end_tag) )
         )

        (?<tag>
            (?&start_tag)
            (?&name)
            (?:
                (?&WS)
                (?&any_nv_pair)
            ) *
            (?&end_tag)
        )

        (?<untag> </ (?&name) > )

        # starts like a tag, but has screwed up quotes inside it
        (?<nasty>
            (?&start_tag)
            (?&name)
            .*?
            (?&end_tag)
        )

        (?<nontag>    [^<] +            )

        (?<string> (?&quoted_value)     )
        (?<word>   (?&name)             )

        (?<doctype>
            <!DOCTYPE
                # please don't feed me nonHTML
                ### (?&WS) HTML
            [^>]* >
        )

        (?<cdata>   <!\[CDATA\[     .*?     \]\]    > )
        (?<script>  (?= <script ) (?&tag)   .*?     </script> )
        (?<style>   (?= <style  ) (?&tag)   .*?     </style> )
        (?<comment> <!--            .*?           --> )

        (?<xml>
            < \? xml
            (?:
                (?&WS)
                (?&any_nv_pair)
            ) *
            (?&WS)
            \? >
        )

        (?<xhook> < \? .*? \? > )

      )

    }six;

    our $Meta_Tag_Rx = qr{                          $RX_SUBS
        (?<META>
            (?&start_tag) meta \b
            (?:
                (?&WS) (?&any_nv_pair)
            ) +
            (?&end_tag)
        )
    }six;

}

# nobody *ever* remembers to do this!
END { close STDOUT }

23
"Her zaman ayrıştırma sınıfları kullanıyorum, özellikle de HTML ise kendimi oluşturmadım." ve "Desenler çirkin olmak zorunda değiller ve zor olmak zorunda değiller. Çirkin desenler oluşturursanız, bu onlar üzerinde değil, size bir yansımadır." Söylediklerine tamamen katılıyorum, bu yüzden sorunu yeniden değerlendiriyorum. böyle detaylı cevap için çok teşekkürler
Salman

168
Bilmeyenler için Tom'un "Programlama Perl" in (Deve kitabı olarak da bilinir) ve en iyi Perl yetkililerinden biri olduğunu söyleyebilirim. Bunun gerçek Tom Christiansen olduğundan şüphe ediyorsanız, geri dönün ve yazıyı okuyun.
Bill Ruppert

21
Özetlemek gerekirse: RegEx'ler yanlış adlandırılmıştır. Bence bu bir utanç, ama değişmeyecek. Uyumlu 'RegEx' motorların normal olmayan dilleri reddetmesine izin verilmez. Bu nedenle, sadece Finte State Makineleri ile doğru şekilde uygulanamazlar. Hesaplamalı sınıfların etrafındaki güçlü kavramlar geçerli değildir. RegEx'lerin kullanılması O (n) yürütme süresini garanti etmez. RegEx'in avantajları kısa sözdizimi ve ima edilen karakter tanıma alanıdır. Bana göre, bu yavaş hareket eden bir tren enkazıdır, uzağa bakmak imkansızdır, ancak korkunç sonuçları ortaya çıkar.
Steve Steiner

27
@tchrist, bu OP'nin orijinal sorusuna asla cevap vermiyor. Ve edilir ayrıştırma burada uygun dönem? Regex'in ifadeleri tokenleştirici / sözcüksel analiz yapıyor, ancak son ayrıştırma, regex'in kendisi değil Perl koduyla yapıldı.
QTax

66
@tchrist Çok etkileyici. Çok yetenekli ve yetenekli bir Perl programcısısınız ve modern düzenli ifadeler hakkında son derece bilgilisiniz. Bununla birlikte, yazdıklarınızın gerçekten düzenli bir ifade (modern, düzenli veya başka türlü) değil, daha çok düzenli ifadeleri yoğun olarak kullanan bir Perl programı olduğunu belirtmek isterim. Yayınınız, normal ifadelerin HTML'yi doğru şekilde ayrıştırabileceği iddiasını gerçekten destekliyor mu? Yoksa Perl'in HTML'yi doğru bir şekilde ayrıştırabileceğine dair kanıtlar mı? Her iki şekilde de, iyi çalışmalar!
Mike Clark

127
  1. Trist'in yaptığı gibi bir roman yazabilirsiniz
  2. Bir DOM kütüphanesi kullanabilir, HTML'yi yükleyebilir ve xpath kullanabilir ve kullanabilirsiniz //input[@type="hidden"]. Veya xpath kullanmak istemiyorsanız, tüm girdileri alın ve hangilerinin gizlendiğini filtreleyin getAttribute.

# 2'yi tercih ederim.

<?php

$d = new DOMDocument();
$d->loadHTML(
    '
    <p>fsdjl</p>
    <form><div>fdsjl</div></form>
    <input type="hidden" name="blah" value="hide yo kids">
    <input type="text" name="blah" value="hide yo kids">
    <input type="hidden" name="blah" value="hide yo wife">
');
$x = new DOMXpath($d);
$inputs = $x->evaluate('//input[@type="hidden"]');

foreach ( $inputs as $input ) {
    echo $input->getAttribute('value'), '<br>';
}

Sonuç:

hide yo kids<br>hide yo wife<br>

72
Aslýnda bu benim açýmdan. Ne kadar zor olduğunu göstermek istedim.
tchrist

19
Orada çok iyi şeyler. İnsanların ayrıştırma sınıfını ne kadar kolay kullandığını göstermelerini umuyordum, bu yüzden teşekkürler! Sadece regexes kullanarak sıfırdan yapmak için uğraşmak zorunda aşırı sorun çalışan bir örnek istedim. Umarım çoğu insan prefabrik ayrıştırıcıları kendi HTML'lerini kullanmak yerine jenerik HTML'de kullanmaya karar verir. Normal ifadeler, kendi yaptıkları basit HTML için hala harika, çünkü bu, karmaşıklığın% 99,98'inden kurtuluyor.
tchrist

5
Bu iki çok ilginç yaklaşımı okuduktan sonra, bir yaklaşımın hızını / bellek kullanımını / CPU'yu diğerine (yani normal ifade tabanlı VS ayrıştırma sınıfına) benzetmek iyi olurdu.
the_yellow_logo

1
@ Avt'W Evet, eğer Regexes daha hızlı olsaydı bir 'roman' yazmalısın, ama aslında gerçekten bilmek ilginç olurdu. :) Ama benim tahminim zaten, bir ayrıştırıcı da daha az kaynak alır ..
Dennis98

Bu yüzden XPath ilk başta icat edildi!
Thorbjørn Ravn Andersen

21

Tom Christiansen'in lexer çözümünün ruhuna göre, Robert Cameron'un 1998'de unutulmuş gibi görünen REX: Düzenli İfadelerle XML Sığ Ayrıştırma makalesine bir bağlantı .

http://www.cs.sfu.ca/~cameron/REX.html

Öz

XML sözdizimi, bir XML belgesini tek bir normal ifade kullanarak biçimlendirme ve metin öğelerinin bir listesine ayrıştırılabilecek kadar basittir. Bir XML belgesinin böyle sığ bir ayrışması, çeşitli hafif XML işleme araçlarının oluşturulması için çok yararlı olabilir. Bununla birlikte, karmaşık düzenli ifadelerin oluşturulması zor ve okunması daha da zor olabilir. Düzenli ifadeler için bir okuryazar programlama biçimi kullanan bu makale, basit, doğru, verimli, sağlam ve dilden bağımsız XML sığ ayrıştırma için bir temel olarak kullanılabilecek bir dizi XML sığ ayrıştırma ifadesi belgelemektedir. Perl, JavaScript ve Lex / Flex'te her biri 50 satırdan az eksiksiz ayrıştırıcı uygulamaları da verilmiştir.

Düzenli ifadeleri okumaktan hoşlanıyorsanız, Cameron'un makalesi büyüleyici. Yazısı özlü, kapsamlı ve çok detaylıdır. Size sadece REX düzenli ifadesinin nasıl oluşturulacağını değil, aynı zamanda daha küçük parçalardan herhangi bir karmaşık regex oluşturmak için bir yaklaşım da gösteriyor.

İlk posterin sorduğu problemi çözmek için 10 yıldır REX düzenli ifadesini açıp kapadım (bu belirli etiketi nasıl eşleştirebilirim, ancak başka bir çok benzer etiketi değil mi?). Geliştirdiği normal ifadeyi tamamen güvenilir buldum.

REX özellikle bir belgenin sözcük ayrıntılarına odaklanırken kullanışlıdır - örneğin, bir tür metin belgesini (örneğin, düz metin, XML, SGML, HTML) belgenin geçerli olmayabileceği başka bir belgeye dönüştürürken, iyi biçimlendirilmiş, hatta dönüşümün çoğu için ayrıştırılabilir. Belgenin geri kalanını rahatsız etmeden, işaretleme adalarını belgenin herhangi bir yerine hedeflemenizi sağlar.


7

Bu cevapların geri kalanının içeriğini sevsem de, soruyu doğrudan veya doğru şekilde gerçekten cevaplamadılar. Platinum'un cevabı bile aşırı derecede karmaşıktı ve daha az etkili oldu. Bu yüzden bunu koymak zorunda kaldım.

Doğru kullanıldığında Regex'in büyük bir savunucusuyum. Ancak damgalama (ve performans) nedeniyle, her zaman iyi biçimlendirilmiş XML veya HTML'nin bir XML Ayrıştırıcı kullanması gerektiğini belirtirim. Ve daha da iyi performans dize ayrıştırma olurdu, ancak eğer bu çok elden çıkarsa okunabilirlik arasında bir çizgi vardır. Ancak, soru bu değil. Soru, gizli tip giriş etiketinin nasıl eşleştirileceği. Cevap:

<input[^>]*type="hidden"[^>]*>

Lezzetinize bağlı olarak, eklemeniz gereken tek normal ifade seçeneği yok sayma seçeneğidir.


5
<input type='hidden' name='Oh, <really>?' value='Try a real HTML parser instead.'>
Ilmari Karonen

4
Örneğiniz kendi kendine kapanır. /> İle bitmelidir. Ayrıca, >ad alanında bir a'ya sahip olma şansı neredeyse hiç olmasa da >, bir eylem tutamacında olması mümkündür . EG: OnClick özelliğinde satır içi bir javascript çağrısı. Olduğu söyleniyor, ben onlar için bir XML ayrıştırıcı var, ama aynı zamanda bana verilen belgenin XML ayrıştırıcıları işlemek için çok berbat olanlar için bir Regex var, ama bir Regex olabilir. Ayrıca, soru bu değildi. Gizli bir girişle asla bu durumlarla karşılaşmazsınız ve cevabım en iyisidir. Ya, <really>!.
Suamere

3
/>bir XML-izmidir; XHTML dışında hiçbir HTML sürümünde gerekli değildir (hiçbir zaman çok fazla çekiş elde etmez ve HTML5'in yerini almıştır). Ve orada çok geçerli olmayan çok geçerli olmayan HTML var haklısınız, ancak iyi bir HTML ( XML değil ) ayrıştırıcısı bunun çoğuyla başa çıkabilmelidir; yapmazlarsa, büyük olasılıkla tarayıcılar da olmaz.
Ilmari Karonen

1
İhtiyacınız olan tek ayrıştırma veya arama, gizli girdi alanlarının bir koleksiyonunu döndürmek için tek bir isabetse, bu normal ifade mükemmel olacaktır. .NET XML Belge sınıflarını veya sınıflarını kullanmak ya da bir yöntemi çağırmak için bir üçüncü taraf XML / HTML Ayrıştırıcısına başvurmak, Regex yerleşik olduğunda aşırıya kaçar. İyi bir HTML ayrıştırıcı bunun üstesinden gelemezdi, muhtemelen bir geliştiricinin bakacağı bir şey bile değildir. Ancak şirketim, bazen (her zaman değil), Regex'in en iyi seçenek olduğu pek çok şekilde birleştirilen ve çoğaltılan milyonlarca sayfayı ayda bir kez dağıtıyor.
Suamere

1
Tek nokta, bu geliştiricinin bu cevabı istemesinin nedeninin tüm şirketten emin olmadığımızdır. Ama bunu istedi.
Suamere

3

bunu deneyebilirsiniz:

<[A-Za-z ="/_0-9+]*>

ve daha yakın bir sonuç için şunu deneyebilirsiniz:

<[ ]*input[ ]+type="hidden"[ ]*name=[A-Za-z ="_0-9+]*[ ]*[/]*>

regex deseninizi burada test edebilirsiniz http://regexpal.com/

bu pattens bunun için iyidir:

<input type="hidden" name="SaveRequired" value="False" /><input type="hidden" name="__VIEWSTATE1" value="1H4sIAAtzrkX7QfL5VEGj6nGi+nP" /><input type="hidden" name="__VIEWSTATE2" value="0351118MK" /><input type="hidden" name="__VIEWSTATE3" value="ZVVV91yjY" />

ve rastgele sipariş için type, nameve valueu bu kullanabilirsiniz:

<[ ]*input[ ]*[A-Za-z ="_0-9+/]*>

veya

<[ ]*input[ ]*[A-Za-z ="_0-9+/]*[ ]*[/]>

bu konuda:

<input  name="SaveRequired" type="hidden" value="False" /><input type="hidden" name="__VIEWSTATE1" value="1H4sIAAtzrkX7QfL5VEGj6nGi+nP" /><input type="hidden" name="__VIEWSTATE2" value="0351118MK" /><input  name="__VIEWSTATE3" type="hidden" value="ZVVV91yjY" />

'

Bu arada böyle bir şey istediğinizi düşünüyorum:

<[ ]*input(([ ]*type="hidden"[ ]*name=[A-Za-z0-9_+"]*[ ]*value=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*type="hidden"[ ]*value=[A-Za-z0-9_+"]*[ ]*name=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*name=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*value=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*value=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*name=[A-Za-z0-9_+"]*[ ]*)+)[ ]*/>|<[ ]*input(([ ]*name=[A-Za-z0-9_+"]*[ ]*value=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*)+)[ ]*/>|<[ ]*input(([ ]*value=[A-Za-z0-9_+"]*[ ]*name=[A-Za-z0-9_+"]*[ ]*type="hidden"[ ]*)+)[ ]*/>

iyi değil ama herhangi bir şekilde çalışıyor.

test edin: http://regexpal.com/


1

**DOMDocument**Html kodunu ayıklamak için kullanmak istiyorum .

$dom = new DOMDocument();
$dom ->loadHTML($input);
$x = new DOMXpath($dom );
$results = $x->evaluate('//input[@type="hidden"]');

foreach ( $results as $item) {
    print_r( $item->getAttribute('value') );
}

BTW, burada test edebilirsiniz - regex101.com. Sonucu gerçek zamanlı olarak gösterir. Regexp ile ilgili bazı kurallar: http://www.eclipse.org/tptp/home/downloads/installguide/gla_42/ref/rregexp.html Okuyucu .


0

html içeriğinizin html dizesinde saklandığını varsayalım, daha sonra türü içeren her girdiyi gizli tutmak için normal ifadeyi kullanabilirsiniz

var regex = /(<input.*?type\s?=\s?["']hidden["'].*?>)/g;
html.match(regex);

Yukarıdaki normal ifade bulucusunun <inputardından gelen karakter sayısı kadar type="hidden"veya type = 'hidden' ve ardından karakter sayısı kadar elde edilene kadar>

/ g verilen ifadeyle eşleşen her alt dizeyi bulmasını normal ifadeye söyle.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.