1. Requests 库
1.1 抓取数据
import requests
# 封装头
my_headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36'
}
my_url = 'https://www.sogou.com/web'
# 处理url携带的参数:封装到字典中
my_param = {
'query':'hi'
}
# 对指定的url发起的请求是携带参数的
response = requests.get(url=my_url, params=my_param, headers=my_headers)
page_text = response.text
print(page_text)
1.2 文件下载
1.2.1 第一种
如果要下载文件的话,就不能用response.text属性了,应该用response.content,区别如下
- text 返回的是unicode 型的数据
- content返回的是bytes,二级制型的数据。
示例代码:下载图片
import requests
content = requests.get('http://image.nmc.cn/assets/img/w/40x40/4/0.png').content
# 写入文件
with open('0.png','wb') as fp:
fp.write(content)
1.2.2 第二种
from urllib.request import urlretrieve
urlretrieve('http://image.nmc.cn/assets/img/w/40x40/4/0.png', '0.png')
2. BeautifulSoup库解析数据
lxml是BeautifulSoup的一种解析器,可以用来解析HTML代码。
下面使用的网页文件来自于alice.html,完整代码如下
<html>
<body>
<p>html_doc = """
</p>
<title>The Dormouse's story</title>
<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>
2.1 加载对象到BeautifulSoup
有两种方法加载对象,一种是读取本地文件,另一种加载上述使用requests方法得到的response.text
两种方法分别如下
# 0. 引入库
from bs4 import BeaitifulSoup
# 1. 将本地html的数据加载到对象中
fp = open('alice.html', 'r', encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')# 文件对象 解析器
# 2. 将网上的页面源代码加载到对象中
page_text = response.text
soup = BeautifulSoup(page_text, 'lxml')
# 按照标准缩进格式输出
print(soup.prettify())
2.2 寻找标签数据
标签就是TagName,也就是<div>, <head>, <p>, <a>这些东西,BeautifulSoup提供了很便捷的提取方法,能够使用户快速获得内容。
2.2.1 .TagName方法
使用将TagName替换成上述列出的标签,就可以返回找到的第一个标签值,如下图所示

2.2.2 soup.find(TagName)方法
与2.2.1的方法一样,都是返回第一个标签。
soup.find('a')
output >> <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
2.2.2 soup.find_all(TagName)方法
返回拥有该标签的全部值。
soup.find_all('a')
output >> [<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>]
2.2.3 soup.select(‘某种选择器’) ⭐️
该方法功能强大,可以花式取得几乎所有的标签。接下来介绍选择器的语法
2.2.3.1 标签选择器
标签选择器是最简单的一种,他的语法为标签名
,实际使用结果如下
soup.select('title')
output >> [<title>The Dormouse's story</title>]
2.2.3.2 类选择器
类也就是标签中的class属性,类选择器的语法为.类名
实际使用结果如下
soup.select('.sister')
output>>[<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>]
2.2.3.3 id选择器
id也就是标签中的id属性,类选择器的语法为#id
实际使用结果如下
soup.select('#link1')
output >> [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
2.2.3.4 组合查找
组合查找即和单独select标签名,类名,id名进行的原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开,实际的使用结果如下
soup.select('p #link1')
output >> [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
2.2.3.5 属性查找
属性也就是标签中的其他附加内容,属性需要用中括号括起来,由于属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。使用的实例如下
soup.select('a[href="http://example.com/lacie"]')
output >> [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
2.2.3.6 层级查找⭐️
这里要介绍一下层级

假设红框是第一层级,那么所有并列的蓝框就都是第二层级,也就是相对于红框的单层级;黑框就是第三层级,也就是相对于红框的多层级。层级查找有两种语法,分别对应着单层级查找和多层级查找。
单层级的查找语法如下,注意>前后一定要有空格
# 标签1 > 标签2
soup.select('body > p')
多层级的查找语法如下,只需要标签中间有空格就行
# 标签1 标签2
soup.select('body a')
因此,如果直接用>查找多层级,是会失败的,如下面的运行结果
soup.select('body > a')
output >> []
2.3 提取标签属性or文本内容
2.3.1 提取属性
定位到标签后,直接使用[Name]
就可以获取属性值,使用实例如下
soup.select('body a.sister')[0]['href']
output >> 'http://example.com/elsie'
也可以使用get方法获得属性值
for link in soup.find_all('a'):
print(link.get('href'))
output >> http://example.com/elsie
http://example.com/lacie
http://example.com/tillie
2.3.2 提取文本
定位到标签后,直接使用两个属性一个方法.text/.string/.get_text(),其中
- text/get_text()可以获得一个标签中所有的内容,包括当前标签的+子节点标签的
- string只获得当前标签的文本内容
使用实例如下
soup.select('body')[0].text
output >> 'html_doc = """\nThe Dormouse\'s story\nThe Dormouse\'s story\nOnce upon a time there were three little sisters; and their names were\nElsie,\nLacie and\nTillie;\nand they lived at the bottom of a well.\n...\n"""'
soup.select('body')[0].string
output >> None
3. 总结
3.1 bs4
环境的安装:
pip install bs4
pip install lxml
bs4的解析原理
实例化一个BeautifulSoup的对象,并且将即将被解析的页面源码数据加载到该对象中
调用BeautifulSoup对象中的相关属性和方法进行标签定位和数据提取
如何实例化BeautifulSoup对象呢?
BeautifulSoup(fp,'lxml'):专门用作于解析本地存储的html文档中的数据
BeautifulSoup(page_text,'lxml'):专门用作于将互联网上请求到的页面源码数据进行解析
3.2 bs4的基本语法
基础语法:
soup = BeautifulSoup(page_text,'lxml')
(1)根据标签名查找
- soup.a 只能找到第一个符合要求的标签
(2)获取属性
- soup.a.attrs 获取a所有的属性和属性值,返回一个字典
- soup.a.attrs['href'] 获取href属性
- soup.a['href'] 也可简写为这种形式
(3)获取内容
- soup.a.string 获取a标签的直系文本
- soup.a.text 这是属性,获取a子类的所有文本
- soup.a.get_text() 这是方法,获取a标签子类的所有文本
【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容
(4)find:找到第一个符合要求的标签
- soup.find('a') 找到第一个符合要求的
- soup.find('a', title="xxx") 具有title=a属性的
- soup.find('a', alt="xxx")
- soup.find('a', class_="xxx")
- soup.find('a', id="xxx")
(5)find_all:找到所有符合要求的标签
- soup.find_all('a')
- soup.find_all(['a','b']) 找到所有的a和b标签
- soup.find_all('a', limit=2) 限制前两个
(6)根据选择器选择指定的内容
select:soup.select('#feng')
- 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
- 层级选择器:
div .dudu #lala .meme .xixi 下面好多级
div > p > a > .lala 只能是下面一级
select就是css选择器
【注意】select选择器返回永远是列表,需要通过索引提取指定的对象
3.3 select 和 find 和findall
soup = BeautifulSoup(html, ‘lxml‘)
s = soup.select(‘div .lily‘)#select的写法和find有区别,select是标签和class都在一个字符串里,find是两个字符串,用逗号隔开
f = soup.find(‘div‘,class_ = ‘lily‘) #find只取第一个值,返回的是字符串
fa = soup.find_all(‘div‘,class_ = ‘lily‘)#find——all是全部的值和select一样,是一个列表
fal = soup.find_all(‘div‘,class_ = ‘lily‘,limit=1)#find——all是全部的值和select一样,是一个列表,加limit属性后只返回第一个
print(s)
print(f)
print(fa)
print(fal)
>>>
[<div class="lily" id="ben">大笨蛋</div>, <div class="lily" id="ben">是个大笨蛋吗?</div>]
<div class="lily" id="ben">大笨蛋</div>
[<div class="lily" id="ben">大笨蛋</div>, <div class="lily" id="ben">个大笨蛋吗?</div>]
[<div class="lily" id="ben">大笨蛋</div>]
属性定位:soup.find('tagName',attrName='value'),返回也是单数
find_all:和find用法一致,但是返回值是列表
1. name参数的四种过滤器
soup=Beautifulsoup('page','lxml')
不带过滤器: print(soup.find_all()) #没有过滤,查找所有标签
字符串过滤器: print (soup.find_all()) #字符串过滤器,即标签名
列表: print(soup.find_(['a','b'])) #找到所有的a标签和b标签,任一即可
正则: print(soup.find_all(re.complie('^b'))) #找到所有b开头的标签
方法: def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
print(soup.find_all(has_class_but_no_id))
2、按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
print(soup.find_all('a',class_='sister')) #查找类为sister的a标签
print(soup.find_all('a',class_='sister ssss')) #查找类为sister和sss的a标签,顺序错误也匹配不成功
print(soup.find_all(class_=re.compile('^sis'))) #查找类为sister的所有标签
3、attrs
print(soup.find_all('p',attrs={'class':'story'}))
4、text: 值可以是:字符,列表,True,正则
print(soup.find_all(text='Elsie'))
print(soup.find_all('a',text='Elsie'))
5、limit参数:如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,
当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果
print(soup.find_all('a',limit=2))
6、recursive:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .
print(soup.html.find_all('a'))
print(soup.html.find_all('a',recursive=False))
7. find和find_all一样
爬取三国演义的章节信息和文章内容
分析:
1.先获取三国演义的主页面,里面包含了三国演义的文章章节标题,每个文章的章节都是一个a标签,访问这个a标签,就能查看文章的内容
2.发送请求,请求三国演义的主界面
3.在三国演义的主页面的html源码中找到章节的标签位置,定位标签位置
4.拿到列表数据,循环列表,循环发送章节的内容的请求
import requests
from bs4 import BeautifulSoup # 导入BeautifulSoup
url = 'http://www.shicimingju.com/book/sanguoyanyi.html' #
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
}
sg_list = requests.get(url=url,headers=headers).text
soup = BeautifulSoup(sg_list,'lxml')
content = soup.select('.book-mulu > ul > li > a') #章节的标签
f = open('sanguo.txt','w',encoding='utf-8')
for i in content:
new_url = 'http://www.shicimingju.com'+i['href'] #拼接标签的访问路径
title = i.string
detail = requests.get(url=new_url,headers=headers).text #循环发送对文章内容的请求
soup = BeautifulSoup(detail,'lxml')
new_detail = soup.find('div',class_="chapter_content").text
f.write(new_detail)
print(title+'爬取成功')
f.close()
网友评论