XQuery/链接收集
外观
< XQuery
您想收集博客页面上的链接。
我们使用 doc() 函数对远程网页执行 HTTP GET 操作。如果页面是格式良好的 XML 文件,那么您可以通过向 doc 函数添加 ul 谓词来提取所有无序列表项。
此脚本获取博客页面并选择链接部分中的 URL,这些 URL 引用其他博客文章。获取每个引用的文章并选择标记为外部的 URL。结果以 XML 格式返回。
declare namespace q = "http://www.w3.org/1999/xhtml";
<results>
{
let $nav := doc("http://www.advocatehope.org/tech-tidbits/theory-of-the-web-as-one-big-database")//q:ul[@class="portletNavigationTree navTreeLevel0"]
for $href in $nav//@href
let $page := data($href)
let $content := doc($page)//q:div[@id="content"]
for $links in $content//q:a[@title="external-link"]
return
<link>{ data($links/@href) }</link>
}
</results>
删除中间变量可以更清晰地看到结构
declare namespace q = "http://www.w3.org/1999/xhtml";
let $uri := "http://www.advocatehope.org/tech-tidbits/theory-of-the-web-as-one-big-database"
return
<results>
{
for $page in doc($uri)//q:ul[@class="portletNavigationTree navTreeLevel0"]//@href
for $link in doc($page)//q:div[@id="content"]//q:a[@title="external-link"]/@href
return
<link>{data($link)}</link>
}
</results>
Daniel 正在提议一项支持从站点中提取此类数据的标准。此类模式将定义一组文档的视图,该视图足以允许以上提取基于模式。
我们可以通过用添加了实现相关的路径的 ER 模型表示的视图模式来实现这一目标。
<model name="blog-links">
<type name="url" datatype="string"/>
<entity name="page" >
<attribute name="inner" max="N" path="//q:ul[@class='portletNavigationTree navTreeLevel0']//@href" type="page"/>
<attribute name="external" max="N" path="//q:div[@id='content']//q:a[@title='external-link']/@href" type="url"/>
</entity>
</model>
然后,此模式可以被通用脚本链接收集脚本使用
let $start := request:get-parameter("page",())
let $view := request:get-parameter("view",())
let $schema := doc($view)
let $inner := $schema//entity[@name='page']/attribute[@name='inner']/@path
let $external := $schema//entity[@name='page']/attribute[@name='external']/@path
return
<results>
{
for $page in util:eval(concat('doc($start)',$inner))
for $link in util:eval(concat('doc($page)',$external))
return
<link>{string($link)}</link>
}
</results>
此脚本现在在任何页面结构可以在模式方面用适当的路径定义的站点上执行链接收集任务。
前一个版本仅在 URI 为绝对值时才有效。如果 URI 不是绝对值,则需要做更多工作。
declare namespace q = "http://www.w3.org/1999/xhtml";
declare variable $start := request:get-parameter("page",());
declare variable $view := request:get-parameter("view",());
declare variable $schema := doc($view);
declare variable $base := substring-before($start,local:local-uri($start));
declare function local:local-uri($uri) {
if (contains($uri,"/"))
then local:local-uri(substring-after($uri,"/"))
else $uri
};
declare function local:absolute-uri($url) {
if (starts-with($url,"http://"))
then $url
else concat($base,$url)
};
let $inner := $schema//entity[@name='page']/attribute[@name='inner']/@path
let $external := $schema//entity[@name='page']/attribute[@name='external']/@path
let $starturi := local:absolute-uri($start)
return
<results>
{
for $page in util:eval(concat('doc($starturi)',$inner))
let $pageuri := local:absolute-uri($page)
for $link in util:eval(concat('doc($pageuri)',$external))
return
<link>{string($link)}</link>
}
</results>
因此,使用不同的模式 - 相同的模型,不同的路径
<model name="site-links">
<type name="url" datatype="string"/>
<entity name="page" >
<attribute name="inner" max="N" path="//div[@class='nav']//a/@href" type="page"/>
<attribute name="external" max="N" path="//div[@class='content']//a/@href" type="url"/>
</entity>
</model>
这是 测试站点 的视图模式
导航路径仍然在脚本中硬编码。我们希望编写路径表达式,其中步骤在模式中定义。然后,此路径将在模式的上下文中解释。
在此示例中,测试站点已扩展为包括一个单独的索引页面以及视图中的一些其他组件
<model name="site-links">
<entity name="externalPage">
<attribute name="title" path="/head/title"/>
</entity>
<entity name="index">
<attribute name="link" max="N" path="//div[@class='index']//a/@href" type="page"/>
</entity>
<entity name="page">
<attribute name="title" path="//head/title"/>
<attribute name="inner" max="N" path="//div[@class='nav']//a/@href" type="page"/>
<attribute name="external" max="N" path="//div[@class='content']//a/@href"
type="externalPage"/>
<attribute name="author" min="0" path="//div[@class='content']/span[@class='author']"/>
</entity>
</model>
此原型使用简单的路径语言。步骤 -> 取消引用相对或绝对 URL。如果步骤被识别为当前实体的属性,则使用关联的路径表达式,否则步骤将作为 XPath 执行。第一步标识初始文档的(实体)类型。
例如
列出索引中页面的标题。
import module namespace vp ="http://www.cems.uwe.ac.uk/xmlwiki/vp" at "../Gov/vp.xqm";
let $uri := "http://www.cems.uwe.ac.uk/xmlwiki/Gov/site/index.html"
let $schema := "/db/Wiki/Gov/site3.xml"
return
<result>
{vp:process-path($uri,"index/link/->/title",$schema) }
</result>
列出索引中引用的页面的作者。
import module namespace vp ="http://www.cems.uwe.ac.uk/xmlwiki/vp" at "../Gov/vp.xqm";
let $uri := "http://www.cems.uwe.ac.uk/xmlwiki/Gov/site/index.html"
let $schema := "/db/Wiki/Gov/site3.xml"
return
<result>
{vp:process-path($uri,"index/link/->/author/string(.)",$schema) }
</result>
列出索引页面引用的所有页面的所有不同外部链接的 URL。
import module namespace vp ="http://www.cems.uwe.ac.uk/xmlwiki/vp" at "../Gov/vp.xqm";
declare option exist:serialize "method=xhtml media-type=text/html";
let $uri := "http://www.cems.uwe.ac.uk/xmlwiki/Gov/site/index.html"
let $schema := "/db/Wiki/Gov/site3.xml"
return
<ul>
{for $uri in distinct-values(vp:process-path($uri,"index/link/->/external",$schema))
order by $uri
return
<li>
<a href="{$uri}">{string($uri)}</a>
</li>
}
</ul>
列出链接到初始页面的页面的标题。
import module namespace vp ="http://www.cems.uwe.ac.uk/xmlwiki/vp" at "../Gov/vp.xqm";
let $uri := "http://www.cems.uwe.ac.uk/xmlwiki/Gov/site/test1.html"
let $schema := "/db/Wiki/Gov/site3.xml"
return
<result>
{vp:process-path($uri,"page/inner/->/inner/->/title",$schema) }
</result>
核心函数在模式的上下文中处理虚拟路径。
declare function vp:process-steps($nodes,$context,$steps,$base,$schema) {
if (empty($steps))
then $nodes
else
let $step := $steps[1]
let $entity := $schema//entity[@name=$context]
return
if ( $step = "->" )
then
let $newnodes :=
for $node in $nodes
return vp:get-doc($node,$base)
return
vp:process-steps($newnodes, $context, subsequence($steps,2),$base,$schema)
else
if ($entity/attribute[@name=$step])
then
let $attribute :=$entity/attribute[@name=$step]
let $next := string($schema//entity[@name=$attribute/@type]/@name)
let $path := string($attribute/@path)
let $newnodes :=
for $node in $nodes
let $newnode := util:eval(concat("$node",$path))
return $newnode
return
vp:process-steps($newnodes, $next, subsequence($steps,2),$base,$schema)
else
let $newnodes :=
for $node in $nodes
let $newnode := util:eval(concat("$node/",$step))
return $newnode
return
vp:process-steps($newnodes, $context, subsequence($steps,2),$base,$schema)
};
此示例基于 Daniel Bennett 的文章 [1]。