跳转到内容

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 为绝对值时才有效。如果 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 执行。第一步标识初始文档的(实体)类型。

例如

index/link/->/title

[编辑 | 编辑源代码]

列出索引中页面的标题。

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>

运行

index/link/->/author/string(.)

[编辑 | 编辑源代码]

列出索引中引用的页面的作者。

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>

运行

page/inner/->/external

[编辑 | 编辑源代码]

列出索引页面引用的所有页面的所有不同外部链接的 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>

运行

page/inner/->/inner/->/title

[编辑 | 编辑源代码]

列出链接到初始页面的页面的标题。

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]

华夏公益教科书