让适合的人走进你的生活吧,让旧梦逝去吧,让不合适的那个离开吧。人生,看轻看淡多少,痛苦就离开你多少。因为看轻,所以快乐!
2. keyword传参
使用关键字传参,如果参数名不是find系函数已定义的位置参数名,参数会被kwargs收集并被当做标签的属性来搜索(拥有ID属性的标签)。
属性的传参可以是字符串、正则表达式对象、True、列表。
div:Tag = soup.div.div
print(soup.find_all(id='first')) # id为first的所有节点列表
print('--------------------------------------')
#print(soup.find_all(id=re.compile('\w+'))) # 相当于找有id的所有节点
print('-------------------------')
#print(soup.find_all(id=True)) # 所有有id的节点
print('--------------------------------------')
print(list(map(lambda x:x['id'], soup.find_all(id=True)))) # 拥有id属性的标签;
print('--------------------------------------')
print(soup.find_all(id=['first', re.compile(r'^sec')])) # 指定id的名称列表
print(soup.find_all(id=True, src=True)) # 相当于条件and,既有id又有src属性的节点列表
#-----------------------------------------------------------------
[<p id="first">字典</p>]
--------------------------------------
-------------------------
--------------------------------------
['main', 'first', 'second', 'bg1', 'bg2']
--------------------------------------
[<p id="first">字典</p>, <p id="second">列表</p>]
[<img id="bg1" src="http://www.magedu.com/"/>, <img id="bg2" src="http://httpbin.org/"/>]
3. css的class的特殊处理
class是Python关键字,所以使用 class_ 。class是多值属性,可以匹配其中任意一个,也可以完全匹配。
print(soup.find_all(class_="content")) # 全部内容
print('---------------------------')
print(soup.find_all(class_="title")) # 可以使用任意一个css类
print(soup.find_all(class_="highlight")) # 可以使用任意一个css类
print(soup.find_all(class_="highlight title")) # 顺序错了, 找不到
print(soup.find_all(class_="title highlight")) # 顺序一致, 找到, 就是字符串完全匹配
#-------------------------------------------------------------------------------------------------
[<div class="content">
<p id="first">字典</p>
<p id="second">列表</p>
<input name="_csrf" type="hidden" value="7139e401481ef2f46ce98b22af4f4bed"/>
<!-- comment -->
<img id="bg1" src="http://www.magedu.com/"/>
<img id="bg2" src="http://httpbin.org/"/>
</div>]
---------------------------
[<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
[<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
[]
[<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
4. attrs参数
属性:attrs接收一个字典, 字典的key为属性名, value可以是字符串、正则表达式对象、True、列表
print(soup.find_all(attrs={'class':'title'} ) )
print(soup.find_all(attrs={'class':'highlight'} ) )
print(soup.find_all(attrs={'class':'title highlight'} ) )
print('---------------------------')
print(soup.find_all(attrs={'id':re.compile('bg'), 'src':True} ) ) # 相当于 bg and src
# print(soup.find_all(attrs={'id':True} ) )
print('---------------------------')
print(soup.find_all(attrs={'id':re.compile(r'\d$')} ) )
# ----------------------------------------------------
[<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
[<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
[<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
---------------------------
[<img id="bg1" src="http://www.magedu.com/"/>, <img id="bg2" src="http://httpbin.org/"/>]
---------------------------
[<img id="bg1" src="http://www.magedu.com/"/>, <img id="bg2" src="http://httpbin.org/"/>]
5. text参数
可以通过text参数搜索文档中的字符串内容,接受字符串、正则表达式对象、True、列表
print(list(map('body', soup.find_all(text=re.compile('\w+')))))
print(list(map(lambda x: (type(x), x), soup.find_all(text=re.compile('\w+')))))
print('-----------------------------------------------|')
print(list(map(lambda x: (type(x), x), soup.find_all(text=re.compile('[a-z]+')))))
print(soup.find_all(re.compile(r'^(h|p)'), text=re.compile('[a-z]+'))) # 相当于过滤出Tag对象,并看 p标签,text为字母的
#-------------------------------------------------------------------
[(<class 'bs4.element.Doctype'>, 'html'), (<class 'bs4.element.NavigableString'>, '首页'), (<class 'bs4.element.NavigableString'>, '马哥教育欢迎您'), (<class 'bs4.element.NavigableString'>, 'python'), (<class 'bs4.element.NavigableString'>, '高级班'), (<class 'bs4.element.NavigableString'>, '字典'), (<class 'bs4.element.NavigableString'>, '列表'), (<class 'bs4.element.Comment'>, ' comment '), (<class 'bs4.element.NavigableString'>, 'bottom')]
-----------------------------------------------|
[(<class 'bs4.element.Doctype'>, 'html'), (<class 'bs4.element.NavigableString'>, 'python'), (<class 'bs4.element.Comment'>, ' comment '), (<class 'bs4.element.NavigableString'>, 'bottom')]
[<p>bottom</p>]
6. limit参数
限制返回结果的数量
print(soup.find_all(id=True)) # 返回列表中有3个结果
7. recursive 参数
默认是递归搜索所有子孙节点,如果不需要请设置为False
8. 简化写法
find_all()是非常常用的方法,可以简化省略掉
print(soup('img')) # 所有img标签对象的列表,不等价于soup.a
print(soup.img) # 深度优先第一个img
print(soup.a.find_all(text=True)) # 返回文本
print(soup.a(text=True)) # 返回文本,和上面等价
print(soup('a', text=True)) # 返回a标签对象
print(soup.find_all('img', attrs={'id': 'bg1'}))
print(soup('img', attrs={'id': 'bg1'})) # find_all的省略
print(soup('img', attrs={'id': re.compile('1')}))
# -----------------------------------------------------------------
[<img id="bg1" src="http://www.magedu.com/"/>, <img id="bg2" src="http://httpbin.org/"/>]
<img id="bg1" src="http://www.magedu.com/"/>
['python']
['python']
[<a href="http://www.python.org">python</a>]
[<img id="bg1" src="http://www.magedu.com/"/>]
[<img id="bg1" src="http://www.magedu.com/"/>]
[<img id="bg1" src="http://www.magedu.com/"/>]
9. find方法
find( name , attrs , recursive , text , **kwargs )
参数几乎和find_all一样。
找到了, find_all返回一个列表, 而find返回一个单值, 元素对象。
找不到, find_all返回一个空列表, 而find返回一个None。
print(soup.find('img',attrs = {'id':'bg1'} ).attrs.get('src','mage du') )
print(soup.find('img',attrs = {'id':'bg1'} ).get('src') ) # 简化了attrs
print(soup.find('img',attrs = {'id':'bg1'} ) ['src'] )
----------------------------------------------------------
http://www.magedu.com/
http://www.magedu.com/
http://www.magedu.com/
3.5 CSS选择器 ***(重点)
和JQuery一样,可以使用CSS选择器来查找节点
使用soup.select()方法,select方法支持大部分CSS选择器,返回列表。
CSS中,标签名直接使用,类名前加.点号,id名前加#井号。
from bs4 import BeautifulSoup
from bs4.element import Tag
import re
with open('./test.html', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'lxml')
# 元素选择器
print(1, soup.select('p')) # 所有的p标签
# 类选择器
print(2, soup.select('.title'))
# 使用了伪类
# 直接子标签是p的同类型的所有p标签中的第二个
# (同类型)同标签名p的第2个,伪类只实现了nth-of-type,且要求是数字
print(3, soup.select('div.content > p:nth-of-type(2)')) # 只实现了这个伪类
# id选择器
print(4, soup.select('p#second'))
print(5, soup.select('#bg1'))
# 后代选择器
print(6, soup.select('div p')) # div下逐层找p
print(7, soup.select('div div p')) # div下逐层找div下逐层找p
# 子选择器,直接后代
print(8, soup.select('div > p')) # div下直接子标签的p,有2个
# 相邻兄弟选择器
print(9, soup.select('div p:nth-of-type(1) + [src]')) # 返回[]
# 普通兄弟选择器
print(10, soup.select('div p:nth-of-type(1) ~ [src]')) # 返回2个img
# 属性选择器
print(11, soup.select('[src]')) # 有属性src
print(12, soup.select('[src="/"]')) # 属性src等于/
print(13, soup.select('[src="http://www.magedu.com/"]')) # 完全匹配
print(14, soup.select('[src^="http://www"]')) # 以http://www开头
print(15, soup.select('[src$="com/"]')) # 以com/结尾
print(16, soup.select('img[src*="mage du"] ') )
print(17, soup.select('img[src*=".com"] ') )
print(18, soup.select('[class="title highlight"] ') )
print(19, soup.select('[class~=title] ') ) # 多值属性中有一个title
------------------------------------------------------------
1 [<p id="first">字典</p>, <p id="second">列表</p>, <p>bottom</p>]
2 [<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
3 [<p id="second">列表</p>]
4 [<p id="second">列表</p>]
5 [<img id="bg1" src="http://www.magedu.com/"/>]
6 [<p id="first">字典</p>, <p id="second">列表</p>]
7 [<p id="first">字典</p>, <p id="second">列表</p>]
8 [<p id="first">字典</p>, <p id="second">列表</p>]
9 []
10 [<img id="bg1" src="http://www.magedu.com/"/>, <img id="bg2" src="http://httpbin.org/"/>]
11 [<img id="bg1" src="http://www.magedu.com/"/>, <img id="bg2" src="http://httpbin.org/"/>]
12 []
13 [<img id="bg1" src="http://www.magedu.com/"/>]
14 [<img id="bg1" src="http://www.magedu.com/"/>]
15 [<img id="bg1" src="http://www.magedu.com/"/>]
16 []
17 [<img id="bg1" src="http://www.magedu.com/"/>]
18 [<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
19 [<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
3.6. 获取文本内容
搜索节点的目的往往是为了提取该节点的文本内容, 一般不需要HTML标记, 只需要文字
from bs4 import BeautifulSoup
with open('./test.html', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'lxml')
# 元素选择器
ele = soup.select('div') # 所有的div标签
print(ele[0].string, end='\n------------\n') # 内容仅仅只能是文本类型,否则返回None
print(list(ele[0].strings), end='\n------------\n') # 迭代保留空白字符
print(list(ele[0].stripped_strings), end='\n------------\n') # 迭代不保留空白字符
print(ele[0], end='\n------------\n')
print(ele[0].text, end='\n------------\n') # 本质上就是get_text(),保留空白字符的strings
print(ele[0].get_text(), end='\n------------\n') # 迭代并join,保留空白字符,strip默认为False
print(ele[0].get_text(strip=True)) # 迭代并join,不保留空白字符
2. Json解析
拿到一个Json字符串,如果想提取其中的部分内容,就需要遍历了。在遍历过程中进行判断。
还有一种方式,类似于XPath,叫做JsonPath。
安装
$ pip install jsonpath
官网 http://goessner.net/articles/JsonPath/ (查询 example)
下表中列出了对应了JSONPath以及对应的XPath用法
XPath | JsonPath | 说明 |
---|---|---|
/ | $ | 根节点 |
. | @ | 现行节点 |
/ | .or[] | 取子节点 |
.. | n/a | 取父节点,Jsonpath未支持 |
// | .. | 就是不管位置,选择所有符合条件的条件 |
* | * | 匹配所有元素节点 |
@ | n/a | 根据属性访问,Json不支持,因为Json是个Key-value递归结构,不需要属性访问。 |
[] | [] | 迭代器标示(可以在里边做简单的迭代操作,如数组下标,根据内容选值等) |
\ | | [,] | 支持迭代器中做多选。 |
不支持 | [start:stop:step] | 切片 |
[] | ?() | 支持过滤操作. |
n/a | () | 支持表达式计算 |
() | n/a | 分组,JsonPath不支持 |
依然用豆瓣电影的热门电影的Json, 找到得分高于8分的
https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&page_limit=50&page_start=0
# subjects对象
{
subjects:
[
{
rate: "8.3",
cover_x: 2309,
title: "爱你,西蒙",
url: "https://movie.douban.com/subject/26654498/",
playable: false,
cover:
"https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2523592367.webp",
id: "26654498",
cover_y: 3464,
is_new: false
},
{
rate: "8.3",
cover_x: 3578,
title: "暴裂无声",
url: "https://movie.douban.com/subject/26647117/",
playable: true,
cover:
"https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2517333671.webp",
id: "26647117",
cover_y: 5078,
is_new: false
}
]
}
import requests,simplejson
from jsonpath import jsonpath
from lxml import etree
from bs4 import BeautifulSoup
# ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
url = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&page_limit=50&page_start=0"
req = requests.request('GET',url,headers={'User-agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"})
if req:
text = req.text # 分析的 HTML
# soup = BeautifulSoup(text,'lxml') # soup 会套一层html 标签;
subjects = simplejson.loads(text)
print(subjects)
#-----------------------------------------------------------------subject对象当前级别 50 个
{'subjects': [{'rate': '5.2', 'cover_x': 1080, 'title': '釜山行2:半岛', 'url': 'https://movie.douban.com/subject/30299515/', 'playable': False, 'cover': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2588307420.jpg', 'id': '30299515', 'cover_y': 1539, 'is_new': False}, {'rate': '6.7', 'cover_x': 1050, 'title': '烂故事', 'url': 'https://movie.douban.com/subject/34958737/', 'playable': False, 'cover': 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2601922615.jpg', 'id': '34958737', 'cover_y': 1500, 'is_new': False}]}
rs1 = jsonpath(subjects,'$..title') # 从根开始任意层的title 50 个
print(rs1,len(rs1))
#----------------------------------------------------------
['釜山行2:半岛', '铁雨2:首脑峰会', '四平青年之三傻罪途', '超人:明日之子', '夏日国度']
rs2 = jsonpath(subjects,'$..subjects[?(@.rate>"8")]') # 查询subjects中 评分大于8的电影 ;
rs2title = jsonpath(subjects,'$..subjects[?(@.rate>"8")].title') # 去大于8分电影的标题;
print(rs2)
print(rs2title[:5])
#----------------------------------------------------------
[{'rate': '8.6', 'cover_x': 2977, 'title': '极速车王', 'url': 'https://movie.douban.com/subject/6538866/', 'playable': False, 'cover': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2614997281.jpg', 'id': '6538866', 'cover_y': 4410, 'is_new': False}, {'rate': '8.5', 'cover_x': 3000, 'title': '1917', 'url': 'https://movie.douban.com/subject/30252495/', 'playable': False, 'cover': 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2615015805.jpg', 'id': '30252495', 'cover_y': 4200, 'is_new': False}, {'rate': '8.0', 'cover_x': 1080, 'title': '马丁·伊登', 'url': 'https://movie.douban.com/subject/26952994/', 'playable': False, 'cover': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2578760530.jpg', 'id': '26952994', 'cover_y': 1543, 'is_new': False}]
['极速车王', '1917', '乔乔的异想世界', '少年的你', '进击的巨人:编年史']
异步请求的数据在XHR中,request请求的html不一定有我们想要的数据;
能否通过其他地方获取信息思路
找到title非常容易, 但是要用其兄弟节点rate判断是否大于8分, 就不好做了。
能够从父节点下手, subjects的多个子节点中, 要用[] , 某一个当前节点的rate和字符串8比较来过滤的得到符合要求的subjects的子节点, 取这个子节点的title。
网友评论