python的lxml模块解析问题

用python的lxml模块过程中遇到些问题,解决后记录如下

Hint

declaration environment: python3

Here are some errors

有一天,李明用python去爬一个页面的某些信息,想用xpath方法提取网页的内容。于是他熟练地操作了起来。

1
2
3
4
5
6
from lxml import etree

#省略326行...
def getPage():
res = requests.get(...)
DOM = etree.HTML(res.text)

然而一个报错让他大吃一惊,这个是xml解析报错,如下:

1
Unicode strings with encoding declaration are not supported. Please use bytes input or XML fragments without declaration.

经过排查,他发现这个网站切为手机版之后,网页的源代码开头的文档声明之前还有一个xml声明,就是

1
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html>

而他爬的正是这种页面!
一般来说html的文档类型声明之前是空的,但这里有些特殊。根据报错的提示,lxml不支持解析带有 encoding 声明的字符串,需要转换为bytes类型

How to solve

1
2
3
4
5
6
7
8
9
def getPage():
res = requests.get(...)
# method1: 需要传入的是二进制数据,也就是bytes类型
DOM = etree.HTML(res.content)


# method2: 将字符串类型转为bytes类型
bres = bytes(res.text,"utf-8")
DOM = etree.HTML(bres)

不过他简单粗暴地将xml的encoding去掉了,这也不失为一个好方法。看来他变聪明了!

1
2
3
4
5
6
import re
def getPage():
res = requests.get(...)
# method3: 将encoding去掉
res_xml = re.sub(" encoding=\"utf-8\"", "", res_text)
DOM = etree.HTML(res_xml)

Happy ending

为何网页的html文档类型声明前会出现xml文档声明呢?并且还设置了编码。这也许是为了反爬吧……
(反爬:有被冒犯到)

reference

How to parse XML with Python and lxml