首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >网站中的UTF-8文本在使用python 3和请求时被不正确地解码,与Python 2和机械化很好地工作。

网站中的UTF-8文本在使用python 3和请求时被不正确地解码,与Python 2和机械化很好地工作。
EN

Stack Overflow用户
提问于 2018-08-31 05:38:30
回答 1查看 311关注 0票数 2

我一直在修改Python,在我的iPad上使用Pythonista。我决定写一个简单的脚本,用日语从一个网站中提取歌词,并向另一个网站发出帖子请求,该网站基本上用额外的信息对歌词进行注释。

当我在第二个网站中使用Python2和模块mechanize时,一切都很好,但是当我使用Python3和requests时,结果却是一派胡言。

这是一个很小的脚本,没有显示问题:

代码语言:javascript
复制
#!/usr/bin/env python2

from bs4 import BeautifulSoup

import requests
import mechanize

def main():
    # Get lyrics from first website (lyrical-nonsense.com)
    url = 'https://www.lyrical-nonsense.com/lyrics/bump-of-chicken/hello-world/'
    html_raw_lyrics = BeautifulSoup(requests.get(url).text, "html5lib") 
    raw_lyrics = html_raw_lyrics.find("div", id="Lyrics").get_text()

    # Use second website to anotate lyrics with fugigana
    browser = mechanize.Browser()
    browser.open('http://furigana.sourceforge.net/cgi-bin/index.cgi')
    browser.select_form(nr=0)
    browser.form['text'] = raw_lyrics
    request = browser.submit()

    # My actual script does more stuff at this point, but this snippet doesn't need it

    annotated_lyrics = BeautifulSoup(request.read().decode('utf-8'), "html5lib").find("body").get_text()
    print annotated_lyrics

if __name__ == '__main__':
    main()

截断的输出是:

代码语言:javascript
复制
扉(とびら)開(ひら)けば捻(ねじ)れた昼(ひる)の夜(よる)昨日(きのう)どうやって帰(かえ)った体(からだ)だけが確(たし)かおはよう これからまた迷子(まいご)の続(つづ)き見慣(みな)れた知(し)らない景色(けしき)の中(なか)でもう駄目(だめ)って思(おも)ってから わりと何(なん)だかやれている死(し)にきらないくらいに丈夫(じょうぶ)何(なに)かちょっと恥(は)ずかしいやるべきことは忘(わす)れていても解(わか)るそうしないと とても苦(くる)しいから顔(かお)を上(あ)げて黒(くろ)い目(め)の人(にん)君(くん)が見(み)たから光(ひかり)は生(う)まれた選(えら)んだ色(しょく)で塗(ぬ)った世界(せかい)に [...]

这是一个显示问题的最低限度脚本:

代码语言:javascript
复制
#!/usr/bin/env python3
from bs4 import BeautifulSoup

import requests

def main():
    # Get lyrics from first website (lyrical-nonsense.com)
    url = 'https://www.lyrical-nonsense.com/lyrics/bump-of-chicken/hello-world/'
    html_raw_lyrics = BeautifulSoup(requests.get(url).text, "html5lib") 
    raw_lyrics = html_raw_lyrics.find("div", id="Lyrics").get_text()

    # Use second website to anotate lyrics with fugigana
    url = 'http://furigana.sourceforge.net/cgi-bin/index.cgi'
    data = {'text': raw_lyrics, 'state': 'output'}
    html_annotated_lyrics = BeautifulSoup(requests.post(url, data=data).text, "html5lib")
    annotated_lyrics = html_annotated_lyrics.find("body").get_text()

    print(annotated_lyrics)

if __name__ == '__main__':
    main()

其截断输出为:

代码语言:javascript
复制
IQp{_<n(åiFcf0c_S`QLºKJoFSK~_÷PnMc_åjDorn-gFÄîcfcfKhU`KfD{kMjDOD+UKacheZKWDyMSho،fDfã]FWjDhhfæWDKTRfÒDînºL_KIo~_x`rgWc_Lkò~fxyjD·nsoiS`FTê`QLÒüíüLn [...]

值得注意的是,如果我尝试获取第二个请求的HTML,如下所示:

代码语言:javascript
复制
# Use second website to anotate lyrics with fugigana
url = 'http://furigana.sourceforge.net/cgi-bin/index.cgi'
data = {'text': raw_lyrics, 'state': 'output'}
annotated_lyrics = requests.post(url, data=data).content.decode('utf-8')

打印embedded null character时发生annotated_lyrics错误。可以通过将截短的歌词传递给post请求来解决此问题。在当前示例中,只能传递一个字符。

然而,与

代码语言:javascript
复制
url = 'https://www.lyrical-nonsense.com/lyrics/aimer/brave-shine/'

我最多可以传递51个字符,如下所示:

代码语言:javascript
复制
data = {'text': raw_lyrics[0:51], 'state': 'output'}

在触发embedded null character错误之前。

我尝试使用urllib而不是requests,解码和编码生成的post请求的HTML,或者作为参数传递给这个请求的data。我还检查了网站的编码是否为utf-8,它与post请求的编码匹配:

代码语言:javascript
复制
r = requests.post(url, data=data)   
print(r.encoding)

打印utf-8

我认为这个问题与Python 3如何更严格地对待字符串和字节有关,但我一直无法确定确切原因。

虽然我希望在Python 3中提供一个工作代码示例,但我更感兴趣的是我到底做错了什么,哪些代码会导致失败。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-09-01 18:42:05

我可以在python3.x中正确地获得这段代码的歌词:

代码语言:javascript
复制
url = 'https://www.lyrical-nonsense.com/lyrics/bump-of-chicken/hello-world/'
resp = requests.get(url)
print(BeautifulSoup(resp.text).find('div', class_='olyrictext').get_text())

印刷(截短)

代码语言:javascript
复制
>>> BeautifulSoup(resp.text).find('div', class_='olyrictext').get_text()
'扉開けば\u3000捻れた昼の夜\r\n昨日どうやって帰った\u3000体だけ...'

有几件事让我觉得很奇怪,特别是\r\n (窗口行结束)和\u3000 (表意空间),但这可能不是问题所在。

关于表单提交(浏览器模拟器可能成功的原因),我注意到的一件事是表单使用多部分而不是用户编码的表单数据。(以enctype="multipart/form-data"表示)

requests中发送多个部分的表单数据有点奇怪,我不得不四处打听一下,最终找到了this,它帮助展示了如何以支持服务器理解的方式格式化多部分数据。要做到这一点,您必须滥用files,但有一个"None“文件名。“为了人类”哈!

代码语言:javascript
复制
url2 = 'http://furigana.sourceforge.net/cgi-bin/index.cgi'
resp2 = requests.post(url2, files={'text': (None, raw_lyrics), 'state': (None, 'output')})

文字现在也没弄坏了!

代码语言:javascript
复制
>>> BeautifulSoup(resp2.text).find('body').get_text()
'\n扉(とびら)開(ひら)けば捻(ねじ)れた昼(ひる)...'

(请注意,这段代码应该在python2或python3中工作)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52109055

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档