互联网并不是一个 HTML 页面的集合。它是一个信息集合,而 HTML 文件只是展示信息的一个框架而已。 如果我们的爬虫不能读取其他类型的文件,包括纯文本、PDF、图像、视频、邮件等,我们将会失去很大一部分数据。
1.文档编码
文档编码是一种告诉程序——无论是计算机的操作系统还是 Python 代码——读取文档的规则.通常可以根据文件的扩展名进行判断.
2.纯文本
from urllib.request import urlopen
textPage = urlopen("http://www.pythonscraping.com/pages/warandpeace/chapter1.txt")
print(textPage.read())
通常,当用 urlopen 获取了网页之后,我们会把它转变成 BeautifulSoup 对象,方便后面对 HTML 进行分析。在这段代码中,我们直接读取页面内容。你可能觉得,如果把它转变成 BeautifulSoup 对象应该也不错,但那样做其实适得其反——这个页面不是 HTML,所以BeautifulSoup 库就没用了。一旦纯文本文件被读成字符串,你就只能用普通 Python 字符串
的方法分析它了。 当然,这么做有个缺点,就是你不能对字符串使用 HTML 标签,去定位那些你真正需要的文字, 避开那些你不需要的文字。如果现在你想从纯文本文件中抽取某些信息,还是有些难度的.
文本编码和全球互联网
互联网上的文本文件会比较复杂。包括 ASCII、 Unicode 和 ISO 编码。
编码类型简介
Unicode 联盟( The Unicode Consortium)的非营利组织尝试将地球上所有用于书写的符号进行统一编码。 其目标包括拉丁字母、斯拉夫字母(кириллица)、中国象形文字( 象形)、数学和逻辑符号( , ≥),甚至表情和“杂项”( miscellaneous)符号。
编码进行时
用默认设置的 urlopen 读取了网上的 .txt 文档。这么做对英文文档没有任何问题。但是,如果你遇到的是俄语、阿拉伯语文档,或者文档里有一个像“ résumé”这样的单词,就可能出问题。
from urllib.request import urlopen
textPage = urlopen("http://www.pythonscraping.com/pages/warandpeace/chapter1-ru.txt")
print(textPage.read())
部分打印结果:
b"\xd0\xa7\xd0\x90\xd0\xa1\xd0\xa2\xd0\xac \xd0\x9f\xd0\x95\xd0\xa0\xd0\x92\xd0\x90\xd0\xaf\n\nI\n\n\xe2\x80\x94 Eh bien, mon prince. G\xc3\xaanes et Lucques ne sont plus que des apanages, des \xd0\xbf\xd0\xbe\xd0\xbc\xd0\xb5\xd1\x81\xd1\x82\xd1\x8c\xd1\x8f, de la famille Buonaparte. Non, je vous pr\xc3\xa9viens que si vous ne me dites pas que nous avons la guerre, si vous vous permettez encore de pallier toutes les infamies, toutes les atrocit\xc3\xa9s de cet Antichrist (ma parole, j'y crois) \xe2\x80\x94 je ne vous connais plus, vous n'\xc3\xaates plus mon ami, vous n'\xc3\xaates plus \xd0\xbc\xd0\xbe\xd0\xb9 \xd0\xb2\xd0\xb5\xd1\x80\xd0\xbd\xd1\x8b\xd0\xb9 \xd1\x80\xd0\xb0\xd0\xb1, comme vous dites.
(省略)
这个问题是因为 Python 默认把文本读成ASCII 编码格式, 而浏览器把文本读成 ISO-8859-1 编码格式。其实都不对,应该用 UTF-8编码格式
from urllib.request import urlopen
textPage = urlopen("http://www.pythonscraping.com/pages/warandpeace/chapter1-ru.txt")
print(str(textPage.read(), 'utf-8'))
如果你要做很多网络数据采集工作,尤其是面对国际网站时,建议你先看看 meta 标签的内容,用网站推荐的编码方式读取页面内容
3.CSV
Python 有一个超赞的标准库( https://docs.python.org/3.4/library/csv.html)可以读写CSV 文件。
读取CSV文件
Python 的 csv 库主要是面向本地文件,就是说你的 CSV 文件得存储在你的电脑上。而进行网络数据采集的时候,很多文件都是在线的。不过有一些方法可以解决这个问题:
- 手动把 CSV 文件下载到本机,然后用 Python 定位文件位置;
- 写 Python 程序下载文件,读取之后再把源文件删除;
- 从网上直接把文件读成一个字符串, 然后转换成一个 StringIO 对象,使它具有文件的属性。
下面的程序就是从网上获取一个CSV 文件( 这里用的是 http://pythonscraping.com/files/MontyPythonAlbums.csv 里的 MontyPython 乐团的专辑列表),然后把每一行都打印到命令行里:
from urllib.request import urlopen
from io import StringIO
import csv
data = urlopen("http://pythonscraping.com/files/MontyPythonAlbums.csv").read().decode('ascii', 'ignore')
dataFile = StringIO(data)
csvReader = csv.reader(dataFile)
for row in csvReader:
print("The album \""+row[0]+"\" was released in "+str(row[1]))
输出结果:
The album "Name" was released in Year
The album "Monty Python's Flying Circus" was released in 1970
The album "Another Monty Python Record" was released in 1971
The album "Monty Python's Previous Record" was released in 1972
The album "The Monty Python Matching Tie and Handkerchief" was released in 1973
The album "Monty Python Live at Drury Lane" was released in 1974
The album "An Album of the Soundtrack of the Trailer of the Film of Monty Python and the Holy Grail" was released in 1975
The album "Monty Python Live at City Center" was released in 1977
The album "The Monty Python Instant Record Collection" was released in 1977
The album "Monty Python's Life of Brian" was released in 1979
The album "Monty Python's Cotractual Obligation Album" was released in 1980
The album "Monty Python's The Meaning of Life" was released in 1983
The album "The Final Rip Off" was released in 1987
The album "Monty Python Sings" was released in 1989
The album "The Ultimate Monty Python Rip Off" was released in 1994
The album "Monty Python Sings Again" was released in 2014
看第一行的内容, The album "Name" was released in Year为无用内容。有些程序员可能会简单地跳过 csvReader 对象的第一行,或者写一个简单的条件把第一行处理掉。不过,还有一个函数可以很好地处理这个问题,那就是 csv.DictReader:
from urllib.request import urlopen
from io import StringIO
import csv
data = urlopen("http://pythonscraping.com/files/MontyPythonAlbums.csv").read().decode('ascii', 'ignore')
dataFile = StringIO(data)
dictReader = csv.DictReader(dataFile)
print(dictReader.fieldnames)
for row in dictReader:
print(row)
运行结果:
['Name', 'Year']
{'Name': "Monty Python's Flying Circus", 'Year': '1970'}
{'Name': 'Another Monty Python Record', 'Year': '1971'}
{'Name': "Monty Python's Previous Record", 'Year': '1972'}
{'Name': 'The Monty Python Matching Tie and Handkerchief', 'Year': '1973'}
{'Name': 'Monty Python Live at Drury Lane', 'Year': '1974'}
{'Name': 'An Album of the Soundtrack of the Trailer of the Film of Monty Python and the Holy Grail', 'Year': '1975'}
{'Name': 'Monty Python Live at City Center', 'Year': '1977'}
{'Name': 'The Monty Python Instant Record Collection', 'Year': '1977'}
{'Name': "Monty Python's Life of Brian", 'Year': '1979'}
{'Name': "Monty Python's Cotractual Obligation Album", 'Year': '1980'}
{'Name': "Monty Python's The Meaning of Life", 'Year': '1983'}
{'Name': 'The Final Rip Off', 'Year': '1987'}
{'Name': 'Monty Python Sings', 'Year': '1989'}
{'Name': 'The Ultimate Monty Python Rip Off', 'Year': '1994'}
{'Name': 'Monty Python Sings Again', 'Year': '2014'}
csv.DictReader 会返回把 CSV 文件每一行转换成 Python 的字典对象返回,而不是列表对象,并把字段列表保存在变量 dictReader.fieldnames 里,字段列表同时作为字典对象的键
4.PDF
PDFMiner3K 就是一个非常好用的库( 是 PDFMiner 的 Python 3.x 移植版)。它非常灵活,可以通过命令行使用, 也可以整合到代码中。它还可以处理不同的语言编码,而且对网络文件的处理也非常方便。
你可以下载这个模块的源文件( https://pypi.python.org/pypi/pdfminer3k), 解压并用下面命令安装:
$python setup.py install
文档位于源文件解压文件夹的 /pdfminer3k-1.3.0/docs/index.html 里
下面的例子可以把任意 PDF 读成字符串,然后用 StringIO转换成文件对象:
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from io import StringIO
from io import open
from urllib.request import urlopen
def readPDF(pdfFile):
rsrcmgr = PDFResourceManager()
retstr = StringIO()
laparams = LAParams()
device = TextConverter(rsrcmgr, retstr, laparams=laparams)
process_pdf(rsrcmgr, device, pdfFile)
device.close()
content = retstr.getvalue()
retstr.close()
return content
pdfFile = urlopen("http://pythonscraping.com/pages/warandpeace/chapter1.pdf")
outputString = readPDF(pdfFile)
print(outputString)
pdfFile.close()
5.微软Word和.docx
Python 对这种 Google Docs、 Open Office 和 Microsoft Office都在使用的 .docx 格式 的 支 持 还 不 够 好。 虽 然 有 一 个 python-docx 库(http://python-docx.readthedocs.org/en/latest/),但是只支持创建新文档和读取一些基本的文件数据,如文件大小和文件标题,不支持正文读取。如果想读取 Microsoft Office文件的正文内容,我们需要自己动手找方法。
从文件读取 XML
from zipfile import ZipFile
from urllib.request import urlopen
from io import BytesIO
from bs4 import BeautifulSoup
wordFile = urlopen("http://pythonscraping.com/pages/AWordDocument.docx").read()
wordFile = BytesIO(wordFile)
document = ZipFile(wordFile)
xml_content = document.read('word/document.xml')
wordObj = BeautifulSoup(xml_content.decode('utf-8'))
textStrings = wordObj.findAll("w:t")
for textElem in textStrings:
print(textElem.text)
这段代码把一个远程 Word 文档读成一个二进制文件对象( BytesIO 与StringIO 类似),再用 Python 的标准库 zipfile 解压(所有的 .docx 文件为了节省空间都进行过压缩),然后读取这个解压文件,就变成 XML 了
输出结果:
<!--?xml version="1.0" encoding="UTF-8" standalone="yes"?-->
<w:document mc:ignorable="w14 w15 wp14" xmlns:m="http://schemas.openx
mlformats.org/officeDocument/2006/math" xmlns:mc="http://schemas.open
xmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-micros
oft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/off
iceDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vm
l" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/m
ain" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="htt
p://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://
schemas.microsoft.com/office/word/2012/wordml" xmlns:wne="http://sche
mas.microsoft.com/office/word/2006/wordml" xmlns:wp="http://schemas.o
penxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wp14="h
ttp://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" x
mlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessin
gCanvas" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wor
dprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word
/2010/wordprocessingInk" xmlns:wps="http://schemas.microsoft.com/offi
ce/word/2010/wordprocessingShape"><w:body><w:p w:rsidp="00764658" w:r
sidr="00764658" w:rsidrdefault="00764658"><w:ppr><w:pstyle w:val="Tit
le"></w:pstyle></w:ppr><w:r><w:t>A Word Document on a Website</w:t></
w:r><w:bookmarkstart w:id="0" w:name="_GoBack"></w:bookmarkstart><w:b
ookmarkend w:id="0"></w:bookmarkend></w:p><w:p w:rsidp="00764658" w:r
sidr="00764658" w:rsidrdefault="00764658"></w:p><w:p w:rsidp="0076465
8" w:rsidr="00764658" w:rsidrdefault="00764658" w:rsidrpr="00764658">
<w: r> <w:t>This is a Word document, full of content that you want ve
ry much. Unfortunately, it’s difficult to access because I’m putting
it on my website as a .</w:t></w:r><w:prooferr w:type="spellStart"></
w:prooferr><w:r><w:t>docx</w:t></w:r><w:prooferr w:type="spellEnd"></
w:prooferr> <w:r> <w:t xml:space="preserve"> file, rather than just p
ublishing it as HTML</w:t> </w:r> </w:p> <w:sectpr w:rsidr="00764658"
w:rsidrpr="00764658"> <w:pgszw:h="15840" w:w="12240"></w:pgsz><w:pgm
ar w:bottom="1440" w:footer="720" w:gutter="0" w:header="720" w:left=
"1440" w:right="1440" w:top="1440"></w:pgmar> <w:cols w:space="720"><
/w:cols&g; <w:docgrid w:linepitch="360"></w:docgrid> </w:sectpr> </w:
body> </w:document>