美文网首页
再也不用担心网页编码的坑了!

再也不用担心网页编码的坑了!

作者: 高金01 | 来源:发表于2018-09-29 11:13 被阅读0次

    背景

    大家爬取网页的时候,应该都遇到过这种情况

    当我打印网页源代码的时候

    发现 全部是乱码的

    image

    那这个时候应该怎么办呢?

    requests是如何判断编码

    首先,response.content返回的内容 是二进制内容

    response.text 则是根据设置的encoding来解码

    # Try charset from content-type  
    content = None  
    encoding = self.encoding  
      
    if not self.content:  
      return str('')  
      
    # Fallback to auto-detected encoding.  
    if self.encoding is None:  
        encoding = self.apparent_encoding  
      
    # Decode unicode from given encoding.  
    try:  
        content = str(self.content, encoding, errors='replace')  
    except (LookupError, TypeError):
    
    

    我们可以看到 ,当encoding为None的时候,

    编码是通过chardet.detect来获取的,

    def apparent_encoding(self):  
    """The apparent encoding, provided by the chardet library."""  
        return chardet.detect(self.content)['encoding']
    

    那么chardet.detect 又是干嘛的呢?

    简单的讲,就是根据给定的字节,来返回他的编码

    至于他是如何实现的,欢迎去看源代码。。。

    上面说到了当encoding为None的时候,requests是如何设置encoding的

    那么encoding 默认编码是啥呢?继续查看源代码

    我们在adapters.py 里面找到了~

    response.encoding = get_encoding_from_headers(response.headers)
    
    def get_encoding_from_headers(headers):  
    """Returns encodings from given HTTP Header Dict.  
        :param headers: dictionary to extract encoding from.  
        :rtype: str  
        """  
        content_type = headers.get('content-type')  
      
    if not content_type:  
      return None  
    content_type, params = cgi.parse_header(content_type)  
    
    if 'charset' in params:  
      return params['charset'].strip("'\"")  
      
    if 'text' in content_type:  
      return 'ISO-8859-1'
    

    简单讲就是 如何返回头里面没有content_type,则encoding为None

    如果charset在参数里面的话,则使用charset设置的值(看下图,github返回的)

    image
    如果text在参数里面的话,则使用ISO-8859-1

    然后你打印下 你乱码网页的encoding,发现,还真是ISO-8859-1

    image

    你会很奇怪,为啥当content-type为text/html的时候,编码为iso-8859-1呢?

    image

    现在常见的编码不是utf8么,requests怎么这么傻*呢...

    然后发现是rfc2016的规定。。。

    rfc2016的链接在 https://www.ietf.org/rfc/rfc2616.txt

    感兴趣的同学可以自行查阅...

    image

    最后总结

    当返回头没有content_type 的时候,encoding使用chardet.detect 猜测出来的编码(一般都是很准的)

    当返回头里面有content_type 的时候,如果有charset=xxx,则encoding的编码为chatset的值。如果只是text/html,则编码为ISO-8859-1

    那么当你发现response.text返回乱码的时候,怎么办呢。。。

    只要先设置编码为None...

    再打印.text就可以了..

    response.encoding = None  
    response.text
    

    本来呢,本篇文章到此结束了。。。但是呢。。。

    科普个小知识

    有几种方法可以知道网页的编码呢?

    1. 我们上面讲过的 response.headers中的content_type
    2. 通过chardet.detect猜测出来(上面讲过的)
    3. 网页源代码中的 meta(且有charset的值)如下面的,则表示网页编码为gb2312(不过呢,有时候并不是很准,这个是前端瞎xx写的,这时候就可以用chardet.detect来猜测了...)

    方法3的代码如何写呢(如下)

    def get_encodings_from_content(content):  
    """Returns encodings from given content string.  
        :param content: bytestring to extract encodings from.  
        """  
        warnings.warn((  
    'In requests 3.0, get_encodings_from_content will be removed. For '  
            'more information, please see the discussion on issue #2266. (This'  
            ' warning should only appear once.)'),  
            DeprecationWarning)  
      
        charset_re = re.compile(r'<meta.*?charset=["\']*(.+?)["\'>]', flags=re.I)  
        pragma_re = re.compile(r'<meta.*?content=["\']*;?charset=(.+?)["\'>]', flags=re.I)  
        xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]')  
      
        return (charset_re.findall(content) +  
                pragma_re.findall(content) +  
                xml_re.findall(content))
    

    你会看到requests3.0版本的时候,这个方法会去掉,这又是为什么呢。。。

    截图自己看把,地址在https://github.com/requests/requests/issues/2266

    image

    如果还有猜测编码的方法,欢迎留言

    完...

    相关文章

      网友评论

          本文标题:再也不用担心网页编码的坑了!

          本文链接:https://www.haomeiwen.com/subject/ppuvoftx.html