Frage

Ich versuche, die Attribute eines Anker-Tag (<a>) zu extrahieren. Bisher habe ich diesen Ausdruck:

(?<name>\b\w+\b)\s*=\s*("(?<value>[^"]*)"|'(?<value>[^']*)'|(?<value>[^"'<> \s]+)\s*)+

, die für Zeichenketten wie

funktioniert
<a href="test.html" class="xyz">

und (einfache Anführungszeichen)

<a href='test.html' class="xyz">

, aber nicht für eine Zeichenfolge ohne Anführungszeichen:

<a href=test.html class=xyz>

Wie kann ich meine Regex ändern so dass es ohne Anführungszeichen mit Attributen arbeiten? Oder gibt es einen besseren Weg, das zu tun?

Danke!

Update: Vielen Dank für all die guten Kommentare und Ratschläge so weit. Es gibt eine Sache, die ich nicht erwähnt: Ich habe leider zu Patch / ändere Code nicht von mir geschrieben. Und es gibt keine Zeit / Geld dieses Zeug von unten nach oben neu zu schreiben.

War es hilfreich?

Lösung

Wenn Sie ein Element wie

<name attribute=value attribute="value" attribute='value'>

Diese regex verwendet, um nacheinander jedes Attribut Name und Wert finden könnte

(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?

Angewandt auf:

<a href=test.html class=xyz>
<a href="test.html" class="xyz">
<a href='test.html' class="xyz">

es ergäbe:

'href' => 'test.html'
'class' => 'xyz'
  

Hinweis: Das funktioniert nicht mit numerischen Attributwerten z.B. <div id="1"> wird nicht funktionieren.

Andere Tipps

Obwohl der Rat nicht HTML zu analysieren über regexp gültig ist, hier ist ein Ausdruck, der so ziemlich das tut, was Sie gefragt:

/
   \G                     # start where the last match left off
   (?>                    # begin non-backtracking expression
       .*?                # *anything* until...
       <[Aa]\b            # an anchor tag
    )??                   # but look ahead to see that the rest of the expression
                          #    does not match.
    \s+                   # at least one space
    ( \p{Alpha}           # Our first capture, starting with one alpha
      \p{Alnum}*          # followed by any number of alphanumeric characters
    )                     # end capture #1
    (?: \s* = \s*         # a group starting with a '=', possibly surrounded by spaces.
        (?: (['"])        # capture a single quote character
            (.*?)         # anything else
             \2           # which ever quote character we captured before
        |   ( [^>\s'"]+ ) # any number of non-( '>', space, quote ) chars
        )                 # end group
     )?                   # attribute value was optional
/msx;

„Aber warten“, könnte man sagen. "Was ist * Kommentare?!?!" Okay, dann können Sie die . im nicht-Rückzieher Abschnitt ersetzen mit: (Es behandelt auch CDATA-Abschnitte).

(?:[^<]|<[^!]|<![^-\[]|<!\[(?!CDATA)|<!\[CDATA\[.*?\]\]>|<!--(?:[^-]|-[^-])*-->)
  • Auch wenn Sie eine Substitution unter Perl 5.10 (und ich denke, PCRE) laufen wollen, können Sie \K direkt vor dem Attributnamen setzen und mögen, dass Sie all die Dinge, über die Erfassung kümmern müssen nicht überspringen.

Token Mantra Antwort: Sie sollten nicht zwicken / ändern / Ernte / oder anderweitig html / xml mit regulärem Ausdruck erzeugen.

Es gibt zu may Ecke Fall conditionals wie \‘und \“, die berücksichtigt werden müssen. Sie sind viel besser dran mit einem richtigen DOM-Parser, XML Parser, oder eine der vielen anderen Dutzende von bewährten Tools für dieser Job stattdessen Ihre eigenen erfinden.

Ich weiß nicht wirklich, welche Sie verwenden, solange seine anerkannt, getestet und Sie verwenden ein.

my $foo  = Someclass->parse( $xmlstring ); 
my @links = $foo->getChildrenByTagName("a"); 
my @srcs = map { $_->getAttribute("src") } @links; 
# @srcs now contains an array of src attributes extracted from the page. 

Nur mit jeder zustimmen sonst. Sie analysieren nicht HTML regexp mit

Es ist nicht möglich, einen Ausdruck zu erstellen, die Attribute selbst für ein korrektes Stück HTML herausgreifen wird, nie all möglichen Varianten malformed kümmern. Ihr regexp ist schon ziemlich unleserlich auch ohne zu versuchen, mit dem ungültigen Mangel an Zitate zu bewältigen; jagen weiter in die Schrecken der realen Welt HTML und Sie werden sich verrückt mit einem wartbaren Klecks unzuverlässig Ausdrücke fahren.

Es gibt vorhandenen Bibliotheken entweder gebrochen HTML zu lesen, oder es in gültiges XHTML zu korrigieren, die Sie dann mit einem XML-Parser leicht verschlingen. Verwenden Sie sie.

Sie können nicht den gleichen Namen für mehrere Aufnahmen verwenden. So können Sie keine quantifier auf Ausdrücke mit dem Namen Captures verwenden.

Also entweder nicht genannt Captures verwenden:

(?:(\b\w+\b)\s*=\s*("[^"]*"|'[^']*'|[^"'<>\s]+)\s+)+

oder benutzen Sie nicht die quantifier auf diesem Ausdruck:

(?<name>\b\w+\b)\s*=\s*(?<value>"[^"]*"|'[^']*'|[^"'<>\s]+)

Dies gilt auch Attributwerte wie bar=' baz='quux zulassen:

foo="bar=' baz='quux"

Nun, der Nachteil ist, dass Sie die vorderen und hinteren Anführungszeichen danach abzustreifen haben.

PHP (PCRE) und Python

Einfache Attributextraktion ( Siehe es funktioniert ):

((?:(?!\s|=).)*)\s*?=\s*?["']?((?:(?<=")(?:(?<=\\)"|[^"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!"|')(?:(?!\/>|>|\s).)+))

oder mit dem Tag Öffnen / Schließen Überprüfung, Tag-Name Retrieval und Kommentar zu entkommen. Dieser Ausdruck unquoted / zitiert, Einzel- / doppelte Anführungszeichen sieht, flüchtete Anführungszeichen innerhalb Attribute, Räume um Gleichheitszeichen, unterschiedliche Anzahl von Attributen, überprüfen nur für Attribute innerhalb Tags und andere Zitate in einem Attributwert verwalten. ( Siehe es funktioniert ):

(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)

(Funktioniert besser mit dem "gisx" Flags.)


Javascript

Als Javascript regulärer Ausdrücke nicht Look-behinds unterstützen, wird es nicht die meisten Funktionen der vorherigen Ausdrücke stütze ich vorschlagen. Aber im Falle könnte es jemand Bedürfnisse passen, können Sie diese Version versuchen. ( anzeigen Arbeits ).

(\S+)=[\'"]?((?:(?!\/>|>|"|\'|\s).)+)

splattne,

@VonC Lösung teilweise funktioniert, aber es gibt einige Frage, ob das Tag eine gemischte von nicht notierten hatte und zitierte

Dies funktioniert mit gemischten Attributen

$pat_attributes = "(\S+)=(\"|'| |)(.*)(\"|'| |>)"

um es zu testen

<?php
$pat_attributes = "(\S+)=(\"|'| |)(.*)(\"|'| |>)"

$code = '    <IMG title=09.jpg alt=09.jpg src="http://example.com.jpg?v=185579" border=0 mce_src="example.com.jpg?v=185579"
    ';

preg_match_all( "@$pat_attributes@isU", $code, $ms);
var_dump( $ms );

$code = '
<a href=test.html class=xyz>
<a href="test.html" class="xyz">
<a href=\'test.html\' class="xyz">
<img src="http://"/>      ';

preg_match_all( "@$pat_attributes@isU", $code, $ms);

var_dump( $ms );

$ ms würde dann Schlüssel und Werte auf dem 2. und 3. Element enthalten.

$keys = $ms[1];
$values = $ms[2];

Das ist meine beste RegEx Eigenschaften in HTML-Tag zu extrahieren:

#, das Spiel Trim innerhalb der Anführungszeichen (einfach oder doppelt)

(\S+)\s*=\s*([']|["])\s*([\W\w]*?)\s*\2

# Ohne trim

(\S+)\s*=\s*([']|["])([\W\w]*?)\2

Vorteile:

  • Sie sind in der Lage, den Inhalt innerhalb von Zitaten zu trimmen.
  • passt alle speziellen ASCII-Zeichen innerhalb der Anführungszeichen.
  • Wenn Sie Titel haben = „Du bist mein“ die RegEx nicht gebrochen

Nachteile:

  • Es gibt drei Gruppen; . Zuerst die Eigenschaft dann das Zitat ( „|") und am Ende der Eigenschaft innerhalb der Zitate d.h .: <div title="You're"> Ergebnis ist Gruppe 1: Titel, Gruppe 2:“, Gruppe 3: Sie sind

Dies ist das Online-RegEx Beispiel: https://regex101.com/r/aVz4uG/13



ich normalerweise dieses RegEx verwenden die HTML-Tags zu extrahieren:

Ich empfehle diese Option, wenn Sie nicht über einen Tag-Typen wie <div verwenden, <span, etc.

<[^/]+?(?:\".*?\"|'.*?'|.*?)*?>

Zum Beispiel:

<div title="a>b=c<d" data-type='a>b=c<d'>Hello</div>
<span style="color: >=<red">Nothing</span>
# Returns 
# <div title="a>b=c<d" data-type='a>b=c<d'>
# <span style="color: >=<red">

Dies ist das Online-RegEx Beispiel: https://regex101.com/r/aVz4uG/15

Der Fehler in dieser RegEx ist:

<div[^/]+?(?:\".*?\"|'.*?'|.*?)*?>

In diesem Tag:

<article title="a>b=c<d" data-type='a>b=c<div '>Hello</article>

Gibt <div '> aber es sollte jedes Spiel nicht zurück:

Match:  <div '>

Um "zu lösen" dies die [^/]+? Muster entfernen:

<div(?:\".*?\"|'.*?'|.*?)*?>


Die Antwort # 317.081 gut ist, aber es paßt nicht richtig mit diesen Fällen:

<div id="a"> # It returns "a instead of a
<div style=""> # It doesn't match instead of return only an empty property
<div title = "c"> # It not recognize the space between the equal (=)

Dies ist die Verbesserung:

(\S+)\s*=\s*["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))?[^"']*)["']?

vs

(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?

Vermeiden Sie die Abstände zwischen gleichem Signal: (\ S +) \ s * = \ s * ((?: ...

Ändern Sie den letzten + und. zum: | [> "'])) [^?" '] * ) [ "']

?

Dies ist das Online-RegEx Beispiel: https://regex101.com/r/aVz4uG/8

so etwas wie dies könnte hilfreich sein

'(\S+)\s*?=\s*([\'"])(.*?|)\2

Ich schlage vor, Sie verwenden HTML Tidy die HTML zu XHTML konvertieren, und dann eine passende XPath verwenden Ausdruck der Attribute zu extrahieren.

Wenn Sie allgemeine sein wollen, müssen Sie bei der genauen Angabe der ein Tag sehen, wie hier . Aber auch mit, dass, wenn Sie Ihre perfekte regexp, was, wenn Sie malformed html haben?

würde ich vorschlagen, für eine Bibliothek gehen html zu analysieren, je nach Sprache, mit Ihnen zusammenarbeiten: z.B. wie Pythons Schöne Suppe.

Wenn youre in .NET empfehle ich das HTML Agility Pack, sehr robust auch bei ungültigen HTML.

Dann können Sie XPath verwenden.

Ich würde die Strategie überdenken, nur einen einzigen regulären Ausdruck zu verwenden. Sicher ist es ein schönes Spiel mit einem einzigen regulären Ausdruck zu kommen, die alles kann. Aber in Bezug auf Wartbarkeit sind Sie über sich selbst in beiden Füßen zu schießen.

-Tags in HTML hat die Form

<tag 
   attrnovalue 
   attrnoquote=bli 
   attrdoublequote="blah 'blah'"
   attrsinglequote='bloob "bloob"' >

Zur Anpassung Attribute, benötigen Sie einen regulären Ausdruck attr, die eine der vier Formen findet. Dann müssen Sie sicherstellen, dass nur Spiele innerhalb HTML-Tags gemeldet werden. Vorausgesetzt, dass Sie die richtige Regex haben, würde die gesamte Regex sein:

attr(?=(attr)*\s*/?\s*>)

Der Look-Ahead stellt sicher, dass nur andere Attribute und den End-Tag das Attribute folgen. Ich verwende den folgenden regulären Ausdruck für attr:

\s+(\w+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^><"'\s]+)))?

Unwichtige Gruppen sind nicht Capturing gemacht. Die erste passende Gruppe $1 gibt Ihnen den Namen des Attributs ist der Wert eines $2or $3 oder $4. Ich benutze $2$3$4 den Wert zu extrahieren. Die endgültige regex ist

\s+(\w+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^><"'\s]+)))?(?=(?:\s+\w+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^><"'\s]+))?)*\s*/?\s*>)

Hinweis: entfernte ich alle unnötigen Gruppen in dem Look-Ahead und machte alle nicht Capturing verbleibenden Gruppen

.

ich auch diese benötigt und schrieb Attribute eine Funktion zum Parsen, können Sie es von hier:

https://gist.github.com/4153580

(Hinweis: Es ist nicht regex verwenden)

Ich habe eine PHP-Funktion erstellt dass Attribute aller HTML-Tags extrahieren können. Es kann auch Attribute wie disabled verarbeitet, die keinen Wert haben, und kann auch bestimmen, ob der Tag einen Stand-alone-Tag (keinen schließenden Tag hat) oder nicht (hat einen End-Tag), indem das content Ergebniskontrolle:

/*! Based on <https://github.com/mecha-cms/cms/blob/master/system/kernel/converter.php> */
function extract_html_attributes($input) {
    if( ! preg_match('#^(<)([a-z0-9\-._:]+)((\s)+(.*?))?((>)([\s\S]*?)((<)\/\2(>))|(\s)*\/?(>))$#im', $input, $matches)) return false;
    $matches[5] = preg_replace('#(^|(\s)+)([a-z0-9\-]+)(=)(")(")#i', '$1$2$3$4$5<attr:value>$6', $matches[5]);
    $results = array(
        'element' => $matches[2],
        'attributes' => null,
        'content' => isset($matches[8]) && $matches[9] == '</' . $matches[2] . '>' ? $matches[8] : null
    );
    if(preg_match_all('#([a-z0-9\-]+)((=)(")(.*?)("))?(?:(\s)|$)#i', $matches[5], $attrs)) {
        $results['attributes'] = array();
        foreach($attrs[1] as $i => $attr) {
            $results['attributes'][$attr] = isset($attrs[5][$i]) && ! empty($attrs[5][$i]) ? ($attrs[5][$i] != '<attr:value>' ? $attrs[5][$i] : "") : $attr;
        }
    }
    return $results;
}

Test-Code

$test = array(
    '<div class="foo" id="bar" data-test="1000">',
    '<div>',
    '<div class="foo" id="bar" data-test="1000">test content</div>',
    '<div>test content</div>',
    '<div>test content</span>',
    '<div>test content',
    '<div></div>',
    '<div class="foo" id="bar" data-test="1000"/>',
    '<div class="foo" id="bar" data-test="1000" />',
    '< div  class="foo"     id="bar"   data-test="1000"       />',
    '<div class id data-test>',
    '<id="foo" data-test="1000">',
    '<id data-test>',
    '<select name="foo" id="bar" empty-value-test="" selected disabled><option value="1">Option 1</option></select>'
);

foreach($test as $t) {
    var_dump($t, extract_html_attributes($t));
    echo '<hr>';
}

Das funktioniert für mich. Es berücksichtigt auch einige End-Fälle, die ich erlebt habe.

Ich bin mit diesem Regex für XML-Parser

(?<=\s)[^><:\s]*=*(?=[>,\s])

Extrahieren Sie das Element:

var buttonMatcherRegExp=/<a[\s\S]*?>[\s\S]*?<\/a>/;
htmlStr=string.match( buttonMatcherRegExp )[0]

Dann jQuery verwenden, um zu analysieren und extrahieren das Bit Sie wollen:

$(htmlStr).attr('style') 
scroll top