Beautiful Soup是python的另一个HTML或XML解析库,可以很方便的提取网页中的数据,利用它可以省去很多繁琐的提取工作,提高了解析效率。
首先从一个实例出发:
html = """
<html><head><title>The Dormoues's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse'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
soup =BeautifulSoup(html,'lxml')
print(soup.prettify())
print(soup.title.string)
运行结果:
代码分析:这里首先声明了html,并不是一个完整的HTML字符串,然后调用BeautifulSoup并初始化对象,调用prettify()方法把要解析的字符串以标准的缩进格式输出。然后调用soup.title.string输出HTML中title节点的文本内容。
节点选择器
直接调用节点的名称就可以选择节点元素,在调用string属性就可以得到节点内的文本了。
- 选择元素
print(soup.title) print(type(soup.title)) print(soup.title.string) print(soup.head) print(soup.p)
运行结果如下:当有多个节点时,打印第一个节点。
2. 提取信息
# 提取信息\
#节点名称
print(soup.title.name)
#节点属性
print(soup.p.attrs)
print(soup.p["name"])
print(soup.p["class"])
# 节点内容
print(soup.p.string)
运行结果如下:
3. 嵌套选择
上面的例子中,每一个返回结果都是bs4.element.Tag类型,它同样可以继续调用节点进行下一步的选择。
# 嵌套选择
print(soup.head.title)
print(soup.head.title.string)
4. 关联选择
在做选择的时候不能一步就选到想要的节点,需要选择一个节点,然后以他为基准再选择它的子节点、父节点、兄弟节点等。
(1) 子节点和子孙节点
选择节点元素之后,调用contents属性获取它的直接子节点。
html = """
<html><head><title>The Dormoues's story</title></head>
<body>
<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
soup =BeautifulSoup(html,'lxml')
#直接子节点
print(soup.p.contents)
运行结果如下:
可以看到,返回结果是列表形式,p节点直接字典带你既包含文本,又包含节点。可以调用children属性得到响应的结果:
for i,child in enumerate(soup.p.children):
print(i,child)
运行结果如下:
调用descendants属性获取所有子孙节点:
for i,child in enumerate(soup.p.descendants):
print(i,child)
运行结果如下:
(2) 父节点和祖先节点
parent属性可以获取节点的父节点。
print(soup.a.parent)
运行结果如下:
获取第一个a节点的父节点p。
parents属性可以获取节点的祖先节点。
print(type(soup.a.parents))
print(list(enumerate(soup.a.parents)))
运行结果如下:
(3) 兄弟节点
next_sibling和previous_sibling属性获取节点的下一个和上一个兄弟元素,next_siblings和previous_siblings属性返回节点所有前面的和后面的兄弟节点的生成器。
5. 提取信息
文本string,属性attrs。
html = """
<html><head><title>The Dormoues's story</title></head>
<body>
<p class="story">
Once upon a time there were three little sisters; and their names were
<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>
"""
from bs4 import BeautifulSoup
soup =BeautifulSoup(html,'lxml')
print('-'*15,'获取信息','-'*15)
# 获取信息
print(type(soup.a.next_sibling))
print(soup.a.next_sibling)
print(soup.a.next_sibling.string)
print(type(soup.a.parents))
print(list(soup.a.parents)[0])
print(list(soup.a.parents)[0].attrs['class'])
运行结果如下:
如果返回的是单个节点,可以直接调用string、attrs等属性获取文本和属性值;如果返回结果是多个节点的生成器,则可以转为列表取出某个元素,再调用string、attrs等属性。
方法选择器
前面所讲的选择方法都是通过属性来选择的,这种方法非常快,但是如果进行比较复杂的选择就比较繁琐,find_all()、find()方法可以灵活查询。
1. find_all( )
查询所有符合条件的元素,传入一些属性或文本,就可以的东岸符合条件的元素。
API:find_all(name,attrs,recursive,text,**kwargs)
html = """
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
"""
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(name="ul"))
print(type(soup.find_all(name="ul")[0]))
运行结果如下:
# 同样可以嵌套选择
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)
print(li.attrs)
运行结果如下:
也可以通过属性来选择:
print(soup.find_all(attrs={'id':'list-1'}))
print(soup.find_all(attrs={'name':'elements'}))
对于一些常用属性,如id和class等,我们可以不用attrs来传递。
print(soup.find_all(id="list-1"))
print(soup.find_all(class_="element"))
Text参数可用来匹配节点的文本,传入的形式可以是字符串,也可以是正则表达式,
html = """
<div class="panel">
<div class="panel-body">
<a>Hello, this is a link</a>
<a>Hello, this is a link too</a>
"""
import re
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(text=re.compile('link')))
2. find( )
返回第一个元素。
CSS选择器
BeautifulSoup提供了CSS选择器,只需要调用select()方法传入相应的CSS选择器即可。
html = """
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1" name="elements">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
"""
print('-'*15,'CSS选择器','-'*15)
soup = BeautifulSoup(html,'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select("#list-2 .element"))
print(soup.select('ul')[0])
运行结果如下:
这里调用了3次CSS选择器,返回的结果是符合CSS选择器节点组成的列表。#表示id选择器,.表示class选择器。
1. 嵌套选择
for ul in soup.select('ul'):
print(ul.select('li'))
2. 获取属性
for ul in soup.select('ul'):
print(ul['id'])
print(ul.attrs['id'])
3. 获取文本
for li in ul.select('li'):
print(li.string)
print(li.get_text())
以上就是BeautifulSoup的学习,接下来可以通过一个作业来实践一下:
利用requests和BeautifulSoup爬取某个静态网页的内容,这里以上面那个例子为例子(上面是XPath解析库解析)。(全部代码见news.py)
网页地址为:https://3w.huanqiu.com/a/b1c82f/9CaKrnKmLPE?agt=8
第一步和第二步查看网页和网页源码见上篇博客。
第三步,完整代码
import requests
from lxml import etree
def get_html(url):
headers={"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0"}
response = requests.get(url,headers=headers)
if response.status_code==200:
return response.text
return None
from bs4 import BeautifulSoup
def main1():
url = 'https://3w.huanqiu.com/a/b1c82f/9CaKrnKmLPE?agt=8'
html = get_html(url)
soup = BeautifulSoup(html,'lxml')
#标题
print(soup.select('h1.a-title strong')[0].string)
#时间
print(soup.select('.a-info span.time')[0].string)
#责编
print(soup.select('.a-edit')[0].string)
#内容
#html
print(soup.select('.content-a')[0].select('.a-con')[0])
# text
for p in soup.select('.content-a')[0].select('.a-con')[0].select('p'):
print(p.string)
if __name__=='__main__':
main1()
如果对你有用,点个赞 手动笑脸(*_*)