문자열에서 정규식 패턴이 일치하지 않습니까?


181

<input>이 패턴을 사용하여 "숨겨진"유형의 필드 를 일치 시키려고합니다 .

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

다음은 샘플 양식 데이터입니다.

<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="" />

하지만 난하지 있는지 생각 type, name그리고 value속성이 항상 같은 순서로 나타납니다. 는 IF type속성이 마지막으로 오는 내 패턴으로는 시작이기 때문에, 경기가 실패합니다.

질문 : 태그
의 속성 위치에 관계없이 패턴이 일치하도록 패턴을 변경하려면 어떻게해야 <input>합니까?

추신 : 그런데 정규식 을 테스트하기 위해 Adobe Air 기반 RegEx Desktop Tool 을 사용하고 있습니다.


4
정규식이 아닌 일반적인 토론이기 때문에 생성 된 HTML을 제어 할 수 있다면 정규식은 훌륭한 솔루션입니다.하지만 제 경우에는 HTML이 미래에 어떻게 변경 될지 알지 못하므로 가장 좋은 것은 정규식 대신 파서를 사용하는 것입니다. 내가 제어 할 수있는 부분으로 내 프로젝트의 정규식
Salman

답변:


108

여기에있는 모든 대답과 달리 정규식을 수행하려는 것은 완벽하게 유효한 솔루션입니다. 균형 잡힌 태그와 일치하지 않기 때문에 정규식으로는 불가능합니다! 그러나 하나의 태그에만 일치하며 완벽하게 규칙적입니다.

그래도 문제가 있습니다. 하나의 정규 표현식으로 할 수는 없습니다 ... <input>태그 를 캡처하려면 한 번의 일치를 수행 한 다음 추가 처리를 수행해야합니다. 이것은 속성 값에 >문자 가없는 경우에만 작동 하므로 완벽하지는 않지만 정상적인 입력에는 충분합니다.

다음은 내가 의미하는 바를 보여주는 Perl (의사) 코드입니다.

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
}

기본 원리는 하나의 정규 표현식으로 너무 많은 것을 시도하지 마십시오. 알다시피, 정규 표현식은 일정한 양의 주문을 시행합니다. 따라서 대신 추출하려는 대상의 컨텍스트와 일치시킨 다음 원하는 데이터에서 하위 일치를 수행해야합니다.

편집 : 그러나, 나는 일반적으로 HTML 파서를 사용하는 것이 더 쉽고 더 좋으며 코드를 다시 디자인하거나 목표를 다시 검사하는 것을 고려해야한다고 동의합니다. :-) 그러나이 답변을 HTML의 하위 집합을 파싱하는 것은 불가능하다는 슬픔의 반응에 대한 반론으로 게시해야했습니다. 전체 사양을 고려할 때 HTML과 XML은 모두 불규칙하지만 태그의 사양은 상당히 규칙적입니다. 확실히 PCRE의 힘 안에서.


14
여기 에있는 모든 대답 과 반대되는 것은 아닙니다 . :)
tchrist

6
@ tchrist : 내가 게시 할 때 당신의 대답은 여기에 없었습니다. ;-)
Platinum Azure

7
yah well – 어떤 이유로 당신보다 입력하는데 시간이 더 걸렸습니다. 키보드에 기름칠이 필요하다고 생각합니다. :)
tchrist

6
유효하지 않은 HTML입니다. value = "& lt; 정말로 확실합니까? & gt;" 그가 긁어 모으는 장소가 이와 같은 일을 피하기 위해 열악한 작업을 수행하는 경우 더 정교한 솔루션이 필요합니다.
로스 스나이더

14
주제에 대한 최상의 SO 답변에 대한 의무적 인 링크 (아마도 최고의 SO 답변 기간) : stackoverflow.com/questions/1732348/…
Daniel Ribeiro

682

오 예 당신은 정규 표현식을 사용하여 HTML을 파싱 할 수 있습니다 !

시도중인 작업에 대해 정규 표현식은 완벽하게 좋습니다 !

이다 대부분의 사람들이 정규 표현식 구문 분석 HTML의 어려움을 과소 평가하기 때문에 너무 가난 할 것이 사실.

그러나 이것은 계산 이론과 관련된 근본적인 결함이 아닙니다. 그 어리 석음이 여기 주변에 많이 파묻혀 있지만 믿지 마십시오.

그것은 확실하게 할 수있는 반면 그래서 (본 게시물이 논쟁의 여지가 사실의 존재 증명의 역할), 즉 의미하지 않는다  해야  합니다.

정규 표현식에서 전용의 특수 목적 HTML 파서에 얼마를 쓰는지 결정해야합니다. 대부분의 사람들은 그렇지 않습니다.

그러나 나는 생각합니다. ☻


일반 정규식 기반 HTML 파싱 솔루션

먼저 임의의 HTML을 정규식 으로 구문 분석하는 것이 얼마나 쉬운 지 보여 드리겠습니다 . 전체 프로그램은이 게시물의 끝에 있지만 파서의 핵심은 다음과 같습니다.

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);
    }
  }
}

얼마나 쉬운 지보십시오 보십니까?

작성된대로 각 HTML을 식별하고 해당 부분을 찾은 위치를 알려줍니다. 주어진 유형의 조각으로 또는 다른 것보다 더 특정한 유형으로 원하는 다른 작업을 수행하도록 쉽게 수정할 수 있습니다.

나는 실패한 테스트 사례가 없다 (왼쪽 :) : 100,000 개가 넘는 HTML 파일에서이 코드를 성공적으로 실행했다. 그 외에도, 특별히 구성된 파일에서 실행했습니다. 순진한 파서를 깨뜨리기 위해 .

이것은 아닙니다 순진한 파서 .

오, 나는 그것이 완벽하지 않다고 확신하지만, 아직 그것을 깨뜨리지 못했습니다. 나는 무언가가 있었더라도 프로그램의 명확한 구조 때문에 수정이 쉽게 적용 될 수 있다고 생각합니다. 정규 표현식이 많은 프로그램조차도 구조가 있어야합니다.

이제 그 길을 벗어 났으므로 OP의 질문에 대해 설명하겠습니다.

정규식을 사용하여 OP 작업 해결 데모

html_input_rx아래에 포함 된 작은 프로그램은 다음과 같은 출력을 생성하므로 정규 표현식으로 HTML을 구문 분석하면 원하는 작업에 적합하다는 것을 알 수 있습니다.

% 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"

입력 태그 구문 분석, 악의없는 입력 참조

위의 출력을 생성 한 프로그램의 소스는 다음과 같습니다.

#!/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: $!";
} 

당신은 간다! 아무것도 아니야! :)

오직 당신 정규 표현식에와 당신의 기술이 특정 구문 분석 작업까지인지를 판단 할 수 있습니다. 모든 사람의 기술 수준은 다르며 모든 새로운 작업은 다릅니다. 잘 정의 된 입력 세트가있는 작업의 경우, 정규식이 올바른 선택입니다. 처리 할 HTML의 제한된 서브 세트가있을 때 일부를 조합하는 것이 쉽지 않기 때문입니다. 정규식 초보자조차도 정규식을 사용하여 이러한 작업을 처리해야합니다. 다른 것은 과잉입니다.

그러나 HTML이 잘 못 박 히기 시작하면 예측할 수 없지만 완벽하게 합법적 인 방식으로 시작하기 시작하면 더 많은 종류의 것들이나 더 복잡한 종속성을 일치시켜야하면 결국에는 구문 분석 클래스를 사용하는 것보다 정규 표현식을 사용하는 솔루션을 적용하려면 더 열심히 노력해야합니다. 그 손익 분기점이 떨어지는 곳은 다시 정규 표현식을 사용하는 편안함 수준에 달려 있습니다.

그래서 내가 무엇을해야하니?

당신이 해야 할 일이나 할 수없는 일 을 말하지 않겠습니다 . 나는 그것이 틀렸다고 생각한다. 나는 단지 당신에게 가능성을 제시하고 싶습니다, 당신의 눈을 조금여십시오. 당신은 당신이하고 싶은 일과 원하는 방법을 선택할 수 있습니다. 절대적인 것은 없으며, 자신과 마찬가지로 자신의 상황을 아는 사람은 없습니다. 무언가가 너무 많은 것처럼 보인다면 아마도 그럴 것입니다. 프로그래밍은 재미 있어야합니다 . 그렇지 않으면 잘못했을 수 있습니다.

하나 html_input_rx의 유효한 방법으로 내 프로그램을 볼 수 있습니다 . 그중 하나는 실제로 정규 표현식으로 HTML을 구문 분석 할 수 있다는 것 입니다. 그러나 다른 하나는 그것이 거의 모든 사람이 생각하는 것보다 훨씬, 훨씬, 훨씬 어렵다는 것입니다. 이것은 쉽게 내 프로그램은 당신이해야하는지에 대한 증거라는 결론으로 이어질 수 없는 정말 너무 어렵 기 때문에, 할.

나는 그것에 동의하지 않습니다. 확실히 내 프로그램에서 내가하는 모든 일이 약간의 연구 후에 이해가되지 않는다면, 이런 종류의 작업에 정규식을 사용하려고 시도해서는 안됩니다. 특정 HTML의 경우 정규 표현식은 훌륭하지만 일반적인 HTML의 경우 광기와 관련이 있습니다. 구문 분석 클래스를 항상 사용합니다. 특히 HTML 인 경우 직접 생성하지 않습니다.

에 대한 정규 표현식에 최적 작은 HTML 구문 분석 문제, 큰 사람을위한 pessimal

내 프로그램이 일반적인 HTML을 파싱하기 위해 정규 표현식을 사용 하지 않아야 하는 이유를 설명하는 경우에도 마찬가지입니다. 읽을 수없고, 구조화되지 않았으며, 유지할 수없는 패턴을 작성하는 불쾌하고 불쾌한 습관.

패턴은 추악 할 필요가 없으며 단단하지 않아도됩니다. 못생긴 패턴을 만들면 패턴이 아니라 자신을 반영합니다.

놀랍도록 절묘한 정규식 언어

귀하의 문제에 대한 나의 바람직한 해결책이 Perl로 작성되었음을 알려달라고 요청 받았습니다. 너 놀랐 니? 눈치 채지 못했습니까? 이 계시는 폭탄입니까?

다른 툴과 프로그래밍 언어가 Perl처럼 정규 표현식에있어 편리하고 표현력이 뛰어나고 강력하지는 않습니다. 큰 스펙트럼이 있으며 일부는 다른 것보다 더 적합합니다. 일반적으로 라이브러리 대신 핵심 언어의 일부로 정규 표현식을 표현한 언어는 작업하기가 더 쉽습니다. C를 사용하는 경우 프로그램을 다르게 구성하더라도 PCRE와 같이 할 수없는 정규 표현식으로 아무것도하지 않았습니다.

결국 Perl이 이제 정규식이라는 관점에서 다른 언어를 따라 잡을 수 있습니다. 나는 이것이 Perl이 시작되었을 때 펄의 정규식과 같은 것을 가진 사람이 없기 때문에 이것을 말한다. 당신이 좋아하는 것을 말하지만, 이것은 Perl이 분명히 얻은 곳입니다. 모든 사람들은 Perl의 정규식을 개발 단계마다 다르지만 복사했습니다. Perl은 사용하는 도구 나 언어에 상관없이 오늘날 현대 패턴에 의존하는 거의 모든 것 (거의 전부는 아니지만 거의)을 개척했습니다. 결국 다른 사람들 따라 잡을 것입니다.

그러나 그들은 지금처럼 과거에 Perl이 있었던 곳까지만 따라 잡을 것입니다. 모든 것이 발전합니다. Perl이 이끄는 다른 것이 없다면 정규 표현식에서 다른 사람들이 따릅니다. 펄은 이제 다른 사람들이 펄이있는 곳을 따라 잡을 수 있을까요? 나도 몰라, 그러나 우리도 움직일 것입니다. 아마도 우리는 Perl₆의 제작 패턴 스타일에 더 가까울 것 입니다.

그런 종류의 것을 좋아하지만 Perl₅에서 사용하고 싶다면 Damian Conway의 멋진 Regexp :: Grammars 모듈에 관심이있을 것 입니다. 완전히 훌륭하고 내 프로그램에서 내가 한 일이 사람들이 공백이나 알파벳 식별자없이 함께 모은 패턴을 만드는 것처럼 원시적 인 것처럼 보입니다. 확인 해봐!


간단한 HTML Chunker

다음은이 게시물의 시작 부분에서 중심을 보여준 파서의 완전한 소스입니다.

나는 엄격히 테스트 된 파싱 클래스보다 이것을 사용해야한다고 제안 하지 않습니다 . 하지만 난 그냥 때문에 정규 표현식에와 그 누구도 할 수있는 구문 분석 HTML 척없는 사람들의 피곤 그들이 할 수 없습니다입니다. 당신은 분명히 할 수 있으며,이 프로그램은 그 주장의 증거입니다.

물론, 그것은 쉬운 일이 아닙니다, 그러나 그것은 이다 가능!

좋은 구문 분석 클래스는 어떤 존재하기 때문에 그렇게하려고 노력하는 것은 시간의 끔찍한 낭비입니다 해야 이 작업을 위해 사용합니다. 임의의 HTML 을 구문 분석하려는 사람들에게 정답 은 불가능하다는 것이 아닙니다 . 그것은 쉽고 해가없는 대답입니다. 정직하고 정직한 대답은 처음부터 파악하기에는 너무 귀찮기 때문에 시도하지 않아야한다는 것입니다. 그들은 완벽하게 잘 작동하는 바퀴를 되살리려 고 애 쓰지 말아야합니다.

반면, 예측 가능한 하위 집합에 속하는 HTML 은 정규 표현식으로 구문 분석하기가 매우 쉽습니다. 작은 문제, 장난감 문제, 아마도 쉬운 일이 없기 때문에 사람들이 사용하려고하는 것은 놀라운 일이 아닙니다. 그렇기 때문에 두 가지 작업 (특정 대 일반)을 구별하는 것이 매우 중요한 이유는 동일한 접근 방식을 요구하지 않기 때문입니다.

앞으로 HTML 및 정규 표현식에 대한 질문에 대한 공정하고 정직한 처리가 이루어지기를 바랍니다.

여기 내 HTML 어휘 분석기가있다. 유효성 검사 구문 분석을 시도하지 않습니다. 어휘 요소 만 식별합니다. HTML 파서보다 HTML 청커 로 생각할 수도 있습니다 . 깨진 HTML을 용서하지는 않지만 그 방향으로 약간의 여유를줍니다.

전체 HTML을 직접 구문 분석하지 않아도 (그리고 왜해야합니까? 해결 된 문제입니다!),이 프로그램에는 많은 사람들이 많이 배울 수 있다고 생각하는 멋진 정규식 비트가 많이 있습니다. 즐겨!

#!/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
"나는 구문 분석 클래스를 항상 사용합니다. 특히 HTML 인 경우 직접 생성하지 않았습니다." 그리고 "패턴은 추악 할 필요가없고, 단단 할 필요는 없다. 추악한 패턴을 만들면 패턴이 아닌 자신에게 반성이다." 나는 당신이 말한 것에 완전히 동의하므로 문제를 재평가하고 있습니다. 자세한 답변을 주셔서 감사합니다
Salman

168
모르는 사람들에게 Tom은 "Programming Perl"(일명 Camel book)의 공동 저자이자 최고 Perl 권한 중 하나라고 언급 할 것이라고 생각했습니다. 이것이 진짜 Tom Christiansen인지 의심 스럽다면 돌아가서 게시물을 읽으십시오.
Bill Ruppert

20
요약하면 : RegEx의 이름이 잘못되었습니다. 부끄러운 일이라고 생각하지만 변경되지는 않습니다. 호환되는 'RegEx'엔진은 비정규 언어를 거부 할 수 없습니다. 따라서 Finte State Machine만으로는 올바르게 구현할 수 없습니다. 계산 클래스에 대한 강력한 개념은 적용되지 않습니다. RegEx를 사용해도 O (n) 실행 시간이 보장되지는 않습니다. RegEx의 장점은 간결한 구문과 내재 된 문자 인식 도메인입니다. 나에게 이것은 느리게 움직이는 열차 잔해로, 멀리 볼 수는 없지만 끔찍한 결과가 펼쳐집니다.
Steve Steiner

27
@ tchrist, 이것은 OPs 원래 질문에 대답하지 않습니다. 그리고 여기에 적절한 용어를 파싱 하고 있습니까? 정규 표현식 Afaics는 토큰 화 / 어휘 분석을 수행하지만 최종 구문 분석은 정규 표현식 자체가 아닌 Perl 코드로 수행됩니다.
Qtax

65
@tchrist 매우 인상적입니다. 당신은 분명히 고도로 숙련되고 재능있는 Perl 프로그래머이며, 현대 정규식에 대해 매우 잘 알고 있습니다. 그러나 여러분이 작성한 것은 실제로 정규 표현식 (현대, 정규 또는 기타)이 아니라 정규 표현식을 많이 사용하는 Perl 프로그램이라는 점을 지적합니다. 귀하의 게시물은 정규 표현식이 HTML을 올바르게 구문 분석 할 수 있다는 주장을 실제로 지원합니까? 아니면 Perl 이 HTML을 올바르게 구문 분석 할 수 있다는 증거와 비슷 합니까? 어느 쪽이든, 좋은 일입니다!
Mike Clark

126
  1. tchrist처럼 소설을 쓸 수 있습니다
  2. DOM 라이브러리, HTML을로드하고 xpath를 사용하고을 사용할 수 있습니다 //input[@type="hidden"]. 또는 xpath를 사용하지 않으려면 모든 입력을 가져 와서 숨겨지는 입력을 필터링하십시오 getAttribute.

나는 # 2를 선호합니다.

<?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>';
}

결과:

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

72
그것은 실제로 내 요점이었다. 나는 그것이 얼마나 힘든지 보여주고 싶었다.
tchrist

19
거기에 아주 좋은 것들. 나는 사람들이 파싱 클래스를 사용하는 것이 얼마나 쉬운 지 보여주기를 정말로 희망했기 때문에 감사합니다! 방금 정규식을 사용하여 처음부터 끝내야하는 극단적 인 문제의 실제 예를 원했습니다. 대부분의 사람들이 자신의 롤링 대신 일반 HTML에 프리 팹 파서를 사용하기로 결론을 내릴 수 있기를 바랍니다. 정규 표현식은 여전히 ​​복잡한 HTML의 99.98 %를 제거하기 때문에 스스로 만든 간단한 HTML에 적합합니다.
tchrist

5
이 두 가지 흥미로운 접근 방식을 읽은 후 좋은 점은 한 접근 방식의 속도 / 메모리 사용량 / CPU를 다른 접근 방식과 비교하는 것입니다 (즉, 정규식 기반 VS 구문 분석 클래스).
the_yellow_logo

1
@ Avt'W 그래, Regexes가 더 빠르면 '신규'를 써야하는 것이 아니라 실제로 실제로 아는 것이 흥미로울 것입니다. :)하지만 제 생각은 이미 파서가 더 적은 리소스를 사용한다는 것입니다.
Dennis98

이것이 바로 XPath가 처음에 발명 된 이유입니다!
Thorbjørn Ravn Andersen

21

Tom Christiansen의 렉서 솔루션의 정신으로, 여기에 잊혀진 Robert Cameron의 기사 REX : 정규 표현식을 사용한 XML 얕은 구문 분석에 대한 링크가 있습니다.

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

요약

XML 구문은 XML 문서를 단일 정규식을 사용하여 마크 업 및 텍스트 항목 목록으로 구문 분석 할 수있을 정도로 간단합니다. XML 문서의 이러한 간단한 구문 분석은 다양한 경량 XML 처리 도구를 구성하는 데 매우 유용 할 수 있습니다. 그러나 복잡한 정규식은 구성하기가 어렵고 읽기가 더 어려울 수 있습니다. 이 논문은 정규 표현식을위한 문해력있는 프로그래밍 형태를 사용하여 단순하고 정확하며 효율적이며 강력하며 언어에 독립적 인 XML 얕은 구문 분석의 기초로 사용될 수있는 XML 얕은 구문 분석 표현식을 문서화합니다. Perl, JavaScript 및 Lex / Flex에서 각각 50 줄 미만의 완전한 얕은 파서 구현도 제공됩니다.

정규식에 대해 읽는 것을 좋아한다면 Cameron의 논문은 흥미 롭습니다. 그의 글은 간결하고 철저하며 매우 상세합니다. 그는 단순히 REX 정규 표현식을 구성하는 방법뿐만 아니라 작은 부분에서 복잡한 정규 표현식을 작성하는 방법을 보여줍니다.

REX 정규식을 10 년 동안 사용하거나 사용하지 않아 초기 포스터에 대해 제기 한 문제를 해결했습니다 (이 특정 태그와 어떻게 일치하지만 다른 유사한 태그는 어떻게 일치하지 않습니까?). 나는 그가 개발 한 정규식이 완전히 신뢰할 만하다는 것을 알았습니다.

REX는 문서의 어휘 적 세부 사항에 초점을 맞출 때 특히 유용합니다. 예를 들어 한 종류의 텍스트 문서 (예 : 일반 텍스트, XML, SGML, HTML)를 다른 유형의 문서로 변환 할 때 유용합니다. 대부분의 변형에 대해 잘 형성되거나 파싱 가능합니다. 문서의 나머지 부분을 방해하지 않으면 서 문서의 어느 곳에서나 마크 업을 대상으로 할 수 있습니다.


7

나는이 답변의 나머지 내용을 좋아하지만 실제로 질문에 직접 또는 올바르게 대답하지는 않았습니다. 플래티넘의 대답조차도 지나치게 복잡했고 효율성도 떨어졌습니다. 그래서 나는 이것을 넣어야했습니다.

나는 올바르게 사용될 때 Regex의 큰 지지자입니다. 그러나 낙인 (및 성능) 때문에 항상 올바른 형식의 XML 또는 HTML이 XML 파서를 사용해야한다고 말합니다. 더 나은 성능은 문자열 파싱이지만, 너무 손이 잘 들지 않으면 가독성 사이에 경계가 있습니다. 그러나 그것은 문제가 아닙니다. 문제는 숨겨진 유형의 입력 태그를 일치시키는 방법입니다. 정답은:

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

특징에 따라 포함해야 할 유일한 정규식 옵션은 ignorecase 옵션입니다.


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

4
당신의 예는 자기 결말입니다. />로 끝나야합니다. 또한 >이름 필드에 입력 할 가능성 은 거의 없지만 실제로 >작업 핸들 에있을 수 있습니다 . EG : OnClick 속성에 대한 인라인 자바 스크립트 호출. 즉, XML 파서를 가지고 있지만 XML 파서가 처리하기에는 내가 제공 한 문서가 너무 엉망이지만 Regex가 할 수있는 사람들을위한 Regex도 있습니다. 또한 이것은 질문이 아닙니다. 숨겨진 입력으로 이러한 상황에 처할 수 없으며 내 대답이 가장 좋습니다. Ya, <really>!.
Suamere

3
/>XML-ism입니다. XHTML을 제외하고는 모든 HTML 버전에서 필요하지 않습니다. 그리고 실제로는 실제로 유효하지 않은 많은 HTML이 있지만 올바른 XML ( XML이 아닌 ) 파서는 대부분의 HTML 에 대처할 수 있어야합니다. 그렇지 않은 경우 브라우저가 아닐 것입니다.
Ilmari Karonen

1
숨겨진 입력 필드 컬렉션을 반환하는 데 필요한 유일한 구문 분석 또는 검색 만 있으면이 정규식이 완벽합니다. .NET XML 문서 클래스를 사용하거나 하나의 메소드를 호출하기 위해 써드 파티 XML / HTML 파서를 참조하는 것은 Regex가 내장되어있을 때 과잉이 될 것입니다. 파서는 그것을 처리 할 수 ​​없었습니다. 아마도 개발자 가보고있는 것이 아닐 수도 있습니다. 그러나 우리 회사는 한 달에 수백만 페이지를 넘겨주고 때로는 항상 그런 것은 아니지만 Regex가 최선의 선택이 될 수 있도록 여러 가지 방법으로 연결되어 있습니다.
Suamere

1
이 개발자가이 답변을 원하는 전체 회사 이유를 확신 할 수 없다는 점만 지적하십시오. 그러나 그가 요구 한 것입니다.
Suamere

3

당신은 이것을 시도 할 수 있습니다 :

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

더 가까운 결과를 얻으려면 다음을 시도하십시오.

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

http://regexpal.com/에서 정규식 패턴을 테스트 할 수 있습니다.

이 패튼은 이것에 좋습니다 :

<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" />

과의 임의의 순서를 위해 type, name그리고 valueu는 이것을 사용할 수 있습니다 :

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

또는

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

이에 :

<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" />

`

그건 그렇고 나는 당신이 이와 같은 것을 원한다고 생각합니다 :

<[ ]*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"[ ]*)+)[ ]*/>

좋지는 않지만 어떤 식 으로든 작동합니다.

http://regexpal.com/ 에서 테스트하십시오.


1

**DOMDocument**html 코드를 추출하는 데 사용 하고 싶습니다 .

$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, 당신은 여기에서 테스트 할 수 있습니다-regex101.com. 실시간으로 결과를 보여줍니다. Regexp에 대한 몇 가지 규칙 : http://www.eclipse.org/tptp/home/downloads/installguide/gla_42/ref/rregexp.html Reader .


0

HTML 내용이 문자열 html에 저장된 다음 숨겨진 유형을 포함하는 모든 입력을 얻으려면 정규 표현식을 사용할 수 있다고 가정하십시오.

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

위의 정규식 찾기 <input다음에 도착할 때까지 문자 수를 type="hidden"입력하거나 type = 'hidden'다음에 도착할 때까지 문자 수를 입력하십시오.>

/ g 정규식에 주어진 패턴과 일치하는 모든 하위 문자열을 찾도록 지시합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.