如何将PDF文件中的行提取到csv文件中?

如何将PDF文件中的行提取到csv文件中?,pdf,Pdf,我想从这里得到美国所有大学的列表,并将其放入CSV文件中。然后,我将CSV文件导入SQL server(以便可以轻松运行查询) 我尝试了几个在线pdf-to-csv转换器和基于Java的pdf-to-csv教程。什么都没用。我今天为此花了6-8个小时,但失败了。导入csv时,我的csv文件乱七八糟,数据库中有很多空值。 我甚至试着搜索一个DHS api,它可以给我这个信息,但没有找到 有没有人能帮我把这些学院提取出来,就像pdf文件中显示的那样 PS:你可以看到所有的大学也在使用它。但是,您必须

我想从这里得到美国所有大学的列表,并将其放入CSV文件中。然后,我将CSV文件导入SQL server(以便可以轻松运行查询)

我尝试了几个在线pdf-to-csv转换器和基于Java的pdf-to-csv教程。什么都没用。我今天为此花了6-8个小时,但失败了。导入csv时,我的csv文件乱七八糟,数据库中有很多空值。 我甚至试着搜索一个DHS api,它可以给我这个信息,但没有找到

有没有人能帮我把这些学院提取出来,就像pdf文件中显示的那样


PS:你可以看到所有的大学也在使用它。但是,您必须手动滚动以提取所有结果。这将花费太长时间,并且数据将不会以pdf文件中给出的格式提供。

我曾经有一个Ruby项目做过这种工作。 我使用了gem pdf/reader,它甚至可以正常工作,但我建议不要使用这种方法,pdf的内容没有字段开始和停止的标记,而是必须测量每段文本的位置(每个字段有许多段),这里是第一个字段的示例

"I
NUL ETX
Am"
NUL ETX
School
NUL ETX
Inc.
并将其与您必须通过实验找到的边界进行比较,如“如果位置距离左边缘>2.54cm,距离左边缘<5.78cm”等。。 它冗长乏味,容易出错

最简单的解决方案是通过手动滚动、选择并将内容复制到编辑器中,然后删除额外的头和尾,或者使用一些类似于mechanize的web scraping gem,然后将此文本转换为CSV,以某种方式读取整个文本内容。最后一部分很简单,因为结构是固定的

"I Am" School
118 Siskiyou Avenue
Mount Shasta , CA , 96067
5309266263  <--end of first record
424 Aviation
13230 SW 132 Ave.
Miami , FL , 33186
7862424848  <--end of second record
“我是”学校
西斯基尤大街118号
加利福尼亚州沙斯塔山,96067

5309266263正如在对该问题的评论中所说

考虑到相当直接的页面内容流样式,应该可以使用不太复杂的自定义文本提取器提取数据

详细内容:

页面内容流样式 常规表格条目内容是逐条绘制的,每个条目按读取顺序逐字段绘制。因此,在浏览内容流时,我们不必尝试重新安排内容以建立顺序。这使得这项任务相当容易

因此,主要工作是忽略非条目,即第一页上的标题、指示新第一个字母开始位置的栏以及页码

我们是通过

  • 忽略图形和非黑色文本,它们负责标题和首字母栏
  • 不接受不以“学校名称”列中的数据开头的条目,该列负责只存在于“校园名称”列中的页码
(其他方法也可以这样做,例如忽略底部页面区域中的所有内容来处理页码。)

现在我们只需将条目拆分为字段

文档结构也有帮助,因为它是一个非常统一的文档,表列在每页上的位置和维度都相同。所以我们只需要在固定的x值下进行解析

只有一个障碍:在某些条目中,原子文本块包含不同列的内容。例如,有时FM列的内容被绘制为类似“YN”的单个字符串,并且通过字符间距引入光学距离

所以我们必须一个字符一个字符地处理文本块,而不是作为一个整体

一个示例实现 我在这里使用Java和PDF库iText(当前版本5.5.7开发快照)。这并不意味着不能使用不同的设置,这只是我最习惯的设置

作为分隔符,我使用制表符,因为其他可能的候选字符也作为文本的一部分出现,我不想处理转义

这是为处理上述内容而引入的自定义
RenderListener
类:

public class CertifiedSchoolListExtractionStrategy implements RenderListener
{
    public CertifiedSchoolListExtractionStrategy(Appendable data, Appendable nonData)
    {
        this.data = data;
        this.nonData = nonData;
    }

    //
    // RenderListener implementation
    //
    @Override
    public void beginTextBlock() { }

    @Override
    public void endTextBlock() { }

    @Override
    public void renderImage(ImageRenderInfo renderInfo) { }

    @Override
    public void renderText(TextRenderInfo renderInfo)
    {
        try
        {
            Vector startPoint = renderInfo.getBaseline().getStartPoint();
            BaseColor fillColor = renderInfo.getFillColor();
            if (fillColor instanceof GrayColor && ((GrayColor)fillColor).getGray() == 0)
            {
                if (debug)
                    data.append(String.format("%4d\t%3.3f %3.3f\t%s\n", chunk, startPoint.get(I1), startPoint.get(I2), renderInfo.getText()));
                for (TextRenderInfo info : renderInfo.getCharacterRenderInfos())
                {
                    renderCharacter(info);
                }
            }
            else
            {
                if (debug)
                    nonData.append(String.format("%4d\t%3.3f %3.3f\t%s\n", chunk, startPoint.get(I1), startPoint.get(I2), renderInfo.getText()));
                if (currentField > -1)
                    finishEntry();
                entryBuilder.append(renderInfo.getText());
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            chunk++;
        }
    }

    public void renderCharacter(TextRenderInfo renderInfo) throws IOException
    {
        Vector startPoint = renderInfo.getBaseline().getStartPoint();

        float x = startPoint.get(I1);

        if (currentField > -1)
        {
            if (isInCurrentField(x))
            {
                entryBuilder.append(renderInfo.getText());
                return;
            }
            if (isInNextField(x))
            {
                currentField++;
                entryBuilder.append('\t').append(renderInfo.getText());
                return;
            }
            finishEntry();
        }
        if (isInNextField(x))
        {
            finishEntry();
            currentField = 0;
        }
        entryBuilder.append(renderInfo.getText());
    }

    public void close() throws IOException
    {
        finishEntry();
    }

    boolean isInCurrentField(float x)
    {
        if (currentField == -1)
            return false;

        if (x < fieldstarts[currentField])
            return false;

        if (currentField == fieldstarts.length - 1)
            return true;

        return x <= fieldstarts[currentField + 1];
    }

    boolean isInNextField(float x)
    {
        if (currentField == fieldstarts.length - 1)
            return false;

        if (x < fieldstarts[currentField + 1])
            return false;

        if (currentField == fieldstarts.length - 2)
            return true;

        return x <= fieldstarts[currentField + 2];
    }

    void finishEntry() throws IOException
    {
        if (entryBuilder.length() > 0)
        {
            if (currentField == fieldstarts.length - 1)
            {
                data.append(entryBuilder).append('\n');
            }
            else
            {
                nonData.append(entryBuilder).append('\n');
            }

            entryBuilder.setLength(0);
        }
        currentField = -1;
    }

    //
    // hidden members
    //
    final Appendable data, nonData;
    boolean debug = false;

    int chunk = 0;
    int currentField = -1;
    StringBuilder entryBuilder = new StringBuilder();

    final int[] fieldstarts = {20, 254, 404, 415, 431, 508, 534};
}
选择大小为1的字体TT2

9.72 0 0 9.72 20.16 687.36 Tm
设置文本矩阵以将文本插入坐标移动到20.16687.36,然后按9.72的因子缩放所有内容

0 g
选择灰度填充颜色黑色

0 Tc
0 Tw
选择附加字符和字间距为0

(SCHOOL)Tj
在这里画“学校”

选择字体TT1

3.4082 0 TD
将文本插入点沿x方向移动3.4082

<0003>Tj
选择字体,移动文本插入点,并绘制字符串“NAME”,然后间隔17887.4个文本单位,然后绘制“CAMPUS”

向左移动56.782,向下移动1.3086,即到达第一条入口线的起点

("I)Tj
/TT3 1 Tf
.6528 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(Am")Tj
/TT3 1 Tf
1.7783 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(School)Tj
/TT3 1 Tf
2.6919 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
[(Inc.)-16894.2("I)]TJ
/TT3 1 Tf
18.9997 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(Am")Tj
/TT3 1 Tf
1.7783 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(School)Tj
/TT3 1 Tf
2.6919 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
[(Inc.)-8239.9(Y)-1018.9(N)-576.7(Mount)]TJ
/TT3 1 Tf
15.189 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
[(Shasta)-2423.3(CA)-2443.7(41789)]TJ
再次上移到条目的主线

[(Y)-1018.9(N)-576.7(Westlake)]TJ 
因此,我们可以按原样消化文本,无需排序(内容可以以完全不同的方式排序)

但我们也看到,没有明显的列起点和终点。因此,要将文本与列关联,我们必须计算每个字符的位置,并将它们与外部给定的列起始位置进行比较

库支持的解析 PDF库通常提供一些机制来帮助解析此类内容流

这有两种基本架构,一个库可以解析内容流

  • 作为一个整体,并将其作为定位文本块或
  • 或者使用侦听器模式分段转发单个定位的文本块
前一个变体一开始似乎更容易处理,但可能需要大量资源(我遇到过多MB的内容流),而第二个变体似乎更难处理,但内存需求更小

我使用的库(iText)遵循后一种方法,但您的问题也可以使用前一种方法之后的库来解决

RenderListener
是一个侦听器
<0003>Tj
/TT2 1 Tf
.2261 0 TD
[(NAME)-17887.4(CAMPUS)]TJ
/TT1 1 Tf
24.1809 0 TD
<0003>Tj
/TT2 1 Tf
.2261 0 TD
[(NAME)-8986.6(F)-923.7(M)-459.3(CITY)-6349.9(ST)-1390.2(CAMPUS)]TJ
/TT1 1 Tf
28.5147 0 TD
<0003>Tj
/TT2 1 Tf
.2261 0 TD
(ID)Tj
/TT4 1 Tf
-56.782 -1.3086 TD
("I)Tj
/TT3 1 Tf
.6528 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(Am")Tj
/TT3 1 Tf
1.7783 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(School)Tj
/TT3 1 Tf
2.6919 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
[(Inc.)-16894.2("I)]TJ
/TT3 1 Tf
18.9997 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(Am")Tj
/TT3 1 Tf
1.7783 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(School)Tj
/TT3 1 Tf
2.6919 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
[(Inc.)-8239.9(Y)-1018.9(N)-576.7(Mount)]TJ
/TT3 1 Tf
15.189 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
[(Shasta)-2423.3(CA)-2443.7(41789)]TJ
[(Inc.)-7228.7(A)]TJ
/TT3 1 Tf
9.26 0 TD 
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(F)Tj
/TT3 1 Tf
.4595 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(International)Tj
/TT3 1 Tf
5.2886 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(of)Tj
/TT3 1 Tf
.8325 0 TD
<0003>Tj
/TT4 1 Tf
.2261 0 TD
(Westlake)Tj
/TT3 1 Tf
3.7739 0 TD
<0003>Tj
/TT4 1 Tf
-11.8374 -1.3086 TD
(Village)Tj
15.4938 1.3086 TD
[(Y)-1018.9(N)-576.7(Westlake)]TJ 
$ pdftotext -layout <input.pdf> <output.txt>
$ head -30 test.txt
                                                                                                          Updated
                                     SEVP Certified Schools                                      September 16, 2015
SCHOOL NAME                                     CAMPUS NAME                            F M CITY                     ST   CAMPUS ID
"I Am" School Inc.                              "I Am" School Inc.                     Y N Mount Shasta             CA     41789
424 Aviation                                    424 Aviation                           N Y Miami                    FL     103705
                                                            ‐ A ‐
A F International School of Languages Inc.      A F International College              Y   N Los Angeles            CA      9538
A F International School of Languages Inc.      A F International of Westlake          Y   N Westlake Village       CA     57589
                                                Village
A. T. Still University of Health Sciences       Kirksville Coll of Osteopathic         Y   N Kirksville         MO         3606
                                                Medicine
Aaron School                                    Aaron School ‐ 30th Street             Y   N   New York             NY    159091
Aaron School                                    Aaron School                           Y   N   New York             NY    114558
ABC Beauty Academy, INC.                        ABC Beauty Academy, INC.               N   Y   Flushing             NY    95879
ABC Beauty Academy, LLC                         ABC Beauty Academy                     N   Y   Garland              TX    50677
Abcott Institute                                Abcott Institute                       N   Y   Southfield           MI    197890
Aberdeen Catholic School System                 Roncalli Primary                       Y   N   Aberdeen             SD    180510
Aberdeen Catholic School System                 Roncalli                               Y   N   Aberdeen             SD    21405
Aberdeen Catholic School System                 Roncalli Elementary                    Y   N   Aberdeen             SD    180511
Aberdeen School District 6‐1                    Aberdeen Central High School           Y   N   Aberdeen             SD    36568
Abiding Savior Lutheran School                  Abiding Savior Lutheran School         Y   N   Lake Forest          CA     9920
Abilene Christian Schools                       Abilene Christian Schools              Y   N   Abilene              TX     8973
Abilene Christian University                    Abilene Christian University           Y   N   Abilene              TX     7498
Abington Friends School                         Abington Friends School                Y   N   Jenkintown           PA    20191
Above It All, Inc                               Benchmark Flight /Hawaii Flight        N   Y   Kailua‐Kona          HI    24353
                                                Academy
Abraham Baldwin Agricultural College            Tifton Campus                          Y   N Tifton             GA         6931
Abraham Joshua Heschel School                   Abraham Joshua Heschel School          Y   N New York           NY        106824

ABT Jacqueline Kennedy Onassis School           ABT Jacqueline Kennedy Onassis         Y   Y New York               NY     52401
userVariables = school, campus;
{ start = IsNumeric(Substring(Line(0),112,115)); 
  school = ""; campus = "";
  { start = 1;
    maxCount = 2;
    school = Concat(school, " ", Trim(Substring(Line(0),1,52)));
    campus = Concat(campus, " ", Trim(Substring(Line(0),53,82)));
  }
  output = Concat(Trim(school), "|", Trim(campus));
}