zehntech/readability

Readability.js 的 Laravel 版本

dev-main 2023-09-01 05:37 UTC

This package is not auto-updated.

Last update: 2024-09-28 07:59:41 UTC


README

要求

PHP 8+, ext-dom, ext-xml, 和 ext-mbstring。如果您系统尚未安装这些依赖项,可以在类似 *nix 的环境中尝试以下操作

如何使用

首先,您需要使用 composer 引入库

composer require zehntech/readability

然后,创建一个 Readability 类并传递一个配置类,将 HTML 喂入 parse() 函数,并输出变量

<?php

use Zehntech\Readability\Readability;
use Zehntech\Readability\Configuration;
use Zehntech\Readability\ParseException;

$readability = new Readability(new Configuration());

$html = file_get_contents('http://your.favorite.article.com/article.html');

try {
    $readability->parse($html);
    echo $readability;
} catch (ParseException $e) {
    echo sprintf('Error processing text: %s', $e->getMessage());
}

您的脚本将输出解析后的文本或报告任何错误。您应始终将 ->parse 调用包裹在 try/catch 块中,因为如果 HTML 无法正确解析,将抛出 ParseException

如果您想对输出有更精细的控制,只需逐个调用属性,并用您自己的 HTML 包裹它们。

<h1><?= $readability->getTitle(); ?></h1>
<h2>By <?= $readability->getAuthor(); ?></h2>
<div class="content"><?= $readability->getContent(); ?></div>

以下是可用的属性列表

  • 文章标题:->getTitle();
  • 文章内容:->getContent();
  • 摘要:->getExcerpt();
  • 主要图片:->getImage();
  • 所有图片:->getImages();
  • 作者:->getAuthor();
  • 文本方向(ltr 或 rtl):->getDirection();

如果您需要调整最终的 HTML,可以通过调用 ->getDOMDocument() 获取结果的 DOMDocument。

选项

您可以通过配置对象更改 Readability 的行为。例如,如果您想修复相对 URL 并声明原始 URL,可以设置配置如下

$configuration = new Configuration();
$configuration
    ->setFixRelativeURLs(true)
    ->setOriginalURL('http://my.newspaper.url/article/something-interesting-to-read.html');

您还可以将配置参数数组传递给构造函数

$configuration = new Configuration([
    'fixRelativeURLs' => true,
    'originalURL'     => 'http://my.newspaper.url/article/something-interesting-to-read.html',
    // other parameters ... listing below
]);

然后将此配置对象传递给 Readability。以下是一些可用的选项。记住,在使用原生设置器调用它们时,请始终在前面加上 set

  • MaxTopCandidates:默认值 5,顶级候选人的最大数量。
  • CharThreshold:默认值 500,文章被成功解析的最小字符数。
  • ArticleByLine:默认值 false,通过文章的作者行进行搜索,并将其从文本中删除。它将被移动到文章元数据中。
  • StripUnlikelyCandidates:默认值 true,删除不太可能包含相关信息节点。对于调试或解析复杂或非标准文章很有用。
  • CleanConditionally:默认值 true,在解析后删除某些节点以返回更干净的结果。
  • WeightClasses:默认值 true,在评分阶段权衡类。
  • FixRelativeURLs:默认值 false,将相对 URL 转换为绝对 URL。例如,将 /test 转换为 http://host/test
  • SubstituteEntities:默认值 false,禁用 libxml 的 substituteEntities 标志。将避免替换 HTML 实体。例如,将 &aacute; 转换为 á。
  • NormalizeEntities:默认值 false,将 UTF-8 字符转换为其 HTML 实体等效物。对于解析具有混合编码的 HTML 很有用。
  • OriginalURL:默认值 http://fakehost,用于修复相对 URL 的文章的原始 URL。
  • KeepClasses:默认值 false,从 HTML 元素中删除所有 class="..." 属性值。
  • Parser:默认值 html5,使用 HTML5-PHP 进行解析。将值设置为 libxml 以使用它(不推荐用于现代 HTML 文档)。
  • SummonCthulhu:默认值 false,通过正则表达式删除所有 <script> 节点。这并不是最佳方案,因为它可能会破坏某些功能,但如果您已将解析器设置为 libxml(见上文),这可能是解决 libxml 解析未转义 JavaScript 的问题 的唯一方法。

调试日志

记录是可选的,您需要自己注入记录器以保存所有调试消息。为此,使用实现了 PSR-3 记录接口 的记录器,并将其传递给配置对象。例如

// Using monolog

$log = new Logger('Readability');
$log->pushHandler(new StreamHandler('path/to/my/log.txt'));

$configuration->setLogger($log);

在日志中,您将找到有关解析节点的信息,为什么删除它们,以及为什么认为它们与最终文章相关。

限制

当然,主要限制是 PHP。通过懒加载、AJAX 或任何类型的 JavaScript 调用加载内容的网站将被忽略(实际上,不运行)并且生成的文本将不正确,与 readability.js 的结果相比。所有您想使用 readability.php 解析的文章都需要完整,并且所有内容都应该在 HTML 中。

已知的 libxml 解析问题

readability.php 自 3.0.0 版本起使用 HTML5 解析器。早期版本使用 libxml。以下问题适用于 libxml 解析,因此如果您正在使用 readability.php 的早期版本(3.0.0 之前),或者如果您在配置中将解析器设置为 libxml,请继续阅读...

JavaScript 泄露到文本主体

DOMDocument 在解析带有未转义 HTML 的字符串时存在一些问题。考虑以下代码

<div> <!-- Offending div without closing tag -->
<script type="text/javascript">
       var test = '</div>';
       // I should not appear on the result
</script>

如果您想从 HTML 中删除脚本(如 readability 所做的那样),您可能会期望最终只留下一个 div 和一个注释。问题是 libxml 将该关闭 div 标签视为一个 HTML 标签,有效地关闭了未关闭的标签,并将剩余的 JavaScript 作为字符串保留在 P 标签中。如果保存该节点,最终的 HTML 将变成这样

<div> <!-- Offending div without closing tag -->
<p>';
       // I should not appear on the result
</p></div>

这是一个 libxml 问题,而不是 readability.php 的错误。

有一个解决方案:使用 summonCthulhu 选项。这将通过正则表达式删除所有脚本标签,这并不是最佳方案,因为您可能会召唤 黑暗之主

&nbsp 实体消失

&nbsp 实体被 libxml 自动转换为空格,并且无法禁用此功能。

自闭合标签渲染为完全展开的标签

自闭合标签,如 <br />,自动展开为 <br></br。在 libxml 中无法禁用此功能。

依赖

readability.php 使用

  • HTML5-PHP 来解析和序列化 HTML。
  • PSR Log 接口来定义允许的记录器类型。
  • Monolog 只在开发安装上需要。(在 composer install 期间使用 --dev 选项)。

待办事项

  • 跟进 Readability.js 的更改
  • 为 __toString() 方法添加一个小型模板引擎,而不是使用硬编码的一个。
  • 将所有 iterator_to_array 调用替换为跟踪删除或修改的节点的自定义 PHP 生成器。

它是如何工作的

readability 使用 DOMDocument 解析所有文本,扫描文本节点并根据单词数量、链接数量和元素类型给出分数。然后它选择得分最高的元素,并创建一个新的 DOMDocument,其中包含所有其兄弟节点。每个兄弟节点都会得到评分,以丢弃无用的元素,如导航栏、空节点等。

安全

如果您打算使用Readability处理不可信的输入(无论是以HTML还是DOM的形式),我们强烈建议您使用像HTML Purifier这样的清理库,以避免在处理Readability的输出时发生脚本注入。我们还建议您使用CSP来添加更深层次的防御措施,限制结果内容可以执行的操作。Firefox的阅读模式集成本身也使用了这两种技术。从输入中清理不安全的内容并不是Readability本身的目标——其他地方还有优秀的清理库,请使用它们!

测试

本地安装的PHP 7.4及以上版本的任何版本都应该足够开发新功能和添加新测试用例。如果您想确保您的更改不会与其他版本的PHP产生任何问题,您可以使用提供的Docker容器来测试当前的7.4、8.0、8.1版本。

如果您使用composer下载此包,请确保传递--prefer-source标志,否则test/文件夹将不会被下载。

您需要Docker和Docker Compose来完成此操作。要运行三个PHP版本以上的所有测试,只需输入以下命令

make test-all

这将启动所有容器,并在每个支持的PHP版本上运行所有测试。如果您想针对特定版本进行测试,可以使用make test-7.4make test-8.0make test-8.1

libxml的不同版本

如果您想测试与支持的PHP版本以及多个libxml版本,请运行test-all-versions。这将测试PHP 7.4到8.1的版本以及libxml的2.9.10、2.9.13和2.9.14版本。通常,除非您认为您在特定版本的libxml上发现了一个bug,否则您不需要这样做。

更新预期测试

如果您对代码进行了改进,您可能想检查这里的测试用例的Readability.php输出。为此,首先从项目文件夹的根目录运行以下命令

docker-compose up -d php-7.4-libxml-2.9.10

现在,您应该有一个运行中的Docker镜像,项目根文件夹被映射到Docker实例上的/app/(请参阅docker-compose.yml)。从现在开始,这些文件的任何更改都可以从Docker实例访问。

接下来,在tests/中创建一个名为/changed的文件夹,然后运行以下命令以运行测试套件

docker-compose exec -e output-changes=1 -e output-diff=1 php-7.4-libxml-2.9.10 php /app/vendor/phpunit/phpunit/phpunit --configuration /app/phpunit.xml

这两个环境变量(output-changes=1output-diff=1)将导致任何失败的测试的新输出(以及更改的差异)被写入changed/文件夹。

如果您对更改满意,将output-diff=0设置为0,差异文件将不再写入,这使得将新的预期输出文件复制到test-pages中相应的位置变得更容易。

许可证

基于Arc90的readability.js(1.7.1)脚本,可在以下位置获得:http://code.google.com/p/arc90labs-readability

Copyright (c) 2010 Arc90 Inc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   https://apache.ac.cn/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.