• Python beautifulSoup


    BeautifulSoup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

    Tag、NavigableString、BeautifulSoup 、Comment

    Tag对象与XML或HTML原生文档中的Tag相同,比如<title>The Dormouse's story </title>或者<a href ="http://example.com/elsie" class="sister" id="link1">Elsie</a>,title和a标记及其里面的内容称为Tag对象

    怎么样从soup对象中抽取Tag呢?示例如下html

    <html><head><title> The Dormouse's story</title></head>
    <body>
    <p class="title"><b> The Dormouse's story </b></p>
    <p class="story"> Once upon a time there were three little sisters;and their names were

    <a href="http://example.com/elsie" class="sister" id='link1'><!--Elsie--></a>
    <a href="http://example.com/lacie" class="sister" id='link2'><!--Lacie--></a>
    <a href="http://example.com/tillie" class="sister" id='link3'>Tillie</a>

    and they lived at the bottom of a well</p>

    <p class="story">...</p>

    </body></html>

    #coding:utf-8

    from bs4 import BeautifulSoup

    soup = BeautifulSoup(open('index.html'),'lxml')

    print soup.title  #抽取title

    print soup.a    #抽取 a

    print soup.p   #抽取p

    从例子中可以看到利用soup加标记名就可以获取这些标记的内容。不过利用这种方式,查找的是所有内容中第一个符合要求的标记

    Tag中有两个重要的属性:name 和attributes。每个Tag都有自己的名字,通过.name来获取

    print soup.p.name

    print soup.a.name

    Tag不仅可以获取name,还可以修改name,改变之后将影响所有通过当前Beautiful Soup对象生成的HTML文档

    soup.title.name = 'mytitle'

    print soup.title

    print soup.mytitle

    这里已经将title标记成功修改为mytitle

    Tag中的属性,<p class="title"><b> The Dormouse's story</b></p> 有一个class属性值为title,Tag的属性的操作方法与字典相同:

    print soup.p['class']

    print soup.p.get('class')

    也可以直接点取属性,比如.attrs,用于获取Tag中所有属性:

    print soup.p.attrs

    和name一样,我们可以对标记中的这些属性和内容进行修改,示例如下

    soup.p['class'] = 'myClass'

    NavigableString

    我们已经得到了标记的内容,要想获取标记内部的文字,需要用到.string示例如下:

    print soup.p.string

    print type(soup.p.string)

    BeautifulSoup用NavigableString类来包装Tag中的字符串,一个NavigableString字符串与Python中的Unicode字符串相同,通过unicode()方法可以直接将NavigableString对象转换成Unicode字符串

    BeautifulSoup

    BeautifulSoup对象表示的是一个文档的全部内容。大部分时候,可以把它当做Tag对象,是一个特殊的Tag,因为BeautifulSoup对象并不是真正的HTML或XML的标记,所以它没有name和attribute属性

    Comment

    Tag,NavigableString、BeautifulSoup几乎覆盖了HTML和XML中的所有内容,但是还有一些特殊对象,容易让人担心的内容是文档的注释部分

    print soup.a.string

    a标记里的内容实际上是注释,但是如果我们利用.string来输出它的内容,会发现它已经把注释符号去掉了。另外如果打印输出它的类型,会发现它是一个Comment类型。如果在我们不清楚这个标记.string的情况下,可能造成数据提取混乱。因此在提取字符串时,可以判断一下类型

    if type(soup.a.string) == "bs4.element.Comment":

    print soup.a.string

    遍历文档树

    BeautifulSoup 会将HTML转换为文档树进行搜索,既然是树形结构,节点的概念必不可少

    子节点:

    首先说一下直接子节点,Tag中的.contents和.children是非常重要的。Tag的.content属性可以将Tag子节点以列表的方式输出

    print soup.head.contents

    有一点需要注意:字符串没有.contents属性,因为字符串没有子节点 .children属性返回的是一个生成器,可以对Tag的子节点进行循环

    for child in soup.head.children:

    print(child)

    .contents和.children属性仅包含Tag的直接子节点。例如,<head>标记只有一个直接子节点<title>。但是<title>标记也包含一个子节点:字符串"The  Dormouse's story",这种情况下字符串也属于<head>标记的子孙节点。.descendants属性可以对所有tag的子孙节点进行

    递归循环


    for child in soup.head.descendants:

    print child

    以上都是关于如何获取子节点,接下来说一下如何获取节点的内容,这就涉及.string、strings、stripped_strings三个属性.

    .string这个属性很有特点:如果一个标记里面没有标记了,那么.string就会返回标记里面的内容。如果标记里面只有唯一的一个标记了,那么.string也会返回最里面的内容。如果tag包含了多个子节点,tag就无法确定,string方法应该调用那个子节点的内容,

    .string的输出结果是None

    .strings属性主要应用于tag中包含多个字符串的情况,可以进行循环遍历

    for string in soup.strings:

    print repr(string)

    .stripped_strings和strings类似,.stripped_strings属性可以去掉输出字符串中包含的空格或空行,示例如下:

    for string in soup.stripped_strings:

    print repr(string)

    父节点: 

    每个Tag或字符串都有父节点:被包含在某个Tag中 通过.parent属性来获取某个元素的父节点

    print soup.title.parent

    通过元素的.parents属性可以递归得到元素的所有父辈节点

    for parent in soup.a.parents:

    if parent is None:

    print(parent)
    else:

    print(parent.name)

    兄弟节点:

    兄弟节点可以理解为和本节点处在同一级的节点,.next_sibling属性可以获取该节点的下一个兄弟节点 .previous_sibling则与之相反,如果节点不存在,则返回None

    print soup.p.next_sibling

    print soup.p.prev_sibling

    print soup.p.next_sibling.next_sibling

    第一个输出结果为空白,因为空白或者换行也可以被视作一个节点,所以得到的结果可能是空白或者换行

    通过.next_siblings和.previous_siblings属性可以对当前节点的兄弟节点迭代输出:

    for sibling in soup.a.next_siblings:

    print(repr(sibling))

    前后节点:

    前后节点需要使用.next_element、previous_element这两个属性与.next_sibling  .previous_sibling不同,它并不是针对于兄弟节点,而是针对所有节点,不分层次

    print soup.head

    print soup.head.next_element

    如果想遍历所有的前节点或者后节点,通过.next_elements 和.previous_elements 的迭代器就可以向前或向后访问文档的解析内容


    for element in soup.a.next_elements:

    print(repr(element))

    搜索文档树:

    BeautifulSoup定义了很多搜素方法,这里着重介绍find_all()方法

    find_all(name,attrs,recursive,text,**kwargs)

    name 参数可以查找所有名字为name的标记,字符串对象会被自动忽略掉。name参数取值可以是字符串、正则表达式、列表、True和方法,最简单的过滤器是字符串。在搜索方法中传入一个字符串参数,BeautifulSoup会查找与字符串完整匹配的内容

    print soup.find_all('b')

    如果传入正则表达式作为参数,BeautifulSoup会通过正则表达式的match()来匹配内容。下面的列子中找出所有以b开头的标记

    import re

    for tag in soup.find_all(re.compile("^b"))

        print(tag.name)

    如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回,下面的代码找到文档中所有<a>标记和<b>标记

    print soup.find_all(['a','b'])

    如果传入的参数是True,True可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点(只会返回标签)

    for tag in soup.find_all(True):
    print tag.name

    如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数Tag节点,如果这个方法返回True表示当前元素匹配并且被找到,如果不是则返回False。比如过滤包含class属性,也包含id属性的元素

    def hasClass_Id(tag):

    return tag.has_attr('class') and tag.has_attr('id')
    tag = soup.find_all(hasClass_Id)

    print tag

    2 kwargs 参数

    kwargs参数在python 中表示keyword参数。如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当做指定名字Tag的属性来搜索。搜索指定名字的属性时可以使用的参数值包括字符串、正则表达式、列表、True

    如果包含id参数,Beautiful Soup会搜索每个tag的id属性

    print soup.find_all(id='link2')

    如果出入href参数,BeautifulSoup会搜索每个Tag的href属性。比如查找href属性中含有elsie的tag

    print soup.find_all(href = re.compile('elsie'))

    下面的代码在文档树中查找所有包含id属性的Tag,无论id的值是什么

    print soup.find_all(id = True)

    如果我们想用class过滤,但是class是python的关键字,需要在class后面加个下划线:

    print soup.find_all('a',class_='sister')

    使用多个指定名字的参数可以同时过滤tag的多个属性

    print soup.find_all(href = re.compile('elsie'),id = 'link1')

    有些tag属性在搜索中不能使用,比如HTML5中的data-*属性

    data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')

    data_soup.find_all(data-foo = "value")这样的代码在Python中是不合法的,但是可以通过find_all()方法的attrs参数定义一个字典参数来搜索包含特殊属性的tag

    data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')

    data_soup.find_all(attrs={"data-foo":"value"})

    text 参数

    通过text参数可以搜索文档中的字符串内容。与name参数的可选值一样,text参数接受 字符串、正则表达式、列表、True。

    print soup.find_all(text = "Elsie")

    print soup.find_all(text = ["Tillie","Elsie","Lacie"])

    print soup.find_all(text = re.compile("Dormouse"))

    limit 参数

    find_all()方法返回全部的搜索结构,如果文档树很大那么搜索会很慢。如果我们不需要全部结果,可以使用limit参数限制返回结果的数量

    print soup.find_all('a',limit = 1)

    print soup.find_all('a',limit = 3)

    recursive参数

    调用tag的find_all()方法时,BeautifulSoup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数recursive=Fasle

    CSS选择器:

    用到的方法是soup.select(),返回类型是list

     1:通过标记名称进行查找

    通过标记名称可以直接查找、逐层查找,也可以找到某个标记下的直接子标记和兄弟节点标记

    #直接查找title标记
    print soup.select('title')

    #逐层查找title标记

    print soup.select('html head title')

    #查找直接子节点
    #查找head下的title标记

    print soup.select("head > title")

    #查找p下的id =‘link1’的标记

    print soup.select("p > #link1")

    #查找兄弟节点
    #查找id=link1之后class=sister的所有兄弟标记

    print soup.select("#link1 ~ .sister")

    #查找紧跟着id = "link1"之后class=sister的子标记
    print soup.select("#link1 + .sister")

    通过css的类名查找:

    print soup.select(".sister")

    print soup.select("[class~=sister]")

    通过tag的id查找: 

    print soup.select("#link1")

    print soup.select("a#link1")

    通过属性值来查找:

    print soup.select('a[href="http://example.com/elsie"]') 

  • 相关阅读:
    Vsftpd 3.0.2 正式版发布
    Putdb WebBuilder 6.5 正式版本发布
    SoaBox 1.1.6 GA 发布,SOA 模拟环境
    pynag 0.4.6 发布,Nagios配置和插件管理
    Percona Playback 0.4,MySQL 负荷回放工具
    xombrero 1.3.1 发布,微型 Web 浏览器
    Hypertable 0.9.6.4 发布,分布式数据库
    libmemcached 1.0.11 发布
    CryptoHeaven 3.7 发布,安全邮件解决方案
    Android Activity生命周期
  • 原文地址:https://www.cnblogs.com/paulversion/p/8330482.html
Copyright © 2020-2023  润新知