如何使用屏幕抓取工具从网络中提取数据

了解如何在 Perl 中使用 HTML::TableExtract 工具。
244 位读者喜欢这篇文章。
Image of spider web

You as a Machine。由 Rikki Endsley 修改。CC BY-SA 2.0。

一个完美的互联网会以客户选择的格式交付数据,无论是 CSV、XML、JSON 等。但现实情况是,互联网有时会挑逗你,提供可用的数据,但通常是以 HTML 或 PDF 文档的形式——这些格式是为数据显示而设计的,而不是为数据交换而设计的。因此,过去的屏幕抓取——提取显示的数据并将其转换为请求的格式——在今天仍然具有现实意义。

Perl 拥有出色的屏幕抓取工具,其中就包括下面抓取程序中描述的 HTML::TableExtract 包。

抓取程序概述

屏幕抓取程序主要包含两个部分,它们按如下方式组合在一起:

  • 文件 data.html 包含要抓取的数据。本例中的数据源自一个正在翻新的大学网站,它探讨了大学学位的收入是否能证明其成本是合理的。这些数据包括中位数收入、百分位数以及有关计算、工程和文科等研究领域的其他信息。要运行抓取程序,data.html 文件应托管在 Web 服务器上,在我这里是一个本地 Nginx 服务器。独立的 Perl Web 服务器(如 HTTP::Server::PSGIHTTP::Server::Simple)也可以胜任。
  • 文件 scrape.pl 包含抓取程序,该程序使用了 Plack/PSGI 包中的功能,特别是 Plack Web 服务器。抓取程序从命令行启动(如下所述)。用户在浏览器中输入 Plack 服务器的 URL (localhost:5000/),然后会发生以下情况:
    • 浏览器连接到 Plack 服务器(HTTP::Server::PSGI 的一个实例),并向抓取程序发出 GET 请求。URL 末尾的单个斜杠 (/) 标识了此程序。(即使使用者没有这样做,现代浏览器也会添加闭合斜杠。)
    • 然后,抓取程序向 data.html 文档发出 GET 请求。如果请求成功,应用程序将使用 HTML::TableExtract 包从文档中提取相关数据,将提取的数据保存到一个文件中,并进行一些基本的统计度量,这些度量代表了对提取数据的处理。一个如下所示的 HTML 报告将返回到用户的浏览器。

HTML report generated by the Scraping program

图 1:抓取程序的最终报告

从用户的浏览器到 Plack 服务器,再到托管 data.html 文档的服务器(例如,Nginx)的请求流量可以描述如下:

              GET localhost:5000/             GET localhost:80/data.html
user's browser------------------->Plack server-------------------------->Nginx

最后一步只涉及 Plack 服务器和用户的浏览器:

             reportFinal.html
Plack server------------------>user's browser

上面的图 1 显示了最终报告文档。

抓取程序的详细信息

源代码和数据文件 (data.html) 可从我的网站下载,压缩文件中包含一个 README 文件。以下是各个部分的快速摘要,后续将进行详细说明:

data.html             ## data source to be hosted by a web server
scrape.pl             ## main source code, run with the plackup utility (see below)
Stats::Controller.pm  ## handles request routing, data extraction, and processing
Stats::Util.pm        ## utility functions used in Controller.pm
report.html           ## HTML template used to generate the report
rawData.dat           ## the extracted data

Plack/PSGI 包附带一个名为 plackup 的命令行实用程序,可用于启动抓取程序。以 % 作为命令行提示符,启动抓取程序的命令是:

% plackup scrape.pl

plackup 命令启动一个独立的 Plack Web 服务器,用于托管抓取程序。抓取代码处理请求路由,从 data.html 文档中提取数据,生成一些基本的统计度量,然后使用 Template::Recall 包为用户生成 HTML 报告。由于 Plack 服务器无限期运行,因此抓取程序会打印进程 ID,该 ID 可用于终止服务器和抓取应用程序。

Plack/PSGI 支持 Rails 风格的路由,其中 HTTP 请求根据两个因素被分派到特定的请求处理程序:

  • HTTP 请求方法(谓词),例如 GET 或 POST。
  • 所请求资源的统一资源标识符(URI 或名词);在本例中,是 URL http://localhost:5000/ 中的独立结尾斜杠 (/),用户在浏览器中输入该 URL 以启动抓取程序。

抓取程序仅处理一种类型的请求:针对名为 / 的资源的 GET 请求,并且此资源是我的 Stats::Controller 包中的屏幕抓取和数据处理代码。下面回顾一下 Plack/PSGI 路由设置,它位于源文件 scrape.pl 的顶部:

my $router = router {
    match '/', {method => 'GET'},   ## noun/verb combo: / is noun, GET is verb
    to {controller => 'Controller', action => 'index'}; ## handler is function get_index
    # Other actions as needed
};

请求处理程序 Controller::get_index 只有高级逻辑,将屏幕抓取和报告生成细节留给 Util.pm 文件中的实用程序函数,如下节所述。

屏幕抓取代码

回想一下,Plack 服务器将针对 localhost:5000/ 的 GET 请求分派到抓取程序的 get_index 函数。此函数作为请求处理程序,然后开始检索要抓取的数据、抓取数据以及生成最终报告的工作。数据检索部分由一个实用程序函数负责,该函数使用 Perl 的 LWP::Agent 包从托管 data.html 文档的任何服务器获取数据。获得数据文档后,抓取程序调用实用程序函数 extract_from_html 来执行数据提取。

data.html 文档恰好是格式良好的 XML,这意味着可以使用 Perl 包(如 XML::LibXML)通过显式 XML 解析来提取数据。但是,HTML::TableExtract 包非常吸引人,因为它绕过了繁琐的 XML 解析,并且(用极少的代码)交付了一个包含提取数据的 Perl 哈希。HTML 文档中的数据聚合通常出现在列表或表格中,而 HTML::TableExtract 包的目标是表格。以下是用于数据提取的三个关键代码行:

my $col_headers = col_headers(); ## col_headers() returns an array of the table's column names
my $te = HTML::TableExtract->new(headers => $col_headers);
$te->parse($page);  ## $page is data.html

$col_headers 指的是 Perl 字符串数组,每个字符串都是 HTML 文档中的列标题。

sub col_headers {    ## column headers in the HTML table
    return ["Area",
            "MedianWage",
            ...
            "BoostFromGradDegree"];
}

在调用 TableExtract::parse 函数之后,抓取程序使用 TableExtract::rows 函数迭代提取数据的行——不包含 HTML 标记的数据行。这些行作为 Perl 列表,被添加到名为 %majors_hash 的 Perl 哈希中,可以将其描述如下:

  • 每个键标识一个研究领域,例如计算工程
  • 每个键的值是七个提取数据项的列表,其中七是 HTML 表格中的列数。对于计算,带有注释的列表是:
       name            median  % with this degree  income boost from GD
        /                 /            /            /
    (Computing  55000  75000  112000  5.1%  32.0%  31.0%)   ## data items
                 /              \           \
           25th-ptile      75th-ptile  % going on for GD = grad degree

包含提取数据的哈希被写入本地文件 rawData.dat。

ForeignLanguage 50000 35000 75000 3.5% 54% 101%
LiberalArts 47000 32000 70000 9.7% 41% 48%
...
Engineering 78000 54000 104000 8.2% 37% 32%
Computing 75000 51000 112000 5.1% 32% 31%
...
PublicPolicy 50000 36000 74000 2.3% 24% 45%

下一步是处理提取的数据,在本例中是使用 Statistics::Descriptive 包进行基本的统计分析。在上面的图 1 中,统计摘要显示在报告底部的单独表格中。

报告生成代码

抓取程序的最后一步是生成报告。Perl 提供了生成 HTML 的选项,而 Template::Recall 就是其中之一。顾名思义,该软件包从 HTML 模板生成 HTML,该模板是标准 HTML 标记和自定义标签的混合体,这些标签充当从后端代码生成的数据的占位符。模板文件是 report.html,而感兴趣的后端函数是 Controller::generate_report。以下是代码和模板如何交互的:

报告文档(图 1)包含两个表格。顶部表格通过迭代生成,因为每一行都具有相同的列(研究领域、第 25 个百分位的收入等等)。在每次迭代中,代码都会创建一个哈希,其中包含特定研究领域的值:

my %row = (
     major => $key,
     wage  => '$' . commify($values[0]), ## commify turns 1234 into 1,234
     p25   => '$' . commify($values[1]),
     p75   => '$' . commify($values[2]),
     population   => $values[3],
     grad  => $values[4],
     boost => $values[5]
);

哈希键是 Perl 裸字,例如 majorwage,它们表示先前从 HTML 数据文档中提取的数据值列表中的项目。相应的 HTML 模板如下所示:

[ === even  === ]
<tr class = 'even'>
   <td>['major']</td>
   <td align = 'right'>['p25']</td>
   <td align = 'right'>['wage']</td>
   <td align = 'right'>['p75']</td>
   <td align = 'right'>['pop']</td>
   <td align = 'right'>['grad']</td>
   <td align = 'right'>['boost']</td>
</tr>
[=== end1 ===]

自定义标签用方括号括起来。顶部和底部的标签分别标记要呈现的模板区域的开始和结束。其他自定义标签标识后端代码的各个目标。例如,标识为 major 的模板列与以 major 作为键的哈希条目匹配。以下是后端代码中将数据绑定到自定义标签的调用:

print OUTFILE $tr->render('end1');

引用 $tr 指的是 Template::Recall 实例,而 OUTFILE 是报告文件 reportFinal.html,它是从模板文件 report.html 以及后端代码生成的。如果一切顺利,用户将在浏览器中看到 reportFinal.html 文档(参见图 1)。

抓取程序借鉴了优秀的 Perl 包,例如 Plack/PSGILWP::AgentHTML::TableExtractTemplate::RecallStatistics::Descriptive,以处理通常很混乱的屏幕抓取数据任务。这些包可以很好地协同工作,因为每个包都针对特定的子任务。最后,可以扩展抓取程序以聚类提取的数据:Algorithm::KMeans 包非常适合此扩展,并且可以使用保存在 rawData.dat 文件中的数据。

标签
User profile image.
我是计算机科学领域的学者(德保罗大学计算与数字媒体学院),在软件开发方面拥有广泛的经验,主要是在生产计划和调度(钢铁行业)以及产品配置(卡车和客车制造)方面。有关书籍和其他出版物的详细信息,请访问:

2 条评论

这篇文章非常棒,正是我需要帮助团队实施 Web 迁移项目的内容 - 谢谢!

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可发布。
© . All rights reserved.