小问题鼓捣了一天,本来想写一个简单的客户端和服务器通信的程序,结果出现了各种问题。

问题1.版本问题

首先上网找了一段客户端代码,如下:

import socket
# 创建一个socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接:
s.connect(('www.sina.com.cn', 80))
# 发送数据:
s.send('GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
# 接收数据:
buffer = []
while True:
    # 每次最多接收1k字节:
    d = s.recv(1024)
    if d:
        buffer.append(d)
    else:
        break
data = ''.join(buffer)
# 关闭连接:
s.close()
header, html = data.split('\r\n\r\n', 1)
print header
# 把接收的数据写入文件:
with open('sina.html', 'wb') as f:
    f.write(html)

这个应该是Python2.X的代码,放在Python3.X第一个出错的地方在‘发送数据’那,

s.send(''GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n'')(这里报错是:TypeError: 'str' does not support the buffer interface),

在Python3.X要是看文档的话,你会发现socket.send(bytes[,flags])参数是bytes,不是str,所以修改的办法是s.send(''GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n''.encode()). str.encode()方法是将str转换为bytes,同时加上编码,默认是utf-8,具体参数可以help(str)看一下。

第二个出错的地方在'data=''.join(buffer)'(这里报的错是:TypeError: sequence item 0: expected str instance, bytes found),错误的原因是buffer这个list里每个元素应该是str,但是却出现了bytes,导致这个错误的原因是在上面'd=s.recv(1024)',同样要是看3.X的文档的话,s.recv(1024)返回的也是bytes,所以解决这个问题的办法是在每个d添加到buffer之前转化为str,bytes转化str也有个方法,bytes.decode()(这样直接decode,不设置里面参数可能会出现乱码问题,具体看问题2),具体的可以查看下help(bytes),所以上面修改的代码为'buffer.append(d.decode('gb2312',))'(这样改完,后面还有可能会报错),注意这里的编码和你具体抓取的网页有关,新浪首页的网页编码是gb2312.(怎么看一个网页是以什么编码的呢?首先打开要抓取的页面,然后看该页面的html,下面我们以优酷为例,用chrome看网页源码)

(1).这是优酷首页

python读取html文本 python read_html_python

(2)在页面某处点右键-点审查元素(其他浏览器看html方法可以百度一下,这个是chrome的),然后是下面的界面

python读取html文本 python read_html_浏览器_02

注意画圈的地方,就是head,里面就有你想要的信息,head是和网页正文一起返回给浏览器的,head里面有一些元信息,然后点开head

(3)点开head

python读取html文本 python read_html_浏览器_03

charset=UTF-8就是网页的编码,浏览器也是根据这个编码将相应的网页进行解码

问题2.编码问题

上面的程序按着刚才的方法改好后,一运行又报错(赶上点正可能也不报,比如请求网页的时候返回来个‘403’,因为这个返回的html里面没有中文,没有一些写错的符号,在用gb2312解析的时候没有问题),报错的原因是这个(UnicodeDecodeError: 'gb2312' codec can't decode byte 0x87 in position 219076: illegal multibyte sequence,要是抓取的网页用'utf-8'解析的话可能报这个UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd0 in position 188: invalid continuation byte),这个问题困扰了我很长时间,一直在想编码也选对了,怎么会有这种情况,各种百度谷歌,后来不经意发现了这篇文章,他说“这 是因为遇到了非法字符——尤其是在某些用C/C++编写的程序中,全角空格往往有多种不同的实现方式,比如\xa3\xa0,或者\xa4\x57,这些 字符,看起来都是全角空格,但它们并不是“合法”的全角空格(真正的全角空格是\xa1\xa1),因此在转码的过程中出现了异常。”搜嘎,于是又把上面的buffer.append()又改成了buffer.append(d.decode('gb2312',errors='ignore')),ignore意思是解析时候遇上非法字符直接忽略,乌云散开了,于是又运行,WTF,又报错,这会问题出现在最后一行f.write(html),报的错是:TypeError: 'str' does not support the buffer interface,当然这个不是编码问题了,这个是因为在open('sina.html','wb')时候以二进制方式(Binary files)写,参数'wb'中的w代表写,b代表binary模式(文档),所以只要把代码改成f.write(html.encode())就可以了,接下来再运行就应该没问题了。下面是我的代码

def fun():
    url="www.sina.com"
#     url1="www.baidu.com/s?word=%E5%B0%8F%E7%8C%AB&tn=sitehao123&ie=utf-8"
    s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#     print(s.connect_ex((url,80)))
    s.connect((url,80))
    s.send('GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n'.encode())
    buffer=[]
    while True:
        d=s.recv(1048576)
        print(d)
        print(d.decode('gb2312',errors='ignore'))
        if d:
            buffer.append(d.decode('gb2312',errors='ignore'))
        else:
            break
    data=''.join(buffer)
#     print('buffer type: ',type(buffer))
    print(data)
    print('data type: ',type(data))
    s.close()
    header,html=data.split('\r\n\r\n', 1)
    print(header)
    with open('baidu.html',mode='wb') as f:
        f.write(data.encode('gb2312',errors='ignore'))

这里有个地方需要注意,最后一行如果直接data.encode()的话是utf-8编码,而如果把baidu.html用浏览器打开的时候,是乱码

python读取html文本 python read_html_python读取html文本_04

这是因为浏览器在提取head里面信息的时候,获得的是'gb2312'而程序写入文件时候的编码是'utf-8'导致的,有两种方法可以改:

第一种:把写入文件的html里的head里面的charset='gb2312'改成utf-8

第二种:写入文件的时候直接'gb2312'编码,data.encode('gb2312')

基本就这些了,有错误的地方欢迎指正。