python3之lxml、css和xpath

页面解析lxml

lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高
XPath,全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言,它最初是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索。
XPath的选择功能十分强大,它提供了非常简明的路径选择表达式,另外,它还提供了超过100个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等,几乎所有我们想要定位的节点,都可以用XPath来选择。
XPath于1999年11月16日成为W3C标准,它被设计为供XSLT、XPointer以及其他XML解析软件使用,更多的文档可以访问其官方网站:https://www.w3.org/TR/xpath/

lxml自动修复标签属性两侧缺失的引号,并闭合标签。

处理非标准的html

import lxml.html

broben_html = "<ul class=country> <li>Area <li id=aa>Population</ul>"
tree = lxml.html.fromstring(broben_html)
print('tree type:', type(tree))
# pretty_print: 优化输出,输出换行符
# fixed_html = lxml.html.tostring(tree, pretty_print=True)
fixed_html = lxml.html.tostring(tree)  # 转换为字节
print('fixed_html type:', type(fixed_html))
print(fixed_html)


输出如下:
tree type: <class 'lxml.html.HtmlElement'>
fixed_html type: <class 'bytes'>
b'<ul class="country"> <li>Area </li><li id="aa">Population</li></ul>'
'''
lxml提供如下方式输入文本:
fromstring():解析字符串
HTML():解析HTML对象
XML():解析XML对象
parse():解析文件类型对象
'''
# 输出就是前面讲的tostring()方法:
>>> root = etree.XML('<root><a><b/></a></root>')
>>> etree.tostring(root)
'<root><a><b/></a></root>'
>>> etree.tostring(root,xml_declaration=True)
"<?xml version='1.0' encoding='ASCII'?>\n<root><a><b/></a></root>"
>>> etree.tostring(root,xml_declaration=True,encoding='utf-8')
"<?xml version='1.0' encoding='utf-8'?>\n<root><a><b/></a></root>"

css

解析完输入内容之后,进入选择元素的步骤,此时lxml有几种不同的方法,比如 XPath 选择器和类似 Beautiful Soup 的find()方法 。我们将会使用 css 选择器 , 因为它更加简沽。
安装cssselect:pip install cssselect

常用的选择器示例

选择所有标签 :*
选择<a>标签:a
选择所有 class="link"的元素: .link
选择class="link" 的 <a>标签: a.link
选择 id= "home" 的 <a>标签: a#home
选择父元素为 <a>标签的所有 <span>子标签: a > span
选择<a>标签内部的所有 <span>标签: a span
选择 title 属性为 "Home" 的所有<a>标签: a[title=Home]

在这里插入图片描述

栗子

import lxml.html
import requests
from fake_useragent import UserAgent

ua = UserAgent()
header = {
    "User-Agent": ua.random
}
html = requests.get("http://www.baidu.com/", headers=header)
html = html.content.decode("utf8")
print(html)
tree = lxml.html.fromstring(html)
mnav = tree.cssselect('div#head .mnav')

# 输出文本内容
# area = td.text_content()
for i in mnav:
    print(i.text)

结果如下:

新闻
hao123
地图
视频
贴吧
学术

ps: 不知为何使用html.text输出的是乱码。

参考css选择器语法
http://www.runoob.com/cssref/css-selectors.html

xpath

常用规则

表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
选取当前节点的父节点
@ @用来选取属性
* 通配符,匹配元素节点
@* 匹配任何属性节点
[@attrib] 选取具有给定属性的所有节点
[@attrib=‘value’] 选取具有给定值属性的所有节点

谓语

谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在方括号中。
在这里插入图片描述

运算符

在这里插入图片描述

常用函数

在这里插入图片描述

栗子

  • 解析字符串类型的html
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
'''
html = etree.HTML(text)
result = etree.tostring(html)
print(result)
# 最后一个li标签不完整,会自动补全
  • 解析文件类型的html,获取所有的 li标签

from lxml import etree
from lxml.etree import HTMLParser
html=etree.parse(‘test.html’, etree.HTMLParser()) # 指定解析器HTMLParser会根据文件修复HTML文件中缺失的如声明信息
result=etree.tostring(html) # 解析成字节
result=etree.tostringlist(html) # 解析成列表

from lxml import etree

html = etree.parse('hello.html')
print type(html)
result = html.xpath('//li')
print result
print len(result)
print type(result)
print type(result[0])
# 解释:etree.parse 的类型是 ElementTree,通过调用 xpath 以后,
# 得到了一个列表,包含了 5 个 <li> 元素,每个元素都是 Element 类型
  • 获取标签名
# 获取 class 为 bold 的标签名
result = html.xpath('//*[@class="bold"]')
print(result[0].tag)

  • 获取标签的属性
from lxml import etree

html = etree.parse('hello.html')
result = html.xpath('//li/@class')
print(result)  # 返回li标签的class属性的值
  • 获取li标签下 href属性 为 link1.html 的a标签
from lxml import etree

html = etree.parse('hello.html')
result = html.xpath('//li/a[@href="link1.html"]')
print(result)
  • 获取标签下的子标签或子孙标签
from lxml import etree

html = etree.parse('hello.html')
# 注意这么写是不对的,因为 / 是用来获取子元素的,而 <span> 并不是 <li> 的子元素,所以,要用双斜杠
# result = html.xpath('//li/span') # 获取li标签下为span的子节点
result = html.xpath('//li//span')  # 获取li标签下为span的子孙节点
print(result)
  • 提取文本内容
from lxml import etree

html = etree.parse('hello.html')
result = html.xpath('//li//span/text()')  # 获取span标签的内容
print(result)
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
'''
html = etree.HTML(text)
nodeList = html.xpath('//a[contains(text(), "first")]/text()')
print(nodeList)

扩展:

#方法1:过滤标签,返回全部文本 
>>> root.xpath('string()')
'child1 testchild2 test'

#方法2:以标签为间隔,返回list
>>> root.xpath('//text()')
['child1 test', 'child2 test', '123']


猜你喜欢

转载自blog.csdn.net/llf_cloud/article/details/83628544