如何提取pdf中的数据
导读:本文的目标是介绍一些Python库,帮助你从类似于PDF和Word DOCX 这样的二进制文件中提取数据。我们也将了解和学习如何从网络信息源(web feeds)(如RSS)中获取数据,以及利用一个库帮助解析HTML文本并从文档中提取原始文本。
我们还将学习如何从不同来源提取原始文本,对其进行规范化,并基于它创建一个用户定义的语料库。
在本文中,你将学习7个不同的实例。我们将学习从PDF文件、Word文档和Web中获取数据。PDF和Word文档是二进制文件,通过Web,你将获得HTML格式的数据,因此,我们也会对数据执行规范化和原始文本转换任务。
作者:克里希纳·巴夫萨、纳雷什·库马尔、普拉塔普·丹蒂
如需转载请联系大数据(ID:hzdashuju)
01 字符串操作的重要性
作为一名NLP专家,你将要处理大量的文本内容。当你在处理文本时,你必须知道一些字符串操作。我们将从几个简短的范例入手,帮助你理解str类及其在Python中的相关操作。
1. 准备工作
这里,你仅仅需要Python解释器和一个文本编辑器。我们将使用join(连接)、split(分割)、addition(加法)和multiplication(乘法)运算符以及索引。
2. 如何实现
(1)创建一个新的Python文件,命名为StringOps1.py。
(2)定义以下两个对象:
namesList = ['Tuffy','Ali','Nysha','Tim' ] sentence = 'My dog sleeps on sofa'
第一个对象nameList是一个包含若干名字的字符串列表,第二个对象sentence是一个包含一句话的字符串对象。
(3)首先,我们看看join函数的特点以及它的功能:
names = ';'.join(namesList) print(type(names), ':', names)
join()函数可以被任意一个string对象调用,它的输入参数是一个str对象的列表。通过将调用字符串的内容作为连接分隔符,它将所有str对象连接成一个str对象,并返回连接后的对象。运行这两行代码后,你得到的输出如下:
: Tuffy;Ali;Nysha;Tim
(4)接下来,我们来看split方法的功能:
wordList = sentence.split(' ') print((type(wordList)), ':', wordList)
当split函数调用一个字符串时,它会将其内容分割为多个str对象,创建一个包含这些字符串对象的列表,并返回该列表。该函数接受单个str对象作为参数,表示分隔符。运行代码,得到如下输出:
: ['My', 'dog', 'sleeps', 'on', 'sofa']
(5)算术运算符 和*也可以用于字符串。添加以下代码并输出:
additionExample = 'ganehsa' 'ganesha' 'ganesha' multiplicationExample = 'ganesha' * 2 print('Text Additions :', additionExample) print('Text Multiplication :', multiplicationExample)
我们首先看一下输出结果,随后讨论其工作原理:
Text Additions: ganehsaganeshaganesha Text Multiplication: ganeshaganesha
运算符被称为连接符,它将字符串连接为单个str对象,产生一个新的字符串。如前所述,我们也可以使用*运算符对字符串做乘法。此外,需要注意的是这些操作不会添加任何额外的内容,例如在字符串之间插入空格。
(6)接下来,我们来了解一下字符串中的字符索引。添加下列几行代码:
str = 'Python NLTK' print(str[1]) print(str[-3])
首先,我们声明一个新的 string 对象。然后可以直接访问字符串中的第二个字符(y)。这里还有个小技巧:Python允许你在访问任何列表对象时使用负索引,比如说-1意味着最后一个成员,-2是倒数第二个成员,依此类推。例如,在前面代码的str对象中,索引7和-4是相同的字符N:
Output:: Tuffy;Ali;Nysha;Tim: ['My', 'dog', 'sleeps', 'on', 'sofa'] Text Additions : ganehsaganeshaganesha Text Multiplication : ganeshaganesha y L
3. 工作原理
我们使用split()函数将一个字符串变成了一个字符串列表,并使用join()函数将一个字符串列表变成了一个字符串。接下来我们了解了有关字符串的一些算术运算符的用法。
需要注意的是,我们不能在字符串中使用“-”(负号)和“/”(除法)运算符。最后,我们了解了如何在任一字符串中访问单个字符,特别值得一提的是,我们可以在访问字符串时使用负索引。
本段实例非常简单和直观,主要是介绍Python允许的一些常见和不常见的字符串操作。接下来,我们将在以上操作基础上继续学习一些字符串操作。
02 深入实践字符串操作
接下来,我们将了解子字符串、字符串替换以及如何访问一个字符串的所有字符。
1. 如何实现
(1)创建一个新的Python文件,命名为StringOps2.py并定义以下string对象:
str = 'NLTK Dolly Python'
(2)访问str对象中以第四个字符作为结束的子串。
print('Substring ends at:',str[:4])
我们知道索引从零开始,因此将返回由第0个到第3个字符组成的子串。运行代码,输出如下:
Substring ends at: NLTK
(3)访问str对象中从某个点开始直到末尾的子串:
print('Substring starts from:',str[11:] )
以上代码指示解释器返回str对象中从索引11到结束的一个子串。运行代码,得到以下输出:
Substring starts from: Python
(4)从str对象中访问包含Dolly的子串。添加以下行:
print('Substring :',str[5:10])
以上代码返回从索引5到10的字符,不包括第10个字符。输出是:
Substring : Dolly
(5)我们在前一段中已经了解了负索引在字符串操作中的应用。现在我们试试看它在获取子串中的作用:
print('Substring fancy:', str[-12:-7]) Run and check the output, it will be – Substring fancy: Dolly
这里得到的输出与上一步完全相同!为了理解这个结果,我们做一些计算:-1表示最后一个字符,-2是倒数第二个字符,依此类推。你将会发现[5:10]和[-12:-7]在这个例子中得出的子串是相同的。
(6)了解in操作符在if语句中的用法:
if 'NLTK' in str: print('found NLTK')
运行以上代码,程序的输出如下所示:
found NLTK
如上所示,in操作符会检查左边的字符串是否属于右边字符串的子串。
(7)了解str对象中replace函数的使用:
replaced = str.replace('Dolly', 'Dorothy') print('Replaced String:', replaced)
replace函数只需要两个参数。第一个是需要被替换的子字符串,第二个是用来替换前面子字符串的新子字符串。replace函数返回一个新的string对象,并且它不会修改调用它的字符串,运行代码,有如下输出:
Replaced String: NLTK Dorothy Python
(8)最后,迭代上面得到的replaced对象并访问它的每一个字符:
print('Accessing each character:') for s in replaced: print(s)
以上操作每次在新的一行输出replaced对象的每个字符,最终输出如下:
Output: Substring ends at: NLTK Substring starts from: Python Substring : Dolly Substring fancy: Dolly found NLTK Replaced String: NLTK Dorothy Python Accessing each character: N L T K D o r o t h y P y t h o n
2. 工作原理
字符串对象只是一个字符列表。正如第一步所示,我们可以像访问一个列表那样用for语句来访问字符串中的每个字符。任何列表的方括号内的字符“:”表示我们想要的一个子列表。
方括号内,如果字符“:”之后是一个数字n,表示我们希望获得一个从列表索引0开始到索引n-1结束的子列表。同样地,一个数字m后跟着字符“:”,则表示我们想要一个从列表索引m开始到列表末尾的子列表。
03 在Python中读取PDF文件
这个实例是从Python中访问PDF文件。首先,你需要安装PyPDF2库。
1. 准备工作
假设你已经安装了pip。然后,在Python2或Python3版本上用pip安装PyPDF2库,你只需要在命令行中运行以下命令:
pip install pypdf2
如果你成功安装了PyPDF2库,就完成了准备工作。与此同时,你需要通过以下链接下载一些我们将在本段用到的测试文档:
https://www.dropbox.com/sh/bk18dizhsu1p534/AABEuJw4TArUbzJf4Aa8gp5Wa?dl=0
2. 如何实现
(1)创建一个新的Python文件,命名为pdf.py并添加以下代码:
from PyPDF2 import PdfFileReader
这行代码会导入PyPDF2库中的PdfFileReader类。
(2)在上面创建的文件中添加如下Python函数,它的功能是读取一个PDF文件并返回其全文:
def getTextPDF(pdfFileName, password = '')
该函数需要两个参数,一个是你要读取的PDF文件路径,一个是这个PDF文件的密码(如果有的话)。可见,password 参数是可选的。
(3)现在我们来定义这个函数。在该函数下添加如下代码:
pdf_file = open(pdfFileName, 'rb') read_pdf = PdfFileReader(pdf_file)
第一行代码会以读取和反向查找模式打开文件。第一行本质是一个Python文件打开命令/函数,仅能打开非文本的二进制文件。第二行将打开的文件传递给PdfFileReader类,用于处理PDF文档。
(4)如果文件设置了密码保护,接下来是解密被密码保护的PDF文件:
if password != '': read_pdf.decrypt(password)
如果在函数调用时设置了密码,那么我们在解密这个文件时也同样需要密码。
(5)从PDF文件中读取文本:
text = [] for i in range(0,read_pdf.getNumPages()-1): text.append(read_pdf.getPage(i).extractText())
创建一个字符串列表,并将每一页的文本都添加到这个列表中。
(6)返回最终的输出结果:
return '\n'.join(text)
将列表中所有的字符串都连接起来,并且在每个字符串之间都加一个换行符,返回连接后的单一字符串。
(7)在pdf.py目录下创建另一个名为TestPDFs.py 的文件,添加以下导入语句:
import pdf
(8)现在我们打印输出两个文档中的文本,其中一个是受密码保护的,一个是未加密的:
pdfFile = 'sample-one-line.pdf' pdfFileEncrypted = 'sample-one-line.protected.pdf' print('PDF 1: \n',pdf.getTextPDF(pdfFile)) print('PDF 2: \n',pdf.getTextPDF(pdfFileEncrypted,'tuffy'))
输出:本实例的前六步只是创建了一个Python函数,并不向控制台输出内容,第七和第八步会输出以下内容:
This is a sample PDF document I am using to demonstrate in the tutorial. This is a sample PDF document password protected.
3. 工作原理
PyPDF2是用于提取PDF文件内容的一个纯Python库。该库有很多功能,可用于裁剪页面、叠加图像数字签名、创建新的PDF文件等。但是,对NLP工程师需要实现的文本分析任务来说,该库只用来读取内容。
在第二步中,以反向查找模式打开文件很重要,因为当加载文件内容时,PyPDF2模块试图从尾部开始读取文件内容。此外,如果PDF文件是受密码保护的,而你没有在访问文件前解密文件,Python解释器将抛出一个PdfReadError错误。
04 在Python中读取Word文件
这里,我们将学习如何加载和读取Word/DOCX文档。用于读取Word/DOCX文件的相关库会更加全面,在这些库中我们还可以处理段落边界、文本样式以及对所谓的run对象的操作。我们将会了解以上提到的所有内容,因为这些内容在文本分析任务中是至关重要的。
Tip: 如果你没有安装Microsoft Word软件,你可以使用Liber Office和Open Office软件的开源版本来创建和编辑“.docx”文件。
1. 准备工作
假设你已经在你的机器上安装了pip,我们将使用pip来安装python-docx库。不要将它与另一个名为docx的库混淆,这是两个完全不同的库。我们将从python docx库中导入docx对象。在命令行中执行下面的命令将安装这个库:
pip install python-docx
成功安装了该库后,继续下一步,我们将在这个实例中使用一个测试文档,如果你已经通过本文第一段提供的链接下载了所有文档,你应该已具备相关文档。如果没有,请从以下链接下载sample-one-line.docx文档。
https://www.dropbox.com/sh/bk18dizhsu1p534/AABEuJw4TArUbzJf4Aa8gp5Wa?dl=0
现在,准备工作就全部完成了。
2. 如何实现
(1)创建一个新的Python文件,命名为word.py并添加以下导入代码:
import docx
这里只需导入python-docx模块的docx对象。
(2)定义getTextWord函数:
def getTextWord(wordFileName):
该函数需要一个字符串参数wordFileName,包含你要读取的Word文件的绝对路径。
(3)初始化doc 对象:
doc = docx.Document(wordFileName)
此时doc对象加载了你要读取的Word文件。
(4)接下来我们要从已经加载文档的doc对象中读取文本,添加以下代码来实现:
fullText = [] for para in doc.paragraphs: fullText.append(para.text)
首先初始化一个字符串列表fullText,然后采用for循环逐段从文档中读取文本,并把每段都放到fullText列表中去。
(5)然后,我们将所有的片段/段落连接为一个字符串对象,并将其作为函数的输出结果返回:
return '\n'.join(fullText)
通过以上操作,我们将fullText数组的所有元素用“\ n”分隔符连接起来,并返回连接后的对象。最后保存该Python文件并退出。
(6)创建另一个Python文件,命名为TestDocX.py,并添加以下导入声明:
import docx import word
这里只需导入docx库以及我们在前五步中实现的word.py文件。
(7)现在我们将要读取一个DOCX文件并使用我们在word.py中实现的API打印输出它的全部内容。添加以下两行代码:
docName = 'sample-one-line.docx' print('Document in full :\n',word.getTextWord(docName))
首先在第一行代码中初始化文档的路径,然后使用API打印输出文档的全部内容。当你运行这部分代码时,得到以下输出:
Document in full :
这是一个带有一些粗体文本、一些斜体文本和一些下划线文本的PDF示例文档。我们还嵌入了一个标题,如下所示:
This is my TITLE. This is my third paragraph.
(8)正如前面提到的,Word / DOCX文档是一个更加丰富的信息来源,除了提供文本内容外,还能提供很多信息。现在我们来看有关段落的信息。添加以下四行代码:
doc = docx.Document(docName) print('Number of paragraphs :',len(doc.paragraphs)) print('Paragraph 2:',doc.paragraphs[1].text) print('Paragraph 2 style:',doc.paragraphs[1].style)
以上代码的第二行打印出了给定文档中段落的数量。第三行打印出了文档中第二段的内容。而第四行将会打印出第二段的样式,比如在这个例子中的样式就是Title类型。当你运行以上代码后,输出将如下所示:
Number of paragraphs : 3 Paragraph 2: This is my TITLE. Paragraph 2 style: _ParagraphStyle('Title') id: 4374023248
(9)接下来,我们将了解什么是run对象。添加以下代码:
print('Paragraph 1:',doc.paragraphs[0].text) print('Number of runs in paragraph 1:',len(doc.paragraphs[0].runs)) for idx, run in enumerate(doc.paragraphs[0].runs): print('Run %s : %s' %(idx,run.text))
首先,我们获得文档第一段的全部内容。然后,我们获得第一段中run对象的数目。最后,我们把每个run对象打印输出。
(10)为了明确每个run对象的格式,添加以下代码:
print('is Run 0 underlined:',doc.paragraphs[0].runs[5].underline) print('is Run 2 bold:',doc.paragraphs[0].runs[1].bold) print('is Run 7 italic:',doc.paragraphs[0].runs[3].italic)
这段代码的各行分别在检查相应run对象的下划线样式、粗体样式以及斜体样式。最终输出如下:
Output: Document in full : This is a sample PDF document with some text in BOLD, some in ITALIC and some underlined. We are also embedding a Title down below. This is my TITLE. This is my third paragraph. Number of paragraphs : 3 Paragraph 2: This is my TITLE. Paragraph 2 style: _ParagraphStyle('Title') id: 4374023248 Paragraph 1: This is a sample PDF document with some text in BOLD, some in ITALIC and some underlined. We're also embedding a Title down below. Number of runs in paragraph 1: 8 Run 0 : This is a sample PDF document with Run 1 : some text in BOLD Run 2 : , Run 3 : some in ITALIC Run 4 : and Run 5 : some underlined. Run 6 : We are also embedding a Title down below Run 7 : . is Run 0 underlined: True is Run 2 bold: True is Run 7 italic: True
3. 工作原理
首先,我们在word.py文件中写了一个函数,它将读取给定的DOCX文件并返回一个包含文件全部内容的字符串对象。前面的输出内容大都是不需要解释的,我特别阐述了关于Paragraph和Run的输出内容。DOCX文件的结构可以用python-docx库的三个数据类型来表示,其中最高一级是Document对象。
每个文档都包含多个段落。文档中出现新的一行或一个回车,就表示开始一个新的段落。每个段落用多个Run对象表示段落内格式的变化,这里的格式包含有字体、尺寸、颜色和其他样式元素(如粗体、斜体、下划线等等)。这些元素每次发生变化时,都会创建一个新的Run对象。
05 使用PDF、DOCX和纯文本文件,创建用户自定义的语料库
现在我们要创建自己的语料库,而不是使用从互联网上得到的语料库。
1. 准备工作
在准备方面,我们将使用本文第一个实例中提到的Dropbox文件夹中的几个文件。如果你已经从那个文件夹中下载了全部的文件,那么你已经完成了准备工作。否则,请从
https://www.dropbox.com/sh/bk18dizhsu1p534/AABEuJw4TArUbzJf4Aa8gp5Wa?dl=0
下载如下文件:
- sample_feed.txt
- sample-pdf.pdf
- sample-one-line.docx
如果你没有按照本文的顺序来完成实例,那么你需要先回头看看本文的前两个实例。我们将用到本文前两个实例中完成的两个模块 word.py和pdf.py。本段实例更多是关于本文前两个实例所做工作的应用以及语料库概念的应用。下面我们来看实际的代码。
2. 如何实现
(1)创建一个新的Python文件,命名为createCorpus.py并添加以下代码:
import os import word, pdf from nltk.corpus.reader.plaintext import PlaintextCorpusReader
我们导入os库用于与文件有关的操作,word库和pdf库是本文前两段完成的库,最后导入的PlaintextCorpusReader是为了完成语料库建立这一最终目标。
(2)编写一个简单的函数,用来打开并读取一个纯文本文件,并将其全部内容作为string对象返回。添加以下代码:
def getText(txtFileName): file = open(txtFileName, 'r') return file.read()
第一行代码定义了函数及其输入参数。第二行代码以只读方式打开文件(open函数的第二个参数r表示以只读方式打开)。第三行代码读取打开文件的内容并将其作为string对象返回。
(3)在磁盘或文件系统中创建一个新文件夹corpus。添加以下三行代码:
newCorpusDir = 'mycorpus/' if not os.path.isdir(newCorpusDir): os.mkdir(newCorpusDir)
第一行定义的string对象包含了新文件夹名,第二行检查该文件夹在磁盘或文件系统中是否存在,第三行则通过执行os.mkdir()函数在磁盘上创建一个给定名字的文件夹。以上代码执行后将在你的Python文件所在的工作目录下创建一个名为mycorpus的新文件夹。
(4)然后,逐个读取前面提到的三个文件。首先从纯文本文件开始,添加以下代码:
txt1 = getText('sample_feed.txt')
调用之前完成的getText函数,它将读取Sample_feed.txt文件并将输出结果存入名为txt1的字符串对象中。
(5)现在,添加以下代码来读取PDF文件:
txt2 = pdf.getTextPDF('sample-pdf.pdf')
这里使用了PDF.py模块的getTextPDF()函数,它将读取sample-pdf.pdf文件并将文件内容存入名为txt2的字符串对象中。
(6)最后,通过以下代码读取DOCX文件:
txt3 = word.getTextWord('sample-one-line.docx')
这里使用了word.py模块的getTexWord()函数,它将读取sample-one-line.docx文件并将文件内容存入名为txt3的字符串对象中。
(7)接下来,将上面读到的三个字符串对象写到磁盘文件中。添加以下代码:
files = [txt1,txt2,txt3] for idx, f in enumerate(files): with open(newCorpusDir str(idx) '.txt', 'w') as fout: fout.write(f)
- 第一行:创建一个包含以上三个字符串对象的数组
- 第二行:使用for循环来遍历files数组
- 第三行:以只写模式打开一个新文件(采用w选项调用open函数)
- 第四行:将当前字符串内容写到文件中
(8)在mycorpus目录下,也就是我们之前存放文件的目录下新建一个PlainTextCorpus对象:
newCorpus = PlaintextCorpusReader(newCorpusDir, '.*')
以上一行代码看似简单,但是它在内部做了很多的文本处理,如识别段落、句子、单词等等。该函数的两个参数分别是语料库目录的路径以及要处理的文件名模式(这里我们已经设置corpus reader可以处理该目录下所有的文件)。通过以上步骤,我们创建了一个用户自定义的语料库。
(9)接下来,我们来看PlainTextCorpusReader是否加载正常。添加以下代码来进行测试:
print(newCorpus.words()) print(newCorpus.sents(newCorpus.fileids()[1])) print(newCorpus.paras(newCorpus.fileids()[0]))
第一行代码将打印输出语料库包含的所有单词数组(部分)。第二行代码将打印输出文件1.txt中的句子。第三行代码将打印输出文件0.txt中的段落:
Output: ['Five', 'months', '.', 'That', "'", 's', 'how', ...] [['A', 'generic', 'NLP'], ['(', 'Natural', 'Language', 'Processing', ')', 'toolset'], ...] [[['Five', 'months', '.']], [['That', "'", 's', 'how', 'long', 'it', "'", 's', 'been', 'since', 'Mass', 'Effect', ':', 'Andromeda', 'launched', ',', 'and', 'that', "'", 's', 'how', 'long', 'it', 'took', 'BioWare', 'Montreal', 'to', 'admit', 'that', 'nothing', 'more', 'can', 'be', 'done', 'with', 'the', 'ailing', 'game', "'", 's', 'story', 'mode', '.'], ['Technically', ',', 'it', 'wasn', "'", 't', 'even', 'a', 'full', 'five', 'months', ',', 'as', 'Andromeda', 'launched', 'on', 'March', '21', '.']], ...]
3. 工作原理
该实例最后一步的输出很简单直接,展示了各个对象不同的特征。输出内容的第一行是新语料库的单词列表,它与句子、段落、文件等更高级的结构没有关系。
第二行是1.txt文件中所有句子组成的列表,其中每个句子都是由该句子中单词组成的列表。
第三行是0.txt文件中所有段落组成的列表,其中每个段落对象又是由该段落中的句子组成的列表。从中可以发现,这些段落和句子保留了很多原有的结构。
06 读取RSS信息源的内容
丰富网站摘要(Rich Site Summary,RSS)信息源(feed)是一种计算机可读格式,用于传送互联网上定期更新的内容。大多数提供通知信息的网站以这种格式提供更新,例如新闻文章、在线出版物等。订阅者可以通过规范化格式定期访问其更新信息。
1. 准备工作
本段实例的目标是读取一个RSS信息源并访问其中的一条内容。为此,我们将使用全球之声(Mashable)提供的RSS信息源。全球之声是一个数字媒体网站。简而言之,它是一个科技和社交媒体的博客列表。该网站的RSS信息源网址(URL)是:
http://feeds.mashable.com/Mashable
另外,我们需要用feedparser库来读取RSS信息源。打开终端并运行以下命令即可在你的计算机上安装这个库:
pip install feedparser
安装好feedparser库后,我们就可以开始实现第一个读取RSS信息源的Python程序。
2. 如何实现
(1)创建一个新的Python文件,命名为rssReader.py,并添加以下代码:
import feedparser
(2)将全球之声信息源(Mashable feed)载入内存中,添加以下代码:
myFeed = feedparser.parse("http://feeds.mashable.com/Mashable")
myFeed对象包含全球之声信息源的第一页,通过feedparser自动下载和解析该信息源并填充到合适的位置。myFeed对象的条目列表将包含每个帖子(post)。
(3)检查当前信息源的标题并计算帖子数目:
print('Feed Title :', myFeed['feed']['title']) print('Number of posts :', len(myFeed.entries))
在第一行代码中,我们通过myFeed对象获取到了信息源的标题。在第二行代码中,我们计算了myFeed对象中entries对象的长度。如前所述,entries对象是一个包含解析后信息源中所有帖子的列表。运行代码,输出如下所示:
Feed Title: Mashable Number of posts : 30
标题是Mashable,当前,Mashable每次最多存放30个帖子到信息源。
(4)从entries列表中获取第一个post,并打印输出其标题:
post = myFeed.entries[0] print('Post Title :',post.title)
在第一行代码中,我们获取了entries列表中的第一个元素并将其加载到post对象中。在第二行代码中,我们打印输出了post对象的标题。运行代码,输出应该与以下内容相似:
Post Title: The moon literally blocked the sun on Twitter
这里提到输出内容应该与其相似而不是完全一样,是因为信息源在不断自我更新。
(5)访问post的原始HTML内容,并将其打印输出:
content = post.content[0].value print('Raw content :\n',content)
首先,我们访问post的内容对象并获取其具体值,打印输出如下:
Output: Feed Title: Mashable Number of posts : 30 Post Title: The moon literally blocked the sun on Twitter Raw content :The national space agency threw shade the best way it knows how: by blocking the sun. Yep, you read that right.HA HA HA I've blocked the Sun! Make way for the Moon#Solar Eclipse2017 pic.twitter.com/nZCoqBlSTe— NASA Moon (@NASAMoon) Augus t 21, 2017
3. 工作原理
互联网上大多数的RSS信息源都以时间顺序排列,将最新的帖子放到最上面。因此,在该实例中我们每次访问的都是信息源提供的最新内容。信息源本身是不断更新的。所以,每次运行程序时,输出的格式保持不变,但是输出的内容却可能发生改变,这取决于信息源更新的速度。
另外,我们在控制台直接输出原始的HTML文本而不是其文本内容。接下来,我们将解析HTML并从页面获取我们需要的信息。最后,本实例可以附加以下内容:读取你想要的任何信息源,将信息源中所有帖子的信息存储到磁盘,并利用它创建一个纯文本的语料库。当然,你可以从上一个和下一个实例中获得启发。
07 使用BeautifulSoup解析HTML
大多数情况下,你需要处理的网上数据都以HTML页面的形式存在。因此,我们认为有必要向你介绍Python的HTML解析方法。有很多Python模块可以用来解析HTML,在接下来的实例中,我们将使用BeautifulSoup4库来解析HTML。
1. 准备工作
BeautifulSoup4包适用于Python2和Python3。在使用这个包之前,我们需要提前下载并将它安装在解释器上。和之前一样,我们将使用pip来安装这个包。在命令行运行以下命令:
pip install beautifulsoup4
另外,你还需要本文Dropbox文件夹中的sample-html.html文件。如果你还没有下载该文件,请从以下链接下载:
https://www.dropbox.com/sh/bk18dizhsu1p534/AABEuJw4TArUbzJf4Aa8gp5Wa?dl=0
2. 如何实现
(1)完成所有准备工作后,从导入以下声明开始:
from bs4 import BeautifulSoup
从bs4模块中导入BeautifulSoup类,它将用于解析HTML。
(2)将一个HTML文件加载到BeautifulSoup对象中:
html_doc = open('sample-html.html', 'r').read() soup = BeautifulSoup(html_doc, 'html.parser')
在第一行代码中,我们将sample-html.html文件的内容加载到str对象html_doc中。然后,创建了一个BeautifulSoup对象,需要解析的HTML文件作为第一个参数,html.parser作为第二个参数。通过以上操作,BeautifulSoup对象使用html解析器来解析文档。它将文档内容加载到soup对象中进行解析以备使用。
(3)soup对象最主要、最简单且最有用的功能就是去除所有的HTML标签并获取文本内容。添加以下代码:
print('\n\nFull text HTML Stripped:') print(soup.get_text())
在soup对象上调用的get_text()方法将返回HTML标签去除后的文件内容。运行以上代码,将得到以下输出:
Full text HTML Stripped: Sample Web Page Main heading This is a very simple HTML document Improve your image by including an image. Add a link to your favorite Web site. This is a new sentence without a paragraph break, in bold italics. This is purely the contents of our sample HTML document without any of the HTML tags.
(4)有时不仅需要去除HTML标签,可能还需要获取特定标签的内容。访问其中的一个标签:
print('Accessing thetag :', end=' ') print(soup.title)
soup.title将返回文件中的第一个标题(title)标签。以上代码的输出如下所示:
Accessing thetag :Sample Web Page
(5)现在,我们需要某个HTML标签的文本内容。通过以下代码获取
标签的内容:
print('Accessing the text oftag :', end=' ') print(soup.h1.string)
soup.h1.string命令将返回以
标签开头的文本。以上代码的输出如下所示:
Accessing the text oftag : Main heading
(6)访问标签的属性。这里,我们将访问img标签的alt属性。添加以下代码行:
print('Accessing property of tag :', end=' ') print(soup.img['alt'])
通过仔细观察,你会发现访问标签属性的语法和访问标签文本的语法是不同的。运行以上代码,得到以下输出:
Accessing property of tag : A Great HTML Resource
(7)最后,一个HTML文件中同一类型的标签可能多次出现。使用“.”语法仅能获取文件中第一次出现的标签。为了获取所有的标签,我们将使用find_all()函数,如下所示:
print('\nAccessing all occurences of thetag :') for p in soup.find_all('p'): print(p.string)
在BeautifulSoup对象上调用find_all()函数,参数是标签名,它将搜索整个HTML树并返回符合条件的标签列表。我们使用for循环来遍历该列表,并将BeautifulSoup对象中所有
标签的内容/文本打印并输出:
Output: Full text HTML Stripped: Sample Web Page Main heading This is a very simple HTML document Improve your image by including an image. Add a link to your favorite Web site. This is a new sentence without a paragraph break, in bold italics. Accessing thetag :Sample Web PageAccessing the text oftag : Main heading Accessing property of tag : A Great HTML Resource Accessing all occurences of thetag : This is a very simple HTML document Improve your image by including an image. None
3. 工作原理
BeautifulSoup4是一个很方便的库,可以用于解析任何HTML和XML内容。它支持Python内置的HTML解析器,但是你也可以使用其他第三方的解析器,例如,lxml解析器和纯Python的html5lib解析器。
这里,我们使用Python内置的HTML解析器。如果你了解了HTML并会编写简单的HTML代码的话,输出结果是非常容易理解的。
关于作者:克里希纳·巴夫萨(KrishnaBhavsar)花了大约10年时间在各行业领域如酒店业、银行业、医疗行业等进行自然语言处理、社交媒体分析和文本挖掘方面的研究。他致力于用不同的NLP语料库如StanfordCoreNLP、IBM的 SystemText和BigInsights、GATE和NLTK来解决与文本分析有关的行业问题。纳雷什·库马尔(NareshKumar)曾为财富500强企业设计、实施和运行超大型因特网应用程序,在这方面他拥有超过十年的专业经验。他是一位全栈架构师,在电子商务、网络托管、医疗、大数据及分析、数据流、广告和数据库等领域拥有丰富的实践经验。
本文摘编自《自然语言处理Python进阶》,经出版方授权发布。
延伸阅读《自然语言处理Python进阶》
推荐语:本书包含的实例可以让你学会使用NLTK(处理NLP任务的主要Python平台)完成自然语言处理的各种任务,涵盖了自然语言理解、自然语言处理和句法分析等。