2013-08-17 4 views
3

Я пытаюсь найти лучший способ использовать HTML :: TreeBuilder в Perl, чтобы извлечь несколько абзацев текста из некоторого HTML в XML-файл.найти div с абзацем в perl с HTML :: TreeBuilder

У меня было это с использованием $tree->address (или так я думал), пока не понял, что не все записи находятся в одном порядке.

Не выходя за каждый элемент в списке, кажется, что каждая запись имеет несколько элементов <div>, но только один из <div> имеет <p> элементов в нем. И ни у одного из <div> нет классов, что сделало бы это легко.

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

Было бы хорошо, если бы что-то вроде этого работал:

$bodyText = $tree->look_down('_tag' => 'div' => 'p'); 

Но это дает мне ошибку:

param list to look_down ends in a key! 

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

Спасибо!

Джон

+0

Не могли бы вы добавить примерный фрагмент HTML? – simbabque

ответ

2

Ваше сообщение об ошибке имеет смысл. Метод look_down ожидает хеш (который, конечно же, является списком). Вы даете ему три элемента, поэтому последний - это ключ. Имейте в виду, что => также называется жирной запятой и является более читаемым способом написать ,. Однако это немного странное сообщение об ошибке.

Что вам нужно сделать, так это разобрать на <div> сперва, и разобрать их на <p> s. Вы не можете сделать это за один раз с помощью HTML :: TreeBuilder. Вы получите объекты HTML :: Element для каждого из <div> s с первого foreach. Имейте их look_down для <p> s.

use strict; 
use warnings; 
use feature qw(say); 
use HTML::TreeBuilder 5 -weak; 

my $tree = HTML::TreeBuilder->new_from_content(<DATA>); 
foreach my $e ($tree->look_down(_tag => 'div')) { 
    foreach my $f ($e->look_down(_tag => 'p')) { 
    say $f->as_text; 
    } 
} 

__DATA__ 
<html> 
<body> 
<div>foo</div> 
<div><p>hello world</p></div> 
<div>foo2</div> 
<div>foo3</div> 
<div><p>hello again</p></div> 
</body> 
</html> 
0
use Web::Query 'wq'; 
print wq("<html><div><p>I'm trapped under a hat</p></div><div /><div /><div /><div /><div />") 
     ->find('div p')->text; 
+0

Очень аккуратный. Однако объяснение было бы приятным. ;-) – simbabque

2

Я рекомендую использовать XPath для этого:

use strict; 
use warnings; 
use feature qw(say); 
use HTML::TreeBuilder::XPath; 

my $tree = HTML::TreeBuilder->new_from_content(<DATA>); 
foreach my $e ($tree->findnodes('//div/p')) { 

    say $e->as_text; 
} 

__DATA__ 
<html> 
<body> 
<div>foo</div> 
<div><p>hello world</p></div> 
<div>foo2</div> 
<div>foo3</div> 
<div><p>hello again</p></div> 
</body> 
</html> 
3

С ванильной формой HTML::TreeBuilder, лучше всего это сделать, используя ссылку коды в качестве критерия look_down. Подпрограмма будет вызываться для каждого узла в дереве, который передает все предыдущие критерии, и узел будет сохранен, если подпрограмма вернет истинное значение.

Эта программа показывает свое использование. Анонимная подпрограмма использует grep для проверки дочерних узлов передаваемого ему узла, считая все элементы, имеющие тег p. Затем массив @divs содержит все элементы div, которые имеют дочерний элемент p. Вы можете убедиться, что @divs содержит ровно один элемент.

use strict; 
use warnings; 

use feature 'say'; 

use HTML::TreeBuilder; 

my $doc = HTML::TreeBuilder->new_from_content(<<__HTML__); 
<div>content</div> 
<div>content</div> 
<div><p>paragraph</p></div> 
<div>content</div> 
<div>content</div> 
__HTML__ 

my @divs = $doc->look_down(
    _tag => 'div', 
    sub { grep { ref eq 'HTML::Element' and $_->tag eq 'p' } $_[0]->content_list } 
); 

say scalar @divs, " found:\n"; 
say $divs[0]->as_HTML('<>&', ' '); 

выход

1 found: 

<div> 
    <p>paragraph</div> 

Тем не менее, это очень аккуратным, чтобы использовать улучшенную HTML::TreeBuilder::XPath, что позволяет данные, которые должны быть решены с помощью XPath выражений. Это позволяет look_down быть заменено findnodes вызова:

my @divs = $doc->findnodes('//div[p]'); 

и результат идентичен приведенной выше коды.

+0

Попытка вашего первого примера, я получил следующую ошибку: 'Не могу вызвать метод isa" без ссылки на пакет или объект в ... ', но не уверен, почему? –

+0

@JohnB: Прошу прощения. Я забыл, что вы не можете вызывать 'isa' на пустой строке. Я исправил это. – Borodin