HTML文本转DOM树的两种方式

  • 使用lxml.etree
    1
    2
    3
    4
    from lxml import etree

    html = etree.HTML(html_text)
    html.xpath("//div[@class='tabslider']/ul")
  • 使用scrapy团队开源的parsel库(推荐),Parsel官方文档
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    from parsel import Selector

    # 构建DOM树,可解析html文本和xml文本
    html = Selector(text=html_text or xml_text)

    # 解析结果的获取和处理

    # 返回单个字符串
    html.xpath("...").get()
    html.xpath("...").extract_first()

    # 返回结果列表
    html.xpath("...").getall()
    html.xpath("...").extract()

    # 获取节点属性
    html.xpath("...").attrib # 返回字典
    html.xpath("...").attrib["href"] # 获取属性值

    # 使用正则进行截取
    html.xpath("...").re("\d+") # 返回多个结果
    html.xpath("...").re_first("\d+")

一些注意点:

  • 使用requests获取网页源码后,直接使用text有时候会遇到乱码问题,可以尝试如下方式:
    1
    2
    res = requests.get(url, headers={...})
    selector = Selector(text=res.content.decode("utf8")) # 一般为utf8,具体情况具体分析

关于tbody标签

在解析列表数据时,在审查元素中发现 <tbody> 标签时

1
2
3
4
<table>
<tbody>
<tr>...</tr>
<tr>...</tr>

先别急着在xpath规则中加入tbody标签,先 Ctrl + U 检查网页源码中是否存在(我被坑了好几次,有时在有时不在 = = !)

关于xpath

  • xpath解析默认是从 根节点 开始,加上 . 表示从 当前节点 开始
  • // 表示检索所有 子孙节点
  • / 表示检索所有 子节点

xpath语法总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 多个表达式(优先从第一个取)
tree.xpath("//ul//a/text() | //li/@class")

# child:: 平时省略不写
tree.xpath("//ul/child::li")
# 相当于
tree.xpath("//ul/li")

# 使用 position 定位第几个
tree.xpath('./li[@class="cxs" and postion()>1]')

# count统计节点的数量
tree.xpath("./ul[count(li)]")

# 获取节点及子孙节点所有本文
# 返回字符串列表
tree.xpath("//span//text()")
# 返回合并后的字符串
tree.xpath("string(//li[@class])")

CSS选择器语法总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 奇数节点
tree.css("ul li:odd")

# 偶数节点
tree.css("ul li:even")

# 排除第一个子节点
tree.css("tr>th:not(:first-child)")

# 两个表达式
tree.css('div.cxs, div#cxs')

# 包含关键词
tree.css('div.span[name~="cxs"]')

# 后面的兄弟节点
tree.css('div.cxs~div#cxs')
# 排除部分节点
tree.css('div.para-title~*:not(.anchor-list)')

first-child 和 first-of-type 区别

  • div的第一个子节点,即父节点的第一个子节点
    1
    div:first-child  
  • div的第一个p节点,即父节点下该种类型的子节点的第一个
    1
    div>p:first-of-type  
  • 指定第几个子节点
    1
    dl#TOP>dd:nth-child(8)

需要注意一点,冒号:前的节点名不可以带属性值 class id 之类的