爬虫学习:解析库的使用

解析库的使用

  • 1. 使用 XPath
    • 1.1 XPath 概览
    • 1.2 XPath 常用规则
    • 1.3 实例引入
    • 1.4 所有节点
    • 1.5 子节点
    • 1.6 父节点
    • 1.7 属性匹配
    • 1.8 文本获取
    • 1.9 属性获取
    • 1.10 属性多值匹配
    • 1.11 多属性匹配
    • 1.12 按序选择
    • 1.13 节点轴选择
  • 2. 使用 Beautiful Soup
    • 2.1 解析器
    • 2.2 基本用法
    • 2.3 节点选择器
    • 2.4 方法选择器
    • 2.5 CSS 选择器
  • 3. 使用 pyquery
    • 3.1 初始化
    • 3.2 基本 CSS 选择器
    • 3.3 查找节点
    • 3.4 遍历
    • 3.5 获取信息
    • 3.6 节点操作
    • 3.7 伪类选择器

1. 使用 XPath

  • XPath,全称为 XML Path Language,即 XML 路径语言,是一门在 XML 文档中查找信息的语言,但也适用于 HTML 文档的搜索

1.1 XPath 概览

  • 拥有简洁明了的路径选择表达式
  • 提供超过 100 个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等
  • 官方网站:https://www.w3.org/TR/xpath/

1.2 XPath 常用规则

  • XPath 常用规则如下:

    • nodename 选取此节点的所有子节点
    • / 从当前节点选取直接子节点
    • // 从当前节点选取子孙节点
    • . 选取当前节点
    • … 选取当前节点的父节点
    • @ 选取属性
  • 一个示例,代表选择所有名称为 title,同时属性为 lang 的值为 eng 的节点:

    //title[@lang='eng']
    

1.3 实例引入

  • 一个实例:

    • 首先声明一段 HTML 文本
    • 然后调用 HTML 类进行初始化,构造一个 XPath 解析对象。注意最后一个 li 节点是没有闭合的,但 etree 模块可以自动修正
    • 使用 tostring 方法输出修正后的 HTML 代码,但是是 bytes 类型
    • 通过 decode() 方法转化为 str 类型

    经过处理后,li 节点标签被补全,并且自动添加了 body, html 节点

from lxml import etree

# 声明一段 HTML 文本
text = '''

'''
# 调用 HTML 类进行初始化,构造一个 XPath 解析对象
# 注意最后一个 li 节点是没有闭合的,但 etree 模块可以自动修正
html = etree.HTML(text)
# 使用 tostring 方法输出修正后的 HTML 代码,但是是 bytes 类型
result = etree.tostring(html)
# 通过 decode() 方法转化为 str 类型
print(result.decode('utf-8'))


  • 直接读取文本文件进行解析:
    • test.html 文件的内容和上面的一模一样
    • 输出多了也给 DOCTYPE 声明,不过对解析无影响
    • 节点后面的 代表什么?
from lxml import etree

html = etree.parse('test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))


1.4 所有节点

  • 一般用 // 开头的 XPath 规则来选取所有符合要求的节点,如下例子,其中 * 代表匹配所有节点,返回形式是一个列表,每个元素都是 Element 类型,其后跟了节点的名称:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)
[, , , , , , , , , , , , , ]
  • 也可以指定节点名称,如下获取所有 li 节点:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//li')
print(result)
[, , , , ]

1.5 子节点

  • 通过 / 或 // 即可查找元素的子节点或孙节点,如下选择 li 节点的所有直接 a 子节点:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//li/a')
print(result)
[, , , , ]
  • 如下使用 // 选择 ul 节点下的 a 节点:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//ul//a')
print(result)
[, , , , ]

1.6 父节点

  • 父节点的选择通过 . . 来实现,如下实例先选中 href 属性为 link4.html 的 a 节点,然后再获取其父节点,然后再获取其 class 属性:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/../@class')
print(result)
['item-1']
  • 也可以通过 parent:: 来获取父节点:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/parent::*/@class')
print(result)
['item-1']

1.7 属性匹配

  • 如上例,使用 @ 符号进行属性过滤
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]')
print(result)
[, ]

1.8 文本获取

  • 使用 XPath 中的 text() 方法获取节点中的文本,如下实例尝试获取前面 li 节点的文本:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]/text()')
print(result)
['\r\n     ']
  • 但输出只有一个回车符和一个换行符,是因为 text() 前面是 /,/ 的含义是选取直接子节点,li 的直接子节点都是 a 节点,文本都在 a 节点的内部的,因此这里匹配到的结果就是被修正的 li 节点内部的回车符和换行符,因为自动修正的 li 节点的尾标签换行了;
  • 也就是说提取到的文本是 li 节点的尾标签和 a 节点的尾标签之间的文本
  • 要获取 li 节点内部的文本,一个是先选取 a 节点再获取文本,一种是使用 // 获取子孙节点的文本,后者获取到的内容包括前者,因为选择的是所有子孙节点的文本,而前者只是子节点 a 内部的文本
# 先获取 a 节点:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]/a/text()')
print(result)

# 使用 // 获取子孙节点:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]//text()')
print(result)
['first item', 'fifth item']
['first item', 'fifth item', '\r\n     ']

1.9 属性获取

  • 属性通过 @ 来获取:
from lxml import etree
html = etree.parse('test.html', etree.HTMLParser())
result = html.xpath('//li/a/@href')
print(result)
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']

1.10 属性多值匹配

  • 当一个节点的属性有多个值,就无法使用之前的属性匹配获取
  • 这种情况下使用 contains() 方法,contains() 方法的第一个参数为属性名称,第二个参数为属性值,示例如下:
from lxml import etree
text = '''
  • first item
  • '''
    html = etree.HTML(text) # 使用之前的匹配方法获得的结果为空 result = html.xpath('//li[@class="li"]/a/text()') print(result) # 使用 contains() 方法 result = html.xpath('//li[contains(@class, "li")]/a/text()') print(result)
    []
    ['first item']
    

    1.11 多属性匹配

    • 根据多个属性确定一个节点,比如同时满足两个属性,或者满足两个属性中的一个,需要用到 XPath 中的运算符,如 and, or, mod, |(计算两个节点集), +, -, *, div, =, !=, <, <=, >, >= 等
    from lxml import etree
    text = '''
    
  • first item
  • '''
    html = etree.HTML(text) result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()') print(result)
    ['first item']
    

    1.12 按序选择

    • 我们在选择的时候,某些属性可能同时匹配了多个节点,但是只想要其中的某个节点,可以利用中括号传入所有的方法进行获取
    • 中括号不仅可以是数字,也可以是位置函数如 last(), last()-2, position() < 3 等,另外需要注意这里 1 即第一个,和代码不同
    from lxml import etree
     
    text = '''
    
    '''
    html = etree.HTML(text)
    result = html.xpath('//li[1]/a/text()')
    print(result)
    result = html.xpath('//li[last()]/a/text()')
    print(result)
    result = html.xpath('//li[last()-2]/a/text()')
    print(result)
    result = html.xpath('//li[position()<3]/a/text()')
    print(result)
    ['first item']
    ['fifth item']
    ['third item']
    ['first item', 'second item']
    

    1.13 节点轴选择

    • XPath 具有很多节点轴选择方法,包括获取子元素,兄弟元素,父元素,祖先元素等,如下示例:
      • 第一次选择:调用 acestor 轴,其后加了两个冒号以及 * 号,以获取所有祖先节点
      • 第二次选择,调用 acestor 轴,冒号后面加了 div,代表选择祖先节点中的 div 节点
      • 第三次选择,调用 attribute 轴,可以获取所有属性值
      • 第四次选择,调用 child 轴,获取直接子节点
      • 第五次选择,调用 descendant 轴,获取子孙节点
      • 第六次选择,调用 following 轴,获取当前节点及其之后的节点
      • 第七次选择,调用 following-sibling 轴,获取当前节点及其之后的同级节点
    from lxml import etree
     
    text = '''
    
    '''
    html = etree.HTML(text)
    result = html.xpath('//li[1]/ancestor::*')
    print(result)
    result = html.xpath('//li[1]/ancestor::div')
    print(result)
    result = html.xpath('//li[1]/attribute::*')
    print(result)
    result = html.xpath('//li[1]/child::a[@href="link1.html"]')
    print(result)
    result = html.xpath('//li[1]/descendant::span')
    print(result)
    result = html.xpath('//li[1]/following::*[2]')              # 1 为当前节点
    print(result)
    result = html.xpath('//li[1]/following-sibling::*')         # 包含当前节点
    print(result)
    [, , , ]
    []
    ['item-0']
    []
    []
    []
    [, , , ]
    

    2. 使用 Beautiful Soup

    • Beautiful Soup 借助网页的结构和属性等特性来解析网页,因此不必写一些复杂的正则表达式

    2.1 解析器

    • Beautiful Soup 在解析时实际上以来解析器,除了支持 Python 标准库中的 HTML 解析器外,还支持一些第三方解析器如 lxml
    • lmxl 解析器有解析 HTML 和 XML 的功能,而且速度快,容错能力强,因此后面统一使用这个解析器

    2.2 基本用法

    • 实例引入:
      • 声明 html:html 并不是一个完整的 HTML 字符串, body 和 html 节点都没有闭合:
      • 实例化 BeautifulSoup 对象:将 html 作为第一个参数传入 soup,一个 BeautifulSoup 对象,该对象的第二个参数是解析器的类型,该步骤同时自动更正 html,补充缺失的尾标签
      • prettify() 方法:将要解析的字符串以标准的缩进格式输出
      • soup.title.string:选择 HTML 的 title 节点,并获得其文本
    from bs4 import BeautifulSoup
    
    # 声明 html
    html = """
    The Dormouse's story
    
    

    The Dormouse's story

    Once upon a time there were three little sisters; and their names were , Lacie and Tillie; and they lived at the bottom of a well.

    ...

    """
    # 实例化 BeautifulSoup 对象 soup = BeautifulSoup(html, 'lxml') print(soup.prettify()) print(soup.title.string)
    
     
      
       The Dormouse's story
      
     
     
      

    The Dormouse's story

    Once upon a time there were three little sisters; and their names were , Lacie and Tillie ; and they lived at the bottom of a well.

    ...

    The Dormouse's story

    2.3 节点选择器

    1. 选择元素
    • 如下例子详细说明选择元素的方法:
      • 首先打印输出 title 节点的选择结果,输出为 title 节点及其里面的文字内容
      • 然后输出 title 节点的类型,为 bs4.element.Tag 类型,是 BeautifulSoup 的一个重要的数据结构
      • Tag 具有一些属性如 string,调用后可得到节点的文本内容
      • 最后一个 p 节点输出的内容表明此方式只会输出第一个匹配到的节点
    from bs4 import BeautifulSoup
    
    # 声明 html
    html = """
    The Dormouse's story
    
    

    The Dormouse's story

    Once upon a time there were three little sisters; and their names were , Lacie and Tillie; and they lived at the bottom of a well.

    ...

    """
    # 实例化 BeautifulSoup 对象 soup = BeautifulSoup(html, 'lxml') print(soup.title) print(type(soup.title)) print(soup.title.string) print(soup.head) print(soup.p)
    The Dormouse's story
    
    The Dormouse's story
    The Dormouse's story
    

    The Dormouse's story

    1. 提取信息
    • 获取名称
    print(soup.title.name)
    title
    
    • 获取属性
    print(soup.p.attrs)          # 获取所有属性
    print(soup.p.attrs['name'])  # 获取 name 属性
    print(soup.p['name'])        # 同上
    print(soup.p['class'])       # 获取 class 属性,由于 class 可能有多个,因此返回的是列表
    {'class': ['title'], 'name': 'dromouse'}
    dromouse
    dromouse
    ['title']
    
    1. 嵌套选择
    • 我们在获取了节点之后,还可以继续调用来选取其内部的节点:
    from bs4 import BeautifulSoup
    
    html = """
    The Dormouse's story
    
    """
    
    soup = BeautifulSoup(html, 'lxml')
    print(soup.head.title)
    print(type(soup.head.title))
    print(soup.head.title.string)
    The Dormouse's story
    
    The Dormouse's story
    
    1. 关联选择
    • 先选择某个节点,再以该节点为基准,选择它的子节点、父节点、兄弟节点等
    • 子节点和子孙节点:
      • contents 属性得到直接子节点的列表
      • children 属性得到直接子节点的生成器
      • descendants 属性得到所有子孙节点的生成器
    from bs4 import BeautifulSoup
    
    html = """
    
        
            The Dormouse's story
        
        
            

    Once upon a time there were three little sisters; and their names were Elsie Lacie and Tillie and they lived at the bottom of a well.

    ...

    """
    soup = BeautifulSoup(html, 'lxml') # contents 属性 print(soup.p.contents)
    ['\n            Once upon a time there were three little sisters; and their names were\n            ', 
    Elsie
    , '\n', Lacie, ' \n            and\n            ', Tillie, '\n            and they lived at the bottom of a well.\n        ']
    
    # children 属性
    print(soup.p.children)
    for i, child in enumerate(soup.p.children):
        print(i, child)
    
    0 
                Once upon a time there were three little sisters; and their names were
                
    1 
    Elsie
    
    2 
    
    3 Lacie
    4  
                and
                
    5 Tillie
    6 
                and they lived at the bottom of a well.
    
    # descendants 属性
    print(soup.p.descendants)
    for i, child in enumerate(soup.p.descendants):
        print(i, child)
    
    0 
                Once upon a time there were three little sisters; and their names were
                
    1 
    Elsie
    
    2 
    
    3 Elsie
    4 Elsie
    5 
    
    6 
    
    7 Lacie
    8 Lacie
    9  
                and
                
    10 Tillie
    11 Tillie
    12 
                and they lived at the bottom of a well.
    
    • 父节点和子孙节点
      • parent 属性获取直接父节点
      • parents 属性获取所有祖先节点,返回生成器类型
    from bs4 import BeautifulSoup
    
    html = """
    
        
            The Dormouse's story
        
        
            

    Once upon a time there were three little sisters; and their names were Elsie

    ...

    """
    soup = BeautifulSoup(html, 'lxml') # parent 属性 print(soup.a.parent)

    Once upon a time there were three little sisters; and their names were Elsie

    # parents 属性
    print(soup.a.parents)
    for i, parent in enumerate(soup.a.parents):
        print(i, parent)
    
    0 

    Once upon a time there were three little sisters; and their names were Elsie

    1

    Once upon a time there were three little sisters; and their names were Elsie

    ...

    2 The Dormouse's story

    Once upon a time there were three little sisters; and their names were Elsie

    ...

    3 The Dormouse's story

    Once upon a time there were three little sisters; and their names were Elsie

    ...

    • 兄弟节点
      • next_sibling 和 previous_sibling 分别获取节点的下一个和前一个兄弟元素或节点
      • next_siblings 和 previous_siblings 分别获取节点的后面和前面的兄弟元素和节点
    from bs4 import BeautifulSoup
    
    html = """
    
        
            

    Once upon a time there were three little sisters; and their names were Elsie Hello Lacie and Tillie and they lived at the bottom of a well.

    """
    soup = BeautifulSoup(html, 'lxml') # parent 属性 print('Next Sibling', soup.a.next_sibling) print('Previous Sibling', soup.a.previous_sibling) print('Next Siblings', list(enumerate(soup.a.next_siblings))) print('Previous Siblings', list(enumerate(soup.a.previous_siblings)))
    Next Sibling 
                Hello
                
    Previous Sibling 
                Once upon a time there were three little sisters; and their names were
                
    Next Siblings [(0, '\n            Hello\n            '), (1, Lacie), (2, ' \n            and\n            '), (3, Tillie), (4, '\n            and they lived at the bottom of a well.\n        ')]
    Previous Siblings [(0, '\n            Once upon a time there were three little sisters; and their names were\n            ')]
    
    1. 提取信息
    • 提取信息的方法和之前一样:
    from bs4 import BeautifulSoup
    
    html = """
    
        
            

    Once upon a time there were three little sisters; and their names were BobLacie

    """
    soup = BeautifulSoup(html, 'lxml') print('Next Sibling:') print(type(soup.a.next_sibling)) print(soup.a.next_sibling) print(soup.a.next_sibling.string) print('Parents:') print(type(soup.a.parents)) print(list(soup.a.parents)[0]) print(list(soup.a.parents)[0].attrs['class'])
    Next Sibling:
    
    Lacie
    Lacie
    Parents:
    
    

    Once upon a time there were three little sisters; and their names were BobLacie

    ['story']

    2.4 方法选择器

    • 属性选择器很快但是不适合复杂的选择,通过 find_all(), find() 等方法,通过传入一些参数,可以很灵活地进行查询
    1. find_all()
    • find_all() 查询所有符合条件的元素,API 如下:

      find_all(name, attrs, recursive, text, **kwargs)
      
    • name:根据节点名查询元素:

      • 返回结果是列表类型,其元素仍为 bs4.element.Tag 类型
      • 因为都是 Tag 类型,所以仍可以进行嵌套查询
    from bs4 import BeautifulSoup
    
    html='''
    

    Hello

    • Foo
    • Bar
    • Jay
    • Foo
    • Bar
    '''
    soup = BeautifulSoup(html, 'lxml') print(soup.find_all(name='ul')) print(type(soup.find_all(name='ul')[0]))
    [
    • Foo
    • Bar
    • Jay
    ,
    • Foo
    • Bar
    ]
    # 嵌套查询
    for ul in soup.find_all(name='ul'):
        print(ul.find_all(name='li'))
        for li in ul.find_all(name='li'):
            print(li.string)
    [
  • Foo
  • ,
  • Bar
  • ,
  • Jay
  • ] Foo Bar Jay [
  • Foo
  • ,
  • Bar
  • ] Foo Bar
    • attrs:根据属性查询:
      • 传入的参数是字典类型
      • 对于常用的属性,如 id 和 class 等,可不用 attrs 传递,直接传入 id=‘list-1’ 或 class_=‘element’ (因为 class 是 Python 里的关键字,因此需要加上 _ )
    from bs4 import BeautifulSoup
    
    html='''
    

    Hello

    • Foo
    • Bar
    • Jay
    • Foo
    • Bar
    '''
    soup = BeautifulSoup(html, 'lxml') print(soup.find_all(attrs={'id': 'list-1'}), '\n') print(soup.find_all(id='list-1'), '\n') print(soup.find_all(attrs={'class': 'element'}), '\n') print(soup.find_all(class_='element'))
    [
    • Foo
    • Bar
    • Jay
    ] [
    • Foo
    • Bar
    • Jay
    ] [
  • Foo
  • ,
  • Bar
  • ,
  • Jay
  • ,
  • Foo
  • ,
  • Bar
  • ] [
  • Foo
  • ,
  • Bar
  • ,
  • Jay
  • ,
  • Foo
  • ,
  • Bar
  • ]
    • text:
      • text 参数可用来匹配节点的文本,传入的形式可以是字符串或正则表达式对象:
    import re
    from bs4 import BeautifulSoup
    
    html='''
    
    '''
    
    soup = BeautifulSoup(html, 'lxml')
    print(soup.find_all(text=re.compile('link')))
    ['Hello, this is a link', 'Hello, this is a link, too']
    
    1. find():find() 返回的是单个元素,也就是第一个匹配的元素
    2. 其他查询方法
      • find_parents() 和 find_parent():前者返回所有祖先节点,后者返回直接父节点
      • find_next_siblings() 和 find_next_sibling():前者返回后面所有的兄弟节点,后者返回后面第一个兄弟节点
      • find_previous_siblings() 和 find_next_sibling():前者返回前面所有的兄弟节点,后者返回前面第一个兄弟节点
      • find_all_next() 和 find_next():前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点
      • find_all_previous() 和 find_previous():前者返回节点前所有符合条件的节点,后者返回第一个符合条件的节点

    2.5 CSS 选择器

    • 使用 CSS 选择器时,只需要调用 select() 方法,传入相应的 CSS 选择器即可:
    from bs4 import BeautifulSoup
    
    html='''
    

    Hello

    • Foo
    • Bar
    • Jay
    • Foo
    • Bar
    '''
    soup = BeautifulSoup(html, 'lxml') print(soup.select('.panel .panel-heading')) # 选择 class 为 panel 中 class 为 panel-heading 的节点 print(soup.select('ul li')) # 选择 ul 节点里的 li 节点 print(soup.select('#list-2 .element')) # 选择 id 为 list-2 中 class 为 element 的节点 print(type(soup.select('ul')[0]))
    [

    Hello

    ] [
  • Foo
  • ,
  • Bar
  • ,
  • Jay
  • ,
  • Foo
  • ,
  • Bar
  • ] [
  • Foo
  • ,
  • Bar
  • ]
    • 嵌套选择:
    for ul in soup.select('ul'):
        print(ul.select('li'))
    [
  • Foo
  • ,
  • Bar
  • ,
  • Jay
  • ] [
  • Foo
  • ,
  • Bar
  • ]
    • 获取属性
    for ul in soup.select('ul'):
        print(ul['id'])
        print(ul.attrs['id'])
    list-1
    list-1
    list-2
    list-2
    
    • 获取文本:除了 string 属性,还有一个 get_text() 方法
    for li in soup.select('li'):
        print('Get Text:', li.get_text())
        print('String:', li.string)
    Get Text: Foo
    String: Foo
    Get Text: Bar
    String: Bar
    Get Text: Jay
    String: Jay
    Get Text: Foo
    String: Foo
    Get Text: Bar
    String: Bar
    

    3. 使用 pyquery

    3.1 初始化

    • 与 Beautiful Soup 一样,初始化 pyquery 时,也需要传入 HTML 文本来初始化一个 PyQuery 对象,初始化方式有多种,比如直接传入字符串,传入 URL,传入文件名等
    1. 字符串初始化:将 HTML 字符串直接传递给 PyQuery 类,然后将初始化的对象传入 CSS 选择器
    from pyquery import PyQuery as pq
    
    html = '''
    
    '''
    
    doc = pq(html)
    print(doc('li'))
  • first item
  • second item
  • third item
  • fourth item
  • fifth item
    1. URL 初始化:将网页的 URL 传入对象 PyQuery,对象会首先请求这个 URL,然后用得到的 HTML 内容完成初始化:
    from pyquery import PyQuery as pq
    
    doc = pq(url='https://www.taobao.com')
    print(doc('title'))
    
    # 如下代码等效
    from pyquery import PyQuery as pq
    import requests
    doc = pq(requests.get('https://www.taobao.com').text)
    print(doc('title'))
    淘宝网 - 淘!我喜欢
    
    淘宝网 - 淘!我喜欢
    
    1. 文件初始化:将本地文件传入对象:
    from pyquery import PyQuery as pq
    
    doc = pq(filename='demo.html')
    print(doc('title'))
    This is a Demo
    

    3.2 基本 CSS 选择器

    from pyquery import PyQuery as pq
    html = '''
    
    '''
    
    doc = pq(html)
    print(doc('#container .list li'))
    print(type(doc('#container .list li')))
  • first item
  • second item
  • third item
  • fourth item
  • fifth item
  • 3.3 查找节点

    1. 子节点
    • 查找子孙节点使用 find() 方法,传入的参数是 CSS 选择器
    • 查找直接子节点使用 children() 方法
    from pyquery import PyQuery as pq
    html = '''
    
    '''
    doc = pq(html)
    items = doc('#container')
    print(type(items))
    print(items)
    
    # 所有子孙节点
    lis = items.find('ul, li')
    print(type(lis))
    print(lis)
    
    # 直接子节点
    lis = items.children('.list')
    print(type(lis))
    print(lis)
    
    
    
    
     
  • first item
  • second item
  • third item
  • fourth item
  • fifth item
    1. 父节点
    • 使用 parent() 方法,获取直接父节点
    • 使用 parents() 方法,获取祖先节点
    from pyquery import PyQuery as pq
    html = '''
    
    '''
    doc = pq(html)
    items = doc('.list')
    
    # 直接父节点
    container = items.parent()
    print(type(container))
    print(container, '\n')
    
    # 所有祖先节点
    parents = items.parents()
    print(type(parents))
    print(parents, '\n')
    
    # class 为 wrap 的祖先节点
    parent = items.parents('.wrap')
    print(type(parent))
    print(parent)
    
    
      
    
    
    
      
    
    
    
    
    1. 兄弟节点
    • 兄弟节点使用 siblings() 方法
    from pyquery import PyQuery as pq
    html = '''
    
    '''
    doc = pq(html)
    li = doc('.list .item-0.active')
    
    # 所有兄弟节点
    print(li.siblings(), '\n')
    
    # 筛选后的兄弟节点
    print(li.siblings('.active'))
  • second item
  • first item
  • fourth item
  • fifth item
  • fourth item
  • 3.4 遍历

    • pyquery 选择的结果可能是单个节点,也可能是多个节点,对于单个节点,可以直接打印输出,也可以转为字符串;对于多个节点,需要使用 items() 方法得到生成器后进行遍历
    from pyquery import PyQuery as pq
    html = '''
    
    '''
    doc = pq(html)
    
    # 单个节点
    li = doc('.item-0.active')
    print(li)
    print(str(li))
    
    # 多个节点
    lis = doc('li').items()
    print(type(lis))
    for li in lis:
        print(str(li).strip(), type(li), sep='\n')
  • third item
  • third item
  • first item
  • second item
  • third item
  • fourth item
  • fifth item
  • 3.5 获取信息

    1. 获取属性
    • 获取到某个 PyQuery 类型的节点后,可调用 attr() 方法获取其属性,也可调用 atrr 属性来获取
    • 如果选中的是多个元素,直接调用 attr() 方法,会得到第一个元素的属性,要得到所有元素的属性,需要进行遍历
    from pyquery import PyQuery as pq
    
    html = '''
    
    '''
    
    doc = pq(html)
    
    # 单个元素
    a = doc('.item-0.active a')
    print(a, type(a))
    print(a.attr('href'))
    print(a.attr.href)
    print('\n')
    # 多个元素
    a = doc('a')
    for item in a.items():
        print(item.attr('href'))
    third item 
    link3.html
    link3.html
    
    
    link2.html
    link3.html
    link4.html
    link5.html
    
    1. 获取文本
    • 获取纯文本:text() 方法,对于多节点来说,会返回所有选中节点的纯文本,中间用空格分开,合成一个字符串,不用进行遍历
    • 获取 HTML 文本:html() 方法,对于多节点需要进行遍历
    from pyquery import PyQuery as pq
    
    html = '''
    
    '''
    
    doc = pq(html)
    
    # 单个节点
    a = doc('.item-0.active a')
    print(a)
    print(a.text())    # text()
    print('\n')
    print(a.html())    # html()
    print('\n')
    
    # 多个节点
    li = doc('li')
    print(li)
    print(li.text())
    print('\n')
    for item in li.items():
        print(item.html())
    third item
    third item
    
    
    third item
    
    
    
  • first item
  • second item
  • third item
  • fourth item
  • fifth item
  • first item second item third item fourth item fifth item first item second item third item fourth item fifth item

    3.6 节点操作

    • pyquery 提供了一系列方法来对节点进行动态修改,比如为某个节点添加一个 class,移除某个节点等,如下为一些例子
    1. addClass 和 removeClass,前者添加 class,后者删除 class
    from pyquery import PyQuery as pq
    
    html = '''
    
    '''
    
    doc = pq(html)
    li = doc('.item-0.active')
    print(li)
    li.removeClass('active')
    print(li)
    li.addClass('active')
    print(li)
  • third item
  • third item
  • third item
    1. attr, text 和 html:attr() 方法可以对属性进行操作,其第一个参数是属性名,第二个参数是属性值;text() 和 html() 方法可以改变节点的内容
    from pyquery import PyQuery as pq
    
    html = '''
    
    '''
    
    doc = pq(html)
    li = doc('.item-0.active')
    print(li)
    li.attr('name', 'link')
    print(li)
    li.text('changed item')
    print(li)
    li.html('changed item')
    print(li)
  • third item
  • third item
  • changed item
  • changed item
    1. remove:移除节点
    from pyquery import PyQuery as pq
    
    html = '''
    
    Hello, World

    This is a paragraph.

    '''
    doc = pq(html) wrap = doc('.wrap') print(wrap.text()) # 只要 Hello, World,不要 p 节点里的内容 wrap.find('p').remove() print(wrap.text())
    Hello, World
    This is a paragraph.
    Hello, World
    
    1. 其他方法如 append(), empty(), prepend() 可参考官方文档 http://pyquery.readthedocs.io/en/latest/api.html

    3.7 伪类选择器

    • CSS 选择器还支持多种伪类选择器,如选择第一个节点、最后一个节点、奇偶数节点、包含某一文本的节点等:
    from pyquery import PyQuery as pq
    
    html = '''
    
    '''
    
    doc = pq(html)
    # 第一个 li 节点
    li = doc('li:first-child')
    print('第一个 li 节点', li)
    # 最后一个 li 节点
    li = doc('li:last-child')
    print('最后一个 li 节点', li)
    # 第二个 li 节点
    li = doc('li:nth-child(2)')
    print('第二个 li 节点', li)
    # 第三个 li 之后的 li 节点
    li = doc('li:gt(2)')
    print('第三个 li 之后的 li 节点', li)
    # 偶数位置的 li 节点
    li = doc('li:nth-child(2n)')
    print('偶数位置的 li 节点', li)
    # 包含 second 的 li 节点
    li = doc('li:contains(second)')
    print('包含 second 的 li 节点', li)
    第一个 li 节点 
  • first item
  • 最后一个 li 节点
  • fifth item
  • 第二个 li 节点
  • second item
  • 第三个 li 之后的 li 节点
  • fourth item
  • fifth item
  • 偶数位置的 li 节点
  • second item
  • fourth item
  • 包含 second 的 li 节点
  • second item
  • 你可能感兴趣的:(爬虫学习)