在使用Mojo :: DOM时难以精确定位子元素

时间:2012-12-28 17:38:03

标签: perl mojolicious

我正在尝试使用WWW::MechanizeMojo::DOM从旧的vBulletin论坛中提取文本。

vBulletin不使用HTML和CSS进行语义标记,而我在使用Mojo::DOM->children来获取某些元素时遇到了麻烦。

这些vBulletin帖子的结构取决于其内容。

单个消息:

<div id="postid_12345">The quick brown fox jumps over the lazy dog.<div>

单个消息引用另一个用户:

<div id="postid_12345">
    <div>
    <table>
        <tr>
            <td>
            <div>Quote originally posted by Bob</div>
            <div>Everyone knows the sky is blue.</div>
            </td>
        </tr>
    </table>
    </div>

 I disagree with you, Bob. It's obviously green.
</div>

带有剧透的单个消息:

<div id="postid_12345">
    <div class="spoiler">Yoda is Luke's father!</div>
</div>

单个消息引用另一个用户,使用剧透:

<div id="postid_12345">
    <div>
    <table>
        <tr>
            <td>
            <div>Quote originally posted by Fred</div>
            <div class="spoiler">Yoda is Luke's father!</div>
            </td>
        </tr>
    </table>
    </div>
    <div class="spoiler">No waaaaay!</div>
</div>

假设上面的HTML和一个包含必要帖子ID的数组:

for (@post_ids) {
    $mech->get($full_url_of_specific_forum_post);
    my $dom = Mojo::DOM->new($mech->content);
    my $div_id = 'postid_' . $_;

    say $dom->at($div_id)->children('div')->first;
    say $dom->at($div_id)->text;
}

使用$dom->at($div_id)->all_text为我提供了不间断的一切,这使得很难说出帖子中的引用内容和原始内容。

使用$dom->at($div_id)->text会跳过所有子元素,因此不会选择引用的文本和剧透。

我尝试了$dom->at($div_id)->children('div')->first的各种变体,但这给了我一切,包括HTML。

理想情况下,我希望能够在每个帖子中获取所有文本,每个子元素都在自己的行中,例如。

 POSTID12345:
 + Quote originally posted by Bob
 + Everyone knows the sky is blue. 
 I disagree with you, Bob. It's obviously green. 

我是Mojo的新手并且和Perl一起生气。我想自己解决这个问题,但在查看文档并摆弄它几个小时之后,我的脑子就变得糊里糊涂了,我很茫然。我只是没有得到Mojo::DOMMojo::Collections的工作方式。

任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:3)

查看Mojo :: DOM的源代码,基本上all_text method递归遍历DOM并提取所有文本。使用该源编写自己的DOM函数。它的递归函数依赖于返回一个字符串,在你的函数中你可能会返回一个包含你需要的任何上下文的数组。

修改

在对IRC进行一些讨论之后,网页抓取示例已经更新,它可能会帮助您指导。 http://mojolicio.us/perldoc/Mojolicious/Guides/Cookbook#Web_scraping

答案 1 :(得分:2)

有一个模块来展平HTML树HTML::Linear flatterning HTML tree 的目的解释有点冗长乏味,所以这里有一张图片显示xpathify工具的输出,与该模块绑定:

screenshot

如您所见,HTML树节点成为单键/值列表,其中键是该节点的XPath,值是节点的文本属性。 在一些按键中,这就是你使用HTML :: Linear的方式:

#!/usr/bin/env perl
use strict;
use utf8;
use warnings;

use Data::Printer;
use HTML::Linear;

my $hl = HTML::Linear->new;
$hl->parse_file(q(vboard.html));

for my $el ($hl->as_list) {
    my $hash = $el->as_hash;
    next unless keys %{$hash};
    p $hash;
}