世上除了生死,都是小事。从今天开始,每天微笑吧。不管遇到了什么烦心事,都不要自己为难自己;无论今天发生多么糟糕的事,都不应该感到悲伤!
参考:
Python爬虫系列四(正题片):让bs4帮你解析网页
4.3 遍历文档树
在文档树中找到关心的内容才是日常的工作,也就是说如何遍历树中的节点。使用上面的test.html来测试
1. 使用Tag
soup.div 可以找到从根节点开始查找第一个div节点
soup.div.p 说明从根节点开始找到第一个div后返回一个Tag对象,这个Tag对象下继续找第一个p,找到返回Tag对象
soup.p 说明遍历是深度优先,返回了文字“字典”,而不是文字“bottom”。
2. 遍历直接子节点
print(soup.div.contents) # 将对象的所有类型直接子节点以列表方式输出
print(soup.div.children) # 返回子节点的迭代器
print(list(soup.div.children)) # 等价于soup.div.contents
3. 遍历所有子孙节点
print(list(soup.div.descendants)) # 返回第一个div节点的所有类型子孙节点,可以看出迭代次序是深度优先
from bs4 import BeautifulSoup
from bs4.element import Tag
with open('./test.html', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'lxml')
print(soup.p.string)
print(soup.div.contents) # 直接子标签列表
print(len(soup.div.contents)) # 直接子标签列表
print('-'*30)
print(list(soup.div.children))#直接子标签可迭代对象;
print('-' * 30)
print(list(soup.div.descendants)) # 迭代对象
#-------------------------------------------------------------------------------
C:\ProgramData\Miniconda3\envs\blog\python.exe C:/Users/dell/PycharmProjects/spiders/test.py
字典
['\n', <h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>, '\n', <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>, '\n']
5
------------------------------
['\n', <h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>, '\n', <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>, '\n']
------------------------------
['\n', <h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>, <a href="http://www.python.org">python</a>, 'python', '高级班', '\n', <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>, '\n', <p id="first">字典</p>, '字典', '\n', <p id="second">列表</p>, '列表', '\n', <input name="_csrf" type="hidden" value="7139e401481ef2f46ce98b22af4f4bed"/>, '\n', ' comment ', '\n', <img id="bg1" src="http://www.magedu.com/"/>, '\n', <img id="bg2" src="http://httpbin.org/"/>, '\n', '\n']
4. 遍历字符串
在前面的例子中, soup.div.string返回None, 是因为string要求soup.div只能有一个Navigable String类型子节点,
也就是如这样<div>only string</div>
如果div有很多子孙节点, 如何提取字符串?
print(soup.div.string) # 返回None
print("".join(soup.div.strings)) # 返回迭代器,带多余的空白字符
print("".join(soup.div.stripped_strings)) # 返回迭代器,去除多余空白符 连接
------------------------------
None
python
python高级班字典列表
5. 遍历祖先节点
print(soup.parent) # None 根节点没有父节点
print(soup.div.parent.name) # body,第一个div的父节点
print(soup.p.parent.parent.get('id')) # main
print(list(map(lambda x: x.name, soup.p.parents))) # 父迭代器,由近及远 遍历兄弟节点
------------------------------
None
body
main
['div', 'div', 'body', 'html', '[document]']
6. 遍历兄弟节点
print('{} [{}]'.format(1, soup.p.next_sibling)) # 第一个p元素的下一个兄弟节点,注意可能是一个文本节点
print('{} [{}]'.format(2, soup.p.previous_sibling)) # '\n'
print(list(soup.p.next_siblings)) # previous_siblings
1 [ # '\n'
]
2 [ # '\n'
]
['\n', <p id="second">列表</p>, '\n', <input name="_csrf" type="hidden" value="7139e401481ef2f46ce98b22af4f4bed"/>, '\n', ' comment ', '\n', <img id="bg1" src="http://www.magedu.com/"/>, '\n', <img id="bg2" src="http://httpbin.org/"/>, '\n']
7. 遍历其他元素
<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>
print(soup.h3.next_element) # 返回"字典"2个字
print(soup.h3.next_element.next_element)
print(soup.h3.next_element.next_element.next_element)
print(list(soup.h3.next_elements))
------------------------------
<a href="http://www.python.org">python</a>
python
高级班
[<a href="http://www.python.org">python</a>, 'python', '高级班', '\n', <div class="content">
<p id="first">字典</p>
<p id="second">列表</p>
<input name="_csrf" type="hidden" value="7139e401481ef2f46ce98b22af4f4bed"/>
<!-- comment -->
from bs4 import BeautifulSoup
from bs4.element import Tag
with open('./test.html', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'lxml')
#返回"字典"2个字
print(soup.p.next_element.next_element.next_element) #是什么?
print(soup.p.next_sibling.next_sibling.next_sibling) #是什么?
print('-'*30)
#对比下面的差异
print(list(soup.p.next_elements) )
print(list(soup.p.next_siblings) )
4.4 搜索文档树
find系有很多方法, 请自行查 中文网站,官网非常复杂 ; 最具有代表的是 find_all ;
find_all(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
find_all方法,立即返回一个列表
1. name
官方称为filter过滤器,这个参数可以是以下类型:
1. 字符串
一个标签名称的字符串,会按照这个字符串全长匹配标签名
print(soup.find_all('p')) # 返回文档中所有p标签
——————————————————————————————
[<p id="first">字典</p>, <p id="second">列表</p>, <p>bottom</p>]
2. 正则表达式对象
按照“正则表达式对象”的模式匹配标签名
print(soup.find_all(re.compile('^h\d'))) # 标签名以h开头后接数字
#-------------------------------------------------------------------
[<h1>马哥教育欢迎您</h1>, <h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
3. 列表
print(soup.find_all(['p', 'h1', 'h3'])) # 或关系,找出列表所有的标签
print(soup.find_all(re.compile(r'^(p|h\d)$'))) # 使用正则完成
#-------------------------------------------------------------------
[<h1>马哥教育欢迎您</h1>, <h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>, <p id="first">字典</p>, <p id="second">列表</p>, <p>bottom</p>]
[<h1>马哥教育欢迎您</h1>, <h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>, <p id="first">字典</p>, <p id="second">列表</p>, <p>bottom</p>]
4. True或None
True或None,则find_all返回全部非字符串节点、非注释节点,即Tag标签类型
print(soup.find_all(True)) # 有点像 去掉空格
print(list(map(lambda x:x.name, soup.find_all(True))))
print(list(map(lambda x:x.name, soup.find_all(None))))
print(list(map(lambda x:x.name, soup.find_all())))
5. 函数
如果使用以上过滤器还不能提取出想要的节点,可以使用函数,此函数仅只能接收一个参数。
如果这个函数返回True,表示当前节点匹配;返回False则是不匹配。
练习:找出所有有class属性且有多个值的节点
符合这个要求只有h3标签
from bs4 import BeautifulSoup
from bs4.element import Tag
def many_class(tag:Tag):
# print(type(tag))
# print(tag.attrs)
return True if len(tag.attrs.get('class', [])) > 1 else False
with open('./test.html', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'lxml')
div:Tag = soup.div.div
print(soup.find_all(many_class))
#----------------------------------------------------------
[<h3 class="title highlight"><a href="http://www.python.org">python</a>高级班</h3>]
网友评论