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"]') 

原文地址:https://www.cnblogs.com/paulversion/p/8330482.html