正则,xpath,bs4匹配

正则

在字体反爬过程中,解析页面时,使用正则表达式可以匹配到网页源码中的字体代码,如下所示:

print(re.findall(r'<span class="day font".*?>(.*?)</span>', rsp.text)[0])  # 结果:&#xe412&#xf359&#xf359/天

Xpath

而使用xpath解析,得到的却是Unicode字符串列表:

html = etree.HTML(rsp.text)
res = html.xpath('//span[@class="day font"]/text()')  # ['\ue412\uf359\uf359/天']

遍历结果为乱码:

print(res[0])  # 结果:/天

bs4

使用bs4解析,输出的也是乱码,如下所示:

soup = BeautifulSoup(rsp.text, 'lxml')
sal = soup.select('span.day.font')  # [<span class="day font" data-v-98c756d6="">/天</span>]
print(sal[0].text)  # 结果:/天

若在bs4下继续使用正则匹配,匹配结果也是Unicode字符串列表,遍历输出也是乱码


那么问题来了:请问,都是从源代码中解析,为什么后两种解析方式得到的是Unicode而得不到字体代码啊?

一、本质原因是Unicode在HTML中和Python中的不同表示方法

以unicode e412为例
在HTML中

<p>
&#xe412 
</p>

在python中

'\ue412'

以上代码表达了同一个意思:unicdeo中的e412字符
然后在浏览器渲染(html)和打印的时候(python),会渲染出e412所代表的字符,也就是说:
源码中显示e412,显示时显示字符

二、再来看一下问题中描述的现象

etree和bs4 做的事情

  1. 它们会闭合各种标签,构造完整对称的HTML
  2. 解析HTML,构造为对象,并提供各种选择器方法

在这个过程中,它们会将HTML语法表达的字符,改为python语法表达的字符,以便解析和输出

lxml.etree 中的解析器可以立即处理 unicode 字符串
在将 XML/HTML 数据传递到解析器之前,您通常应该避免将手动转换为 unicode
--- by lxml 官网

也就是说 &#xe412 会被替换为 \ue412 (这样显示效果才一样)


那么问题来了:为什么使用正则就不会这样呢?
因为&xxxx 是HTML的语法,正则根本就识别不出来这是在表示unicode,当然就忽略了
如果它知道这个是表示unicode,说不定会有所作为。。。

三、如何不进行替换?

可能你想在etree和bs4 做元素定位 之后,拿到一个可以在浏览器显示的版本,有两种思路:

  1. 让lxml忽略的unicode的处理 (理论上可行,但是没有找到相关说明)
  2. 将内容重新处理为HTML语法的表达方式 (\uxx替换为 &xxx

代码示例

from lxml import etree

raw_html = "<p>&#xe412&#xf359&#xf359/天</p>"  # 原始HTML内容
html = etree.HTML(raw_html)
x = html.xpath("//p/text()")[0]  # 获取内容 (unicode)
print(x)  # 展示unicode字符
print(x.encode("unicode-escape").decode())  # 转为Python中unicode语法
print(x.encode("unicode-escape").decode().replace("\\u", "&#"))  # 转为HTML中unicode语法

输出结果,注意字符串中的汉字也被转为了unicode代码:

/天
\ue412\uf359\uf359/\u5929
&#e412&#f359&#f359/&#5929   

如果帮助,还请点击【采纳】,支持一下

参考链接: