V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Danpier
V2EX  ›  Python

请教下大家,用 Python 的 socket 模块请求 B 站有时返回乱码问题。

  •  
  •   Danpier · 2019-09-14 18:58:42 +08:00 · 3427 次点击
    这是一个创建于 1897 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码如下:

    import socket, ssl
    
    HOST = 'www.bilibili.com'
    
    sock = socket.socket()
    sock = ssl.wrap_socket(sock)
    sock.connect((HOST, 443))
    
    request = f'GET / HTTP/1.1\r\nHOST: {HOST}\r\nConnection: close\r\n\r\n'
    sock.send(request.encode())
    
    SIZE = 8192
    buffer = []
    while True:
        print(sock)
        data = sock.recv(SIZE)
        if not data:
            break
        buffer.append(data)
    data = b''.join(buffer)
    
    sock.close()
    data = data.split(b'\r\n\r\n', 1)[1]
    print(data.decode())
    

    GET 请求 B 站首页,返回的数据有时能正常解码为 UTF8,有时又会解码到一半报错提示无法解码,报错时我试着先把字节存到一个文件,然后按 UTF8 只解码前面的部分,可以正常解码。 代码跑多几遍就会出现(手动跑个 5、6 遍,应该跟反爬虫没关联吧),访问豆瓣 TOP250 电影( https://movie.douban.com/top250 )页面也会出现这个情况,豆瓣触发次数得跑多几遍,而且是固定位置报错,这个感觉很诡异,固定位置也不可能是传输数据丢了吧?这个问题困扰了我好几天了,望各位不吝赐教。

    17 条回复    2019-09-16 11:23:37 +08:00
    ysc3839
        1
    ysc3839  
       2019-09-14 19:02:43 +08:00 via Android
    发一下有问题的数据看看?
    Danpier
        2
    Danpier  
    OP
       2019-09-14 19:22:02 +08:00
    @ysc3839
    报错提示:
    'utf-8' codec can't decode byte 0xe5 in position 34291: invalid continuation byte
    ipwx
        3
    ipwx  
       2019-09-14 19:23:42 +08:00 via Android
    try: data.decode(errors='ignore')
    Danpier
        4
    Danpier  
    OP
       2019-09-14 19:26:29 +08:00
    @ipwx 我是了解下问题出现的原因,忽视 error 这个我清楚。
    ysc3839
        5
    ysc3839  
       2019-09-14 19:32:34 +08:00 via Android
    @Danpier 我要看看数据,只有错误信息不知道是什么问题。
    aquariumm
        6
    aquariumm  
       2019-09-14 19:34:37 +08:00 via Android
    我猜是 brotli ?
    Danpier
        7
    Danpier  
    OP
       2019-09-14 20:13:52 +08:00
    @ysc3839 V2EX
    链接: https://pan.baidu.com/s/17Abee6spBYuvS7r5EG4mXw 提取码: 8ku1
    贴下 header 部分:

    HTTP/1.1 200 OK
    Date: Sat, 14 Sep 2019 11:18:13 GMT
    Content-Type: text/html; charset=utf-8
    Transfer-Encoding: chunked
    Connection: close
    gear: 1
    vikingrCache: 60000
    Vikingr-Cache-TTL: 4376
    IDC: shjd
    Vary: Origin,Accept-Encoding
    Expires: Sat, 14 Sep 2019 11:18:43 GMT
    Cache-Control: max-age=30
    X-Cache-Webcdn: BYPASS from hw-gz3-webcdn-07

    报错位置在 34556,试了解码前 30000 没问题。
    Danpier
        8
    Danpier  
    OP
       2019-09-14 20:18:17 +08:00
    @aquariumm request 参数没带 Accept-Encoding 也会接收到压缩数据吗?
    gamexg
        9
    gamexg  
       2019-09-14 21:01:56 +08:00
    协议设置为 http 1.0 版本试试,
    Transfer-Encoding 之类功能都可能出问题
    wwqgtxx
        10
    wwqgtxx  
       2019-09-14 21:55:08 +08:00 via iPhone
    默默地问一下为啥不用 requests 或者 aiohttp,或者看看他们的源代码是怎么处理这种编码问题的
    Danpier
        11
    Danpier  
    OP
       2019-09-14 22:55:22 +08:00
    @wwqgtxx 高级 API 能提供现成更好的实现方式,但很多问题就接触不到了
    autogen
        12
    autogen  
       2019-09-15 00:01:22 +08:00
    gzip 了吗?
    ysc3839
        13
    ysc3839  
       2019-09-15 20:23:15 +08:00
    @Danpier 看了数据,是 Transfer-Encoding: chunked 的问题。
    https://imququ.com/post/transfer-encoding-header-in-http.html

    另外,如无特殊需求,不建议自己实现 HTTP 客户端。
    Danpier
        14
    Danpier  
    OP
       2019-09-16 08:30:51 +08:00
    @gamexg
    @ysc3839
    感谢答疑,几个出问题的网站确实都使用 Transfer-Encoding: chunked 来传输,用 HTTP 1.0 请求就不会使用分块编码。搞不懂请求带了 Connection: close 为什么还使用分块编码,另外数据拆分成多块是应用层组装数据时出了问题吗?
    lolizeppelin
        15
    lolizeppelin  
       2019-09-16 09:00:48 +08:00 via Android
    你的问题 应该去看 rfc 标准
    gamexg
        16
    gamexg  
       2019-09-16 10:07:53 +08:00
    @Danpier #14 协议上面没要求 Connection: close 后就不能 Transfer-Encoding: chunked,所以服务器可以这么做。
    另外如果代码如主题所写,那么你并未移除每个块开头的标记信息,这个需要手工移除。

    参考文档:
    https://imququ.com/post/transfer-encoding-header-in-http.html
    ysc3839
        17
    ysc3839  
       2019-09-16 11:23:37 +08:00 via Android
    @Danpier 估计是避免连接意外断开的情况,没有长度的话客户端发现断开就会认为已经传完了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2786 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 07:29 · PVG 15:29 · LAX 23:29 · JFK 02:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.