我正在尝试使用WWW::Mechanize
和Mojo::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::DOM
和Mojo::Collections
的工作方式。
任何帮助将不胜感激。
答案 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工具的输出,与该模块绑定:
如您所见,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;
}