Python sys.stdin引发UnicodeDecodeError

Cha*_*man 5 encoding utf-8 sys python-3.x

我正在尝试使用cURL和Python的BeautifulSoup库编写一个(非常)基本的Web搜寻器(因为它比GNU awk和一堆正则表达式更容易理解)。

目前,我正在尝试使用cURL(即curl http://www.example.com/ | ./parse-html.py)将网页的内容通过管道传递给程序

由于某种原因,Python会UnicodeDecodeError因为一个无效的起始字节而抛出a (我已经看过这个答案以及关于无效起始字节的这个答案,但没有弄清楚如何从中解决问题)。

具体来说,我尝试a.encode('utf-8').split()从第一个答案开始使用。第二个答案只是说明了问题(Python发现无效的启动字节),尽管它没有给出解决方案。

我试图将cURL的输出重定向到文件(即,curl http://www.example.com/ > foobar.html修改程序以接受文件作为命令行参数,尽管这样做会导致相同的结果UnicodeDecodeError。)

我检查了一下,据我所知,locale charmapis 的输出UTF-8表示我的系统正在编码字符UTF-8(这使我对此感到特别困惑UnicodeDecodeError

目前,导致错误的确切行是html_doc = sys.stdin.readlines().encode('utf-8').strip()。我尝试将其重写为for循环,尽管遇到了同样的问题。

到底是什么引起的UnicodeDecodeError,我应该如何解决?

编辑: 通过更改行html_doc = sys.stdin.readlines().encode('utf-8').strip()html_doc = sys.stdin解决此问题

phi*_*hag 4

问题出在读取过程中,而不是编码过程中;输入资源根本不是用 UTF-8 编码的,而是另一种编码。在 UTF-8 shell 中,您可以轻松地重现该问题

\n\n
$ echo 2\xc2\xa5 | iconv -t iso8859-1 | python3 -c \'import sys;sys.stdin.readline()\'\nTraceback (most recent call last):\n  File "<string>", line 1, in <module>\n  File "/usr/lib/python3.5/codecs.py", line 321, in decode\n    (result, consumed) = self._buffer_decode(data, self.errors, final)\nUnicodeDecodeError: \'utf-8\' codec can\'t decode byte 0xa5 in position 1: invalid start byte\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可以将文件(sys.stdin.buffer.read()、 或with open(..., \'rb\') as f: f.read())作为二进制读取(您将获得一个bytes对象),检查它并猜测编码。HTML 标准中记录了执行此操作的实际算法。

\n\n

然而,在许多情况下,编码不是在文件本身中指定的,而是通过 HTTP Content-Typeheader指定的。不幸的是,您对curl 的调用没有捕获此标头。您可以只使用Python,而不是使用curlPython——它已经可以下载URL了。从 youtube-dl窃取编码检测算法,我们得到如下内容:

\n\n
import re\nimport urllib.request\n\n\ndef guess_encoding(content_type, webpage_bytes):\n    m = re.match(\n        r\'[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+\\s*;\\s*charset="?([a-zA-Z0-9_-]+)"?\',\n        content_type)\n    if m:\n        encoding = m.group(1)\n    else:\n        m = re.search(br\'<meta[^>]+charset=[\\\'"]?([a-zA-Z0-9_-]+)[ /\\\'">]\',\n                      webpage_bytes[:1024])\n        if m:\n            encoding = m.group(1).decode(\'ascii\')\n        elif webpage_bytes.startswith(b\'\\xff\\xfe\'):\n            encoding = \'utf-16\'\n        else:\n            encoding = \'utf-8\'\n\n    return encoding\n\n\ndef download_html(url):\n    with urllib.request.urlopen(url) as urlh:\n        content = urlh.read()\n        encoding = guess_encoding(urlh.getheader(\'Content-Type\'), content)\n        return content.decode(encoding)\n\nprint(download_html(\'https://phihag.de/2016/iso8859.php\'))\n
Run Code Online (Sandbox Code Playgroud)\n\n

还有一些库(尽管不在标准库中)支持开箱即用,即requests

\n\n

我还建议您阅读编码的基础知识

\n