Что делать Шаблон регулярного выражения не совпадает нигде в строке? - PullRequest
174 голосов
/ 20 ноября 2010

Я пытаюсь сопоставить <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 всегда будут отображаться в одном и том же порядке.Если атрибут type будет последним, совпадение не будет выполнено, поскольку в моем шаблоне оно находится в начале.

Вопрос:
Как изменить шаблон, чтобы он соответствовал независимо от того,позиций атрибутов в теге <input>?

PS: Кстати, я использую Adobe Air на основе RegExНастольный инструмент для проверки регулярных выражений.

Ответы [ 8 ]

671 голосов
/ 20 ноября 2010

О, да, вы можете Использовать регулярные выражения для разбора HTML!

Для задачи, которую вы пытаетесь, регулярные выражения отлично подходят!

* верно, что большинство людей недооценивают сложность парсинга HTML с помощью регулярных выражений и, следовательно, делают это плохо.

Но это не какой-то фундаментальный недостаток, связанный с вычислительной теорией.Эта глупость много попугаев здесь, но вы не верите им.

Таким образом, хотя это, безусловно, можно сделать (эта публикация служит доказательством существования этого неопровержимого факта), это не означает, что должно быть.

Вы должны решить для себя, подходит ли вам задача написания того, что представляет собой выделенный специализированный HTML-анализатор из регулярных выражений.Большинство людей не такие.

Но I am.10


Общие решения синтаксического анализа 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-файлов - каждый из которых я мог быстро и легко достать.Помимо этого, я также запускаю его на файлах , специально созданных , чтобы сломать наивные парсеры.

Это , а не наивный анализатор.

О, я уверен, что он не идеален, но мне пока не удалось его сломать.Я полагаю, что даже если бы что-то и было, исправить это было бы легко из-за четкой структуры программы.Даже программы с регулярными выражениями должны иметь структуру.

Теперь, когда это не так, позвольте мне ответить на вопрос ОП.

Демонстрация решения задачи ОП с использованием регулярных выражений

Маленькая 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"

Parse Input Tags,См. Зло не вводится

Вот источник для программы, которая произвела вывод выше.

#!/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, пессимальных для больших

Даже если моя программа воспринимается как пример того, почему вы должны не использоватьрегулярные выражения для анализа общего HTML-кода - это нормально, потому что я вроде бы хотел, чтобы это было так - - это все равно должно быть откровением, чтобы больше людей ломало ужасно распространенную и неприятную, неприятную привычку писать нечитаемые, неструктурированные и не поддерживаемые шаблоны,

Шаблоны не должны быть безобразными и не должны быть жесткими.Если вы создаете уродливые шаблоны, это отражение вас, а не их.

Феноменально изысканный язык регулярных выражений

Меня попросили указать, что мое предложенное решение вашей проблемы было написанов перл.Вы удивлены?Вы не заметили?Является ли это откровение бомбой?

Я должен признаться, что нахожу этот запрос странным до крайности, , поскольку любой, кто не может понять это, глядя на самую первую строчку моей программыбезусловно, есть и другие умственные отклонения.

Это правда, что не все другие инструменты и языки программирования столь же удобны, выразительны и мощны, когда речь идет о регулярных выражениях, как Perl.Там есть большой спектр, с некоторыми, более подходящими чем другие.В целом, с языками, в которых регулярные выражения выражены как часть основного языка, а не как библиотека, легче работать.Я ничего не сделал с регулярными выражениями, которые вы не могли бы сделать, скажем, в PCRE, хотя вы бы по-другому структурировали программу, если бы использовали C.

В конце концов другие языки будут догонять то, где сейчас находится Perlс точки зрения регулярных выражений.Я говорю это потому, что когда Perl начинал, никто не имел ничего подобного регулярным выражениям Perl.Скажите что угодно, но именно здесь Perl явно победил: все копировали регулярные выражения Perl, хотя и на разных этапах своего развития.Perl впервые применил почти (не совсем все, но почти) все, на что вы сегодня полагаетесь в современных моделях, независимо от того, какой инструмент или язык вы используете.Так что в конечном итоге остальные будут догонять.

Но они будут догонять только то, где Perl был когда-то в прошлом, так же, как и сейчас.Все продвигается.В регулярных выражениях, если ничего другого, куда ведет Perl, следуют другие.Где будет Perl, когда все наконец поймут, где сейчас находится Perl?Понятия не имею, но знаю, что мы тоже переедем.Вероятно, мы будем ближе к стилю создания Perl patterns .

Если вам нравятся такие вещи, ноЯ хотел бы использовать его в Perl₅, возможно, вас заинтересует модуль замечательного 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 }
122 голосов
/ 20 ноября 2010
  1. Вы можете написать роман как tchrist did
  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>
108 голосов
/ 20 ноября 2010

Вопреки всем ответам здесь, для того, что вы пытаетесь сделать, регулярное выражение является вполне допустимым решением. Это потому, что вы НЕ пытаетесь сопоставить сбалансированные теги - это было бы невозможно с регулярным выражением! Но вы сопоставляете только то, что содержится в одном теге, и это совершенно правильно.

Вот в чем проблема. Вы не можете сделать это с помощью одного регулярного выражения ... вам нужно сделать одно совпадение, чтобы захватить тег <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.

20 голосов
/ 10 июля 2011

В духе лексерского решения Тома Кристиансена, вот ссылка на кажущуюся забытой статью Роберта Кэмерона за 1998 год, REX: поверхностный анализ XML с регулярными выражениями.

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

Аннотация

Синтаксис XML достаточно прост, чтобы можно было проанализировать документ XML в список его разметки и текстовых элементов с помощью одного регулярного выражения.Такой неглубокий анализ XML-документа может быть очень полезен для создания различных легких инструментов обработки XML.Однако сложные регулярные выражения могут быть сложными для создания и даже более сложными для чтения.Используя форму грамотного программирования для регулярных выражений, эта статья документирует набор выражений поверхностного синтаксического анализа XML, которые можно использовать в качестве основы для простого, правильного, эффективного, надежного и независимого от языка поверхностного анализа XML.Также приведены полные реализации синтаксического анализатора длиной менее 50 строк в Perl, JavaScript и Lex / Flex.

Если вам нравится читать регулярные выражения, статья Кэмерона увлекательна.Его письмо является кратким, тщательным и очень подробным.Он не просто показывает вам, как построить регулярное выражение REX, но также подходит для построения любого сложного регулярного выражения из более мелких частей.

Я использую регулярное выражение REX в течение 10 лет для решенияПроблемы, о которых спрашивал первоначальный автор (как мне сопоставить этот конкретный тег, но не какой-то другой очень похожий тег?).Я обнаружил, что разработанное им регулярное выражение является полностью надежным.

REX особенно полезен, когда вы сосредотачиваетесь на лексических деталях документа - например, при преобразовании одного типа текстового документа (например, обычного текста).текст, XML, SGML, HTML) в другой, где документ может быть недействительным, правильно сформированным или даже анализируемым для большей части преобразования.Это позволяет вам ориентироваться на острова разметки в любом месте документа, не нарушая остальную часть документа.

5 голосов
/ 19 сентября 2013

Хотя мне нравится содержание остальных ответов, на самом деле они не ответили на вопрос прямо или неправильно.Даже Платиновый ответ был слишком сложным и менее эффективным.Поэтому я был вынужден поставить это.

Я огромный сторонник Regex, когда используется правильно.Но из-за стигмы (и производительности) я всегда заявляю, что правильно сформированный XML или HTML должен использовать парсер XML.И даже лучшая производительность была бы при разборе строк, хотя между читабельностью есть грань, если она выходит из-под контроля.Однако это не вопрос.Вопрос в том, как сопоставить тег ввода скрытого типа.Ответ:

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

В зависимости от вашего вкуса, единственный параметр регулярных выражений, который вам нужно будет включить, - параметр игнорирования.

3 голосов
/ 01 апреля 2013

Вы можете попробовать это:

<[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 и value вы можете использовать это:

<[ ]*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 голос
/ 11 марта 2015

Я хотел бы использовать **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') );
}

Кстати, вы можете проверить это здесь - regex101.com. Он показывает результат в режиме реального времени. Некоторые правила о регулярных выражениях: http://www.eclipse.org/tptp/home/downloads/installguide/gla_42/ref/rregexp.html Reader .

0 голосов
/ 13 марта 2016

предположим, что ваш html-контент хранится в строковом html, а затем, чтобы получить все входные данные, содержащие скрытый тип, вы можете использовать регулярное выражение

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

вышеупомянутое регулярное выражение находит <input, за которым следует любое количество символов, пока не получится type="hidden" или type = 'hidden', за которым следует любое количество символов, пока не будет получено >

/ g сообщает регулярному выражению, чтобы найти каждую подстроку, соответствующую данному шаблону.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...