【问题标题】:Parse a plain text file into a CSV file using Python使用 Python 将纯文本文件解析为 CSV 文件
【发布时间】:2013-04-27 04:54:35
【问题描述】:

我有一系列使用 Beautiful Soup 解析成单个文本文件的 HTML 文件。 HTML 文件被格式化,使得它们的输出始终是文本文件中的三行,所以输出看起来像:

Hello!
How are you?
Well, Bye!

但它也可以很容易

83957
And I ain't coming back!
hgu39hgd

换句话说,每个 HTML 文件的内容并不是真正标准的,但它们总是产生三行。

所以,我想知道我应该从哪里开始,如果我想获取从 Beautiful Soup 生成的文本文件并将其解析为一个 CSV 文件,其中包含如下列(使用上面的示例):

Title   Intro   Tagline
Hello!    How are you?    Well, Bye!
83957    And I ain't coming back!    hgu39hgd

从文本文件中剥离 HTML 的 Python 代码如下:

import os
import glob
import codecs
import csv
from bs4 import BeautifulSoup

path = "c:\\users\\me\\downloads\\"

for infile in glob.glob(os.path.join(path, "*.html")):
    markup = (infile)
    soup = BeautifulSoup(codecs.open(markup, "r", "utf-8").read())
    with open("extracted.txt", "a") as myfile:
        myfile.write(soup.get_text())

我认为我可以使用它来设置 CSV 文件中的列:

csv.put_HasColumnNames(True)

csv.SetColumnName(0,"title")
csv.SetColumnName(1,"intro")
csv.SetColumnName(2,"tagline")

我在空白处绘制的是如何一次遍历文本文件 (extracted.txt) 一行,当我进入新行时,将其设置为 CSV 文件中的正确单元格。文件的前几行是空白的,每组文本之间有很多空白行。所以,首先我需要打开文件并阅读它:

file = open("extracted.txt")

for line in file.xreadlines():
    pass # csv.SetCell(0,0 X) (obviously, I don't know what to put in X)

另外,我不知道如何告诉 Python 继续读取文件,并添加到 CSV 文件,直到完成。换句话说,没有办法确切知道 HTML 文件中总共有多少行,所以我不能只知道csv.SetCell(0,0) to cdv.SetCell(999,999)

【问题讨论】:

  • 没有人再使用.xreadlinesfor line in file
  • 我不确定我是否理解您要执行的操作。您是否尝试读取 extracted.txt 文件,忽略空行,并将每组三行放入 CSV 文件中的一行中?
  • 啊,差不多了。我正在尝试阅读三行中的第一行并将其设置为“标题”和三行中的第二行并将其设置为“介绍”和三行中的第三行并将其设置为“标语”然后跳过空白直到我读到接下来的三行,然后再做一次。
  • 另外,第一个“标题”和文件顶部之间有空格。
  • 我想我需要使用 fileIN = open(sys.argv[1], "r") 和 line = fileIN.readline()。但我不知道如何跳过空格,或者一旦我得到它如何处理文本?

标签: python csv


【解决方案1】:

我不完全确定您使用的是什么 CSV 库,但它看起来不像 Python's built-in one。无论如何,这就是我的做法:

import csv
import itertools

with open('extracted.txt', 'r') as in_file:
    stripped = (line.strip() for line in in_file)
    lines = (line for line in stripped if line)
    grouped = itertools.izip(*[lines] * 3)
    with open('extracted.csv', 'w') as out_file:
        writer = csv.writer(out_file)
        writer.writerow(('title', 'intro', 'tagline'))
        writer.writerows(grouped)

这种方式构成了一个管道。它首先从文件中获取数据,然后从行中删除所有空格,然后删除所有空行,然后将它们分组为三个一组,然后(在写入 CSV 标头后)将这些组写入 CSV 文件。

要合并您在 cmets 中提到的最后两列,您可以将writerow 调用以明显的方式将writerows 更改为:

writer.writerows((title, intro + tagline) for title, intro, tagline in grouped)

【讨论】:

  • 在我看来,我认为生成器更清晰(就像你在编辑之前所做的那样)。
  • @OscarMederos:它有一个错误:它没有在分组之前去掉换行符。不过,我想我可以再次用生成器推导重写它。
  • @icktoofay 我从来没有听说过 itertools,谢谢你指点我。当我运行它时,我收到错误: File "csvify.py", line5, in lines = itertools.ifilter(bool, itertools.imap(str.strip, in_file)) AttributeError: 'module' object has to属性“ifilter”
  • @ZacBrown:这有点奇怪。 itertools.ifilter 没有 "New in version X" 的东西,所以这会让我相信它在 2.3 版本中引入 itertools 时就存在,但显然它成功导入了,所以我没有真的知道那里发生了什么。无论如何,您可能想尝试我的编辑版本,它使用生成器理解来代替该部分,​​尽管它仍然使用itertools.izip
  • 我今天在使用 Python 时遇到了一些其他问题。我在 Mac 上的 VM 中运行的 Windows 7 上的版本为 3.3.1。我将在 OSX 中运行的 Python 版本进行尝试,看看它是如何工作的。
【解决方案2】:

也许我没有正确理解你,但你可以这样做:

file = open("extracted.txt")

# if you don't want to do .strip() again, just create a list of the stripped 
# lines first.
lines = [line.strip() for line in file if line.strip()]

for i, line in enumerate(lines):
    csv.SetCell(i % 3, line)

【讨论】:

  • 这非常接近,但@icktoofay 明白了。不过,感谢您的帮助!
  • @ZacBrown 非常接近是什么意思?你试过了吗?我只是试图保持它与您尝试的相似(使用csv.SetCell 等)。顺便说一句,我赞成他的回答;)