2012-02-09 1 views
1

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

<html> 
    <head> 
    <title>This tag includes 'name', so it should be retrieved</title> 
    </head> 
    <body> 
    <h1 class="name">This is also a tag to be retrieved</h1> 
    <h2>Generic h2 tag</h2> 
    </body> 
</html> 

Вероятно, я должен попробовать регулярное выражение, чтобы поймать каждый матч между открытием и закрытием "<>", однако, я хотел бы иметь возможность пройти синтаксическое дерево на основе этих совпадений, поэтому я могу получить братьев и сестер или родителей или «nextElements». В приведенном выше примере это составляет <head>*</head> или, может быть, <h2>*</h2>, как только я знаю, что они родители или братья и сестры тега, содержащего совпадение.

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

Предложения?

+0

Использование Regex на Html трудно. Я не предлагаю вам идти по этому пути. Что вы пытаетесь сделать с Html? См. Эту статью: http://www.codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html – AdamV

+0

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

My имя is beerbajay

'? Что вы ожидаете, нужно вернуть? – beerbajay

+0

@beerbajay Нет, это нормально, если получить этот тег ввода (поскольку он содержит «имя». Очевидно, что мой реальный пример не использует «имя» в качестве соответствия. –

ответ

2

Использование lxml.html. Это отличный парсер, он поддерживает xpath, который может выражать все, что вы хотите легко.

В приведенном ниже примере используется это выражение XPath:

//*[contains(text(),'name']/parent::*/following-sibling::*[1]/*[@class='name']/text() 

Это означает, в английском:

Find me any tag that contains the word 'name' in its text, then get the parent, and then the next sibling, and find inside that any tag with the class 'name' and finally return the text content of that.

Результат выполнения кода является:

['This is also a tag to be retrieved'] 

Вот полный код:

text = """ 
<html> 
    <head> 
    <title>This tag includes 'name', so it should be retrieved</title> 
    </head> 
    <body> 
    <h1 class="name">This is also a tag to be retrieved</h1> 
    <h2>Generic h2 tag</h2> 
    </body> 
</html> 
""" 

import lxml.html 
doc = lxml.html.fromstring(text) 
print doc.xpath('//*[contains(text(), $stuff)]/parent::*/' 
    'following-sibling::*[1]/*[@class=$stuff]/text()', stuff='name') 

обязательное чтение, то «пожалуйста, не разобрать HTML с регулярным выражением» Ответ здесь: https://stackoverflow.com/a/1732454/17160

+0

О, это выглядит великолепно (несмотря на страшный синтаксис). Чтобы быть уверенным, что я хочу, «найдите мне любой тег, который содержит слово« имя »(меня не волнует, есть ли он в TextNode или в атрибуте, что угодно), затем получите родителя, а затем следующий брат Сделайте то же самое с любым другим тегом, содержащим слово «имя». Поэтому я думаю, что мне не нужен текст, соответствующий последней части, из тега с «class = 'name». И text() работает для каждой части тег, а не только его TextNode, правильно? –

+0

Uhm, выглядит как «содержит (текст(), $ stuff) ...» получает только первый тег. Он должен также получить то же самое, потому что в нем есть «имя». try "содержит (*, $ stuff) ...", но я получаю только первые два тега (html, head). Знаете ли вы, как получить второй тег? –

+0

Я думаю, что это решает: doc.xpath (" // * [contains (text(), 'name')] | // * [@ * = 'name'] ") –

1

Учитывая следующие условия:

  • Матч должен произойти в стоимости атрибут на теге
  • Соответствие должно происходить в текстовом узле, который является прямым потомком тега

Вы можете использовать красивый суп:

from bs4 import BeautifulSoup 
from bs4 import NavigableString 
import re 

html = '''<html> 
    <head> 
    <title>This tag includes 'name', so it should be retrieved</title> 
    </head> 
    <body> 
    <h1 class="name">This is also a tag to be retrieved</h1> 
    <h2>Generic h2 tag</h2> 
    </body> 
</html>''' 

soup = BeautifulSoup(html) 
p = re.compile("name") 

def match(patt): 
    def closure(tag): 
     for c in tag.contents: 
      if isinstance(c, NavigableString): 
       if patt.search(unicode(c)): 
        return True 
     for v in tag.attrs.values(): 
      if patt.search(v): 
       return True 
    return closure 

for t in soup.find_all(match(p)): 
    print t 

Выход:

<title>This tag includes 'name', so it should be retrieved</title> 
<h1 class="name">This is also a tag to be retrieved</h1> 
+0

Спасибо за ваш ответ. Вы уверены в этом коде? Не должен быть 'findAll' вместо «find_all» ?. Тем не менее, я получаю следующую ошибку: «AttributeError:« list »obje ct не имеет атрибутов 'values'. Я думаю, что вы не передаете какую-либо ценность закрытию (тегу). –

+0

Извините, я не упоминал, что в коде используется 'bs4', который является BeautifulSoup4, который недавно выпущен. – beerbajay

+0

Это что ?. Я изменил «из bs4 import NavigableString» на «из BeautifulSoup import NavigableString», и он не жаловался, но AttributeError остается –

Смежные вопросы