python爬虫学习基础之网页解析(2)BeautifulSoup


 

 

 

网页解析:从网页中提取出所需的信息(例如新的url,数据等等)

网页解析常用的方法有:re(正则表达式),BeautifulSoup,lxml,parsel,requests-html

这一篇只讲BeautifulSoup,其后面的以后面发,敬请期待吧。

 

官方文档:Beautiful Soup 4.4.0 文档 — Beautiful Soup 4.2.0 中文 文档Beautiful Soup Documentation — Beautiful Soup 4.9.0 documentation (crummy.com)

引入:

  Beautiful Soup 是一个可以从HTML(网页)或XML文件中提取数据的Python库。

  Beautiful Soup 3 目前已经停止开发,我们推荐在现在的项目中使用Beautiful Soup 4, 移植到BS4(导入的位置)

 

例子:

html_doc = """
<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> and
<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>
"""



from bs4 import BeautifulSoup

# 使用BeautifulSoup解析这段代码,
# 能够得到一个 BeautifulSoup 的对象,
# 并能按照HTML标准的缩进格式的结构输出,而且会把缺失的标签补齐:
soup = BeautifulSoup(html_doc, 'html.parser')
print(soup)

运行结果:

 

需要安装 (参考):Python安装Bs4及使用方法_python_脚本之家 (jb51.net)

 

 

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

 

一、结点对象

 1、Tag(标签)

粗略的讲一下,不一定准确,html的标签 :在html文件中 <name>  <\name>  出现一对类似这样的,name就是标签。可以利用它定位并获取相关的信息。

例如:

from bs4 import BeautifulSoup

# 获取BeautifulSoup对象,
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'html.parser')

# 标准html输出
print(soup.prettify())

# 获取对象的标签
tag = soup.b

print('------------\n\n')
# 数据类型
print(type(tag))

运行结果:

 

进一步介绍一下Tag中的一些重要属性。

1、Name

  获取标签的名字,在<name>  <\name>中name就是标签名 

用法:标签对象.name

 

2、Attributes(属性)

  标签其它属性。

    属性略作解释,在html中 ,<name>  <\name> 在<name>中添加赋值的情况,例如:<name class="1">。class就是属性,1就是属性值。

  可以自己添加和修改,也可以删除(del 标签对象.['属性'])。

用法:标签.对象['属性'],可以获取属性值。或者标签对象.attrs,可以获取属性与属性值的字典数据

 

3、对于同一个标签,可能有多个属性值在Beautiful Soup中多值属性的返回类型是list

 

例子:

from bs4 import BeautifulSoup

# 获取BeautifulSoup对象,
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'html.parser')


# 获取对象的标签
tag = soup.b


print('标签名:',tag.name)

print('获取属性值: ',tag['class'])

print('获取,属性与属性值:',tag.attrs)

# 修改属性值
tag['class'] = '修改'

# 添加属性
tag['new'] = '新添加的'

# 查看效果
print('看看修改和添加的结果:',tag.attrs)

运行结果:

 

 

 2、 NavigableString 

 为了获取标签内容<name> Text <\name>  Text

实现方法:标签对象.string

对象转换成Unicode字符串: unicode_string = unicode(tag.string)

tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用replace_with() 方法:  tag.string.replace_with("修改的内容")

代码:

from bs4 import BeautifulSoup

# 获取BeautifulSoup对象,
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'html.parser')


# 获取对象的标签
tag = soup.b

print('内容:',soup.string)

运行结果:

 

 

3、BeautifulSoup

 

BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象.

 

 

因为 BeautifulSoup 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.

但有时查看它的 .name 属性是很方便的,所以 BeautifulSoup 对象包含了一个值为 “[document]” 的特殊属性 .name

 

 方法:soup = BeautifulSoup()

 

4、Comment

对象是一个特殊类型的 NavigableString 对象:其输出的内容不包括注释符号。

html注释的写法:<!-- 这里是注释内容--> ;注释符号<!----> 

text = '''
<b><!--Hey, buddy. Want to buy a used parser?--></b>
'''

# 获取BeautifulSoup对象
soup = BeautifulSoup(text,'html.parser')


# 获取内容
string = soup.b.string
print(string)

# 查看一下它的数据类型
print(type(string))

运行结果:

 

二、遍历文档树

在HTML中Tag可以嵌套,所以就会出现各个结点之间关系的描述属性

 

1、子节点

tag的子节点: .contents 或 .children

  tag的 contents属性可以将tag的子节点以列表的方式输出: soup.contents[0].name,第一个子节点的名字

    BeautifulSoup 对象本身一定会包含子节点,也就是说<html>标签也是 BeautifulSoup 对象的子节点:

    字符串没有 .contents 属性,因为字符串没有子节点:

  

  通过tag的 .children生成器,可以对tag的子节点进行循环:

    for child in tag.children:

      print(child)

 

2、孙子节点:子节点的子节点

.descendants属性可以对所有tag的子孙节点进行递归循环

for child in tag.descendants:

  print(child)

 

 2.1、内容问题

.string

如果一个tag仅有一个子节点,那么这个tag也可以使用 .string方法,输出结果与当前唯一子节点的 .string 结果相同:

如果tag包含了多个子节点,tag就无法确定 .string方法应该调用哪个子节点的内容, .string 的输出结果是 None:

 

所以引出:

.strings 和 stripped_strings

如果tag中包含多个字符串  ,可以使用 .strings来循环获取

for string in soup.strings:

  print(repr(string))

 

输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容:

for string in soup.stripped_strings:

  print(repr(string))

全部是空格的行会被忽略掉,段首和段末的空白会被删除

 

 

3、父节点:

.parent

通过 .parent属性来获取某个元素的父节点.

BeautifulSoup 对象的 .parent是None:

.parents

通过元素的 .parents属性可以递归得到元素的所有父辈节点。递归得到父辈元素的所有节点,返回一个生成器

 

4、兄弟节点(同一级别的结点的,必须是同一个父节点)

.previous_sibling和.next_sibling(Tag前一个和后一个结点)

 

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

for sibling in soup.a.next_siblings:

  print(repr(sibling))

for sibling in soup.find(id="link3").previous_siblings:

  print(repr(sibling))

 

5、回退和前进

 

.next_element 和 .previous_element

.next_element 属性指向解析过程中下一个被解析的对象(字符串或tag),结果可能与 .next_sibling 相同,但通常是不一样的.

 

 

.previous_element 属性刚好与 .next_element  相反,它指向当前被解析的对象的前一个解析对象:

 

.next_elements 和 .previous_elements

通过.next_element 和 .previous_element 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样:

 

 

三、方法

1、方法:find_all()

作用: 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件.

语法:soup.find_all(name,  attrs,  recursive,  text,  limit, **kwargs)

参数解释:

 

1、name参数:

  name:参数可以查找所有名字为 name 的tag。

    参数的值可以是:字符串,正则表达式,列表,方法或是 True

    如果是Ture的话:可以匹配任何值, 找到所有的tag,但是不会返回字符串节点

  

2、attrs 参数:

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

    但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:

      soup = BeautifulSoup('<div data-foo="value">foo!</div>', 'html.parser')

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

      

    也可以通过属性与属性值构建字典

      例如:attrs={"id": True}  ,class(不用class_)同理

 

3、recursive参数:

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

  默认recursive=True

 

4、text 参数:

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

  标签内容,<a> text <\a> 这里的text就是要匹配的地方,而且返回也是text。

  一般都用组合的方式来定位 :soup.find_all("a", text="Elsie")

 

5、limit 参数:

  可以使用 limit 参数限制返回结果的数量。

    例如:soup.find_all("a", limit=2)   如果搜索找到a标签的数量2,则会停止搜索。

  

6、kwargs参数:

如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,

  如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性

    例如:soup.find_all(id="link1")  (如果没有属性值,好像会是全部)

 

  如果传入href  参数,Beautiful Soup会搜索每个tag的”href”属性:

    soup.find_all(href=re.compile("elsie"))

 

  如果属性为class不能直接用class(会报错),必须用class_    Beautiful Soup会搜索每个tag的”class”属性:

    例如:soup.find_all(class_="story")

  

  属性值可以是:字符串,正则表达式,列表,True

    id=True的话就是只要有id这个属性都可以,href,class_同理。

   

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

    例如:soup.find_all(href=True , id=True)

 

 后面的相关函数的参数一样则效果一致

 

2、像调用 find_all() 一样调用tag

BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,等价于对象的 find_all()

 

下面两个等价

soup.find_all("a")

soup("a")

 

下面两个等价

soup.title.find_all(string=True)

soup.title(string=True)

 

3、find()

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

find_all() 方法将返回文档中符合条件的所有tag,数据是列表;而find(),只返回一个结果,不是列表

结果只有一个所以没有limit这个参数。

 

 

4、find_parents() 和 find_parent()

pycharm把鼠标放到函数上面,ctrl+Q可以查看函数信息。

find_parent(name , attrs , **kwargs)

find_parents(name, attrs , limit, **kwargs)

 

记住:find_all() 和  find() 只搜索当前节点的所有子节点,孙子节点等.

     find_parents() 和 find_parent 用来搜索当前节点的父辈节点,搜索方法与普通tag的搜索方法相同

 

 

5、find_next_siblings() 合 find_next_sibling()

find_next_sibling(name , attrs , text, **kwargs)

find_next_siblings(name , attrs , text , limit , **kwargs)

  对当tag的所有后面解析  的兄弟tag节点进行迭代, find_next_siblings 方法返回所有符合条件的后面的兄弟节点, 

  find_next_sibling 只返回符合条件的后面的第一个tag节点。

 

6、find_previous_siblings() 和 find_previous_sibling()

find_previous_siblings( name , attrs , text , limit ,**kwargs)

find_previous_sibling(name , attrs ,  text , **kwargs)

  对当前tag的前面解析的兄弟tag节点进行迭代, find_previous_siblings 方法返回所有符合条件的前面的兄弟节点,

  find_previous_sibling 方法返回第一个符合条件的前面的兄弟节点:

 

7、find_all_next() 和 find_next()

find_all_next(name , attrs , text , limit , **kwargs)

find_next(name , attrs , text , **kwargs)

对当前tag的之后的 tag和字符串进行迭代, find_all_next() 方法返回所有符合条件的节点, find_next() 方法返回第一个符合条件的节点:

 

8、find_all_previous() 和 find_previous()

find_all_previous(name , attrs , text , limit ,**kwargs: Any)

find_previous(name , attrs , text , **kwargs: Any)

对当前节点前面 的tag和字符串进行迭代, find_all_previous() 方法返回所有符合条件的节点, find_previous() 方法返回第一个符合条件的节点.

 

 

四、CSS选择器

Beautiful Soup支持大部分的CSS选择器

在 Tag 或 BeautifulSoup 对象的 .select() 方法中传入字符串参数, 即可使用CSS选择器的语法找到tag:

例如:soup.select("title")

 

 

1、通过tag标签逐层查找(空格隔开):

例如:soup.select("body a") ,soup.select("html head title")

 

2、找到某个Tag标签下的直接子标签(>隔开):

例如:soup.select("head > title")

    soup.select("p > a:nth-of-type(3)") 对应的第三个

    soup.select("p > #link1")  #对应id属性值

 

3、找到兄弟节点标签( ~ . 隔开):

  例如:soup.select("#link1 ~ .sister")

 

4、通过CSS的类名查找(.属性值):

soup.select(".sister")

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

 

5、通过tag的id查找:

soup.select("#link1")

soup.select("a#link2")

 

 

6、同时用多种CSS选择器查询元素:

soup.select("#link1,#link2")

 

 

7、通过是否存在某个属性来查找:

soup.select('a[href]')

 

8、通过属性的值来查找:

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

soup.select('a[href^="http://example.com/"]')      属性值以它为开头

soup.select('a[href$="tillie"]')        属性值以它为结尾

soup.select('a[href*=".com/el"]')    属性值包含

 

9、通过语言设置来查找:

multilingual_markup="""

<p lang="en">Hello</p>

<p lang="en-us">Howdy, y'all</p>

 <p lang="en-gb">Pip-pip, old fruit</p>

 <p lang="fr">Bonjour mes amis</p>

"""

multilingual_soup = BeautifulSoup(multilingual_markup)

multilingual_soup.select('p[lang|=en]')

 

10、返回查找到的元素的第一个

例如:soup.select_one(".sister")

 

 

 

四、修改文档树

1、修改tag的名字和属性

重命名一个tag,改变属性的值,添加或删除属性:

例如:

  

soup = BeautifulSoup('<b class="boldest">Extremely bold</b>','html.parser')

  tag = soup.b

  # 修改名字

  tag.name = "blockquote"

  # 修改类名名

  tag['class'] = 'verybold'

  # 添加属性

  tag['id'] = 1

  # 删除属性
  del tag['class']
  del tag['id']

 

 2、修改 .string

给tag的 .string 属性赋值,就相当于用当前的内容替代了原来的内容:

   例如:tag.string = "New Text."

   注意: 如果当前的tag包含了其它tag,那么给它的 .string 属性赋值会覆盖掉原有的所有内容包括子tag

 

3、append()

Tag.append()方法想tag中添加内容

soup = BeautifulSoup("<a>Foo</a>", 'html.parser')
soup.a.append("Bar")

print(soup)

运行结果:

 

4、NavigableString() 和 .new_tag()

from bs4 import BeautifulSoup, NavigableString

soup = BeautifulSoup("<b></b>", 'html.parser') tag = soup.b tag.append("Hello")
new_string
= NavigableString(" there") tag.append(new_string) print(tag)

运行结果:

如果想要创建一段注释,或 NavigableString 的任何子类, 只要调用 NavigableString 的构造方法:

在上面的基础上改
new_comment = soup.new_string("Nice to see you.", Comment) tag.append(new_comment) print(tag)

运行结果:

 

创建一个tag最好的方法是调用工厂方法 BeautifulSoup.new_tag() :

  例如:new_tag = soup.new_tag("a", href="http://www.example.com")

  第一个参数作为tag的name,是必填,其它参数选填

 

 

5、insert()

Tag.insert() 方法与 Tag.append()  方法类似,区别是不会把新元素添加到父节点 .contents 属性的最后,而是把元素插入到指定的位置.

 

6、insert_before() 和 insert_after()

在当前tag或文本节点 前 / 后插入内容:

 

7、clear()

Tag.clear() 方法移除当前tag的内容:

 

8、extract()

PageElement.extract() 方法将当前tag移除文档树,并作为方法结果返回:

 

9、decompose() 

Tag.decompose() 方法将当前节点移除文档树并完全销毁:

 

10、replace_with()

PageElement.replace_with() 方法移除文档树中的某段内容,并用新tag或文本节点替代它:

 

11、wrap()

PageElement.wrap() 方法可以对指定的tag元素进行包装 ,并返回包装后的结果:

 

12、unwrap()

Tag.unwrap() 方法与 wrap() 方法相反.将移除tag内的所有tag标签,该方法常被用来进行标记的解包:

 

 

五、输出

1、格式化输出

prettify() 方法将Beautiful Soup的文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行

 

BeautifulSoup 对象和它的tag节点都可以调用 prettify() 方法:

例如:print(soup.prettify())

 

2、压缩输出

如果只想得到结果字符串,不重视格式,那么可以对一个 BeautifulSoup  对象或  Tag 对象使用Python的  unicode() 或  str() 方法:

 

3、输出格式

Beautiful Soup输出是会将HTML中的特殊字符转换成Unicode,比如“&lquot;”: 

如果将文档转换成字符串,Unicode编码会被编码成UTF-8.这样就无法正确显示HTML特殊字符了:

 

4、get_text()

只得到tag中包含的文本内容, 这个方法获取到tag中包含的所有文版内容包括子孙tag中的内容,并将结果作为Unicode字符串返回:

 

可以通过参数指定tag的文本内容的分隔符:

例如:soup.get_text("|")

 

还可以去除获得文本内容的前后空白:

  例如:soup.get_text("|", strip=True)

 

 

六、文档解析器

  • 要解析的文档是什么类型: 目前支持, “html”, “xml”, 和 “html5”
  • 指定使用哪种解析器: 目前支持, “lxml”, “html5lib”, 和 “html.parser”

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM