Я возился с Python, используя Pythonista на своем iPad. Я решил написать простой сценарий, который извлекает текст песни на японском языке с одного веб-сайта и отправляет запросы на другой веб-сайт, который в основном аннотирует текст песни дополнительной информацией.
Когда я использую 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')
A 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-код запроса post или data
, передаваемый в качестве аргумента этому запросу. Я также проверил, что кодировка сайта - utf-8, что соответствует кодировке почтовых запросов:
r = requests.post(url, data=data)
print(r.encoding)
отпечатков utf-8
.
Я думаю, что проблема связана с тем, как Python 3 более строг в том, как он обрабатывает строки по сравнению с байтами, но я не смог точно определить точную причину.
Хотя я бы оценил пример рабочего кода в Python 3, меня больше интересует, что именно я делаю неправильно, что делает код, приводящий к сбою.