Python和BeautifulSoup编码问题[重复]
Posted
技术标签:
【中文标题】Python和BeautifulSoup编码问题[重复]【英文标题】:Python and BeautifulSoup encoding issues [duplicate] 【发布时间】:2011-11-05 08:52:29 【问题描述】:我正在使用 BeautifulSoup 用 Python 编写一个爬虫,一切都很顺利,直到我遇到了这个网站:
http://www.elnorte.ec/
我正在使用请求库获取内容:
r = requests.get('http://www.elnorte.ec/')
content = r.content
如果我此时打印内容变量,所有西班牙特殊字符似乎都可以正常工作。但是,一旦我尝试将 content 变量提供给 BeautifulSoup,一切都会变得一团糟:
soup = BeautifulSoup(content)
print(soup)
...
<a class="blogCalendarToday" href="/component/blog_calendar/?year=2011&month=08&day=27&modid=203" title="1009 artÃculos en este dÃa">
...
它显然把所有的西班牙特殊字符(口音之类的)都弄乱了。我试过做 content.decode('utf-8'), content.decode('latin-1'),还尝试将 fromEncoding 参数设置为 BeautifulSoup,将其设置为 fromEncoding='utf-8' 和 fromEncoding ='latin-1',但还是没有骰子。
任何指针将不胜感激。
【问题讨论】:
【参考方案1】:在您的情况下,此页面包含错误的 utf-8 数据,这会混淆 BeautifulSoup 并使其认为您的页面使用 windows-1252,您可以这样做:
soup = BeautifulSoup.BeautifulSoup(content.decode('utf-8','ignore'))
通过这样做,您将丢弃页面源中的任何错误符号,BeautifulSoup 将正确猜测编码。
您可以将 'ignore' 替换为 'replace' 并检查文本中的 '?'符号以查看已丢弃的内容。
实际上,编写爬虫是一项非常艰巨的任务,它每次都能以 100% 的机会猜测页面编码(现在的浏览器非常擅长),你可以使用像 'chardet' 这样的模块,但是,例如,在你的情况下会猜测编码为 ISO-8859-2,这也不正确。
如果您确实需要能够为用户可能提供的任何页面获取编码 - 您应该构建一个多级(尝试 utf-8、尝试 latin1、尝试等...)检测功能(就像我们所做的那样在我们的项目中)或使用一些来自 firefox 或 chromium 的检测代码作为 C 模块。
【讨论】:
github.com/LuminosoInsight/python-ftfy 是另一个用于清理 mojibake 的库,得到了好评。 如果您使用的是本地(在我的情况下为 html)文件,那么这可以工作:soup = BeautifulSoup(open("C:\\path\\to\\your\\html\\file.html", encoding="utf8"), "html.parser")
【参考方案2】:
你可以试试:
r = urllib.urlopen('http://www.elnorte.ec/')
x = BeautifulSoup.BeautifulSoup(r.read)
r.close()
print x.prettify('latin-1')
我得到了正确的输出。
哦,在这种特殊情况下你也可以x.__str__(encoding='latin1')
。
我猜这是因为内容采用 ISO-8859-1(5) 格式,并且元 http-equiv 内容类型错误地显示为“UTF-8”。
你能确认一下吗?
【讨论】:
你好外国人,谢谢你的回答。你说得对,如果我用 'latin-1' 参数美化它,我会得到所有正确的重音和所有字符串。但是,我需要通过汤来处理链接,如果我再次尝试用字符串制作汤,它会再次混淆重音。 其实,没关系,现在我在尝试您的建议时遇到错误:UnicodeEncodeError: 'latin-1' codec can't encode characters in position 62-63: ordinal not in range(256 ) 如果我这样做,它似乎又可以工作了:x = BeautifulSoup.BeautifulSoup(r.read(), fromEncoding='latin-1'),但同样,如果我尝试制作新汤美化的字符串,它又搞砸了:/ 终于明白了,只需要:soup = BeautifulSoup(content, fromEncoding='latin-1') 然后在解析链接时:i_title = item.contents[0].encode ('latin-1').decode('utf-8') 似乎可以解决问题。感谢您的帮助:) 代码似乎有误(doubleBeatifulSoup
?):AttributeError: type object 'BeautifulSoup' has no attribute 'BeautifulSoup' - 接口可能改变了?【参考方案3】:
你可以试试这个,它适用于每种编码
from bs4 import BeautifulSoup
from bs4.dammit import EncodingDetector
headers = "User-Agent": USERAGENT
resp = requests.get(url, headers=headers)
http_encoding = resp.encoding if 'charset' in resp.headers.get('content-type', '').lower() else None
html_encoding = EncodingDetector.find_declared_encoding(resp.content, is_html=True)
encoding = html_encoding or http_encoding
soup = BeautifulSoup(resp.content, 'lxml', from_encoding=encoding)
【讨论】:
不错的答案,但我会放弃headers
(不是真的需要,因为你没有定义USERAGENT
,所以不能盲目复制粘贴代码)。【参考方案4】:
我建议采取更有条理的万无一失的方法。
# 1. get the raw data
raw = urllib.urlopen('http://www.elnorte.ec/').read()
# 2. detect the encoding and convert to unicode
content = toUnicode(raw) # see my caricature for toUnicode below
# 3. pass unicode to beautiful soup.
soup = BeautifulSoup(content)
def toUnicode(s):
if type(s) is unicode:
return s
elif type(s) is str:
d = chardet.detect(s)
(cs, conf) = (d['encoding'], d['confidence'])
if conf > 0.80:
try:
return s.decode( cs, errors = 'replace' )
except Exception as ex:
pass
# force and return only ascii subset
return unicode(''.join( [ i if ord(i) < 128 else ' ' for i in s ]))
你可以推理,不管你扔什么,它总是会向 bs 发送有效的 unicode。
因此,每次您有新数据时,您的解析树都会表现得更好,并且不会以更新更有趣的方式失败。
试验和错误在代码中不起作用 - 组合太多:-)
【讨论】:
【参考方案5】:第一个答案是对的,这个功能有时是有效的。
def __if_number_get_string(number):
converted_str = number
if isinstance(number, int) or \
isinstance(number, float):
converted_str = str(number)
return converted_str
def get_unicode(strOrUnicode, encoding='utf-8'):
strOrUnicode = __if_number_get_string(strOrUnicode)
if isinstance(strOrUnicode, unicode):
return strOrUnicode
return unicode(strOrUnicode, encoding, errors='ignore')
def get_string(strOrUnicode, encoding='utf-8'):
strOrUnicode = __if_number_get_string(strOrUnicode)
if isinstance(strOrUnicode, unicode):
return strOrUnicode.encode(encoding)
return strOrUnicode
【讨论】:
以上是关于Python和BeautifulSoup编码问题[重复]的主要内容,如果未能解决你的问题,请参考以下文章