这个问题已被重新定义。我使用CPAN Perl模块WWW::Mechanize来浏览网站,HTML::TreeBuilder-XPath来捕获内容,使用xacobeo来测试HTML / XML上的XPath代码。目标是从基于PHP的网站调用此Perl脚本,并将已删除的内容上载到数据库中。因此,如果内容“缺失”,则仍需要考虑内容。
以下是经过测试的简化示例代码,描述了我的挑战。注意:
ITEMS
;每个商店将存在不同数量的Products*
。这些产品清单可能有也可能没有其下方的逐项表。下面,示例xml每个商店更改(如上所述),但为了简洁起见,我只显示一个“类型”的输出。我意识到所有数据都可以捕获到一个数组中,然后使用正则表达式来解密内容,以便将其上传到数据库中。我正在寻求更好的XPath知识,以帮助简化这个(和未来的)解决方案。
<!DOCTYPE XHTML>
<table id="8jd9c_ITEMS">
<tr><th style="color:red">The Products we have in stock!</th></tr>
<tr><td><span id="Product_NUTS">We have nuts!</span></td></tr>
<tr><td>
<!--Table may or may not exist -->
<table>
<tr><td style="color:blue;text-indent:10px">Almonds</td></tr>
<tr><td style="color:blue;text-indent:10px">Cashews</td></tr>
<tr></tr>
</table>
</td></tr>
<tr><td><span id="Product_VEGGIES">We have veggies!</span></td></tr>
<tr><td>
<!--Table may or may not exist -->
<table>
<tr><td style="color:blue;text-indent:10px">Carrots</td></tr>
<tr><td style="color:blue;text-indent:10px">Celery</td></tr>
<tr></tr>
</table>
</td></tr>
<tr><td><span id="Product_ALCOHOL">We have booze!</span></td></tr>
<!--In this case, the table does not exist -->
</table>
XPath声明:
'//table[contains(@id, "ITEMS")]/tr[position() >1]/td/span/text()'
会找到:
We have nuts!
we have veggies!
We have booze!
和XPath声明:
'//table[contains(@id, "ITEMS")]/tr[position() >1]/td/table/tr/td/text()'
会找到:
Almonds
Cashews
Carrots
Celery
可以组合两个XPath语句:
'//table[contains(@id, "ITEMS")]/tr[position() >1]/td/span/text() | //table[contains(@id, "ITEMS")]/tr[position() >1]/table/tr/td/text()'
找到:
We have nuts!
Almonds
Cashews
We have veggies!
Carrots
Celery
We have booze!
同样,上面的数组可以使用正则表达式对其产品到列表关联进行解密(在实际代码中)。 但是,可以使用XPath以保持该关联的方式构建阵列吗?
例如(假说,这不起作用):
'//table[contains(@id, "ITEMS")]/tr[position()>1]/td/span/text() |
if exists('//table[contains(@id, "ITEMS")]/tr[position() >1]/table))
then ("NoTable") else ("TableRef") |
Save this result into @TableRef ('//table[contains(@id, "ITEMS")]/tr[position() >1]/table/tr/td/text()')'
在Perl中构建多维数组(在传统意义上)是不可能的,请参阅perldoc perlref但是希望类似于上面的解决方案可以创建类似的东西:
@ITEMS[0] => We have nuts!
@ITEMS[1] => nutsREF <-- say, the last word of the span value + REF
@ITEMS[2] => We have veggies!
@ITEMS[3] => veggiesREF <-- say, the last word of the span value + REF
@ITEMS[4] => We have booze!
@ITEMS[5] => NoTable <-- value accounts for the missing info
@nutsREF[0] => Almonds
@nutsREF[1] => Cashews
@veggiesREF[0] => Carrots
@veggiesREF[1] => Celery
在实际代码中,产品是已知的,因此可以在预期XPath输出时定义my @veggiesREF
和my @nutsREF
。
我意识到XPath if / else / then功能是在XPath 2.0版本中。我在ubuntu系统上并在本地工作,但我仍然不清楚我的apache2服务器是使用它还是1.0版本。我该如何检查?
最后,如果您可以展示如何从PHP表单提交调用Perl脚本以及如何将Perl数组传递回调用PHP函数,那么这将获得赏金。 :)
谢谢!
最终编辑:
这篇文章下方的评论是针对一个过于模糊的初始帖子。随后的重新发布(和赏金)由ikegami以非常有创意的方式回应解决了伪问题,但在我的实际应用程序中难以掌握和重用 - 这需要在各种html页面上进行多次使用。在关于我们对话框中的第18条评论中,我终于发现了他的意思和使用($ cat) - 他使用的未记录的Perl语法。对于新读者来说,理解该语法可以理解(并重新格式化)他对问题的智能解决方案。他的帖子肯定符合OP中寻求的基本要求,但不使用HTML :: TreeBuilder :: XPath来实现。
jpalecek使用HTML :: TreeBuilder :: XPath,但不会将捕获的数据放入数组中,以便传回PHP函数并上传到数据库中。
我从两位响应者那里学到了很多,希望这篇文章可以帮助像Perl这样的新人,比如我自己。任何最后的贡献将不胜感激。
答案 0 :(得分:5)
如果我猜,你的问题是:“如何从提供的输入中获得以下内容?”
my $categorized_items = {
'We have nuts!' => [ 'Almonds', 'Cashwes' ],
'We have veggies!' => [ 'Carrots', 'Celery' ],
'We have booze!' => [ ],
};
如果是这样,我就是这样做的:
use Data::Dumper qw( Dumper );
use XML::LibXML qw( );
my $root = XML::LibXML->load_xml(IO=>\*DATA)->documentElement;
my %cat_items;
for my $cat_tr ($root->findnodes('//table[contains(@id, "ITEMS")]/tr[td/span]')) {
my ($cat) = map $_->textContent(),
$cat_tr->findnodes('td/span');
my @items = map $_->textContent(),
$cat_tr->findnodes('following-sibling::tr[position()=1]/td/table/tr/td');
$cat_items{$cat} = \@items;
}
print(Dumper(\%cat_items));
__DATA__
...xml...
PS - 你所拥有的是无效的HTML。
答案 1 :(得分:2)
如何在运行something
之前确定query
存在。例如。如果存在//p[@class='red']
,则返回//table
:
/.[//p[@class='red']]//table
x[3 and 4 and 5]
:3 and 4 and 5
是一个布尔表达式,产生true
。因此,它将为您提供所有x
个。对于你想要的第3,第4和第5个
x[position() >= 3 and position() <= 5]
回答编辑过的问题:
为什么不将XML::XPathEngine
用于多个查询?
my $xp = XML::XPathEngine->new;
my $tree = HTML::TreeBuilder::XPath->new;
$tree->parse (something);
然后,您可以查询:
my $shops = $xp->findnodes('//table[contains(@id, "ITEMS")]/tr[position() >1]/td[@span]', $tree);
for($shops->get_nodelist) {
print "Name of shop is ".$xp->findvalue('span/text()', $_)."\n"; # <- query relative to $_
print "The shop sells:\n". join("\n", $xp->findvalues('parent::*/following-sibling::tr[1][not(span)]/td/table/tr/td', $_));
}
这与@ ikegami的回答相同(XML::XPathEngine
使用HTML::TreeBuilder::XPath
)。顺便说一句,如果商店可以有更多的产品线,那么应该更新。