我们可以使用Xidel将站点中的数据提取到搜索文件中吗?

时间:2018-04-02 17:58:07

标签: xidel

背景:我们正在汇总某些网站(有权限)的内容,以用于其他应用程序的补充搜索功能。一个例子是https://centenary.bahai.us的新闻部分。我们考虑过将xidel用于此目的,因为模板文件范例似乎是从html中提取数据的一种优雅方式,例如对于模板:

level5

...我们可以运行如下命令:

<h1 class="title">{$title}</h1>?
<div class="node build-mode-full">
  {$url:=$url}
  <div class="field-field-audio">?
    <audio src="{$audio:='https://' || $host || .}"></audio>?
  </div>?
  <div class="field-field-clip-img">
    <a href="{$image:='https://' || $host || .}" class="imagefield-field_clip_img"></a>*
  </div>?
  <div class="field-field-pubname">{$publication}</div>?
  <div class="field-field-historical-date">{$date}</div>?
  <div class="location"><div class="adr">{$location}</div>?</div>?
  <div class="node-body">{$text}</div>
</div>?

...这将为我们提供来自centenary.bahai.us所有新闻页面的json格式数据。示例文章如下所示:

xidel "https://centenary.bahai.us" -e "$(< template.html)" -f "//a[contains(@href, '/news/')]" --silent --color=never --output-format=json-wrapped > index.json

那只是美丽的,比httrack和pup的一些混乱或者(上帝禁止)sed和regex更容易,但是有一些问题:

  1. 我们希望每个文档都有单独的文件,而这会给我们一个大的json文件。
  2. 即使使用{ "title": "Bahá’ísm the Religion of Brotherhood", "url": "https://centenary.bahai.us/news/bahaism-religion-brotherhood", "audio": "https://centenary.bahai.us/sites/default/files/453_0.mp3", "image": "https://centenary.bahai.us/sites/default/files/imagecache/lightbox-large/images/press_clippings/03-31-1912_NYT_Bahaism_the_Religion_of_Brotherhood.png", "publication": "The New York Times", "date": "March 31, 1912", "location": "New York, NY", "text": "A posthumous volume of “Essays in Radical Empiricism,” by William James, will be published in April by Longmans, Green & Co. This house will also bring out “Leo XIII, and Anglican Orders,” by Viscount Halifax, and “Bahá’ísm, the Religion of Brotherhood, and Its Place in the Evolution of Creeds,” by Francis H. Skrine. In the latter an analysis is made of the Gospel of Bahá’u’lláh and his successor. ‘Abdu’l-Bahá — whose arrival in this country is expected early in April — and a forecast is attempted of its influence on civilization." }, 标记,我们仍会在输出中获取使json无效的状态消息,例如--silent**** Retrieving (GET): https://centenary.bahai.us ******** Processing: https://centenary.bahai.us/ ****
  3. 这个过程似乎太脆弱了;如果模板和实际的html之间存在任何差异,整个过程就会出错,我们什么也得不到。我们希望它只为一个页面输出错误,然后继续使用下一个URL。
  4. Xidel似乎是一个改变游戏规则的工具,它可以通过一行命令和一个简单的提取模板文件来完成这项工作。我在这里错过了什么?

2 个答案:

答案 0 :(得分:0)

从使用$(< template.html)来判断,我猜你是在Linux发行版上。在那种情况下,你的引用是错误的。见#9和#10 here

由于您使用的是提取模板文件,我会说--extract-file=template.html是要使用的参数,但您的-e "$(< template.html)"似乎也可以使用。这对我来说很新。谢谢。
感谢BeniBela的回答,我也知道-e @template.html也有效。

接下来是参数的错误顺序。我不得不承认,Xidel的自述文件在这方面并不是很清楚 在xidel到来--silent --color=never之后,您显然必须&#34;关注&#34;在您可以进行提取之前,首先是一个URL。所以这应该有效:

$ ./xidel -s --color=never "https://centenary.bahai.us" -f '//a[contains(@href,"/news/")]' --extract-file=template.html --output-format=json-wrapped > index.json

我自己几乎没有使用模板,所以我自己建立json会做一些不同的事情。
正如BeniBela建议的那样,你可以使用file:write-text()让Xidel将输出保存为紧凑的json:

$ ./xidel -s "https://centenary.bahai.us" \
> -f '//a[contains(@href,"/news/")]' \
> --xquery 'file:write-text(
>   replace(
>     $url,
>     ".*/(.*)",
>     "$1.json"
>   ),
>   serialize-json(
>     [
>       {
>         "title":string(//h1),
>         "url":$url,
>         "audio"://audio/resolve-uri(@src),
>         "image"://div[contains(@class,"img")]//img/resolve-uri(@src),
>         "publication":string(//div[contains(@class,"pubname")]),
>         "date":string(//div[contains(@class,"date")]),
>         "location":string(//span[@class="locality"]),
>         "text":string(//div[@class="node-body"])
>       }
>     ]
>   )
> )'

- 虽然'//a[contains(@href,"/news/")]'只返回了部分网址的两倍,但Xidel足够聪明,可以按照已解析的网址进行操作,并按照以下步骤进行操作。
- 它选择文件名的url路径。例如head-bahaiism-coming-denver.json - 一定要&#34; stringify&#34;在这种情况下的节点。否则输出将包含节点的外部html。

它也可能有漂亮的json文件(带缩进等),但这需要第二个Xidel实例:

$ ./xidel -s "https://centenary.bahai.us" \
> -e 'distinct-values(
>       //a[contains(@href,"/news/")]/resolve-html(.)
>     )' |
> while read -r u; do
>   ./xidel-0.9.8-openssl.exe -s $u \
>   -e '[
>         {
>           "title"://h1,
>           "url":$url,
>           "audio"://audio/resolve-uri(@src),
>           "image"://div[contains(@class,"img")]//img/resolve-uri(@src),
>           "publication"://div[contains(@class,"pubname")],
>           "date"://div[contains(@class,"date")],
>           "location"://span[@class="locality"],
>           "text"://div[@class="node-body"]
>         }
>       ]' \
>   > $(sed 's/.*\///' <<< $u).json;
> done

- 在这种情况下,您必须提取整个网址并删除所有重复项 - 这里sed选择文件名的URL路径。

答案 1 :(得分:0)

您可以在Xidel中构建自己的输出。

您可以使用XQuery中的file module将JSON保存到文件中,例如:

file:write-text("/tmp/test.json", serialize-json({"a": 1}))

您可以将任何JSON传递给serialize-json

 { "title": $title, "url": $url, "audio": $audio}

或者从变量列表构建它:

 {| ("title", "url", "audio") ! {.: get(.)}  |} 

有多种方法可以捕获错误:

在bash中多次调用Xidel

旧学校方式:

xidel --silent "https://centenary.bahai.us/news" -e "//a[contains(@href, '/news/')]/resolve-html(.)"  | sort | uniq | 
while read -r u; do 
  xidel $u -e @template.html >> output.json
done

使用多页模板

您可以将模板模式放在模板xml文件中,以获得比在命令行上更好的控制:

  <action>
    <page url="https://centenary.bahai.us/news"/>
    <s>$temp := distinct-values( //a[contains(@href, '/news/')]/resolve-html(.) )</s>

    <loop var="u" list="$temp">
      <page url="{$u}"/>
      <try>
        <pattern><body>
          <h1 class="title">{$title}</h1>?
          <div class="node build-mode-full">
            {$url:=$url}
            <div class="field-field-audio">?
              <audio src="{$audio:='https://' || $host || .}"></audio>?
            </div>?
            <div class="field-field-clip-img">
              <a href="{$image:='https://' || $host || .}" class="imagefield-field_clip_img"></a>*
            </div>?
            <div class="field-field-pubname">{$publication}</div>?
            <div class="field-field-historical-date">{$date}</div>?
            <div class="location"><div class="adr">{$location}</div>?</div>?
            <div class="node-body">{$text}</div>
          </div>?
        </body></pattern>
        <catch>
        </catch>
      </try>
    </loop>

  </action>

<loop>重复一些内容,<try><catch>抓住所有错误。

然后使用xidel --template-file action.xml

调用它

从XQuery

调用模式匹配

您也可以使用函数pxp:match从XQuery调用模式匹配,尽管您可以使用特殊的内置变量(例如$ url或$ host .btw)避免修改这些变量,不确定会发生什么如果你这样做。):

 xidel  'https://centenary.bahai.us/news' --xquery '
    let $pattern := doc("file://./template.html")
    for $u in distinct-values( //a[contains(@href, "/news/")]/resolve-html(.) )
    return try {
      pxp:match($pattern, doc($u))
    } catch * { 
       "error"
    }
 '