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

2019-09-14 18:58:42 +08:00
 Danpier

代码如下:

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 )页面也会出现这个情况,豆瓣触发次数得跑多几遍,而且是固定位置报错,这个感觉很诡异,固定位置也不可能是传输数据丢了吧?这个问题困扰了我好几天了,望各位不吝赐教。

3286 次点击
所在节点    Python
17 条回复
ysc3839
2019-09-14 19:02:43 +08:00
发一下有问题的数据看看?
Danpier
2019-09-14 19:22:02 +08:00
@ysc3839
报错提示:
'utf-8' codec can't decode byte 0xe5 in position 34291: invalid continuation byte
ipwx
2019-09-14 19:23:42 +08:00
try: data.decode(errors='ignore')
Danpier
2019-09-14 19:26:29 +08:00
@ipwx 我是了解下问题出现的原因,忽视 error 这个我清楚。
ysc3839
2019-09-14 19:32:34 +08:00
@Danpier 我要看看数据,只有错误信息不知道是什么问题。
aquariumm
2019-09-14 19:34:37 +08:00
我猜是 brotli ?
Danpier
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
2019-09-14 20:18:17 +08:00
@aquariumm request 参数没带 Accept-Encoding 也会接收到压缩数据吗?
gamexg
2019-09-14 21:01:56 +08:00
协议设置为 http 1.0 版本试试,
Transfer-Encoding 之类功能都可能出问题
wwqgtxx
2019-09-14 21:55:08 +08:00
默默地问一下为啥不用 requests 或者 aiohttp,或者看看他们的源代码是怎么处理这种编码问题的
Danpier
2019-09-14 22:55:22 +08:00
@wwqgtxx 高级 API 能提供现成更好的实现方式,但很多问题就接触不到了
autogen
2019-09-15 00:01:22 +08:00
gzip 了吗?
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
2019-09-16 08:30:51 +08:00
@gamexg
@ysc3839
感谢答疑,几个出问题的网站确实都使用 Transfer-Encoding: chunked 来传输,用 HTTP 1.0 请求就不会使用分块编码。搞不懂请求带了 Connection: close 为什么还使用分块编码,另外数据拆分成多块是应用层组装数据时出了问题吗?
lolizeppelin
2019-09-16 09:00:48 +08:00
你的问题 应该去看 rfc 标准
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
2019-09-16 11:23:37 +08:00
@Danpier 估计是避免连接意外断开的情况,没有长度的话客户端发现断开就会认为已经传完了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/600816

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX