前言:
BeautifulSoup:美味的汤。是一个强大又方便的python网页解析库,可以从网页里HTML或XML文件中提取数据。Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。
一、简单入门:
首先导入BeautifulSoup这个库,若报错则需要在命令行中安装这个库。
from bs4 import BeautifulSoup
首先需要创建一个相应的BS解析对象,使用BeautifulSoup()这个方法。
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> 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>
'''
soup = BeautifulSoup(html, 'lxml')
然后对这个对象的内容输出,会发现这个解析对象对网页代码做了自动补全。
print(soup.prettify()) #prettify:美化,装饰
<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 class="sister" href="http://example.com/elsie" id="link1">
Elsie
</a>
,
<a class="sister" href="http://example.com/lacie" id="link2">
Lacie
</a>
and
<a class="sister" href="http://example.com/tillie" id="link3">
Tillie
</a>
;
and they lived at the bottom of a well.
</p>
<p class="story">
...
</p>
</body>
</html>
通过BS对象直接对标签名的引用,可以获得标签对应的内容。
print(soup.title)
print(soup.p)
print(soup.a)
尽管有很多的<a>标签或<p>标签,但这样在不加条件的情况下总是输出从头开始的第一个对应标签。
<title>The Dormouse's story</title>
<p class="title"><b>The Dormouse's story</b></p>
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
获取标签的名称以及标签内的内容:
print(soup.title.name)
print(soup.title.string)
# soup对象支持嵌套
title
The Dormouse's story
获取标签的属性值:
print(soup.p["class"]) # 括号内为双引号
# print(soup.p.get('class'))
同样,在有多个同类标签的情况下输出第一个结果:
['title']
用get()方法同样可以获得相应的属性值:
for i in soup.find_all('a'):
print(i.get('href')) # 括号内为单引号
http://example.com/elsie
http://example.com/lacie
http://example.com/tillie
这两种获得标签属性值的方法区别是[]内是双引号,()内是单引号。
find_all() :
获取所有指定类型的标签,返回一个列表:
print(soup.find_all('a')) # 注意括号内要加引号
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
find():
获取指定id标识的标签:
print(soup.find(id='link3'))
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
二、结点关系:
标签同样拥有祖先与孩子的关系逻辑。
获取子结点:
print(soup.p.contents)
[<b>The Dormouse's story</b>] # 以列表形式返回
for i,child in enumerate(soup.p.children):
print(i,child)
0 <b>The Dormouse's story</b> # 以迭代器的形式返回
获取父节点:
print(soup.p.parent.name) # 获取父节点的名称
print(soup.p.parent) # 获取包含父节点的所有标签内容
body
<body>
<p class="title" id="robot"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
</body>
获取祖先结点内容:
for i in soup.p.parents: # 返回一个迭代器
print(i)
兄弟结点:
# soup.a.next_sibling 获取下一个兄弟标签内容
# souo.a.previous_sinbling 获取上一个兄弟标签内容
# soup.a.next_siblings 获取后面的兄弟节点
# soup.a.previous_siblings 获取前面的兄弟节点
三、查找标签:
BS提供find()与findall()方法查找标签,在前面已近介绍过,findall()方法可以对查找到的内容进行嵌套查找:
content = '''
<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-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
soup = BeautifulSoup(content, 'lxml')
for i in soup.find_all('ul'):
print(i.find_all('li'))
[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>]
[<li class="element">Foo</li>, <li class="element">Bar</li>]
查找特定属性值的标签:
对于如向class这种属性的属性值,应使用attrs{"标签名":"属性值"}的方法:
print(soup.find_all(attrs={"class": "element"}))
对于id这种特殊属性的属性值,可以直接在findall()中书写:
print(soup.find_all(id = 'list-2'))
# 查找出与text属性相关的内容,并以列表的方式显示,通常用于做对比的情况
print(soup.find_all(text="Foo"))
# find_parents()返回所有祖先节点,find_parent()返回直接父节点。
# find_next_siblings()返回后面所有兄弟节点,find_next_sibling()返回后面第一个兄弟节点。
# find_previous_siblings()返回前面所有兄弟节点,find_previous_sibling()返回前面第一个兄弟节点。
# find_all_next()返回节点后所有符合条件的节点, find_next()返回第一个符合条件的节点
# find_all_previous()返回节点后所有符合条件的节点, find_previous()返回第一个符合条件的节点
四、CSS选择器:
XPath也支持CSS选择器的形式来访问标签,对于前端熟悉的人来说是十分方便的。
语法规则:
- • : 表示class
- # :表示id
- <标签1> <标签2>:表示标签1内部的所有的标签2
- <标签1>,<标签2>:表示标签1与标签2
对BS对象使用select()方法可以通过CSS语法来获取标签,以列表的形式返回:
content = '''
<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-small" id="list-2">
<li class="element1">Foo</li>
<li class="element2">Bar</li>
</ul>
</div>
</div>
'''
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2 .element1'))
[<div class="panel-heading">
<h4>Hello</h4>
</div>]
[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>, <li class="element">Foo</li>, <li class="element">Bar</li>]
[<li class="element">Foo</li>, <li class="element">Bar</li>]
通过get_text()方法来获取标签内容:
for i in soup.select('li'):
print(i.get_text)
<bound method Tag.get_text of <li class="element">Foo</li>>
<bound method Tag.get_text of <li class="element">Bar</li>>
<bound method Tag.get_text of <li class="element">Jay</li>>
<bound method Tag.get_text of <li class="element1">Foo</li>>
<bound method Tag.get_text of <li class="element2">Bar</li>>
获取标签内的属性值,通过attrs[]属性:
for i in soup.select('ul'):
# print(i["class"])
print(i.attrs["class"])
五、BS实战:
通过BS库来爬取豆瓣电影250中的所有海报图片并保存到本地:
import requests
import os
from bs4 import BeautifulSoup
from requests.exceptions import RequestException
def get_one_page(url):
try:
res = requests.get(url)
if res.status_code == 200:
return res.text
return None
except RequestException:
return None
def save_pic(all_list):
for item in all_list:
num = item.find('em').text
src = item.find('img')["src"]
name = item.find('img')["alt"]
# 首先得到<img>中的src链接并获取相应
res = requests.get(src)
with open( '.\\out\\' + num + '.' + name + '.jpg', 'wb') as f:
print("正在写入海报:" + name)
f.write(res.content) # 写入相应的二进制格式
f.close()
def parse_page(context):
soup = BeautifulSoup(context, 'lxml')
all_list = soup.select('.item')
save_pic(all_list)
def main(offset):
url = "https://movie.douban.com/top250?start=" + str(offset)
context = get_one_page(url)
parse_page(context)
if __name__ == "__main__":
# 创建out文件
os.mkdir("out")
for i in range(10):
main(i * 25)
参考: