抓取跨越多个页面的大型 pdf 表

Posted

技术标签:

【中文标题】抓取跨越多个页面的大型 pdf 表【英文标题】:Scraping large pdf tables which span across multiple pages 【发布时间】:2013-08-07 08:22:33 【问题描述】:

我正在尝试抓取PDF tables which span across multiple pages。我尝试了很多东西,但最好的似乎是pdftotext -layout 和advised here。问题是生成的文本文件不容易使用,因为表格布局在页面之间有所不同,因此列没有对齐。还要注意以“Solsonès”开头的行中的缺失值:

                                                                        TEMPERATURA MITJANA MENSUAL ( ºC ) - 2012

COMARCA          CODI i NOM EMA                    GEN    FEB    MAR         ABR       MAI      JUN      JUL          AGO        SET        OCT        N

Alt Camp         VY   Nulles                        7,5    5,5   10,9         12,3     16,7     21,6     22,3         24,4       20,1        15,9
Alt Camp         DQ   Vila-rodona                   7,9    5,6   11,0         12,0     16,6     21,6     22,0         24,3       19,9        15,8
Alt Empordà      U1   Cabanes                       8,2    6,5   11,7         12,6     17,5     22,0     23,1         24,4       20,4        16,6
Alt Empordà      W1   Castelló d'Empúries           8,1    6,4   11,6         12,9     17,0     21,1     22,0         23,4       20,1        16,4

[...]
                                                                                 TEMPERATURA MITJANA MENSUAL ( ºC ) - 2012

COMARCA          CODI i NOM EMA                             GEN    FEB    MAR         ABR       MAI      JUN      JUL          AGO        SET        OCT

Baix Empordà     DF   la Bisbal d'Empordà                    6,6    5,3   10,9         12,6     17,2     21,9     22,9         24,6       20,3        16
Baix Empordà     UB   la Tallada d'Empordà                   6,1    5,2   10,7         12,3     16,6     21,3     22,2         23,8       19,7        15
Baix Empordà     UC   Monells                                6,1    4,6    9,9         11,4     16,5     21,7     23,0         24,5       19,6        15

[...]

                                                                        TEMPERATURA MITJANA MENSUAL ( ºC ) - 2012

COMARCA         CODI i NOM EMA                      GEN    FEB    MAR         ABR       MAI      JUN      JUL           AGO        SET        OCT
[...]

Solsonès        CA   Clariana de Cardener            4,6    3,3   10,3         10,2     16,7     22,3      d.i.
Solsonès        Z8   el Port del Comte (2.316 m)    -0,9   -6,3   -0,2         -2,0      5,3     10,5     10,9          13,8        7,8         4,2
Solsonès        VO   Lladurs                         3,0    2,6    9,5          9,0     15,3     21,4     21,6          24,3       17,5        13,0
Solsonès        VP   Pinós                           3,0    1,6    8,9          9,2     15,4     21,1     21,3          23,8       17,6        13,3
Solsonès        XT   Solsona                                                                               d.i.         24,3       18,0        13,5
Tarragonès      VQ   Constantí                       7,9   6,0    11,2         13,1     17,1     21,9     22,6          24,6       20,6        16,6
Tarragonès      XE   Tarragona - Complex Educatiu   10,2   7,8    12,3         14,6     18,3     23,0     24,2          26,2       23,0 *      18,4
Tarragonès      DK   Torredembarra                   9,7   7,7    12,3         14,3     17,9     22,8     24,3          26,2       22,7        18,5
Terra Alta      WD   Batea                           6,3   5,0    11,2         12,1     18,3     23,0     23,3          25,5       20,2        15,9
Terra Alta      XP   Gandesa                         6,6   5,2    11,2         12,2     18,1     22,9     23,4          25,6       20,4        16,0

complete file for download - UTF8

所以,这个输出不是很容易解析。还有什么其他方法可用?

似乎我使用的每个工具都只能提取有关表格单元格的布局的信息,但它不能提取属于特定列的信息。如果单元格为空,这一点非常明显 - 空单元格不在输出中,您只会得到带有布局的非空“单元格”。 PDF 本身是否包含此表格信息? 如果没有,搜索将提取它的工具没有意义。

付费解决方案并非没有问题,因为它最终可能比花费我的几个工作日更便宜...


我尝试过的:

复制粘贴 - 出现缺失值问题(第 5 页) 从 Acrobat 中另存为文本(比复制粘贴效果更差) 在 Excel 中作为外部数据源打开 - 无法识别表格 https://www.pdftoexcelonline.com/ - 导致错误 http://www.pdftoexcel.org/ 以及他们对 Able2Extract 的试用 - they messed up some columns。他们在预览中正确识别了列,但在 excel 输出中却搞砸了 http://www.pdftoword.com/ - 只接收我的电子邮件,从不发送任何内容 在 scraperwiki 上使用 python http://schoolofdata.org/2013/06/18/get-started-with-scraping-extracting-simple-tables-from-pdf-documents/ 似乎非常复杂,尤其是对于非 python 用户而言,而且https://scraperwiki.com/ 不是免费的

我遇到过几个像pdftables 这样的python 库,但是对于像我这样的非python 开发人员来说,它们并不容易使用(我什至无法运行这些东西)。有没有更简单的方法来完成任务?

我正在尝试将 R 中的 tm 库用作 recommended here,但 I have encountered some problems

编辑:Ian 推荐的 Cloud SDK。我注册了,但我绝对不知道从这里去哪里 - 如何上传页面,识别它们等:

【问题讨论】:

您希望 PDF 的最后一页中的数据如何显示?在该页面上,似乎有一些“列”具有两个值。 大多数页面每月有两列,但这似乎不是一个很大的问题。看起来最大的问题是如何处理页面标题处的数据。目前,这个问题似乎被严重低估了。 你在说什么时说“注意以*Solsonès'”开头的行中的缺失值?? -- 很明显,原始 PDF 文件中已经缺少这些值。 【参考方案1】:

过去我使用过pdftohtml,它可以用来生成xml,描述为here。列通常分离得很好,因此您可以使用定位来提取列。

我写了pdftables的很大一部分,为不透明道歉!它适用于您显示的文档的某些页面,例如第 2 页在底部给出了这个回复的输出。例如,对于其他页面,它会在第 33 页上翻倒。这里的问题是一个列标题下有两个数字,它们被 pdftables 粘在一起。 “COMARCA、CODI i、NOM EMA”列在任何一种情况下都不会分开。您可以在GitHub 上为 pdftables 提交问题,我目前没有积极处理它。它可以通过 pip install 获得。

如果你想走商业路线,那么Abbyy FineReader 非常好,他们会生成一个cloud SDK,可以免费为你提供大约 30 页。他们有多种语言的示例代码,但他们的支持不是很好。

     14 columns, 39 rows
                                      0    1    2    3    4    5    6    7    8    9   10   11   12   13
    -----------------------------------------------------------------------------------------------------
  0 |             COMARCACODI i NOM EMA| GEN| FEB| MAR| ABR| MAI| JUN| JUL| AGO| SET| OCT| NOV| DES| ANY|
  1 |                  VYNullesAlt Camp| 7,5| 5,5|10,9|12,3|16,7|21,6|22,3|24,4|20,1|15,9|11,0| 8,5|14,8|
  2 |             DQVila-rodonaAlt Camp| 7,9| 5,6|11,0|12,0|16,6|21,6|22,0|24,3|19,9|15,8|11,0| 8,6|14,7|
  3 |              Alt EmpordàU1Cabanes| 8,2| 6,5|11,7|12,6|17,5|22,0|23,1|24,4|20,4|16,6|11,8| 8,3|15,3|
  4 |  Alt EmpordàW1Castelló d'Empúries| 8,1| 6,4|11,6|12,9|17,0|21,1|22,0|23,4|20,1|16,4|12,1| 8,5|15,0|
  5 |              Alt EmpordàVZEspolla| 9,0| 6,7|12,4|12,7|17,8|22,0|23,3|24,8|20,9|16,7|12,0| 8,9|15,6|
  6 |              D6PortbouAlt Empordà| 9,6| 5,5|12,7|12,5|17,4|21,5|22,9|24,4|19,8|17,0|12,3|10,1|15,5|
  7 |                D4RosesAlt Empordà| 9,3| 7,2|13,0|13,6|18,2|22,6|23,9|25,7|21,3|17,5|13,2| 9,9|16,3|
  8 |   Alt EmpordàU2Sant Pere Pescador| 7,8| 6,3|11,5|12,9|16,8|21,2|22,2|23,6|20,2|16,5|12,3| 8,5|15,0|
  9 |  Alt EmpordàW2Torroella de Fluvià| 7,4| 6,0|11,2|12,6|16,4|21,2|22,3|23,7|19,9|16,1|11,7| 8,0|14,7|
 10 |             Alt EmpordàW3Ventalló| 7,3| 6,2|11,4|12,8|16,9|21,8|22,8|24,3|20,4|16,5|12,0| 8,1|15,1|
 11 |            Alt PenedèsWPCanaletes| 7,0| 5,2|11,3|11,9|16,7|21,5|22,0|24,2|19,7|15,6|10,7| 8,1|14,5|
 12 |            Alt PenedèsDIFont-rubí| 8,1| 6,2|12,0|11,9|16,9|21,8|22,0|24,4|20,0|15,9|11,4| 8,9|15,0|
 13 |           Alt PenedèsW4la Granada| 7,0| 5,5|11,2|12,6|17,2|21,9|22,4|24,3|20,0|16,0|11,1| 8,3|14,8|
 14 |   Alt PenedèsU3Sant Martí Sarroca| 6,4| 5,1|10,9|12,4|17,0|21,8|22,3|24,3|19,9|15,7|10,8| 8,0|14,6|
 15 | Alt PenedèsWYSant Sadurní d'Anoia| 6,4| 5,1|11,0|12,8|17,6|22,6|23,2|25,0|20,5|16,2|10,9| 7,8|15,0|
 16 |       CDla Seu d'UrgellAlt Urgell| 3,6| 2,5| 8,5| 8,4|14,6|20,3|21,0|23,4|16,9|12,2| 7,0| 3,2|11,8|
 17 |                W5OlianaAlt Urgell| 2,0| 2,7| 9,8|10,2|16,8|23,0|22,9|25,6|19,1|13,9| 8,6| 3,1|13,2|
 18 |               Alt UrgellCJOrganyà| 2,6| 3,5| 9,8| 9,9|16,1|22,0|22,6|25,3|18,8|13,5| 8,2| 2,9|13,0|
 19 |     Alta RibagorçaZ2Boí (2.535 m)|-2,4|-7,5|-1,3|-3,4| 3,8| 8,6| 9,4|12,0| 6,3| 2,7|-1,1|-3,2| 2,0|
 20 |  Alta RibagorçaCTel Pont de Suert| 0,5| 1,6| 6,9| 7,9|14,1|18,0|19,1|20,4|15,7|10,7| 6,1| 1,3|10,2|
 21 |   CEels Hostalets de PierolaAnoia| 7,3| 5,5|11,7|12,1|17,4|22,4|22,9|25,2|20,3|16,2|11,1| 8,3|15,1|
 22 |                 XBla LlacunaAnoia| 5,4| 3,3| 9,3|10,3|15,6|20,8|20,9|23,3|18,0|14,1| 9,1| 6,9|13,1|
 23 |               AnoiaXAla Panadella| 3,6| 1,7| 9,2| 8,7|14,9|20,5|20,4|23,2|17,2|13,3| 7,9| 5,1|12,2|
 24 |                      H1Ã’denaAnoia| 5,1| 3,3| 9,4|11,5|16,3|21,7|22,5|24,6|19,4|15,2| 9,3| 6,0|13,7|
 25 |                      WWArtésBages| 3,5| 2,8| 9,2|11,2|16,6|22,4|23,2|25,1|19,3|15,0| 9,1| 4,3|13,5|
 26 |        U4Castellnou de BagesBages| 4,8| 3,8|10,5|10,9|16,3|22,0|22,5|25,0|19,3|15,0| 9,6| 5,9|13,9|
 27 |        R1el Pont de VilomaraBages| 3,8| 3,1| 9,9|12,3|17,4|22,9|23,5|25,4|20,0|15,7| 9,7| 5,0|14,1|
 28 |    BagesWNMontserrat - Sant Dimes| 6,2| 3,3| 9,7| 8,6|14,8|19,5|19,5|22,4|16,9|13,5| 9,0| 7,1|12,6|
 29 | CLSant Salvador de GuardiolaBages| 3,3| 2,8| 9,1|11,5|16,4|22,0|22,4|24,6|19,2|14,9| 9,1| 4,8|13,4|
 30 |   U5Prades - los HortalsBaix Camp| 2,8| 0,0| 6,4| 7,4|13,0|18,4|18,0|21,3|15,0|11,3| 6,5| 4,1|10,4|
 31 |                W6RiudomsBaix Camp| 9,7| 7,1|12,0|13,4|17,6|22,4|23,1|25,2|21,2|17,1|12,3|10,1|16,0|
 32 |     U6Vinyols i els ArcsBaix Camp|10,2| 7,6|12,0|13,8|17,6|22,5|24,0|25,9|22,3|18,2|13,2|11,1|16,6|
 33 |                Baix EbreU7Aldover|10,0| 8,5|13,2|14,8|19,7|24,6|25,2|27,1|22,7|18,3|12,9|11,1|17,4|
 34 |             DBel PerellóBaix Ebre| 8,7| 7,0|12,0|13,3|17,9|22,6|23,3|25,3|21,4|17,2|11,9|10,3|15,9|
 35 |                U9l'AldeaBaix Ebre| 9,9| 8,1|12,5|14,3|18,5|23,3|24,1|26,0|22,1|17,9|13,1|10,7|16,8|
 36 |       UAl'Ametlla de MarBaix Ebre| 9,6| 7,8|12,3|13,8|18,0|22,9|23,9|25,8|22,0|17,6|12,5|10,6|16,4|
 37 |          Baix EbreX5PN dels Ports| 3,4|-0,2| 6,5| 6,8|13,4|18,7|17,8|21,2|15,2|11,3| 6,1| 4,9|10,5|
 38 |       Baix EmpordàDOCastell d'Aro| 6,7| 5,1|10,6|12,0|16,2|20,9|21,8|23,8|20,1|16,3|12,2| 8,1|14,5|
    -----------------------------------------------------------------------------------------------------  

unicode 问题归结于我的开发环境 (Spyder)。

【讨论】:

亲爱的伊恩,谢谢!很抱歉回复晚了。但是 1)当表中缺少值时(对于 comarca Solsonès),您的工具将如何在第 5 页上工作? 2)您的工具是否能够将跨多个页面拆分为一个的表合并为一个,强制相应的列?这些问题实际上对我来说是必不可少的,我可以在合并的“COMARCA,CODI i,NOM EMA”列中幸存下来。 我尝试了您推荐的 Cloud SDK,我注册了,但我绝对不知道从哪里开始 - 如何上传页面,识别它们等。请查看我更新的问题。 抱歉耽搁了 - 我以为我收到了通知,但我没有!缺失值没有问题,它们只会在 pdftables 中显示为空单元格。在页面之间连接表格将是一个编程问题(也就是说,按照当前的配置,它将为每个页面提取一个表格,以及如何连接它们将是进一步编程的问题)。看起来每个页面上的列数相同,只是它们在页面上的绝对位置不同 - 在这种情况下,连接起来应该很简单。 cloud sdk 仅供程序化使用,这里有代码示例:ocrsdk.com/documentation/code-samples 但是,我不清楚您是否在这里寻找程序化解决方案【参考方案2】:

虽然使用 pdftotext 时页面的布局不同,但请注意各个页面(COMARCA、CODI 等)上的列标题似乎与该页面上的数据一致。 p>

此外,您的 pdf 中有许多不同类型的数据 - 风向、风强、湿度、降水等。因此,不仅相同数据的页面布局不同,而且布局也不同,因为还有不同的数据集。

为了完整起见 - “Solsonès”(作为一个例子)的缺失数据存在于原始 PDF 中。 pdftotext 似乎做得很合理 - 丢失的数据是空白,就像在原始 PDF 中一样。

因此,保留pdftotext 并将页面(由换页符分隔)视为列数据并使用struct 解析可能是有意义的,如下所述:

How to efficiently parse fixed width files?

完成这项工作的一种方法是检测换页,查找以“COMARCA”开头的下一行,并使用该行中的间距为struct 设置列。

【讨论】:

是的,因为我尝试过的许多工具都是用 Python 编写的,而且抓取社区似乎是面向 Python 的,但如果可能的话,我更喜欢非 Python 解决方案。或者,如果有一个不需要 python 知识的 python 工具 - 例如。一些为我进行解析的命令行工具,那就太好了。我可以在 PERL、php、bash 等中完成其余的工作...... 要在 Perl 中遵循类似的方法,unpack 是否对您有帮助,如这里所述? ***.com/questions/4911044/parse-fixed-width-files unpack 在这里似乎很有用(解析固定宽度的列),但我认为您还需要为每个页面使用不同的模板,因为列宽不同。好的一面是它可以处理丢失的条目。教程在这里:perldoc.perl.org/perlpacktut.html【参考方案3】:

好的,我试了一下,我认为它会有所帮助,尽管我不确定您希望最终输出的样子。我很高兴在这方面做更多工作,所以如果有需要帮助的部分,请告诉我。


我首先从 CNET 下载了PDF to Text application。

安装后,我检查了这些设置:

这里的重要部分是我们正在使用物理布局选项。

这给了我们如下所示的输出:

Taules de Dades de la Xarxa d’Estacions
    Meteorològiques Automàtiques
            2                                                                                                   Anuari de dades meteorològiques 2012 / Servei Meteorològic de Catalunya
            2                                                           TEMPERATURA MITJANA MENSUAL ( ºC ) - 2012

COMARCA          CODI i NOM EMA                    GEN    FEB    MAR         ABR       MAI      JUN      JUL          AGO        SET        OCT        NOV         DES         ANY

Alt Camp         VY   Nulles                        7,5    5,5   10,9         12,3     16,7     21,6     22,3         24,4       20,1        15,9       11,0        8,5         14,8
Alt Camp         DQ   Vila-rodona                   7,9    5,6   11,0         12,0     16,6     21,6     22,0         24,3       19,9        15,8       11,0        8,6         14,7
Alt Empordà      U1   Cabanes                       8,2    6,5   11,7         12,6     17,5     22,0     23,1         24,4       20,4        16,6       11,8        8,3         15,3
Alt Empordà      W1   Castelló d'Empúries           8,1    6,4   11,6         12,9     17,0     21,1     22,0         23,4       20,1        16,4       12,1        8,5         15,0
Alt Empordà      VZ   Espolla                       9,0    6,7   12,4         12,7     17,8     22,0     23,3         24,8       20,9        16,7       12,0        8,9         15,6

[......]

             3                                                                                                           Anuari de dades meteorològiques 2012 / Servei Meteorològic de Catalunya
             2                                                                   TEMPERATURA MITJANA MENSUAL ( ºC ) - 2012

COMARCA          CODI i NOM EMA                             GEN    FEB    MAR         ABR       MAI      JUN      JUL          AGO        SET        OCT        NOV         DES         ANY

Baix Empordà     DF   la Bisbal d'Empordà                    6,6    5,3   10,9         12,6     17,2     21,9     22,9         24,6       20,3        16,6       11,9        7,6         14,9
Baix Empordà     UB   la Tallada d'Empordà                   6,1    5,2   10,7         12,3     16,6     21,3     22,2         23,8       19,7        15,8       11,7        7,6         14,4
Baix Empordà     UC   Monells                                6,1    4,6    9,9         11,4     16,5     21,7     23,0         24,5       19,6        15,7       11,7        7,2         14,3
Baix Empordà     UD   Serra de Daró                          6,3    5,3   10,6         12,3     16,8     21,6     22,7         24,3       20,3        16,6       12,2        7,7         14,8

[......]

             4                                                                                                              Anuari de dades meteorològiques 2012 / Servei Meteorològic de Catalunya
             2                                                                      TEMPERATURA MITJANA MENSUAL ( ºC ) - 2012

COMARCA           CODI i NOM EMA                               GEN    FEB    MAR         ABR       MAI      JUN      JUL          AGO        SET        OCT        NOV         DES         ANY

Maresme           UQ   Dosrius - PN Montnegre Corredor          7,2    4,6   10,8         10,7     15,8     20,4     20,8         23,4       18,6        15,1       10,7        7,8         13,9
Maresme           WT   Malgrat de Mar                           7,4    5,4   11,0         13,0     16,7     21,5     22,8         24,6       20,9        17,2       12,9        8,8         15,2
Maresme           DD   Vilassar de Mar                         10,1    7,5   12,6         13,9     17,9     22,4     23,7         25,7       22,1        18,4       13,8       10,8         16,6
Montsià           US   Alcanar                                 10,0    7,6   11,8         14,2     17,9     22,7     24,0         25,8       22,0        18,2       13,7       10,7         16,6
Montsià           UU   Amposta                                  9,6    7,5   12,1         14,3     18,3     22,8     23,5         25,3       21,6        18,0       13,1       10,8         16,4

[......]

您可以看到列排列得更好,但我们也有标题和页码。 COMARCAi NOM EMA 列的长度也不同。我们希望将其标准化为固定宽度的列。

我编写了一个 Perl 程序对其进行规范化,它还结合了具有相同标题的表格,并且只打印顶部的标题。它会创建一个输出文件夹,其中包含以标题为文件名的所有文件。

代码如下:

#!/bin/perl

use strict;
use warnings;
use open qw(:std :utf8);
use utf8;

my $comarca;
my $nom;
my $print_headers;
my $title = "";
my $fh;

while(<>) 

    if (    !/Xarxa d’Estacions/
        and !/Meteorològiques Automàtiques/
        and !/Servei/
        and !/^\s*\d+\s*$/
        and !/^\s*$/ ) 

        chomp($_);


        if ( /^\s*2/ )  #title
            s/^\s*2\s*//;
            if ( $title ne $_ ) 
                $title = $_;
                $print_headers = 1;
            

         elsif ( /COMARCA/ )  #column headers

            my ($first_col, $second_col, @the_rest) = split(/(CODI +i NOM EMA *)/, $_);


            $comarca = length $first_col;
            $nom = length $second_col;

            if ( $print_headers ) 
                my $str = sprintf "%-50s %-50s %s\n", $first_col, $second_col, join("", @the_rest);
                write_string($str);
                $print_headers = 0;
            

         else  #data

            my ($one, $two, $three) = unpack("A$comarcaA$nomA*", $_);
            my $str = sprintf "%-50s %-50s $three\n", $one, $two;
            write_string($str);
        

    


sub write_string 

    my $string = shift;
    my $file_name = $title;
    $file_name =~ s/[\/\\]//g;

    open ($fh, '>>', ".\/output_folder\/$file_name.txt") or die "Couldn't open: $!";
    print $fh $string;
    close ($fh);

输出中仍有一些不完善之处(运行此程序时您会看到这些),但我想就哪种输出最适合您获得一些反馈。我们肯定可以做更多的事情来改进代码!输出目录树如下所示:

Matt@MattPC ~/perl/pdftotext
$ find .
.
./convert.pl
./EMAtaules2012.txt
./output.txt
./output_folder
./output_folder/AMPLITUD TÈRMICA MITJANA MENSUAL ( ºC ) - 2012?.txt
./output_folder/AMPLITUD TÈRMICA MÀXIMA MENSUAL ( ºC ) - 2012?.txt
./output_folder/DIRECCIÓ DOMINANT DEL VENT - 2012?.txt
./output_folder/GRUIX MÀXIM MENSUAL DE NEU AL TERRA ( cm ) - 2012?.txt
./output_folder/HUMITAT RELATIVA MITJANA MENSUAL ( % ) - 2012?.txt
./output_folder/MITJANA MENSUAL DE LA HUMITAT RELATIVA MÀXIMA DIÀRIA ( % ) - 2012?.txt
./output_folder/MITJANA MENSUAL DE LA HUMITAT RELATIVA MÍNIMA DIÀRIA ( % ) - 2012?.txt
[......]

文件可能如下所示:

COMARCA                                            CODI i NOM EMA                                     GEN    FEB    MAR         ABR       MAI      JUN      JUL          AGO        SET        OCT        NOV         DES         ANY
Alt Camp                                           VY   Nulles                                         7,5    5,5   10,9         12,3     16,7     21,6     22,3         24,4       20,1        15,9       11,0        8,5         14,8
Alt Camp                                           DQ   Vila-rodona                                    7,9    5,6   11,0         12,0     16,6     21,6     22,0         24,3       19,9        15,8       11,0        8,6         14,7
Alt Empordà                                        U1   Cabanes                                        8,2    6,5   11,7         12,6     17,5     22,0     23,1         24,4       20,4        16,6       11,8        8,3         15,3
Alt Empordà                                        W1   Castelló d'Empúries                            8,1    6,4   11,6         12,9     17,0     21,1     22,0         23,4       20,1        16,4       12,1        8,5         15,0
Alt Empordà                                        VZ   Espolla                                        9,0    6,7   12,4         12,7     17,8     22,0     23,3         24,8       20,9        16,7       12,0        8,9         15,6
Alt Empordà                                        D6   Portbou                                        9,6    5,5   12,7         12,5     17,4     21,5     22,9         24,4       19,8        17,0       12,3       10,1         15,5
[......]

标题仅在顶部,所有列都排成一行。这个是TEMPERATURA MITJANA MENSUAL ( ºC ) - 2012

我一直在考虑将更多输出上传到文件托管站点,但我不知道哪个会好,建议?

希望这对托马斯有帮助!

编辑:AMPLITUD TÈRMICA MÀXIMA MENSUAL ( ºC ) - 2012 中缺失条目的示例:

Solsonès                                           VP   Pinós                          1              3,1   26   16,9   13   16,7   15   16,6   17   19,2   11   19,6   24   20,4    17      19,1   01   17,5   16   16,5   06   13,1   08   13,9   24   20,4    17/07
Solsonès                                           XT   Solsona                                                                                                              22,2    25      22,2   09   20,1   16   18,6   06   15,3   07   18,2   23   22,2    09/08
Tarragonès                                         VQ   Constantí                      1              6,4   19   21,9   23   19,7   11   12,9   07   17,4   23   17,2   21   15,1    18      14,2   18   18,0   15   15,1   02   14,9   07   16,0   10   21,9    23/02

更新

更新了处理输入文件的脚本:

#!/bin/perl

use strict;
use warnings;
use open qw(:std :utf8);
use utf8;
use charnames ':full';

my @column_lengths;
my $print_headers;
my $title = "";
my $fh;

while(<>) 

    if (    !/Xarxa d’Estacions/
        and !/Meteorològiques Automàtiques/
        and !/Servei/
        and !/^\s*\d+\s*$/
        and !/^\s*$/ ) 

        s/[\r\n]+//g;
        s/ +\d+$//;
        if ( /^\s*2/ )  #title
            s/^\s*2\s*//;
            if ( $title ne $_ ) 
                $title = $_;
                $print_headers = 1;
            

         elsif ( /COMARCA/ )  #column headers

            my $comarca = (split(/(COMARCA *)/, $_))[1];
            my $codi = (split(/(CODI *)/, $_))[1];
            my $inomema = (split(/(i NOM EMA *)  /, $_))[1];

            my $the_rest = (split(/(i NOM EMA *)  /, $_))[2];

            my @rest = split(/( \w+ *)/, $the_rest);

            undef @column_lengths;

            push @column_lengths, length $comarca;
            push @column_lengths, length $codi;
            push @column_lengths, length $inomema;

            for (@rest) 
                if ( $_ ) 
                    push @column_lengths, length $_;
                
            

            $column_lengths[-1] = "*";

            if ( $print_headers ) 
                $print_headers = 0;
                write_string(join(";", unpack( "A" . join("A", @column_lengths), $_)) . "\n");
            

         else  #data

            write_string(join(";", unpack( "A" . join("A", @column_lengths), $_)) . "\n");

        

    


sub write_string 

    my $string = shift;
    my $file_name = $title;
    $file_name =~ s/[º]//g;
    $file_name =~ s/[^\w ]//g;
    $file_name =~ s/ +/ /g;
    $file_name =~ s/È/E/g;
    $file_name =~ s/À/A/g;
    $file_name =~ s/Ó/O/g;
    $file_name =~ s/Í/I/g;
    $file_name =~ s/Ç/C/g;

    open ($fh, '>>', ".\/output_folder\/$file_name.txt") or die "Couldn't open: $!";
    print $fh $string;
    close ($fh);

这个结合了线条和 d.i.在下一行。

#!/bin/perl -i

use strict;
use warnings;

my $last = <>;

while(<>) 

    my @current_array = split(";", $_);

    if ( /^;+[ \t]+.d\.i\./ ) 

        my @last_array = split(";", $last);
        my @combined_array;

        #print "matches\n";

        for my $element (@current_array) 

            if ( $element =~ /d\.i\./ ) 
                push @combined_array, $element;
                shift @last_array;
             else 
                push @combined_array, $last_array[0];
                shift @last_array;
            

        
        undef @current_array;
        @current_array = @combined_array;
    
    $last = join ";", @current_array;
    print $last;


输出为带有分号分隔符的 csv 格式。

【讨论】:

马特,看起来不错!您的脚本是否能够处理 comarca Solsones 中的缺失值(请参阅我的问题)? 脚本确实会处理缺失值,但有时 .di 行会出现在下一行,我们可能希望在最后一列之后添加一些代码来截断,因为有时我们会有额外的页码.我会上传文件,看起来我必须重命名其中一些文件才能将它们压缩成 zip(因为特殊字符)。【参考方案4】:

这是一个 R 解决方案,但它并非没有缺陷。

第 1 部分:设置步骤

# Read the lines of your file into R
x <- readLines("EMAtaules2012.txt")

# Make sure it shows up as UTF-8 to get proper accents and so on
Encoding(x) <- "UTF-8"

# Identify the lines where the data starts
Start <- grep("COMARCA", x)

# Grab the names of each table
ListNames <- gsub("\\s+", " ", x[Start-2])

# Figure out the number of rows of data per page
Runs <- rle(diff(cumsum(x != "")))
Nrows <- Runs$lengths[Runs$lengths > 4]+1

# Make our life easier by making this column name
#  a single string
x <- gsub("i NOM EMA", "i_NOM_EMA", x)

# Since these are fixed width files, we need to figure
#  out the widths of each column. This is the sum of
#  the number of characters in the header row plus
#  the number of spaces between each column name
Spaces <- gregexpr(x[Start], pattern="\\s+")
Spaces <- lapply(Spaces, function(x) c(attr(x, "match.length"), 0))
Chars <- lapply(strsplit(x[Start], "\\s+"), nchar)
Widths <- lapply(seq_along(Spaces), 
                 function(x) rowSums(cbind(Spaces[[x]], 
                                           Chars[[x]])))

第 2 部分:使用read.fwf 获取数据

# Now, you can use `read.fwf` to read your data files in
temp <- lapply(seq_along(Start), function(fwf) 
  A <- read.fwf(textConnection(x), 
                widths = c(Widths[[fwf]]), 
                header = FALSE, 
                skip = Start[fwf]+1, 
                n = Nrows[fwf]-2, 
                blank.lines.skip = TRUE,
                strip.white = TRUE,
                stringsAsFactors = FALSE)
  # Add in the column names
  names(A) <- scan(what = "character", 
                   file = textConnection(x[Start[fwf]]), 
                   quiet = TRUE)
  A
)

# Assign the table names
names(temp) <- ListNames

# Some more cleanup. The original tables span multiple pages
#  in the PDF, but we can `rbind` them together in R
Tables <- unique(ListNames)
final <- lapply(seq_along(Tables), function(final) 
  A <- do.call(rbind, temp[names(temp) %in% Tables[final]])
  rownames(A) <- NULL
  A
)
# Add the names back in
names(final) <- Tables

第 3 部分:成功了吗?

# View the first few rows and columns of the first three tables
lapply(final[1:3], function(y) head(y[1:5], 3))
# $` TEMPERATURA MITJANA MENSUAL ( ºC ) - 2012`
#       COMARCA CODI           i_NOM_EMA GEN FEB
# 1    Alt Camp   DQ         Vila-rodona 7,9 5,6
# 2 Alt Empordà   U1             Cabanes 8,2 6,5
# 3 Alt Empordà   W1 Castelló d'Empúries 8,1 6,4
# 
# $` TEMPERATURA MÀXIMA MITJANA MENSUAL ( ºC ) - 2012`
#       COMARCA CODI           i_NOM_EMA  GEN  FEB
# 1    Alt Camp   DQ         Vila-rodona 13,1 11,7
# 2 Alt Empordà   U1             Cabanes 15,1 12,4
# 3 Alt Empordà   W1 Castelló d'Empúries 14,4 11,7
# 
# $` TEMPERATURA MÍNIMA MITJANA MENSUAL ( ºC ) - 2012`
#       COMARCA CODI           i_NOM_EMA GEN FEB
# 1    Alt Camp   DQ         Vila-rodona 3,8 0,5
# 2 Alt Empordà   U1             Cabanes 2,4 0,9
# 3 Alt Empordà   W1 Castelló d'Empúries 2,1 0,5

# Some tables, like those on page 76 (for the table "DIRECCIÓ DOMINANT DEL VENT"), had more columns than others. 
# Did our script take care of that?
names(final$` DIRECCIÓ DOMINANT DEL VENT`)
#  [1] "COMARCA"   "CODI"      "i_NOM_EMA" "vent"      "GEN"       "FEB"      
#  [7] "MAR"       "ABR"       "MAI"       "JUN"       "JUL"       "AGO"      
# [13] "SET"       "OCT"       "NOV"       "DES"       "ANY"    

有点奏效了。但是,您的输入文件并不完美,这意味着仍有很多需要清理。例如,PDF 中的某些列似乎有多个值。不确定您将如何对这些进行任何分析。

希望上述代码中的 cmets 可以帮助您开始了解如何以更好的方式抓取数据。


更新:仅提取数据

在上面的“第 1 部分”之后继续,这是一个依赖 (gasp) Excel 的解决方案。基本思想是,如果您将文本导入为固定宽度,Excel 实际上可以很好地检测分栏符的位置。

因此,我们使用 R 将文本分成单独的页面,每页一个文件,只有数据(不是列名或行名,它们在所有数据集中大部分都相同)。

至此,这是 R 的最后一步:

# Output just the data
temp <- lapply(seq_along(Widths), function(y) 
  DEL <- sum(Widths[[y]][1:3])-2
  A <- substring(x[(Start[y]+1):(sum(Start[y], Nrows[y]))], DEL)
  writeLines(A, paste("temp_", y, ".txt", collapse = ""))
  A
)

让我们打开文件“temp_9.txt”,这是一个缺少列的文件:

^^ 确保选择“Fixed Width”——默认情况下应该是,因为文件没有分隔符。

^^ Excel 会向您显示列的位置预览。

^^ 我已经突出显示了“问题行”,让您看看它是如何解决的。

【讨论】:

我们似乎正在用类似的策略来解决这个问题(你似乎比我走得更远。)你可能想看看你的方法是否更接近完全成功删除“Ô人物。在这个简单的步骤之后,我得到了 100% 的“注册”。 @DWin,我忘了说:我只是用Encoding(x) &lt;- "UTF-8" 来正确显示字符。 我认为这是默认设置。只是问问。 谢谢阿南达,但是这种方法是如何解决comarca Solsones 中的缺失值的?【参考方案5】:

如果您对深入研究 Python 或其他基于代码的解决方案持谨慎态度,对于少量 pdf 的快速而肮脏的解决方案,另一种完全不同的方法是将任务外包给 MechanicalTurk。

每列有多个用户允许您仔细检查提交的答案,您还可以发布生成的 .csv 表,并为工作人员发现的每个错误支付大量费用(例如,5 美元)。通常最终会比您或其他人编写解决方案的时间便宜得多。

【讨论】:

机械土耳其人???他们是真的吗?对于那些受雇于其中的人来说,这听起来很冒犯和羞辱(几乎是种族主义者)!不过,+1 似乎是一种可能的解决方案。 啊,name is based on'人工智能'。 在过去,二战前和二战期间,“计算机”(通常)是一个拿着计算尺或其他机械数学设备和铅笔的女人。理查德·费曼在洛斯阿拉莫斯的工作之一是管理“计算机”员工。人们也可以问为什么这没有导致早期将并行性作为一种策略来关注。 @Tomas 我希望他们没有任何种族主义的意思。我猜他们只是在间接引用this historical thing。【参考方案6】:

为此构建索引的努力(可能格式的变化与不同的子报告有关。这些似乎都是针对加泰罗尼亚的:

heads <- grep("                                                                .+2012", txt)
notheads <- grep("                                                                .+Anuari de", txt)
 headtxt <-  unique(trim(txt[1:length(txt) %in% heads & !1:length(txt) %in% notheads]))

 [1] "TEMPERATURA MITJANA MENSUAL ( ºC ) - 2012"                            
 [2] "TEMPERATURA MÀXIMA MITJANA MENSUAL ( ºC ) - 2012"                     
 [3] "TEMPERATURA MÍNIMA MITJANA MENSUAL ( ºC ) - 2012"                     
 [4] "TEMPERATURA MÀXIMA ABSOLUTA MENSUAL ( ºC ) - 2012"                    
 [5] "TEMPERATURA MÍNIMA ABSOLUTA MENSUAL ( ºC ) - 2012"                    
 [6] "AMPLITUD TÈRMICA MITJANA MENSUAL ( ºC ) - 2012"                       
 [7] "AMPLITUD TÈRMICA MÀXIMA MENSUAL ( ºC ) - 2012"                        
 [8] "NOMBRE DE DIES DE GLAÇADA ( TN ≤ 0 ºC ) - 2012"                       
 [9] "PRECIPITACIÓ MENSUAL ( mm ) - 2012"                                   
[10] "PRECIPITACIÓ MENSUAL MÀXIMA EN 24 HORES ( mm ) - 2012"                
[11] "PRECIPITACIÓ MENSUAL MÀXIMA EN 1 HORA ( mm ) - 2012"                  
[12] "PRECIPITACIÓ MENSUAL MÀXIMA EN 30 MINUTS ( mm ) - 2012"               
[13] "PRECIPITACIÓ MENSUAL MÀXIMA EN UN 1 MINUT ( mm ) - 2012"              
[14] "NOMBRE DE DIES DE PRECIPITACIÓ (PPT ≥ 0,1 mm) - 2012"                 
[15] "NOMBRE DE DIES DE PRECIPITACIÓ (PPT > 0,2 mm) - 2012"                 
[16] "VELOCITAT MITJANA DEL VENT MENSUAL ( m/s ) - 2012"                    
[17] "DIRECCIÓ DOMINANT DEL VENT - 2012"                                    
[18] "MITJANA MENSUAL DE LA RATXA MÀXIMA DIÀRIA DEL VENT ( m/s ) - 2012"    
[19] "RATXA MÀXIMA ABSOLUTA DEL VENT MENSUAL ( m/s ) - 2012"                
[20] "HUMITAT RELATIVA MITJANA MENSUAL ( % ) - 2012"                        
[21] "MITJANA MENSUAL DE LA HUMITAT RELATIVA MÀXIMA DIÀRIA ( % ) - 2012"    
[22] "MITJANA MENSUAL DE LA HUMITAT RELATIVA MÍNIMA DIÀRIA ( % ) - 2012"    
[23] "MITJANA MENSUAL DE LA IRRADIACIÓ SOLAR GLOBAL DIÀRIA ( MJ/m2 ) - 2012"
[24] "PRESSIÓ ATMOSFÈRICA MITJANA MENSUAL, A NIVELL DE L'EMA ( hPa ) - 2012"
[25] "PRESSIÓ ATMOSFÈRICA MÀXIMA ABSOLUTA MENSUAL ( hPa ) - 2012"           
[26] "PRESSIÓ ATMOSFÈRICA MÍNIMA ABSOLUTA MENSUAL ( hPa ) - 2012"           
[27] "GRUIX MÀXIM MENSUAL DE NEU AL TERRA ( cm ) - 2012"  

括号和破折号会干扰 grepping。因此,通过删除 "\\(.+$" 匹配的单个异常(我决定“手动”修复:

 headtxt[14:15]
#[14] "NOMBRE DE DIES DE PRECIPITACIÓ (PPT ≥ 0,1 mm) - 2012"                 
#[15] "NOMBRE DE DIES DE PRECIPITACIÓ (PPT > 0,2 mm) - 2012"  

headtxt <- gsub("\\(.+$", "", headtxt)

pagedivs <- lapply(headtxt, grep, txt)
# Seemed reasonable that the first 5 (of 10) should be the first section
pagedivs[[14]] <- pagedivs[[14]][1:5]
pagedivs[[15]] <- pagedivs[[15]][6:10]

所以寻找一个标记来结束页面看起来像 4 个空行是可靠的

> length(notheads)
[1] 113
> rl.lens <- rle( nchar(txt) )
> table(rl.lens$lengths[rl.lens$values==0])
#  1   4 
#226 113 

删除了所有的“Ô,因为它们正在创建非固定宽度的列:

txt <- gsub("Ã", "", txt)
write(txt, "txt_noAs.txt)

有趣的是,我的文本编辑器现在会在“Ô出现的位置显示“à”。此时,可以循环从 pagedivs+4 开始的页面类型内的页面到 4 个空行的位置,并使用“utils”包中的read.fwf。剩下来支持这一点的是布局定义,您说您已经掌握了它,但也可以使用 pkg:gsubfn 的 strapply 或正则表达式解决方案来推断。

寻找开发正则表达式解决方案的方法:

> numfields <- gregexpr("[-[:digit:].]+ ", txt)
> table( sapply( numfields,  length))

   1    2    3    5    6    7    8   11   12   13   14   15 
1201  193    8    1   13   15    2    4 1162  869  308   32 
  16   17   19   20   21   23   24   25   26   27   28   30 
   1    3    1    1    1    7   10  688  481  168   13    1 

很明显,这些页面分为两类:数字列数为 12-14 的页面和编号为 23-28 的页面。我原以为这会有点不同,但我猜“ANY”列超出了我的预期。

【讨论】:

感谢 DWin,但是这种方法是如何解决 comarca Solsones 中的缺失值的? 我认为我还没有描述过“一种方法”。我查看了 Solsones 行,因为它们的缺失程度最高,但在任何一页的上下文中,列对齐仍然保留。我还刚刚注意到更简单的分页测试是搜索grep("^\\\f", txt)。我假设我们可以使用@AnandaMahto 的方法进行分页,在它被封装到一个逐页处理的函数中之后。【参考方案7】:

很明显,最初的 Excel 电子表格是由使用不同列宽的不同工作表组成的。

因此 PDF 表格也使用不同的列宽。如果您查看 PDF,您可以看到以下页面范围组,每组具有相同的列宽。每个组还描述了不同的内容,从每个组起始页的更改标题中可以看出(即使无法理解西班牙语,我也可以识别这些差异):

    第 2-6 页(5 页) 第 7-11 页(5 页) 第 12-16 页(5 页) 第 17-21 页(5 页) 第 22-26 页(5 页) 第 27-31 页(5 页) 第 32-36 页(5 页) 第 37-41 页(5 页) 第 42-46 页(5 页) 第 47-51 页(5 页) 第 52-56 页(5 页) 第 57+58 页(2 页) 第 59-62 页(4 页) 第 63-67 页(5 页) 第 68-72 页(5 页) 第 73-76 页(4 页) 第 77-80 页(4 页) 第 81-84 页(4 页) 第 85-88 页(5 页) 第 89-93 页(5 页) 第 84-98 页(5 页) 第 99-103 页(5 页) 第 104-107 页(4 页) 第 108+109 页(2 页) 第 110+111 页(2 页) 第 112+113 页(2 页) 最后,第 114 页(仅 1 页)

所以,您可以让pdftotext 按这些页组提取表格数据。如果结果不是每个页面范围内的完全对齐的列,则必须逐页提取表格。这些应该很容易作为 “固定宽度” 表格数据导入 Excel。

向您展示一个示例(使用 Poppler 的 pdftotext 版本创建):

pdftotext \ -layout \ -enc UTF-8 \ -f 22 -l 26 \ -nopgbrk \ -x 20 -y 82 \ -W 810 -H 450 \ EMAtaules2012.pdf \ -

-f 22 -l 26: 这告诉工具将第 22 页提取为范围中的第一个,将第 26 页提取为最后一个。 -nopgbrk: 告诉工具不要插入分页符。 -x 20 -y 82: 设置要从中提取表格数据的区域的左上角(以像素为单位)。请注意,我在这里使用的这些值也排除了列标题,而不仅仅是页眉和表名。 -W 810 -H 450:设置用于表格数据提取的区域的宽度和高度(以像素为单位)。

注意,如果您使用 XPDF 的 pdftotext 版本(可在 www.foolabs.com/xpdf/download.html 获得)-x 的命令行选项-y-W-H 不受支持。但是如果你使用 -table 而不是 -layout 和 XPDF-pdftotext,那么结果应该是相似的(但是你仍然需要删除页面和列手动标题)。

上面的命令给你这个输出(我只显示前两页的输出,宽度正好在页面边界处,Baix Ebre 条目后 2 行):

Alt Camp VY Nulles -1,4 19 -4,9 12 1,1 07 4,0 07 4,8 01 11,2 13 12,0 02 12,7 31 8,3 27 0,7 29 0,1 30 -1,7 01 -4,9 12/02 Alt Camp DQ Vila-rodona -0,5 30 -4,5 03 1,3 07 3,4 17 5,5 02 13,0 14 12,8 02 14,6 31 8,9 27 2,6 28 0, 2 30 0,6 12 -4,5 03/02 Alt Empordà U1 Cabanes -3,0 15 -6,0 09 -0,3 02 2,9 25 3,6 01 12,2 11 10,5 24 12,6 27 6,6 27 2,8 30 2,0 30 -4,3 12 -6,0 09/02 Alt Empordà W1 Castelló d'Empúries -2,7 15 -6,2 09 0,3 02 3,2 07 6,0 01 12,1 16 11,1 24 13,3 27 7,5 27 0,7 30 2 ,2 23 -3,7 12 -6,2 09/02 Alt Empordà VZ Espolla -1,8 15 -6,8 09 1,5 19 2,9 07 5,7 01 12,2 12 10,3 24 13,7 07 7,6 20 2,5 30 2,5 07 -4,8 12 -6,8 09/02 Alt Empordà D6 Portbou 1,7 29 -4,5 04 4,8 06 3,3 16 9,4 01 12,6 11 13,3 01 15,3 06 12,4 26 4,7 28 4,0 30 1 ,4 12 -4,5 04/02 Alt Empordà D4 玫瑰 -1,6 15 -4,2 09 2,9 16 4,6 07 7,0 01 13,5 12 13,5 24 15,7 27 8,7 27 2,1 30 3,5 23 -2,5 12 -4,2 09/02 Alt Empordà U2 Sant Pere Pescador -3,5 15 -6,1 09 -0,2 02 2,6 07 5,8 01 10,3 12 9,6 24 12,7 27 8,0 27 -0,2 30 1,9 23 -3,5 12 -6,1 09/02 Alt Empordà W2 Torroella de Fluvià -4,0 15 -6,7 09 -1,3 02 1,6 07 3,4 02 9,5 12 9,5 24 12,6 27 6,4 27 -0,6 30 0,9 30 -4,2 12 -6,7 09/02 Alt Empordà W3 Ventalló -5,0 15 -6,8 09 -0,7 02 1,9 07 4,3 01 10,2 12 10,6 24 12,5 27 6,9 27 -0,7 30 -0 ,8 30 -5,2 12 -6,8 09/02 Alt Penedès WP Canaletes -1,0 14 -5,3 12 1,6 07 3,1 17 5,7 03 11,2 13 12,1 02 13,7 31 9,0 27 1,8 29 -0,8 30 -0,6 02 -5,3 12/02 Alt Penedès DI 字体-rubí -1,1 29 -4,9 12 2,0 08 4,4 17 6,9 01 11,6 09 11,8 02 15,1 31 10,0 26 0,3 29 -0 ,3 30 -0,3 02 -4,9 12/02 Alt Penedès W4 la Granada -0,9 31 -5,4 13 1,0 07 3,7 17 5,9 01 11,1 13 12,1 02 13,5 31 9,0 26 1,7 29 -0, 9 30 -0,3 02 -5,4 13/02 Alt Penedès U3 Sant Martí Sarroca -4,1 14 -7,2 13 -0,3 08 3,0 07 4,6 03 11,2 12 11,4 02 13,2 31 8,2 26 -0,6 29 -1,1 30 -4,3 02 -7,2 13/02 Alt Penedès WY Sant Sadurní d'Anoia -2,7 31 -5,7 13 -0,3 08 2,4 07 4,7 01 10,7 12 12,0 02 13,8 31 8,0 27 1,6 30 -2,2 30 -2,8 02 -5,7 13/02 Alt Urgell CD la Seu d'Urgell -6,9 15 -10,7 12 -4,6 06 -1,5 17 2,1 01 6,3 12 7,5 02 7,2 31 3,1 27 -3 ,0 29 -4,0 30 -8,4 12 -10,7 12/02 Alt Urgell W5 奥利安娜 -6,6 31 -12,0 12 -4,3 08 -1,1 14 1,4 01 7,8 12 9,6 02 11,2 26 7,4 26 -3,1 29 - 4,5 30 -6,8 10 -12,0 12/02 Alt Urgell CJ Organyà -8,2 14 -8,8 05 -2,4 19 -0,9 20 1,1 01 6,6 12 9,9 02 10,4 31 5,6 27 -2,2 30 - 1,7 30 -7,8 12 -8,8 05/02 Alta Ribagorça Z2 Boí (2.535 m) -14,3 29 -23,0 03 -13,6 06 -11,5 16 -7,2 01 -1,8 12 0,7 01 -2,0 31 -3, 5 26 -14,2 28 -12,9 29 -11,5 06 -23,0 03/02 Alta Ribagorça CT el Pont de Suert -10,3 15 -11,8 21 -6,4 07 -3,4 17 -0,1 01 3,5 12 5,4 15 5,2 31 1,5 27 -4 ,9 29 -6,7 30 -9,6 12 -11,8 21/02 Anoia CE els Hostalets de Pierola -2,0 14 -5,1 13 1,3 07 3,4 17 5,8 01 12,4 12 12,2 02 13,1 31 10,0 27 1,2 29 -0 ,2 30 -1,9 02 -5,1 13/02 Anoia XB la Llacuna -6,2 14 -8,2 12 -2,8 07 1,1 17 2,4 03 6,4 13 9,8 24 10,2 31 5,0 27 -1,5 29 -3 ,2 30 -3,9 01 -8,2 12/02 Anoia XA la Panadella -3,9 30 -10,1 03 -2,2 06 -1,4 17 4,2 01 8,3 12 8,5 02 9,5 31 7,5 27 -1,2 28 - 2,0 30 -4,4 02 -10,1 03/02 Anoia H1 Òdena -5,6 14 -8,7 13 -4,2 07 0,3 17 2,3 01 7,9 13 10,4 02 12,2 31 5,0 27 -0,7 30 -3, 3 30 -4,8 02 -8,7 13/02 袋装 WW Artés -5,9 14 -10,3 11 -4,9 06 -2,1 17 2,2 01 9,0 12 10,4 24 10,6 31 5,0 27 -2,6 29 -5 ,0 30 -5,6 02 -10,3 11/02 Bages U4 Castellnou de Bages -5,5 14 -7,5 03 -1,7 06 1,3 17 3,8 01 9,6 12 11,3 02 11,6 31 6,7 27 -0,3 29 - 2,9 30 -3,8 02 -7,5 03/02 Bages R1 el Pont de Vilomara -5,3 14 -9,6 13 -3,0 07 -0,6 17 2,9 01 9,6 13 11,3 02 12,3 31 6,0 27 -1,2 29 -3,4 30 -5,0 02 -9,6 13/02 Bages WN Montserrat - Sant Dimes -0,3 29 -7,4 12 0,4 19 1,8 17 5,3 21 9,5 12 9,5 02 11,5 31 8,6 26 2,4 29 -0 ,1 30 -1,0 06 -7,4 12/02 Bages CL Sant Salvador de Guardiola -6,3 30 -10,1 13 -4,2 07 0,3 17 1,6 01 7,8 13 9,9 24 9,9 31 4,7 27 -1,5 30 -5,0 30 -6,4 02 -10,1 13/02 Baix Camp U5 普拉德斯 - los Hortals -6,6 30 -12,9 12 -5,8 09 -2,7 17 0,7 01 6,8 09 4,9 02 7,8 31 3,8 02 -3, 1 29 -5,0 30 -6,6 01 -12,9 12/02 Baix Camp W6 Riudoms 0,0 13 -3,2 03 2,7 01 4,9 07 6,3 01 13,9 13 14,8 02 16,1 31 10,7 26 4,1 28 3,7 30 1 ,6 10 -3,2 03/02 Baix Camp U6 Vinyols i els Arcs -1,1 15 -2,1 03 1,9 15 4,7 07 6,9 01 15,6 02 15,1 01 17,3 31 11,7 26 6,4 28 4 ,6 30 2,4 10 -2,1 03/02 Baix Ebre U7 阿尔多弗 0,4 31 -2,0 03 3,7 01 4,0 07 6,6 01 13,4 09 14,8 02 17,1 31 12,2 27 4,5 30 3,7 30 1 ,0 10 -2,0 03/02 Baix Ebre DB el Perelló -0,2 15 -2,8 03 3,2 07 6,0 17 7,4 01 15,5 09 15,3 02 16,9 31 12,0 29 5,0 30 3,5 30 1,7 01 -2,8 03/02 Baix Ebre U9 l'Aldea -1,3 13 -1,2 04 3,5 01 5,2 07 7,1 01 14,3 09 15,5 01 18,2 31 11,4 27 6,0 30 5, 6 30 0,6 10 -1,3 13/01 Baix Ebre UA l'Ametlla de Mar 1,1 15 -2,2 03 4,5 23 5,0 07 6,6 01 14,9 09 15,2 01 17,1 31 11,7 27 4,8 30 4 ,1 30 2,4 12 -2,2 03/02 Baix Ebre X5 PN dels 端口 -4,5 30 -11,3 04 -4,0 07 -2,8 17 0,2 01 5,8 09 7,4 01 8,0 31 4,8 27 -2,6 29 -4,6 30 -5,8 01 -11,3 04/02 Baix Empordà DO Castell d'Aro -1,7 15 -7,4 05 -0,4 06 2,2 17 4,9 01 11,2 12 12,1 24 13,6 31 9,1 27 -0,7 29 -1,5 30 -3,0 12 -7,4 05/02 Baix Empordà DF la Bisbal d'Empordà -3,2 15 -6,8 12 -2,4 06 0,5 17 4,6 01 11,1 12 10,3 24 11,6 31 7,7 27 -1, 0 29 -2,2 30 -4,2 12 -6,8 12/02 Baix Empordà UB la Tallada d'Empordà -4,1 15 -7,1 12 -2,0 06 1,8 17 4,8 01 11,9 12 10,8 24 12,4 31 7,2 27 -0, 5 30 -2,2 30 -5,1 12 -7,1 12/02 Baix Empordà UC Monells -3,7 15 -8,0 13 -3,2 06 -1,2 17 2,7 01 10,5 13 10,5 24 8,8 31 6,2 27 -2,1 29 - 2,5 30 -4,8 12 -8,0 13/02 Baix Empordà UD Serra de Daró -3,2 15 -6,8 12 -1,7 06 0,9 17 4,6 01 11,7 12 10,1 24 11,5 31 7,3 27 0,5 30 - 1,7 30 -3,8 12 -6,8 12/02 Baix Empordà UE Torroella de Montgrí -1,8 15 -5,6 12 -1,1 02 2,5 07 5,5 01 12,6 12 11,8 24 14,3 27 8,4 27 1,0 30 - 0,5 30 -3,4 12 -5,6 12/02 Baix Llobregat UF Begues - PN del Garraf 0,1 29 -5,8 04 2,5 06 3,1 17 6,4 21 11,8 12 12,3 01 14,2 31 10,1 26 1,8 28 0 ,1 30 -0,4 02 -5,8 04/02 Baix Llobregat XL el Prat de Llobregat 0,6 30 -4,6 05 2,1 06 5,5 07 8,5 01 12,4 12 14,8 02 16,8 31 9,9 26 3,3 29 1, 9 30 0,9 09 -4,6 05/02 Baix Llobregat D3 Vallirana 0,6 29 -3,1 03 4,1 07 5,4 17 6,7 01 12,9 12 13,9 02 15,9 31 11,3 27 4,7 29 1,9 30 0 ,4 01 -3,1 03/02 Baix Llobregat UG Viladecans 1,2 30 -4,1 05 3,8 08 6,2 11 8,4 01 15,0 16 15,4 02 17,5 31 12,1 26 4,2 29 2,2 30 1 ,1 02 -4,1 05/02 Baix Penedès WZ Cunit -1,9 30 -4,7 13 3,1 10 2,2 17 7,1 01 13,0 12 13,5 02 14,4 31 11,3 26 1,8 29 1,4 30 -1,6 02 -4,7 13/02 Baix Penedès UH el Montmell -0,7 29 -4,7 03 1,9 07 3,9 17 5,4 01 11,4 12 10,0 01 13,8 31 9,8 27 1,5 29 0,4 30 0,4 02 -4,7 03/02 Baix Penedès D9 el Vendrell -1,4 30 -4,2 12 1,2 10 5,3 07 6,4 02 12,9 12 13,2 02 17,7 08 10,7 26 4,3 29 1,1 30 0,1 11 -4,2 12/02 Baix Penedès WO la Bisbal del Penedès -5,4 14 -5,9 13 -1,3 10 4,5 02 3,8 01 11,6 15 12,9 24 14,6 08 7,0 27 0,9 30 -2,1 30 -2,9 01 -5,9 13/02 Barcelonès WU Badalona - Museu 2,2 14 -0,8 04 4,9 07 6,7 17 9,9 01 16,7 12 15,9 02 17,2 31 14,2 27 5,6 29 2,9 30 2,4 02 -0,8 04/02 Barcelonès X4 巴塞罗那 - 埃尔拉瓦尔 5,5 30 0,6 04 7,9 09 9,1 17 11,6 01 17,6 12 16,6 01 19,4 30 16,3 29 7,6 29 5,6 30 4,5 02 0,6 04/02 Barcelonès D5 巴塞罗那 - Observatori Fabra 1,0 30 -4,7 03 4,5 07 4,5 17 7,7 21 12,7 12 13,4 02 15,2 31 12,4 27 3,2 28 1,9 30 0,5 02 -4,7 03/02 Barcelonès X8 巴塞罗那 - Zona Universitària 1,9 14 -1,8 04 4,8 06 6,1 17 7,6 01 14,5 12 14,6 01 16,8 31 13,3 27 5,4 29 2,3 30 2,1 02 -1,8 04/02 Barcelonès X2 巴塞罗那 - 动物园 3,1 13 -2,3 05 5,1 10 8,5 07 10,1 01 15,9 12 16,6 02 18,0 31 14,8 02 6,8 29 4,3 30 2,2 02 -2,3 05/02 贝格达 UI Gisclareny -5,1 16 -12,5 04 -4,1 05 -2,7 17 -0,6 01 5,7 13 7,4 02 6,1 31 3,2 26 -2,8 29 - 5,1 30 -5,6 12 -12,5 04/02 贝尔格达 WV 瓜迪奥拉德贝尔格达 -7,4 14 -11,7 12 -5,8 07 -2,9 14 0,6 02 5,7 12 6,3 02 6,3 31 0,9 27 -4,4 30 -5,7 30 -8,4 01 -11,7 12/02 贝尔格达 CR la Quar -3,5 29 -11,5 12 -1,8 07 -2,3 17 1,2 01 5,7 12 10,0 15 8,9 31 5,0 27 -1,9 29 - 2,7 30 -4,7 01 -11,5 12/02 贝尔格达 WM Santuari de Queralt -2,4 29 -9,1 04 -0,8 06 -0,2 11 2,9 01 6,2 12 9,2 02 9,7 31 7,2 26 -1,0 28 -1,3 30 -2,8 12 -9,1 04/02 Cerdanya Z9 Cadí Nord (2.143 m) - Prat d'Aguilo -11,5 30 -19,6 03 -10,4 06 -9,0 17 -4,5 01 1,8 12 2,9 01 0,9 31 -1,0 26 -10,5 28 -11,4 30 -9,2 02 -19,6 03/02 Cerdanya DP Das -12,9 14 -16,6 12 -9,7 10 -5,5 14 -2,2 14 0,6 12 2,3 02 3,6 27 -2,8 27 -6,9 30 -8,3 30 -13,5 12 -16,6 12/02 Cerdanya Z3 Malniu (2.230 m) -12,2 29 -20,6 03 -10,7 06 -9,6 16 -5,4 01 0,4 12 2,9 01 -0,2 31 -0,4 27 -12,1 28 -11,3 30 -9,1 02 -20,6 03/02 Conca de B. W8 布兰卡福特 -3,1 19 -8,2 11 -2,8 07 1,9 17 2,9 01 10,7 13 11,8 02 12,5 31 6,2 27 -0,3 30 -1,2 30 -3,1 11 -8,2 11/02 Conca de B. CW l'Espluga de Francolí -2,0 16 -5,9 04 -0,9 07 2,5 17 2,8 01 11,5 04 10,4 02 13,2 31 6,5 27 - 0,3 30 -1,0 30 -3,2 12 -5,9 04/02 Conca de B. UJ Santa Coloma de Queralt -3,4 14 -8,9 03 -1,1 07 -0,4 17 3,4 01 8,3 13 9,2 02 10,7 31 6,7 27 - 0,3 28 -1,6 30 -3,4 02 -8,9 03/02 Garraf UK Sant Pere de Ribes - PN del Garraf -0,3 29 -3,8 04 2,8 06 4,2 17 7,1 01 12,9 12 12,4 02 13,2 31 12,0 27 2, 6 29 0,3 30 0,2 02 -3,8 04/02 嘉理盖思 UL Castelldans -4,9 26 -7,0 06 -1,9 10 1,7 07 3,2 01 11,5 15 12,8 03 13,6 31 5,8 27 -0,5 30 -1, 5 30 -5,1 12 -7,0 06/02 嘉理盖思 UM la Granadella -3,4 11 -7,6 03 -2,5 10 0,6 17 2,7 01 10,9 13 10,8 02 11,5 31 6,2 02 1,1 29 -0, 9 30 -3,4 12 -7,6 03/02 Garrotxa W9 拉瓦尔登巴斯 -6,3 14 -10,9 13 -5,8 07 -2,2 17 1,7 01 8,8 12 6,7 24 8,5 31 4,3 27 -4 ,3 29 -5,0 30 -6,6 09 -10,9 13/02 Garrotxa DC Olot -4,9 15 -9,9 12 -3,6 07 -1,8 17 2,6 01 9,0 12 9,9 24 9,6 31 5,5 27 -3,3 29 -3 ,9 30 -5,9 12 -9,9 12/02 Gironès UN Cassà de la Selva -4,2 15 -10,7 05 -3,0 06 0,5 17 1,9 01 8,8 12 11,0 24 10,5 31 6,7 27 -3,2 29 -4,4 30 -5,3 12 -10,7 05/02 Gironès UO Fornells de la Selva -5,8 15 -10,4 13 -4,9 07 -1,5 17 2,2 01 9,3 12 9,2 24 10,3 31 6,1 27 -3,5 29 -4,3 30 -6,3 12 -10,4 13/02 Gironès XJ 赫罗纳 -5,1 15 -9,6 13 -4,0 07 -1,6 17 3,1 01 10,2 12 9,7 24 10,4 31 5,7 27 -3,1 29 -3 ,8 30 -5,7 12 -9,6 13/02 Gironès WF Vilablareix -5,2 15 -9,9 13 -4,3 07 -1,7 17 3,0 02 9,0 12 9,7 24 11,7 31 5,7 27 -2,8 29 -2 ,8 30 -4,6 12 -9,9 13/02 Maresme UP Cabrils 1,6 30 -2,6 11 3,2 07 6,7 17 8,5 01 13,9 12 15,1 02 15,9 31 13,3 26 3,7 28 3,0 30 2, 6 12 -2,6 11/02

如果你知道如何正确操作文本编辑器,修复这个文本输出是非常容易和快速的,所以它会顺利被 Excel 导入...

【讨论】:

以上是关于抓取跨越多个页面的大型 pdf 表的主要内容,如果未能解决你的问题,请参考以下文章

使用 BFO、Freemarker 和 HTML 在多个页面上重复表格列标题 - NetSuite

以特定顺序将多个页面导出为 PDF

如何在 Chrome 和 Firefox PDF 查看器上添加手工具(抓取页面并拖动)功能?

当 TABLE 跨越多个页面时,如何使第一页上的 TFOOT 显示“继续...”?

使用VBA宏遍历javascrape网页上的每个表

如何将 QTextBrowser 的多个实例打印到一个 PDF 文件中?