【问题标题】:Python sys.stdin throws a UnicodeDecodeErrorPython sys.stdin 抛出 UnicodeDecodeError
【发布时间】:2016-04-25 17:32:25
【问题描述】:

我正在尝试使用 cURL 和 Python 的 BeautifulSoup 库编写一个(非常)基本的网络爬虫(因为这比 GNU awk 和一堆正则表达式更容易理解)。

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

由于某种原因,Python 抛出了 UnicodeDecodeError,因为起始字节无效(我查看了 this answerthis answer 关于无效起始字节的信息,但没有弄清楚如何从他们那里解决问题) .

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

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

我查了一下,locale charmap 的输出是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 可以解决问题

【问题讨论】:

    标签: python-3.x encoding utf-8 sys


    【解决方案1】:

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

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

    您可以将文件(sys.stdin.buffer.read()with open(..., 'rb') as f: f.read())读取为二进制文件(您将获得一个 bytes 对象),检查它并猜测编码。执行此操作的实际算法is documented in the HTML standard

    但是,在许多情况下,文件本身并没有指定编码,而是通过 HTTP Content-Type header。不幸的是,您对 curl 的调用并未捕获此标头。除了使用 curl Python,您可以只使用 Python - 它已经 can download URLs。窃取the encoding detection algorithm from youtube-dl,我们得到类似:

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

    还有一些开箱即用的库(虽然不在标准库中)支持这一点,即requests

    我还建议您阅读basics of what encodings are

    【讨论】:

      猜你喜欢
      • 2015-06-15
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 2014-10-24
      • 1970-01-01
      • 2014-10-23
      • 1970-01-01
      • 2013-11-17
      相关资源
      最近更新 更多