bakame/html-table

将HTML表格转换为PHP数据结构

0.3.0 2023-09-29 03:59 UTC

This package is auto-updated.

Last update: 2024-09-16 06:39:54 UTC


README

Author Software License Build Latest Version Total Downloads Sponsor development of this project

bakame/html-table是一个小巧的PHP包,允许您将表示为HTML表格的表格数据解析、导入和操作。安装后,您将能够执行以下操作

use Bakame\HtmlTable\Parser;

$table = Parser::new()
    ->tableHeader(['rank', 'move', 'team', 'player', 'won', 'drawn', 'lost', 'for', 'against', 'gd', 'points'])
    ->parseFile('https://www.bbc.com/sport/football/tables');

$table
    ->filter(fn (array $row) => (int) $row['points'] >= 10)
    ->sorted(fn (array $rowA, array $rowB) => (int) $rowB['for'] <=> (int) $rowA['for'])
    ->fetchPairs('team', 'for');

// returns 
// [
//  "Brighton" => "15"
//  "Man City" => "14"
//  "Tottenham" => "13"
//  "Liverpool" => "12"
//  "West Ham" => "10"
//  "Arsenal" => "9"
// ]

系统需求

league\csv >= 9.11.0库是必需的。

安装

使用composer

composer require bakame/html-table

文档

Parser可以将文件(PHP流或带有可选上下文的路径,如fopen)或HTML文档转换为实现League\Csv\TabularData接口的对象。转换后,您可以使用接口提供的方法和功能(参见ResultSet)获取更多信息。

Parser本身是不可变的,每次更改配置选项时都会返回一个新的实例。

Parser构造函数是私有的,要实例化对象,您必须使用new方法。

use Bakame\HtmlTable\Parser;

$parser = Parser::new()
    ->ignoreTableHeader()
    ->ignoreXmlErrors()
    ->withoutFormatter()
    ->tableCaption('This is a beautiful table');

parseHtml 和 parseFile

要提取和解析您的表格,请使用parseHtmlparseFile方法。如果解析不可行,将抛出ParseError异常。

use Bakame\HtmlTable\Parser;

$parser = Parser::new();

$table = $parser->parseHtml('<table>...</table>');
$table = $parser->parseFile('path/to/html/file.html');

parseHtml解析由以下表示的HTML页面

  • 一个string
  • 一个Stringable对象,
  • 一个DOMDocument
  • 一个DOMElement
  • 或一个SimpleXMLElement

parseFile则与

  • 文件路径,
  • 或PHP可读流。

两种方法都返回一个实现League\Csv\TabularDataReader接口的Table实例,并可以通过getCaption方法访问如果有表格标题则通过该方法。

use Bakame\HtmlTable\Parser;

$html = <<<HTML
<div>
<table>
    <caption>Songs</caption>
    <thead>
        <tr>
            <th>Title</th>
            <th>Singer</th>
            <th>Country</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Nakei Nairobi</td>
            <td>Mbilia Bel</td>
            <td rowspan="3">DR Congo</td>
        </tr>
        <tr>
            <td>Muvaro</td>
            <td>Zaiko Langa Langa</td>
        </tr>
        <tr>
            <td>Nzinzi</td>
            <td>Emeneya</td>
        </tr>
    </tbody>
</table>
</div>
HTML;

$table = Parser::new()->parseHtml($html);
$table->getCaption(); //returns 'Songs'
$table->getHeader();  //returns ['Title','Singer', 'Country']
$table->nth(2); //returns ["Title" => "Nzinzi", "Singer" => "Emeneya", "Country" => "DR Congo"]
json_encode($table->slice(0, 1));
//{"caption":"Songs","header":["Title","Singer","Country"],"rows":[{"Title":"Nakei Nairobi","Singer":"Mbilia Bel","Country":"DR Congo"}]}

默认配置

默认情况下,调用Parser::new()命名构造函数时,解析器将

  • 尝试解析页面中找到的第一个表格
  • 期望表头行是表中thead部分找到的第一个tr
  • 在提取表格内容时排除表格thead部分。
  • 忽略XML错误。
  • 没有附加格式化程序。
  • 如果没有在表格中找到标题,则没有默认标题可供使用。

以下每个设置都可以更改,以改进对业务规则的转换

tablePosition 和 tableXpathPosition

可以使用两种方法(2)在HTML页面中选择要解析的表格Parser::tablePositionParser::tableXpathPosition

如果您知道页面中表格的位置及其整数偏移量或知道其id属性值,则应使用Parser::tablePosition;否则,优先使用Parser::tableXpathPosition,它期望一个xpath表达式。如果表达式有效,并且找到一系列表格,则返回第一个结果。

use Bakame\HtmlTable\Parser;

$parser = Parser::new()->tablePosition('table-id'); // parses the <table id='table-id'>
$parser = Parser::new()->tablePosition(3); // parses the 4th table of the page
$parser = Parser::new()->tableXPathPosition("//main/div/table");
//parse the first table that matches the xpath expression

Parser::tableXpathPositionParser::tablePosition会覆盖彼此。建议同时使用其中一个,而不是两个。

tableCaption

如果您没有标题或解析过程中未找到标题,可以可选地定义一个表格标题。

use Bakame\HtmlTable\Parser;

$parser = Parser::new()->tableCaption('this is a generated caption');
$parser = Parser::new()->tableCaption(null);  // remove any default caption set

tableHeader、tableHeaderPosition、ignoreTableHeader 和 resolveTableHeader

以下设置配置了与表格标题相关的Parser。默认情况下,解析器将尝试解析表中thead部分找到的第一个tr标签。但是,您可以使用以下设置之一来覆盖此行为

表头位置

指示定位和解析表头的位置

use Bakame\HtmlTable\Parser;
use Bakame\HtmlTable\Section;

$parser = Parser::new()->tableHeaderPosition(Section::Thead, 3);
// header is the 4th row in the <thead> table section

该方法使用 Bakame\HtmlTable\Section 枚举来指定使用哪个表节来解析表头

use Bakame\HtmlTable\Section;

enum Section
{
    case thead;
    case tbody;
    case tfoot;
    case tr;
}

如果使用 Section::tr,则 tr 标签将独立于其节使用。第二个参数是表头 tr 偏移量;默认为 0(即:第一行)。

ignoreTableHeader 和 resolveTableHeader

指示解析器是否使用 tableHeaderPosition 配置来解析表头。如果不进行解析,则返回的 Table 实例中不包含任何表头。

use Bakame\HtmlTable\Parser;

$parser = Parser::new()->ignoreTableHeader();  // no table header will be resolved
$parser = Parser::new()->resolveTableHeader(); // will attempt to resolve the table header

tableHeader

您可以直接指定表的表头,并使用此配置覆盖任何其他与表头相关的配置

use Bakame\HtmlTable\Parser;
use Bakame\HtmlTable\Section;

$parser = Parser::new()->tableHeader(['rank', 'team', 'winner']);

如果您指定一个非空数组作为表头,它将优先于任何其他与表头相关的选项。

因为它是表格数据,所以每个单元格必须是唯一的,否则将抛出异常

您可以通过跳过它们的偏移量或重新排列偏移量来跳过或重新排列源列。

use Bakame\HtmlTable\Parser;
use Bakame\HtmlTable\Section;

$parser = Parser::new()->tableHeader([3 => 'rank',  7 => 'winner', 5 => 'team']);
// only 3 column will be extracted the 4th, 6th and 8th columns
// and re-arrange as 'rank' first and 'team' last
// if a column is missing its value will be PHP `null` type

includeSection 和 excludeSection

根据 Section 枚举指示应解析哪个节

use Bakame\HtmlTable\Parser;
use Bakame\HtmlTable\Section;

$parser = Parser::new()->includeSection(Section::Tbody);  // thead and tfoot are included during parsing
$parser = Parser::new()->excludeSection(Section::Tr, Section::Tfoot); // table direct tr children and tfoot are not included during parsing

默认情况下,不解析 thead 节。如果选择 thead 行作为表头,则它将独立于此设置进行解析。

⚠️提示:要确保哪些节将被修改,首先删除所有以前的设置,然后再应用以下配置

- Parser::new()->includeSection(Section::tbody);
+ Parser::new()->excludeSection(...Section::cases())->includeSection(Section::tbody);

第一次调用仍将包括 tfoottr 节,而第二次调用将删除任何以前的设置,从而确保只有如果存在,则仅解析 tbody

withFormatter 和 withoutFormatter

在可以访问之前,添加或删除应用于从表中提取的数据的记录格式化器。如果定义了格式化器,则表头不受格式化器的影响。

use Bakame\HtmlTable\Parser;

$parser = Parser::new()->withFormatter($formatter); // attach a formatter to the parser
$parser = Parser::new()->withoutFormatter();        // removed the attached formatter if it exists

格式化器闭包的签名应该是

function (array $record): array;

如果定义了或指定了表头,则提交的记录将设置表头定义,否则提供一个数组列表。

以下格式化器将适用于任何定义为一个字符串的表内容。

$formatter = fn (array $record): array => array_map(strtolower(...), $record);
// the following formatter will convert all the fields from your table to lowercase.

以下格式化器仅在表附有包含名为 count 的列的表头时适用。

$formatter = function (array $record): array {
   $record['count'] = (int) $record['count'];
   
   return $record;
}
// the following formatter will convert the data of all count column into integer..

ignoreXmlErrors 和 failOnXmlErrors

指示解析器是否在遇到格式不正确的 HTML 内容时忽略或抛出异常。

use Bakame\HtmlTable\Parser;

$parser = Parser::new()->ignoreXmlErrors();   // ignore the XML errors
$parser = Parser::new()->failOnXmlErrors(3); // throw on XML errors

测试

该库

  • 有一个 PHPUnit 测试套件
  • 有一个使用 PHP CS Fixer 的编码风格合规性测试套件。
  • 有一个使用 PHPStan 的代码分析合规性测试套件。

要从项目文件夹中运行测试,请运行以下命令。

composer test

安全

如果您发现任何与安全相关的问题,请通过电子邮件 nyamsprod@gmail.com 而不是使用问题跟踪器。

致谢

许可

MIT 许可证(MIT)。有关更多信息,请参阅 许可文件