python简单易懂的lxml读取HTML节点及常用操作方法

python简单易懂的lxml读取HTML节点及常用操作方法

1. 初始化和基本概念

  • lxml 是一个强大的pyth库,用于处理XML和HTML文档。

  • 它提供了类似BeautifulSoup的功能,但性能更高。

  • 在使用lxml时,通常会先解析HTML或XML文档,得到一个ElementTree对象。

    pip install lxml

2. 解析HTML文档

  • 使用lxml.html.fromstring(html)来从字符串中创建一个ElementTree对象。

  • 示例:

    python 复制代码
    from lxml import html
    html_content = '<html><body><p>Hello world!</p></body></html>'
    tree = html.fromstring(html_content)

    或者使用etree.HTML(rhtml_content)来创建ElementTree对象:

python 复制代码
from lxml import etree
html_content = '<html><body><p>Hello world!</p></body></html>'
tree = etree.HTML(html_content)

3. XPath选择器

  • XPath是一种用于在XML文档中查找信息的语言。

  • lxml支持XPath选择器,可以用来定位文档中的元素。

  • 示例:

    python 复制代码
    # 获取所有的段落标签
    paragraphs = tree.xpath('//p')

4. 遍历节点

  • iter_node() 函数可以用来遍历一个节点的所有子节点。

  • 示例:

    python 复制代码
    for node in iter_node(body):
        # 处理每个节点
        pass

5. 获取节点属性

  • 使用.attrib属性来获取节点的所有属性。

  • 示例:

    python 复制代码
    class_name = node.attrib.get('class', '')

在使用lxml处理HTML或XML文档时,获取节点的属性和属性值是非常常见的需求。下面详细解释如何获取标签的全部属性以及如何分别获取单个属性和属性值。

1. 获取标签的全部属性

lxml中,你可以通过访问节点的attrib属性来获取所有属性。attrib是一个字典,键是属性名,值是属性值。

示例代码:
python 复制代码
from lxml import html

html_content = '''
<div id="content" class="main-content">
    <p>Hello, world!</p>
</div>
'''

tree = html.fromstring(html_content)
div_node = tree.xpath('//div')[0]

# 获取所有属性
all_attributes = div_node.attrib
print("All attributes:", all_attributes)

# 输出:
# All attributes: {'id': 'content', 'class': 'main-content'}
2. 分别获取单个属性和属性值

如果你只需要获取某个特定属性的值,可以直接通过键访问字典中的值。如果属性不存在,则可以使用get方法提供一个默认值。

示例代码:
python 复制代码
# 获取单个属性
id_attribute = div_node.attrib.get('id', 'default-id')
print("ID attribute:", id_attribute)

class_attribute = div_node.attrib.get('class', 'default-class')
print("Class attribute:", class_attribute)

# 输出:
# ID attribute: content
# Class attribute: main-content
3. 获取属性值

如果属性名已知,可以直接通过键访问字典中的值。如果属性名未知或需要处理多个属性,可以遍历attrib字典来获取所有属性及其值。

示例代码:
python 复制代码
# 遍历所有属性
for attr_name, attr_value in div_node.attrib.items():
    print(f"Attribute: {attr_name}, Value: {attr_value}")

# 输出:
# Attribute: id, Value: content
# Attribute: class, Value: main-content
4. 处理特殊情况

在某些情况下,属性可能包含空格分隔的多个值(例如class属性),这时你可以使用split方法来分割字符串。

示例代码:
python 复制代码
# 处理包含多个值的属性
class_values = div_node.attrib.get('class', '').split()
print("Class values:", class_values)

# 输出:
# Class values: ['main-content']
5. 综合示例

下面是一个综合示例,展示了如何获取节点的所有属性、单个属性以及如何处理特殊情况下的属性值。

python 复制代码
from lxml import html

html_content = '''
<div id="content" class="main-content secondary">
    <p>Hello, world!</p>
</div>
'''

tree = html.fromstring(html_content)
div_node = tree.xpath('//div')[0]

# 获取所有属性
all_attributes = div_node.attrib
print("All attributes:", all_attributes)

# 获取单个属性
id_attribute = div_node.attrib.get('id', 'default-id')
print("ID attribute:", id_attribute)

class_attribute = div_node.attrib.get('class', 'default-class')
print("Class attribute:", class_attribute)

# 遍历所有属性
for attr_name, attr_value in div_node.attrib.items():
    print(f"Attribute: {attr_name}, Value: {attr_value}")

# 处理包含多个值的属性
class_values = div_node.attrib.get('class', '').split()
print("Class values:", class_values)

# 输出:
# All attributes: {'id': 'content', 'class': 'main-content secondary'}
# ID attribute: content
# Class attribute: main-content secondary
# Attribute: id, Value: content
# Attribute: class, Value: main-content secondary
# Class values: ['main-content', 'secondary']

6. 获取节点文本

  • 使用.text属性来获取节点的文本内容。

  • 示例:

    python 复制代码
    node_text = node.text

或者如果下面还有子节点的话,最好是用:

python 复制代码
# 将所有的文本拼接起来
link_name = ''.join(child.itertext()).strip().replace('\n', '').strip()
# 去掉过多的空格
link_name = re.sub(r'\s+', ' ', link_name ).strip()

7. 获取节点路径

  • 使用.getroottree().getpath(node)来获取节点的完整XPath路径。

  • 示例:

    python 复制代码
    path = node.getroottree().getpath(node)
1.获取最末尾节点路径
python 复制代码
               # 提取最后一个 '/' 后面的元素
                last_element = path.split('/')[-1] if '/' in path else path

8. 检查节点类型(标签名)

  • 通过.tag属性来检查节点的标签名。

  • 示例:

    python 复制代码
    if node.tag == 'p':
        # 处理段落节点
        pass

9. 子节点操作

  • 使用.getchildren()来获取节点的所有子节点,node.getparent()获取节点的所有父节点。

  • 示例:

    python 复制代码
    children = node.getchildren()

10. 提取属性值

  • 使用@属性名来提取属性值。

  • 示例:

    python 复制代码
    image_srcs = node.xpath('.//img/@src')

11. 节点转换为字符串HTML

  • 使用etree.tostring(node)来将节点转换为字符串形式。

  • 示例:

    python 复制代码
    node_html = etree.tostring(node, pretty_print=True, encoding='unicode')

12. 计算文本密度

  • 文本密度是指文本相对于其他非文本内容(如图片、链接)的比例。

  • 通过计算节点中的文本长度与节点总长度的比例来估算文本密度。

  • 示例:

    python 复制代码
    text_density = len(node.text_content()) / len(etree.tostring(node))

13. 处理列表页面

  • 在处理列表页面时,有时候需要检查是否有特定的类名来进行对应操作,如'list'

  • 示例:

    python 复制代码
    if 'list' in a_element[0].get('class', '').lower():
        # 处理列表节点
        pass

扩展

构建包含属性的XPath路径

流程:

1. 获取子节点的XPath路径

首先,我们遍历一个节点的所有子节点,并获取每个子节点的XPath路径。

python 复制代码
for child in node.getchildren():
    xpath_path = '/' + node.getroottree().getpath(child)
2. 获取子节点的所有属性

对于每个子节点,我们获取其所有属性:

python 复制代码
attributes = child.attrib
3. 构建包含属性的XPath路径

接下来,我们根据子节点的属性构建一个完整的XPath路径。如果子节点有属性,我们会在XPath路径后面加上属性条件。

3.1 属性存在时

如果子节点有属性,我们构建一个包含所有属性的XPath路径:

python 复制代码
if attributes:
    xpath_with_attributes = xpath_path + "["
    for i, (key, value) in enumerate(attributes.items()):
        xpath_with_attributes += f"@{key}='{value}'"
        if i < len(attributes) - 1:
            xpath_with_attributes += " and "
    xpath_with_attributes += "]"
  • attributes.items() 返回一个迭代器,其中包含了属性的键值对。
  • enumerate(attributes.items()) 为每个属性分配一个索引。
  • f"@{key}='{value}'" 构建一个XPath条件,表示属性 key 的值为 value
  • 如果不是最后一个属性,则添加 " and " 以连接多个条件。
  • 最后添加 "]" 来结束XPath条件。
3.2 属性不存在时

如果子节点没有属性,我们直接使用原始的XPath路径:

python 复制代码
else:
    xpath_with_attributes = xpath_path
综合示例

下面是一个完整的示例代码,展示了如何遍历节点的子节点并构建包含属性的XPath路径:

python 复制代码
from lxml import html

html_content = '''
<html>
    <body>
        <div id="container" class="main">
            <p>Hello, world!</p>
            <a href="/link" id="example">Example Link</a>
        </div>
    </body>
</html>
'''

tree = html.fromstring(html_content)
root_node = tree.xpath('//div[@id="container"]')[0]

# 遍历根节点的所有子节点
for child in root_node.getchildren():
    xpath_path = '/' + root_node.getroottree().getpath(child)

    # 获取子节点的所有属性
    attributes = child.attrib

    # 构建包含所有属性的XPath路径
    if attributes:
        xpath_with_attributes = xpath_path + "["
        for i, (key, value) in enumerate(attributes.items()):
            xpath_with_attributes += f"@{key}='{value}'"
            if i < len(attributes) - 1:
                xpath_with_attributes += " and "
        xpath_with_attributes += "]"
    else:
        xpath_with_attributes = xpath_path

    print(f"XPath Path: {xpath_path}")
    print(f"XPath with Attributes: {xpath_with_attributes}")
    print("Attributes:", attributes)
    print("")

# 输出:
# XPath Path: /html/body/div/p
# XPath with Attributes: /html/body/div/p
# Attributes: {}
#
# XPath Path: /html/body/div/a
# XPath with Attributes: /html/body/div/a[@href='/link' and @id='example']
# Attributes: {'href': '/link', 'id': 'example'}
详细解释
  1. 获取子节点的XPath路径

    • node.getchildren() 返回节点的所有子节点。
    • node.getroottree().getpath(child) 获取子节点的XPath路径。
    • 我们在路径前加上 / 来确保路径格式正确。
  2. 获取子节点的所有属性

    • child.attrib 返回子节点的所有属性及其值。
  3. 构建包含属性的XPath路径

    • 如果子节点有属性,我们构建一个包含所有属性的XPath路径。
    • 使用 enumerate 来为每个属性分配一个索引,以便在多个属性之间添加 and
    • 如果子节点没有属性,我们直接使用原始的XPath路径。
相关推荐
red润4 分钟前
使用 HTML5 Canvas 实现动态蜈蚣动画
前端·html·html5
Null箘9 分钟前
从零创建一个 Django 项目
后端·python·django
云空13 分钟前
《解锁 Python 数据挖掘的奥秘》
开发语言·python·数据挖掘
青莳吖23 分钟前
Java通过Map实现与SQL中的group by相同的逻辑
java·开发语言·sql
小爬虫程序猿27 分钟前
如何设置爬虫的访问频率?
爬虫
Buleall30 分钟前
期末考学C
java·开发语言
重生之绝世牛码32 分钟前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行38 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
玖年1 小时前
Python re模块 用法详解 学习py正则表达式看这一篇就够了 超详细
python