Синтаксический анализ HTML с помощью регулярных выражений имеет слишком много ошибок для надежной обработки.BeautifulSoup и другие анализаторы HTML работают, создавая полную структуру данных документа, по которой вы затем перемещаетесь, чтобы извлечь интересные фрагменты - это детально и всесторонне, но если в источнике есть какой-то ошибочный HTML, даже если его в какой-то части нетне волнуйтесь, это может победить процесс разбора.Pyparsing использует средний подход - вы можете определить мини-парсеры, которые соответствуют только битам, которые вы хотите, и пропустить все остальное (это также упрощает навигацию после разбора).Чтобы устранить некоторые переменные в стилях HTML, pyparsing предоставляет функцию makeHTMLTags
, которая возвращает пару выражений pyparsing для открывающих и закрывающих тегов:
foo_start, foo_end = pp.makeHTMLTags('foo')
foo_start
будет соответствовать:
<foo>
<foo/>
<foo class='bar'>
<foo href=something_not_in_quotes>
и многие другие варианты атрибутов и пробелов.
Выражение foo_start
(как и все выражения переноса) вернет объект ParseResults.Это облегчает доступ к частям анализируемого тега:
foo_data = foo_start.parseString("<foo img='bar.jpg'>")
print(foo_data.img)
Для вашего скребка для страниц Nintendo см. Аннотированный источник ниже:
import pyparsing as pp
# define expressions to match opening and closing tags <h3>
h3, h3_end = pp.makeHTMLTags("h3")
# define a specific type of <h3> tag that has the desired 'class' attribute
h3_b3 = h3().addCondition(lambda t: t['class'] == "b3")
# similar for <p>
p, p_end = pp.makeHTMLTags("p")
p_b3_row_price = p().addCondition(lambda t: t['class'] == "b3 row-price")
# similar for <img>
img, _ = pp.makeHTMLTags("img")
img_expr = img().addCondition(lambda t: t.src.startswith("//media.nintendo.com/nintendo/bin"))
# define expressions to capture tag body for title and price - include negative lookahead for '<' so that
# tags with embedded tags are not matched
LT = pp.Literal('<')
title_expr = h3_b3 + ~LT + pp.SkipTo(h3_end)('title') + h3_end
price_expr = p_b3_row_price + ~LT + pp.SkipTo(p_end)('price') + p_end
# compose a scanner expression by '|'ing the 3 sub-expressions into one
scanner = title_expr | price_expr | img_expr
# not shown - read web page into variable 'html'
# use searchString to search through the retrieved HTML for matches
for match in scanner.searchString(html):
if 'title' in match:
print("Title:", match.title)
elif 'price' in match:
print("Price:", match.price)
elif 'src' in match:
print("Img src:", match.src)
else:
print("???", match.dump())
Первые несколько напечатанных совпадений:
Img src: //media.nintendo.com/nintendo/bin/SF6LoN-xgX1iT617eWfBrNcWH6RQXnSh/I_IRYaBzJ61i-3hnYt_k7hVxHtqGmM_w.png
Title: Hyrule Warriors: Definitive Edition
Price: $59.99
Img src: //media.nintendo.com/nintendo/bin/wcfCyAd7t2N78FkGvEwCOGzVFBNQRbhy/AvG-_d4kEvEplp0mJoUew8IAg71YQveM.png
Title: Donkey Kong Country: Tropical Freeze
Price: $59.99
Img src: //media.nintendo.com/nintendo/bin/QKPpE587ZIA5fUhUL4nSbH3c_PpXYojl/J_Wd79pnFLX1NQISxouLGp636sdewhMS.png
Title: Wizard of Legend
Price: $15.99