使用python 3和请求时,来自网站的UTF-8文本被不正确地解码,适用于Python 2和机械化

问题描述 投票:2回答:1

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

当我使用Python 2和模块mechanize作为第二个网站时,一切正常,但是当我使用Python 3和requests时,生成的文本是无稽之谈。

这是一个不会出现问题的最小脚本:

#!/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()

截断的输出是:

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

这是一个展示问题的最小脚本:

#!/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()

截断的输出是:

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,就像这样:

# 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错误。将截断的歌词传递给帖子请求可以避免这个问题。在当前示例中,只能传递一个字符。

然而,随着

url = 'https://www.lyrical-nonsense.com/lyrics/aimer/brave-shine/'

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

data = {'text': raw_lyrics[0:51], 'state': 'output'}

在触发embedded null character错误之前。

我已经尝试使用urllib而不是requests,解码和编码utf-8发布请求的结果HTML,或者data作为参数传递给此请求。我还检查过网站的编码是utf-8,它匹配帖子请求的编码:

r = requests.post(url, data=data)   
print(r.encoding)

打印utf-8

我认为问题与Python 3如何处理字符串与字节更严格有关,但我一直无法确定确切原因。

虽然我很欣赏Python 3中的一个工作代码示例,但我更感兴趣的是我做错了什么,导致失败的代码是什么。

python python-3.x python-2.7 encoding python-requests
1个回答
1
投票

我可以在python3.x中使用此代码正确获取歌词:

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())

印刷(截断)

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

有些事情让我觉得奇怪,特别是\r\n(窗口行结束)和\u3000(IDEOGRAPHIC SPACE),但这可能不是问题

我注意到表单提交(以及浏览器模拟器可能成功的原因)的奇怪之处在于表单使用的是multipart而不是urlencoded表单数据。 (由enctype="multipart/form-data"表示)

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

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

现在文字没有被破坏!

>>> BeautifulSoup(resp2.text).find('body').get_text()
'\n扉(とびら)開(ひら)けば捻(ねじ)れた昼(ひる)...'

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

© www.soinside.com 2019 - 2024. All rights reserved.