目录
BeautifulSoup 是一个可以从HTML或XML文件中提取数据的Python库,支持你使用喜欢的解析器实现对文档的导航、查找、修改等操作。
一、安装BeautifulSoup4
pip install beautifulsoup4
二、导入 BeautifulSoup4
from bs4 import BeautifulSoup
三、生成BeautifulSoup对象
解析源代码生成BeautifulSoup对象,使用以下代码:
soup = BeautifulSoup(网页源代码,‘解析器’)
BeautifulSoup 第一个参数应该是要被解析的文档字符串或是文件句柄,第二个参数用来标识怎样解析文档。如果第二个参数为空,那么Beautiful Soup根据当前系统安装的库自动选择解析器,解析器的优先数序: lxml,html5lib, Python标准库(html.parser)。本文中的例子将都以lxml作为解析器。
1.解析器
解析器安装:
Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml .根据操作系统不同,可以选择下列方法来安装lxml:
pip install lxml
另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:
pip install html5lib
解析器之间的区别:详见BeautifulSoup文档
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
Python标准库 | BeautifulSoup(markup, "html.parser") |
|
|
lxml HTML 解析器 | BeautifulSoup(markup, "lxml") |
|
|
lxml XML 解析器 |
|
|
|
html5lib | BeautifulSoup(markup, "html5lib") |
|
|
2.requests与BeautifulSoup结合使用
对于网络爬虫来说,BeautifulSoup第一个参数一般来源于通过requests获得的网页源代码,也可以是一个字符串或者一个文件名称。
html = requests.get(url).content.decode('utf-8')
soup = BeautifulSoup(html,'lxml')
四、BeautifulSoup语法与使用技巧
1.查找内容
以下是BeautifulSoup文档中的例子
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>
"""
使用BeautifulSoupl浏览结构化数据
soup.title # 获得title标签及值
# <title>The Dormouse's story</title>
soup.title.name # 获得title标签的标签名称
# u'title'
soup.title.string # 获得title标签下的字符串
# u'The Dormouse's story'
soup.title.parent.name # 获得title标签的父辈的标签名称
# u'head'
soup.p # 获得第一个p标签中的所有内容
# <p class="title"><b>The Dormouse's story</b></p>
soup.p['class'] # 获得第一个p标签中class属性的值
# u'title'
soup.a # 获得第一个a标签中的所有内容
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
soup.find_all('a') # 获得所有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>]
soup.find(id="link3") # 获得id值为"link3"的标签的所有内容
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','lxml')
print(data_soup.find_all(attrs={"data-foo": "value"})) # 使用attrs属性获得具有特殊属性名称的标签
# [<div data-foo="value">foo!</div>]
for item in soup.find_all('a'): # 获取所有的a标签,并遍历打印a标签中的href的值
print(i.get('href'))
# http://example.com/elsie
# http://example.com/lacie
# http://example.com/tillie
for item in soup.find_all('a'): # 获取所有的a标签,并遍历打印a标签的文本值
print(item.get_text())
# Elsie
# Lacie
# Tillie
for item in soup.find_all(class_ = 'sister'): # 获得所有class属性为sister的标签
print(item)
# <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>
print(soup.a.attrs) # 获得第一个a标签中的所有属性
print([item.attrs for item in soup.find_all('a')]) # 获得所有的a标签,并遍历获得a标签中的所有属性存到列表中
2.实例1:排除无用的信息
html_doc = """
<!DOCTYPE html>
<html lang="zh-en">
<head>
<meta charset="UTF-8"/>
<title>网络爬虫</title>
</head>
<body>
<div id='useful'>
<ul>有用的信息
<li>html</li>
<li>css</li>
<li>javascript</li>
</ul>
</div>
<div id='useless'>
<ul>无用的信息
<li>python</li>
<li>java</li>
<li>c#</li>
</ul>
</div>
</body>
</html>
"""
代码如下:
# 方法一:
soup = BeautifulSoup(html_doc, 'lxml')
for item in soup.find_all(id='useful'): # 使用find_all方法找到id值为“useful”的标签,find_all方法返回的是多个<class 'bs4.element.Tag'>对象的列表
print([x.string for x in item.find_all('li')])
# 方法二:
item = soup.find(id="useful") # 使用find方法找到id值为“useful”的标签,find方法返回的是一个<class 'bs4.element.Tag'>对象
print([x.string for x in item.find_all('li')])
# 方法三:
for item in soup.select('div[id="useful"]'): # 使用select找到id值为“useful”的div标签,select方法返回的是一个<class 'bs4.element.Tag'>对象
print([x.string for x in item.find_all('li')])
# 方法四:
for item in soup.select("#useful > ul > li"): # 使用css选择器逐层查找
print(item.string)
3.实例2:根据要求找到目标文本(根据属性值查找内容)
html_doc = """
<html>
<body>
<div id="data-jobid">6575906</div>
<div id="data-positionid">6575906</div>
<div id="data-salary">20k-30k·16薪</div>
<div id="data-company">耀天游戏</div>
<div id="data-positionname">用户产品经理</div>
<div id="data-companyid">10723</div>
<div id="useless">无效的信息<div>
</body>
</html>
"""
找到所有属性值以"data"开头的文本
soup = BeautifulSoup(html_doc, 'lxml')
for item in soup.select('div[id^="data"]'): # ^符号代表匹配以某个字符串作为开头的属性值
print(item.get_text())
# 6575906
# 6575906
# 20k-30k·16薪
# 耀天游戏
# 用户产品经理
# 10723
找到所有属性值以“id”结尾的文本
soup = BeautifulSoup(html_doc, 'lxml')
for item in soup.select('div[id$="id"]'): # $符号代表匹配以某个字符串作为结尾的属性值
print(item.get_text())
# 6575906
# 6575906
# 10723
找到所有属性值中包含"company"的文本
soup = BeautifulSoup(html_doc, 'lxml')
for item in soup.select('div[id*="company"]'): # *符号代表匹配包含某个字符串的属性值
print(item.get_text())
# 耀天游戏
# 10723
***使用find_all方法和正则表达式配合也可以快捷的完成以上任务:
for item in soup.find_all(id=re.compile('^data')): # 找到所有属性值以"data"开头的文本
print(item.get_text())
for item in soup.find_all(id=re.compile('id$')): # 找到所有属性值以“id”结尾的文本
print(item.get_text())
for item in soup.find_all(id=re.compile('company')): # 找到所有属性值中包含"company"的文本
print(item.get_text())
4.实例3:查找招聘信息中的“职位名称“”薪酬“信息。
(数据来源:猎聘网)
html_doc = """
<html>
<body>
<li data-info="%7B%22job_id%22%3A35282943%2C%22job_kind%22%3A2%7D">
<div class="sojob-item-main clearfix">
<div class="job-info">
<h3 title="招聘爬虫工程师">
<a data-promid="imscid=R000000075&siTag=L4PYZPMRcCAS50uKXZeG6w%7EfA9rXquZc5IkJpXC-Ycixw&d_sfrom=search_fp_bar&d_ckId=e3bc2e3b605ecb75d5dcdbcd2bd2be39&d_curPage=0&d_pageSize=40&d_headId=e3bc2e3b605ecb75d5dcdbcd2bd2be39&d_posi=0" href="https://www.liepin.com/job/1935282943.shtml" target="_blank">
爬虫工程师
</a>
</h3>
<p class="condition clearfix" title="15-25k·12薪_杭州_学历不限_3-5年">
<span class="text-warning">
15-25k·12薪
</span>
</p>
</div>
</div>
</li>
<li data-info="%7B%22job_id%22%3A33568633%2C%22job_kind%22%3A2%7D">
<div class="sojob-item-main clearfix">
<div class="job-info">
<h3 title="招聘高级爬虫工程师">
<a data-promid="imscid=R000000075&siTag=L4PYZPMRcCAS50uKXZeG6w%7EfA9rXquZc5IkJpXC-Ycixw&d_sfrom=search_fp_bar&d_ckId=e3bc2e3b605ecb75d5dcdbcd2bd2be39&d_curPage=0&d_pageSize=40&d_headId=e3bc2e3b605ecb75d5dcdbcd2bd2be39&d_posi=1" href="https://www.liepin.com/job/1933568633.shtml" target="_blank">
高级爬虫工程师
</a>
</h3>
<p class="condition clearfix" title="20-40k·12薪_上海_统招本科_经验不限">
<span class="text-warning">
20-40k·12薪
</span>
</p>
</div>
</div>
</li>
</body>
</html>
"""
代码如下:
soup = BeautifulSoup(html_doc, 'lxml')
job_list = []
for item in soup.find_all('li'): # 先抓大后抓小,分楼层抓取数据
job_info = {}
job_info['job_name'] = item.a.string.strip() # 获得职位名称
job_info['wages'] = item.span.string.strip() # 获得薪酬信息
job_list.append(job_info)
print(job_list)
# [{'job_name': '爬虫工程师', 'wages': '15-25k·12薪'}, {'job_name': '高级爬虫工程师', 'wages': '20-40k·12薪'}]
五、HTML内容解析小结
从网页中提取信息,是爬虫开发中最重要且是最基本的操作。正则表达式、XPath、BeautifulSoup都能用以解析网页内容,掌握并灵活运用他们从网页中获取信息,爬虫才算是入门。
正则表达式使用灵活,但构造起来较为复杂、可读性差;XPath是通过追踪解析后的标签来获得数据,使用简单,且它底层使用C语言开发,在三者之间效率最高;BeautifulSoup也是根据追踪标签来获取信息,功能强大,入门容易,但是由于是基于Python开发的,速度比XPath要满。
在实际应用中,较为推荐的是优先选择XPath,然后结合正则表达式解决一些特殊复杂情况。
六、练习:爬取猎聘网上的职位信息
猎聘网以“爬虫工程师”作为检索内容找到的职位信息
初始检索网站::https://www.liepin.com/zhaopin/?d_sfrom=search_fp_nvbar&init=1
爬取内容:职位名称、薪酬、城市、学历要求、工作经历、公司名称
变量名称:job_name,wages,city,education,work_experience,company
爬取技术:requests、BeautifulSoup4
提交资料:代码文件、数据文件csv
技术提示:1.需要装饰请求头;2.先抓大后抓小;3.薪酬、城市、学习要求和工作经历是在一个title中,需要分割;4.目前检索到的结果有10个页面,需要找到页面链接的规律。