Python爬虫小白入门(三)BeautifulSoup库
# 一、前言
***
上一篇演示了如何使用requests模块向网站发送http请求,获取到网页的HTML数据。这篇来演示如何使用BeautifulSoup模块来从HTML文本中提取我们想要的数据。
update on 2016-12-28:之前忘记给BeautifulSoup的官网了,今天补上,顺便再补点BeautifulSoup的用法。
# 二、运行环境
***
我的运行环境如下:
- 系统版本
Windows10。
- Python版本
Python3.5,推荐使用Anaconda 这个科学计算版本,主要是因为它自带一个包管理工具,可以解决有些包安装错误的问题。去[Anaconda官网](https://www.continuum.io/downloads),选择Python3.5版本,然后下载安装。
- IDE
我使用的是PyCharm,是专门为Python开发的IDE。这是JetBrians的产品,[点我下载](http://www.jetbrains.com/pycharm/)。
# 三、模块安装
***
BeautifulSoup 有多个版本,我们使用BeautifulSoup4。详细使用看[BeautifuSoup4官方文档](http://beautifulsoup.readthedocs.io/zh_CN/latest/)。
使用管理员权限打开cmd命令窗口,在窗口中输入下面的命令即可安装:
`conda install beautifulsoup4`
![](http://upload-images.jianshu.io/upload_images/3879861-a611fda59a4d39ff.gif?imageMogr2/auto-orient/strip)
直接使用Python3.5 没有使用Anaconda版本的童鞋使用下面命令安装:
`pip install beautifulsoup4`
然后我们安装lxml,这是一个解析器,BeautifulSoup可以使用它来解析HTML,然后提取内容。
Anaconda 使用下面命令安装lxml:
`conda install lxml`
![](http://upload-images.jianshu.io/upload_images/3879861-6ba0d9fca86f0771.gif?imageMogr2/auto-orient/strip)
使用Python3.5 的童鞋们直接使用pip安装会报错(所以才推荐使用Anaconda版本),安装教程[看这里](http://blog.csdn.net/qq_33279781/article/details/52026806)。
如果不安装lxml,则BeautifulSoup会使用Python内置的解析器对文档进行解析。之所以使用lxml,是因为它速度快。
文档解析器对照表如下:
解析器 | 使用方法 | 优势 | 劣势
--- | --- | --- | ---
Python标准库 | BeautifulSoup(markup,"html.parser") | 1. Python的内置标准库
2. 执行速度适
3. 中文档容错能力强 | Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器 | BeautifulSoup(markup,"lxml") | 1. 速度快
2. 文档容错能力强 | 需要安装C语言库
lxml XML 解析器 | BeautifulSoup(markup,["lxml-xml"])
BeautifulSoup(markup,"xml") | 1. 速度快
2. 唯一支持XML的解析器 | 需要安装C语言库
html5lib | BeautifulSoup(markup,"html5lib") | 1. 最好的容错性
2. 以浏览器的方式解析文档
3. 生成HTML5格式的文档 | 速度慢,不依赖外部扩展
# 四、BeautifulSoup 库的使用
***
网上找到的几个官方文档:[BeautifulSoup4.4.0中文官方文档](http://beautifulsoup.readthedocs.io/zh_CN/latest/),[BeautifulSoup4.2.0中文官方文档](https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/)。不同版本的用法差不多,几个常用的语法都一样。
首先来看BeautifulSoup的对象种类,在使用的过程中就会了解你获取到的东西接下来应该如何操作。
## 4.1 BeautifulSoup对象的类型
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象。所有对象可以归纳为4种类型: Tag , NavigableString , BeautifulSoup , Comment 。下面我们分别看看这四种类型都是什么东西。
### 4.1.1 Tag
这个就跟HTML或者XML(还能解析XML?是的,能!)中的标签是一样一样的。我们使用find()方法返回的类型就是这个(插一句:使用find-all()返回的是多个该对象的集合,是可以用for循环遍历的。)。返回标签之后,还可以对提取标签中的信息。
###### 提取标签的名字:
`tag.name`
###### 提取标签的属性:
`tag['attribute'] `
我们用一个例子来了解这个类型:
```
from bs4 import BeautifulSoup
html_doc = """
The Dormouse's story
The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well.
...
"""
soup = BeautifulSoup(html_doc, 'lxml') #声明BeautifulSoup对象
find = soup.find('p') #使用find方法查到第一个p标签
print("find's return type is ", type(find)) #输出返回值类型
print("find's content is", find) #输出find获取的值
print("find's Tag Name is ", find.name) #输出标签的名字
print("find's Attribute(class) is ", find['class']) #输出标签的class属性值
```
### 4.1.2 NavigableString
NavigableString就是标签中的文本内容(不包含标签)。获取方式如下:
`tag.string`
还是以上面那个例子,加上下面这行,然后执行:
`print('NavigableString is:', find.string)`
![](http://upload-images.jianshu.io/upload_images/3879861-f0f918aeedf2682d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
### 4.1.3 BeautifulSoup
BeautifulSoup对象表示一个文档的全部内容。支持遍历文档树和搜索文档树。
### 4.1.4 Comment
这个对象其实就是HTML和XML中的注释。
```
markup = ""
soup = BeautifulSoup(markup)
comment = soup.b.string
type(comment)
#
```
有些时候,我们并不想获取HTML中的注释内容,所以用这个类型来判断是否是注释。
```
if type(SomeString) == bs4.element.Comment:
print('该字符是注释')
else:
print('该字符不是注释')
```
## 4.2 BeautifulSoup遍历方法
### 4.2.1 节点和标签名
可以使用子节点、父节点、 及标签名的方式遍历:
```
soup.head #查找head标签
soup.p #查找第一个p标签
#对标签的直接子节点进行循环
for child in title_tag.children:
print(child)
soup.parent #父节点
# 所有父节点
for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
# 兄弟节点
sibling_soup.b.next_sibling #后面的兄弟节点
sibling_soup.c.previous_sibling #前面的兄弟节点
#所有兄弟节点
for sibling in soup.a.next_siblings:
print(repr(sibling))
for sibling in soup.find(id="link3").previous_siblings:
print(repr(sibling))
```
### 4.2.2 搜索文档树
最常用的当然是find()和find_all()啦,当然还有其他的。比如find_parent() 和 find_parents()、 find_next_sibling() 和 find_next_siblings() 、find_all_next() 和 find_next()、find_all_previous() 和 find_previous() 等等。
我们就看几个常用的,其余的如果用到就去看官方文档哦。
- find_all()
搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。返回值类型是bs4.element.ResultSet。
完整的语法:
`find_all( name , attrs , recursive , string , **kwargs )`
这里有几个例子
```
soup.find_all("title")
# [The Dormouse's story]
#
soup.find_all("p", "title")
# [
The Dormouse's story
]
#
soup.find_all("a")
# [Elsie,
# Lacie,
# Tillie]
#
soup.find_all(id="link2")
# [Lacie]
#
import re
soup.find(string=re.compile("sisters"))
# u'Once upon a time there were three little sisters; and their names were\n'
```
name 参数:可以查找所有名字为 name 的tag。
attr 参数:就是tag里的属性。
string 参数:搜索文档中字符串的内容。
recursive 参数: 调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点。如果只想搜索tag的直接子节点,可以使用参数 recursive=False 。
- find()
与find_all()类似,只不过只返回找到的第一个值。返回值类型是bs4.element.Tag。
完整语法:
`find( name , attrs , recursive , string , **kwargs )`
看例子:
```
soup.find('title')
# The Dormouse's story
#
soup.find("head").find("title")
# The Dormouse's story
```
基本功已经练完,开始实战!
# 五、继续上一篇实例
***
继续上一篇的网站Unsplash,我们在首页选中图片,查看html代码。发现所有的图片都在a标签里,并且class都是cV68d,如下图。
![](http://upload-images.jianshu.io/upload_images/3879861-9c048de3d30d4725.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
通过仔细观察,发现图片的链接在style中的background-image中有个url。这个url就包含了图片的地址,url后面跟了一堆参数,可以看到其中有&w=XXX&h=XXX,这个是宽度和高度参数。我们把高度和宽度的参数去掉,就能获取到大图。下面,我们先获取到所有的含有图片的a标签,然后在循环获取a标签中的style内容。
其实在图片的右下方有一个下载按钮,按钮的标签中有一个下载链接,但是该链接并不能直接请求到图片,需要跳转几次,通过获取表头里的Location才能获取到真正的图片地址。后续我再以这个角度获取图片写一篇博文,咱们现根据能直接获取到的url稍做处理来获取图片。小伙伴儿们也可能会发现其他的方式来获取图片的url,都是可以的,尽情的尝试吧!
```
import requests #导入requests 模块
from bs4 import BeautifulSoup #导入BeautifulSoup 模块
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'} #给请求指定一个请求头来模拟chrome浏览器
web_url = 'https://unsplash.com'r = requests.get(web_url, headers=headers) #像目标url地址发送get请求,返回一个response对象
all_a = BeautifulSoup(r.text, 'lxml').find_all('a', class_='cV68d') #获取网页中的class为cV68d的所有a标签
for a in all_a:
print(a['style']) #循环获取a标签中的style
```
这里的find_all('a', class_='cV68d') 是找到所有class为cV68d的a标签,返回的是一个list,所以可以用for循环获取每个a标签。
还有,get请求使用了headers参数,这个是用来模拟浏览器的。如何知道‘User-Agent’是什么呢?
在你的Chrome浏览器中,按F12,然后刷新网页,看下图就可以找到啦。
![](http://upload-images.jianshu.io/upload_images/3879861-7d4c19a99770ea20.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
OK,我们来执行以下上面的代码,结果如下:
![](http://upload-images.jianshu.io/upload_images/3879861-d1d6d51e980f29a7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
接下来的任务是在一行的文本中取到图片的url。仔细看每一行的字符串,两个双引号之间的内容就是图片的url了,所以我们Python的切片功能来截取这中间的内容。
改写for循环中的内容:
```
for a in all_a:
img_str = a['style'] #a标签中完整的style字符串
print(img_str[img_str.index('"')+1 : img_str.index('"',img_str[img_str.index('"')+1)]) #使用Python的切片功能截取双引号之间的内容
```
获取到url后还要把宽度和高度的参数去掉。
```
for a in all_a:
img_str = a['style'] #a标签中完整的style字符串
print('a标签的style内容是:', img_str)
first_pos = img_str.index('"') + 1
second_pos = img_str.index('"',first_pos)
img_url = img_str[first_pos: second_pos] #使用Python的切片功能截取双引号之间的内容
width_pos = img_url.index('&w=')
height_pos = img_url.index('&q=')
width_height_str = img_url[width_pos : height_pos]
print('高度和宽度数据字符串是:', width_height_str)
img_url_final = img_url.replace(width_height_str, '')
print('截取后的图片的url是:', img_url_final)
```
有了这些图片的url,就可以通过继续发请求的方式获取图片啦。接下来我们先来封装一下发请求的代码。
先创建一个类:
```
class BeautifulPicture():
def __init__(self): #类的初始化操作
self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'} #给请求指定一个请求头来模拟chrome浏览器
self.web_url = 'https://unsplash.com' #要访问的网页地址
self.folder_path = 'D:\BeautifulPicture' #设置图片要存放的文件目录
```
然后封装request请求:
```
def request(self, url): #返回网页的response
r = requests.get(url) # 像目标url地址发送get请求,返回一个response对象
return r
```
我们在文件目录下保存图片的话,要先创建文件目录。所以再添加一个创建目录的方法:
要先引入os库哦。
`import os`
然后是方法定义:
```
def mkdir(self, path): ##这个函数创建文件夹
path = path.strip()
isExists = os.path.exists(path)
if not isExists:
print('创建名字叫做', path, '的文件夹')
os.makedirs(path)
print('创建成功!')
else:
print(path, '文件夹已经存在了,不再创建')
```
再然后是保存图片啦。
```
def save_img(self, url, name): ##保存图片
print('开始保存图片...')
img = self.request(url)
time.sleep(5)
file_name = name + '.jpg'
print('开始保存文件')
f = open(file_name, 'ab')
f.write(img.content)
print(file_name,'文件保存成功!')
f.close()
```
工具方法都已经准备完毕,开始我们的逻辑部分:
```
def get_pic(self):
print('开始网页get请求')
r = self.request(self.web_url)
print('开始获取所有a标签')
all_a = BeautifulSoup(r.text, 'lxml').find_all('a', class_='cV68d') #获取网页中的class为cV68d的所有a标签
print('开始创建文件夹')
self.mkdir(self.folder_path) #创建文件夹
print('开始切换文件夹')
os.chdir(self.folder_path) #切换路径至上面创建的文件夹
i = 1 #后面用来给图片命名
for a in all_a:
img_str = a['style'] #a标签中完整的style字符串
print('a标签的style内容是:', img_str)
first_pos = img_str.index('"') + 1
second_pos = img_str.index('"',first_pos)
img_url = img_str[first_pos: second_pos] #使用Python的切片功能截取双引号之间的内容
width_pos = img_url.index('&w=')
height_pos = img_url.index('&q=')
width_height_str = img_url[width_pos : height_pos]
print('高度和宽度数据字符串是:', width_height_str)
img_url_final = img_url.replace(width_height_str, '')
print('截取后的图片的url是:', img_url_final)
self.save_img(img_url_final, str(i))
i += 1
```
最后就是执行啦:
```
beauty = BeautifulPicture() #创建一个类的实例
beauty.get_pic() #执行类中的方法
```
最后来一个完整的代码,对中间的一些部分进行了封装和改动,并添加了每部分的注释,一看就明白了。有哪块有疑惑的可以留言~~
```
import requests #导入requests 模块
from bs4 import BeautifulSoup #导入BeautifulSoup 模块
import os #导入os模块
class BeautifulPicture():
def __init__(self): #类的初始化操作
self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1'} #给请求指定一个请求头来模拟chrome浏览器
self.web_url = 'https://unsplash.com' #要访问的网页地址
self.folder_path = 'D:\BeautifulPicture' #设置图片要存放的文件目录
def get_pic(self):
print('开始网页get请求')
r = self.request(self.web_url)
print('开始获取所有a标签')
all_a = BeautifulSoup(r.text, 'lxml').find_all('a', class_='cV68d') #获取网页中的class为cV68d的所有a标签
print('开始创建文件夹')
self.mkdir(self.folder_path) #创建文件夹
print('开始切换文件夹')
os.chdir(self.folder_path) #切换路径至上面创建的文件夹
for a in all_a: #循环每个标签,获取标签中图片的url并且进行网络请求,最后保存图片
img_str = a['style'] #a标签中完整的style字符串
print('a标签的style内容是:', img_str)
first_pos = img_str.index('"') + 1
second_pos = img_str.index('"',first_pos)
img_url = img_str[first_pos: second_pos] #使用Python的切片功能截取双引号之间的内容
#获取高度和宽度的字符在字符串中的位置
width_pos = img_url.index('&w=')
height_pos = img_url.index('&q=')
width_height_str = img_url[width_pos : height_pos] #使用切片功能截取高度和宽度参数,后面用来将该参数替换掉
print('高度和宽度数据字符串是:', width_height_str)
img_url_final = img_url.replace(width_height_str, '') #把高度和宽度的字符串替换成空字符
print('截取后的图片的url是:', img_url_final)
#截取url中参数前面、网址后面的字符串为图片名
name_start_pos = img_url.index('photo')
name_end_pos = img_url.index('?')
img_name = img_url[name_start_pos : name_end_pos]
self.save_img(img_url_final, img_name) #调用save_img方法来保存图片
def save_img(self, url, name): ##保存图片
print('开始请求图片地址,过程会有点长...')
img = self.request(url)
file_name = name + '.jpg'
print('开始保存图片')
f = open(file_name, 'ab')
f.write(img.content)
print(file_name,'图片保存成功!')
f.close()
def request(self, url): #返回网页的response
r = requests.get(url, headers=self.headers) # 像目标url地址发送get请求,返回一个response对象。有没有headers参数都可以。
return r
def mkdir(self, path): ##这个函数创建文件夹
path = path.strip()
isExists = os.path.exists(path)
if not isExists:
print('创建名字叫做', path, '的文件夹')
os.makedirs(path)
print('创建成功!')
else:
print(path, '文件夹已经存在了,不再创建')
beauty = BeautifulPicture() #创建类的实例
beauty.get_pic() #执行类中的方法
```
执行的过程中可能会有点慢,这是因为图片本身比较大!如果仅仅是为了测试爬虫,则可以不把图片的宽度和高度替换掉,图片就没那么大啦,运行过程会快很多。
# 六、后语
伙伴儿们是不是发现,我们只获取到了10张图片,并没有把网站所有照片都下载下来。
这是因为咱们爬取的网站是下拉刷新的,下拉一次,刷新10张照片。那么,该如何爬取这种下拉刷新的网页呢?请看下一篇喽。
Python爬虫小白入门(三)BeautifulSoup库的更多相关文章
- Python爬虫小白入门(二)requests库
一.前言 为什么要先说Requests库呢,因为这是个功能很强大的网络请求库,可以实现跟浏览器一样发送各种HTTP请求来获取网站的数据.网络上的模块.库.包指的都是同一种东西,所以后文中可能会在不同地 ...
- Python爬虫小白入门(六)爬取披头士乐队历年专辑封面-网易云音乐
一.前言 前文说过我的设计师小伙伴的设计需求,他想做一个披头士乐队历年专辑的瀑布图. 通过搜索,发现网易云音乐上有比较全的历年专辑信息加配图,图片质量还可以,虽然有大有小. 我的例子怎么都是爬取图片? ...
- python爬虫学习(一):BeautifulSoup库基础及一般元素提取方法
最近在看爬虫相关的东西,一方面是兴趣,另一方面也是借学习爬虫练习python的使用,推荐一个很好的入门教程:中国大学MOOC的<python网络爬虫与信息提取>,是由北京理工的副教授嵩天老 ...
- python爬虫学习之使用BeautifulSoup库爬取开奖网站信息-模块化
实例需求:运用python语言爬取http://kaijiang.zhcw.com/zhcw/html/ssq/list_1.html这个开奖网站所有的信息,并且保存为txt文件和excel文件. 实 ...
- Python爬虫小白入门(一)写在前面
一.前言 你是不是在为想收集数据而不知道如何收集而着急? 你是不是在为想学习爬虫而找不到一个专门为小白写的教程而烦恼? Bingo! 你没有看错,这就是专门面向小白学习爬虫而写的!我会采用实例的方式, ...
- Python爬虫小白入门(一)入门介绍
一.前言 你是不是在为想收集数据而不知道如何收集而着急? 你是不是在为想学习爬虫而找不到一个专门为小白写的教程而烦恼? Bingo! 你没有看错,这就是专门面向小白学习爬虫而写的!我会采用实例的方式, ...
- Python爬虫小白入门(四)PhatomJS+Selenium第一篇
一.前言 在上一篇博文中,我们的爬虫面临着一个问题,在爬取Unsplash网站的时候,由于网站是下拉刷新,并没有分页.所以不能够通过页码获取页面的url来分别发送网络请求.我也尝试了其他方式,比如下拉 ...
- Python爬虫小白入门(五)PhatomJS+Selenium第二篇
一.前言 前文介绍了PhatomJS 和Selenium 的用法,工具准备完毕,我们来看看如何使用它们来改造我们之前写的小爬虫. 我们的目的是模拟页面下拉到底部,然后页面会刷出新的内容,每次会加载10 ...
- Python爬虫小白入门(七)爬取豆瓣音乐top250
抓取目标: 豆瓣音乐top250的歌名.作者(专辑).评分和歌曲链接 使用工具: requests + lxml + xpath. 我认为这种工具组合是最适合初学者的,requests比pytho ...
随机推荐
- 使用Visual Studio 2015 开发ASP.NET MVC 5 项目部署到Mono/Jexus
最新的Mono 4.4已经支持运行asp.net mvc5项目,有的同学听了这句话就兴高采烈的拿起Visual Studio 2015创建了一个mvc 5的项目,然后部署到Mono上,浏览下发现一堆错 ...
- CSS 特殊属性介绍之 pointer-events
首先看一下 MDN 上关于 pointer-events 的介绍: CSS属性 pointer-events 允许作者控制特定的图形元素在何时成为鼠标事件的 target.当未指定该属性时,SVG 内 ...
- zookeeper源码分析之一服务端启动过程
zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...
- Golang 编写的图片压缩程序,质量、尺寸压缩,批量、单张压缩
目录: 前序 效果图 简介 全部代码 前序: 接触 golang 不久,一直是边学边做,边总结,深深感到这门语言的魅力,等下要跟大家分享是最近项目 服务端 用到的图片压缩程序,我单独分离了出来,做成了 ...
- IOS FMDB 获取数据库表和表中的数据
ios开发中,经常会用到数据库sqlite的知识,除了增,删,改,查之外,我们说说如何获取数据库中有多少表和表相关的内容. 前言 跟数据库使用相关的一般的增删改查的语句,这里就不做解释了.在网上有很多 ...
- 【Machine Learning】Python开发工具:Anaconda+Sublime
Python开发工具:Anaconda+Sublime 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现 ...
- Android Studio:Failed to resolve ***
更换电脑后,也更新了所有的SDK的tool,仍然报错:Failed to resolve 各种jar包,出现这种问题主要是因为在Android studio中默认不允许在线更新,修改方法如下:
- 烂泥:VMWare Workation双网卡配置IP地址
本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb 前几天给一个客户做远程项目实施,客户那边的服务器是Windows OS的,我们这边的业务 ...
- 万向节锁(Gimbal Lock)的理解
[TOC] 结论 我直接抛出结论: Gimbal Lock 产生的原因不是欧拉角也不是旋转顺序,而是我們的思维方式和程序的执行逻辑没有对应,也就是说是我们的观念导致这个情况的发生. 他人解释 首先我们 ...
- TypeScript
TypeScript: Angular 2 的秘密武器(译) 本文整理自Dan Wahlin在ng-conf上的talk.原视频地址: https://www.youtube.com/watch? ...