p3k/xray

X-Ray 从任何 URL 返回结构化数据


README

XRay 解析来自 URL 的结构化内容。

发现内容

XRay 将解析以下格式的内容。首先将 URL 与已知服务进行核对

  • GitHub
  • XKCD
  • Hackernews

如果 URL 的内容是 XML 或 JSON,则 XRay 将解析 Atom、RSS 或 JSONFeed 格式。

最后,XRay 将在页面上查找 Microformats,并据此确定内容。

  • h-card
  • h-entry
  • h-event
  • h-review
  • h-recipe
  • h-product
  • h-item
  • h-feed

XRay 可以作为 PHP 项目中的库使用。最简单的安装方法是使用 composer。

composer require p3k/xray

您还可以 下载一个发行版 ,这是一个包含所有依赖项的 zip 文件。

解析

$xray = new p3k\XRay();
$parsed = $xray->parse('https://aaronparecki.com/2017/04/28/9/');

如果您已经有了一个要解析的 HTML 或 JSON 文档,您可以将它作为字符串传递到第二个参数中。

$xray = new p3k\XRay();
$html = '<html>....</html>';
$parsed = $xray->parse('https://aaronparecki.com/2017/04/28/9/', $html);
$xray = new p3k\XRay();
$jsonfeed = '{"version":"https://jsonfeed.org/version/1","title":"Manton Reece", ... ';
// Note that the JSON document must be passed in as a string in this case
$parsed = $xray->parse('https://manton.micro.blog/feed.json', $jsonfeed);

在这两种情况下,您都可以添加一个额外的参数来配置 XRay 的各种行为选项。以下是一个选项列表。

  • timeout - 等待任何 HTTP 请求的超时时间(秒)
  • max_redirects - 要遵循的最大重定向数
  • include_original - 也会返回获取的完整文档
  • target - 指定一个目标 URL,XRay 首先会检查该 URL 是否在页面上,如果是,则继续解析页面。这对于您使用 XRay 验证传入的 webmention 非常有用。
  • expect=feed - 如果您知道您正在解析的内容是一个源,包含此参数可以避免运行自动检测规则,并将为某些源提供更好的结果。
  • accept - (选项:htmljsonactivitypubxml) - 如果没有此参数,XRay 会发送一个默认的 Accept 标头,以便从页面上获取最有可能的最佳结果。如果您正在解析页面以进行特定目的,并期望找到特定类型的内容(例如,webmentions 很可能只来自 HTML 页面),则可以包含此参数以调整 XRay 发送的 Accept 标头。

在使用 GitHub API 进行请求时,支持额外的参数。有关详细信息,请参阅下面的身份验证部分。

XRay 构造函数可以可选地传递一个默认选项数组,这些选项将应用于(并且可以由传递给单个 parse() 调用的选项覆盖)。

$xray = new p3k\XRay([
  'timeout' => 30 // Time-out all requests which take longer than 30s
]);

$parsed = $xray->parse('https://aaronparecki.com/2017/04/28/9/', [
  'timeout' => 40 // Override the default 30s timeout for this specific request
]);

$parsed = $xray->parse('https://aaronparecki.com/2017/04/28/9/', $html, [
  'target' => 'http://example.com/'
]);

$parsed 返回值将如下所示。有关返回的词汇表的解释,请参阅下面的“主要数据”。

$parsed = Array
(
    [data] => Array
        (
            [type] => card
            [name] => Aaron Parecki
            [url] => https://aaronparecki.com/
            [photo] => https://aaronparecki.com/images/profile.jpg
        )

    [url] => https://aaronparecki.com/
    [code] => 200,
    [source-format] => mf2+html
)

处理 Microformats2 JSON

如果您已经有一个解析后的 Microformats2 文档作为数组,您可以使用一个特殊函数将其处理成 XRay 的原生格式。确保您传递整个解析后的文档,而不仅仅是单个项。

$html = '<div class="h-entry"><p class="p-content p-name">Hello World</p><img src="/photo.jpg"></p></div>';
$mf2 = Mf2\parse($html, 'http://example.com/entry');

$xray = new p3k\XRay();
$parsed = $xray->process('http://example.com/entry', $mf2); // note the use of `process` not `parse`

Array
(
    [data] => Array
        (
            [type] => entry
            [post-type] => photo
            [photo] => Array
                (
                    [0] => http://example.com/photo.jpg
                )

            [content] => Array
                (
                    [text] => Hello World
                )

        )

    [url] => http://example.com/entry

    [source-format] => mf2+json
)

Rels

您还可以使用 XRay 获取页面上的所有 rel 值,将 HTTP Link 标头的 rel 值与页面的 HTML rel 值合并。

$xray = new p3k\XRay();
$rels = $xray->rels('https://aaronparecki.com/');

这将返回与解析器类似的结果,但与包含解析页面的 data 键不同,将有一个 rels,这是一个关联数组。每个键将包含匹配该 rel 值的所有值的数组。

Array
(
    [url] => https://aaronparecki.com/
    [code] => 200
    [rels] => Array
        (
            [hub] => Array
                (
                    [0] => https://switchboard.p3k.io/
                )

            [authorization_endpoint] => Array
                (
                    [0] => https://aaronparecki.com/auth
                )
            ...

内容发现

您可以使用XRay来发现URL中可用的内容类型。

$xray = new p3k\XRay();
$feeds = $xray->feeds('http://percolator.today');

这将获取URL,检查是否存在Microformats内容,同时检查是否存在指向Atom、RSS或JSONFeed URL的rel=alternates链接。响应将如下所示。

Array
(
    [url] => https://percolator.today/
    [code] => 200
    [feeds] => Array
        (
            [0] => Array
                (
                    [url] => https://percolator.today/
                    [type] => microformats
                )

            [1] => Array
                (
                    [url] => https://percolator.today/podcast.xml
                    [type] => rss
                )

        )

)

自定义用户代理

要设置唯一的用户代理(某些网站可能需要设置用户代理),可以将对象的http属性设置为p3k\HTTP对象。

$xray = new p3k\XRay();
$xray->http = new p3k\HTTP('MyProject/1.0.0 (http://example.com/)');
$xray->parse('http://example.com/');

API

XRay也可以作为API,通过HTTP服务提供其解析能力。

要解析页面并返回页面内容的结构化数据,只需将URL传递给/parse路由。

GET /parse?url=https://aaronparecki.com/2016/01/16/11/

要首先检查页面是否包含指向目标URL的链接后再条件性地解析页面,请包含目标URL作为参数。当使用XRay验证传入的webmention时,这很有用。

GET /parse?url=https://aaronparecki.com/2016/01/16/11/&target=http://example.com

在这两种情况下,响应将是一个包含“type”键的JSON对象。如果发生错误,“type”将设置为字符串“error”,否则它将指向在URL中找到的内容类型,通常是“entry”。

您也可以使用相同的参数名称进行POST请求。

如果您已经有了要解析的HTML或JSON文档,您可以将它包含在POST参数body中。此POST请求如下所示

POST /parse
Content-type: application/x-www-form-urlencoded

url=https://aaronparecki.com/2016/01/16/11/
&body=<html>....</html>

或者对于GitHub,您可能已经有了JSON

POST /parse
Content-type: application/x-www-form-urlencoded

url=https://github.com/aaronpk/XRay
&body={"repo":......}

参数

当调用/parse时,XRay接受以下参数

  • url - 要解析的页面的URL
  • target - 指定一个目标 URL,XRay 首先会检查该 URL 是否在页面上,如果是,则继续解析页面。这对于您使用 XRay 验证传入的 webmention 非常有用。
  • timeout - 等待任何 HTTP 请求的超时时间(秒)
  • max_redirects - 要遵循的最大重定向数
  • include_original - 也会返回获取的完整文档
  • expect=feed - 如果您知道您正在解析的内容是一个源,包含此参数可以避免运行自动检测规则,并将为某些源提供更好的结果。

身份验证

如果获取的URL需要身份验证,请将访问令牌包含在“token”参数中,它将在获取URL时包含在“Authorization”头中。(在这种情况下建议使用POST请求,以避免访问令牌可能被作为查询字符串的一部分记录。)这对于私有Webmention验证很有用。

POST /parse

url=https://aaronparecki.com/2016/01/16/11/
&target=http://example.com
&token=12341234123412341234

API身份验证

XRay使用GitHub API获取帖子,这些API需要身份验证。为了保持XRay无状态,需要将凭据传递给解析调用。

您应该仅在要解析的URL是GitHub URL时发送凭据,因此您在包含凭据之前需要检查主机名是否为github.com等。

GitHub身份验证

XRay使用GitHub API获取GitHub URL,当使用身份验证时,它提供了更高的速率限制。您可以在请求中传递GitHub访问令牌,XRay将在对API进行请求时使用它。

  • github_access_token - GitHub访问令牌

错误响应

{
  "error": "not_found",
  "error_description": "The URL provided was not found"
}

以下列出了可能的错误

  • not_found:提供的URL未找到。(获取时返回404)
  • ssl_cert_error:验证SSL证书时出错。这可能会发生如果SSL证书已过期。
  • ssl_unsupported_cipher:Web服务器不支持服务已知的所有SSL密码。
  • timeout:服务尝试连接到URL时超时。
  • invalid_content:URL上的内容无效。例如,提供指向图像的URL将返回此错误。
  • no_link_found:在页面上未找到目标链接。当提供目标参数时,如果在页面上找不到目标,将返回此错误。
  • no_content:在给定的URL上找不到可用的内容。
  • unauthorized:URL返回HTTP 401未授权。
  • forbidden:URL返回HTTP 403禁止。

响应格式

{
  "data":{
    "type":"entry",
    "post-type":"photo",
    "published":"2017-03-01T19:00:33-08:00",
    "url":"https://aaronparecki.com/2017/03/01/14/hwc",
    "category":[
      "indieweb",
      "hwc"
    ],
    "photo":[
      "https://aaronparecki.com/2017/03/01/14/photo.jpg"
    ],
    "syndication":[
      "https://twitter.com/aaronpk/status/837135519427395584"
    ],
    "content":{
      "text":"Hello from Homebrew Website Club PDX! Thanks to @DreamHost for hosting us! 🍕🎉 #indieweb",
      "html":"Hello from Homebrew Website Club PDX! Thanks to <a href=\"https://twitter.com/DreamHost\">@DreamHost</a> for hosting us! <a href=\"https://aaronparecki.com/emoji/%F0%9F%8D%95\">🍕</a><a href=\"https://aaronparecki.com/emoji/%F0%9F%8E%89\">🎉</a> <a href=\"https://aaronparecki.com/tag/indieweb\">#indieweb</a>"
    },
    "author":{
      "type":"card",
      "name":"Aaron Parecki",
      "url":"https://aaronparecki.com/",
      "photo":"https://aaronparecki.com/images/profile.jpg"
    }
  },
  "url":"https://aaronparecki.com/2017/03/01/14/hwc",
  "code":200,
  "source-format":"mf2+html"
}

主要数据

页面上的主要对象保存在 data 属性中。这会指示对象的类型(例如 entry),并且将包含它从页面中解析出的词汇的属性。

  • type - 在页面上的主要对象中找到的 Microformats 2 词汇,没有 h- 前缀(例如 entryevent
  • post-type - 仅适用于 "posts"(例如,不适用于 card)- 文章的 类型(例如 notephotoreply

如果属性支持多个值,它将始终以数组形式返回。以下属性支持多个值

  • in-reply-to
  • like-of
  • repost-of
  • bookmark-of
  • follow-of
  • syndication
  • photo(条目的照片,而非卡片的照片)
  • video
  • audio
  • category

内容将是一个对象,它始终包含一个 "text" 属性,如果源文档发布了 HTML 内容,则可能包含一个 "html" 属性。在将其作为 HTML 显示之前,必须始终对 "text" 属性进行 HTML 转义,因为它可能包含未转义的字符,如 <>

如果可用,作者将始终在条目中设置。服务遵循 作者发现 算法,以尝试在源文档的条目之外的其他地方找到作者信息。

输出中提供的所有 URL 都是绝对 URL。如果源文档包含相对 URL,则首先将其解析。

文章类型发现

XRay 运行 文章类型发现 算法,还包括一个 post-type 属性。

以下文章类型被返回,这略微超出了当前由文章类型发现规范所记录的内容。

  • event
  • recipe
  • review
  • rsvp
  • repost
  • like
  • reply
  • bookmark
  • follow
  • checkin
  • video
  • audio
  • photo
  • article
  • note

其他属性

其他属性与 data 属性在相同的级别返回。

  • url - 从中检索文档的有效 URL。这将是在跟随任何重定向后的最终 URL。
  • code - URL 返回的 HTTP 响应代码。通常这将是一个 200,但如果 URL 返回了包含 h-entry 的其他 HTTP 代码(例如,包含 stub h-entry 的 410 已删除通知),则可以使用此代码了解原始 URL 实际上已被删除。
  • source-format - 指示用于生成解析结果的源 URL 的格式。可能的值包括
    • mf2+html
    • mf2+json
    • feed+json
    • xml
    • github/xkcd

XRay 可以返回几种不同类型的源信息。传递给 XRay 的 URL(或正文)将检查以下格式

如果正在解析的页面代表一个源,则响应将如下所示

{
  "data": {
    "type": "feed",
    "items": [
      {...},
      {...}
    ]
  }
}

items 数组中的每个对象都包含一个项目的解析版本,其格式与 XRay 通常返回的格式相同。当解析 Microformats 源时,将为每个项目运行 作者发现 以构建作者信息。

Atom、RSS 和 JSONFeed 都将规范化为 XRay 的词汇,并且仅返回已识别的属性。

关系API

还有一个API方法可以解析并返回页面上的所有rel值,包括HTTP Link头和HTML rel值。

GET /rels?url=https://aaronparecki.com/

关于响应格式,请参阅上面

内容发现API

GET /feeds?url=https://aaronparecki.com/

关于响应格式,请参阅上面

令牌API

在验证私有Webmention时,您需要在源URL指定的令牌端点处交换代码以获取访问令牌。

XRay提供了一个API,可以一步完成此操作。您可以提供从webmention获得的源URL和代码,XRay将发现令牌端点,然后返回访问令牌。

POST /token

source=http://example.com/private-post
&code=1234567812345678

响应将是令牌端点的响应,其中将包括一个access_token属性,以及可能的一个expires_in属性。

{
  "access_token": "eyJ0eXAXBlIjoI6Imh0dHB8idGFyZ2V0IjoraW0uZGV2bb-ZO6MV-DIqbUn_3LZs",
  "token_type": "bearer",
  "expires_in": 3600
}

如果在获取访问令牌时出现问题,您将除了从解析API返回的与HTTP相关的错误之外,还会收到以下错误之一

  • no_token_endpoint - 无法找到指定令牌端点的HTTP头。

安装

从源代码

# Clone this repository

git clone git@github.com:aaronpk/XRay.git
cd XRay

# Install dependencies
composer install

从ZIP存档

Web服务器配置

将您的Web服务器配置为指向public文件夹。

确保所有请求都路由到index.php。XRay附带了Apache的.htaccess文件。对于nginx,您需要在您的服务器配置块中添加如下规则。

  try_files $uri /index.php?$args;