使用XML:LibXML使用PERL解析RSS提要

使用XML:LibXML使用PERL解析RSS提要,xml,perl,parsing,rss,Xml,Perl,Parsing,Rss,不幸的是,我需要解析RSS提要,因为没有其他方法来获取数据。我有一个Perl脚本,它以前曾用于解析XML文件。我想我可以修改它,以便对RSS提要执行相同的操作,以便将数据转换为更易于使用的格式。考虑到这一点,我修改了我的文件。但它实际上似乎没有从提要中找到任何数据。这是代码的核心 foreach my $channel ($root->findnodes('channel')) { foreach my $item ($root->findnodes('item')) {

不幸的是,我需要解析RSS提要,因为没有其他方法来获取数据。我有一个Perl脚本,它以前曾用于解析XML文件。我想我可以修改它,以便对RSS提要执行相同的操作,以便将数据转换为更易于使用的格式。考虑到这一点,我修改了我的文件。但它实际上似乎没有从提要中找到任何数据。这是代码的核心

foreach my $channel ($root->findnodes('channel')) {
  foreach my $item ($root->findnodes('item')) {
    my $guid = $item->findvalue('guid');
    my $title = $item->findvalue('title');
    my $link = $item->findvalue('link');
    my $description = $item->findvalue('description');
    my $pubdate = $item->findvalue('pubdate');
    print DATA "INSERT INTO events VALUES ( \"$guid\", \"$title\", \"$link\",\"$description\", \"$pubdate\" ); \n";
  }
}

有什么想法吗?

暂且不提使用XML::RSS的建议

我认为您遇到的主要问题与XML名称空间有关。考虑脚本的这一行:

$root->findnodes('channel')
它正在查找“channel”类型的元素,但您的源文档可能不包含此类元素。您应该查找的内容类似于:URI“”标识的命名空间中的“channel”类型的元素

使用名称空间非常复杂。有两种类型:默认名称空间(例如:xmlns=”http://purl.org/rss/1.0/"); 以及使用前缀声明的名称空间(例如:xmlns:rss=”http://purl.org/rss/1.0/"). 在这两种情况下,唯一重要的是名称空间URI。文档中声明的前缀(例如:“rss:”)与脚本无关

要将名称空间与libxml一起使用,需要为每个名称空间URI声明自己的前缀,然后在对findnodes的调用中使用该前缀。您可以选择与文档中的前缀相同或不同的前缀-只要URI相同,这并不重要。您需要使用XML::LibXML::XPathContext对象将命名空间URI与前缀相关联,然后通过该上下文对象路由查询

这是您的脚本的一个版本,可能更接近您想要的

#!/usr/bin/perl

use strict;
use warnings;

use XML::LibXML;
use XML::LibXML::XPathContext;

my $parser = XML::LibXML->new();
my $doc    = $parser->parse_file('slashdot.rss');
my $root   = $doc->documentElement();

my $xc     = XML::LibXML::XPathContext->new( $root );
$xc->registerNs( rss => 'http://purl.org/rss/1.0/' );

foreach my $channel ($xc->findnodes('rss:channel')) {
    foreach my $item ($xc->findnodes('rss:item')) {
        my $guid = $xc->findvalue('rss:guid', $item);
        my $title = $xc->findvalue('rss:title', $item);
        my $link = $xc->findvalue('rss:link', $item);
        my $description = $xc->findvalue('rss:description', $item);
        my $pubdate = $xc->findvalue('rss:pubDate', $item);
        print "INSERT INTO events VALUES ( \"$guid\", \"$title\", \"$link\",\"$description\", \"$pubdate\" ); \n";
    }
}
您试图解析的文档可能使用了不同版本的RSS,因此使用了不同的RSS名称空间URI——这只是使用RSS模块而不是手动执行的众多原因之一


正如前面所指出的,将值插入SQL实际上是一个糟糕的主意。在您的示例中,您正在使用双引号字符串文本生成SQL(您可能打算使用单引号)。如果从RSS提取的任何值包含双引号字符,则此操作将失败。RSS中极有可能出现单引号和双引号字符。

暂且不提使用XML::RSS的极好建议

我认为您遇到的主要问题与XML名称空间有关。考虑脚本的这一行:

$root->findnodes('channel')
它正在查找“channel”类型的元素,但您的源文档可能不包含此类元素。您应该查找的内容类似于:URI“”标识的命名空间中的“channel”类型的元素

使用名称空间非常复杂。有两种类型:默认名称空间(例如:xmlns=”http://purl.org/rss/1.0/"); 以及使用前缀声明的名称空间(例如:xmlns:rss=”http://purl.org/rss/1.0/"). 在这两种情况下,唯一重要的是名称空间URI。文档中声明的前缀(例如:“rss:”)与脚本无关

要将名称空间与libxml一起使用,需要为每个名称空间URI声明自己的前缀,然后在对findnodes的调用中使用该前缀。您可以选择与文档中的前缀相同或不同的前缀-只要URI相同,这并不重要。您需要使用XML::LibXML::XPathContext对象将命名空间URI与前缀相关联,然后通过该上下文对象路由查询

这是您的脚本的一个版本,可能更接近您想要的

#!/usr/bin/perl

use strict;
use warnings;

use XML::LibXML;
use XML::LibXML::XPathContext;

my $parser = XML::LibXML->new();
my $doc    = $parser->parse_file('slashdot.rss');
my $root   = $doc->documentElement();

my $xc     = XML::LibXML::XPathContext->new( $root );
$xc->registerNs( rss => 'http://purl.org/rss/1.0/' );

foreach my $channel ($xc->findnodes('rss:channel')) {
    foreach my $item ($xc->findnodes('rss:item')) {
        my $guid = $xc->findvalue('rss:guid', $item);
        my $title = $xc->findvalue('rss:title', $item);
        my $link = $xc->findvalue('rss:link', $item);
        my $description = $xc->findvalue('rss:description', $item);
        my $pubdate = $xc->findvalue('rss:pubDate', $item);
        print "INSERT INTO events VALUES ( \"$guid\", \"$title\", \"$link\",\"$description\", \"$pubdate\" ); \n";
    }
}
您试图解析的文档可能使用了不同版本的RSS,因此使用了不同的RSS名称空间URI——这只是使用RSS模块而不是手动执行的众多原因之一


正如前面所指出的,将值插入SQL实际上是一个糟糕的主意。在您的示例中,您正在使用双引号字符串文本生成SQL(您可能打算使用单引号)。如果从RSS提取的任何值包含双引号字符,则此操作将失败。单引号和双引号字符极有可能出现在RSS中。

旁注:请研究如何在数据库插入中使用。您不想让小Bobby表进行访问,XML提取看起来很好。请提供问题的最小可运行演示。(数据库代码完全被破坏了。考虑如果代码> $标题<代码>或<代码> $描述<代码>包含了<代码>“<代码>,换行符,或者更邪恶的东西。我理解您的安全顾虑,但是此脚本的目的是将数据转储到只读sqlite数据文件中。此文件不会用于web消费。此脚本的目的是动态生成一个新的独立sqlite数据文件。您是否考虑过使用,例如?编写自己的RSS解析器通常是一项艰巨的任务策略。@oljones,这就是为什么我要求提供一个可运行的演示。我会在30秒后发现。旁注:研究如何使用数据库插入。你不想让小Bobby表进行访问。XML提取看起来很好。请提供一个关于该问题的最低可运行演示。(数据库代码完全被破坏了。考虑如果代码> $标题<代码>或<代码> $描述<代码>包含了<代码>“<代码>,换行符,或者更邪恶的东西。我理解您的安全顾虑,但是此脚本的目的是将数据转储到只读sqlite数据文件中。此文件不会用于web消费。此脚本的目的是动态生成一个新的独立sqlite数据文件。您是否考虑过使用,例如?编写自己的RSS解析器通常是一项艰巨的任务策略。@oljones,这就是为什么我要求一个可运行的演示。我会在30秒内找到它。