为什么此代码不适用于所有网址?

时间:2014-10-10 19:07:29

标签: python python-3.x unicode urllib

我对python很新,正在玩一些代码。我实际上是在尝试解析html网页并从解析的文档中提取一些信息:

from urllib import request
from bs4 import BeautifulSoup

#some code here...

link = str(input("Enter URL: "))
sock = request.urlopen(link)
pageText = sock.read()
sock.close()

#some code here...

file = open("C:/test.txt", 'w')
file.write(pageText.decode("utf-8"))

#some code here...

我在file.write()行中收到此错误,我在互联网上搜索仍然不知道如何解决这个问题。

错误:

Traceback (most recent call last):
  File "C:/Users/Monster/PycharmProjects/TestPro_1/Testfile.py", line 16, in <module>
    file.write(pageText.decode("utf-8"))
  File "C:\Python34\lib\encodings\cp1252.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode characters in position 413334-413340: character maps to <undefined>

我的代码适用于某些网站,例如www.google.com或www.flipkart.com,并为某些网址提供错误,例如www.facebook.com和www.youtube.com。我认为一个可能的原因是它不适用于www.facebook.com和youtube.com,因为它们是用PHP或其他语言开发的,而不是HTML网页是正确的吗?

1 个答案:

答案 0 :(得分:2)

问题是您尝试使用cp1252编码写入文本文件,但您的数据包含cp1252中不存在的字符。

在Python中,open函数为文本文件采用可选的encoding参数。正如文档所说,如果你没有指定任何东西:

  

默认编码取决于平台(无论locale.getpreferredencoding()返回什么)

在Windows上,该功能返回的“首选编码”将是您设置为系统默认值的任何内容。在美国版本的Windows上,如果您尚未更改设置,则预先配置的默认值为“代码页1252”,这是Microsoft对Latin的Latin-1变体的变体。这只能处理256个不同的字符(几乎,但不完全相同,与Unicode中的前256个字符相同)。如果你有任何其他角色,你将会收到错误。

这在某些页面上有效但不在其他页面上有效的原因是某些页面除了适合每个字符集的普通英文字符外没有任何其他内容。


如果您确实要保存UTF-8文本文件,则必须明确地执行此操作:

f = open('C:/test.txt', 'w', encoding='utf-8')
f.write(pageText.decode('utf-8'))

如果你想保存一个cp1252文本文件 - 或者更确切地说,无论你系统的默认编码是什么,如果有人在Mac上运行你的脚本或者在日语上运行基于Shift-JIS的cp932,则可能是UTF-8 Windows通过跳过或替换或转义不适合cp1252的字符,你也可以这样做:

f = open('C:/test.txt', 'w', errors='replace')
f.write(pageText.decode('utf-8'))

或者,当然,如果您想要cp1252,无论系统设置的是什么,请说:

f = open('C:/test.txt', 'w', encoding='cp1252', errors='replace')
f.write(pageText.decode('utf-8'))

如果要保存原始字节而不必担心它们是什么,请以二进制模式打开文件,并且首先不要decode字节:

f = open('C:/test.txt', 'wb')
f.write(pageText)

当然,如果你在cp1252(或Shift-JIS等)文本编辑器中打开该文件,它看起来就像是mojibake ......但这不再是你程序的错。 :)


但是,你在这里遇到了另一个问题。您假设每个网页都是UTF-8。这不是真的。事实上,HTML5之前的网页默认为Latin-1,但是它们可以在标题中(或meta标记中指定不同的编码,或者对于XHTML,在顶级XML标记中指定不同的编码) )。特别是,尝试使用Facebook页面:

>>> print(sock.getheader('Content-Type'))
'text/html; charset=utf-8'

你知道这就是UTF-8。

对于HTML5,它是...... a lot more complicated。理想情况下,您会想要使用一个为您执行此操作的库。 (因为你已经在使用BeautifulSoup了,对于很多常见的情况,它的“Unicode,dammit”会运行得很好 - 而且对于HTML5之前的版本也很好 - 但是标准正确的实现甚至更好。)