大家好,上篇推文介绍了BeautifulSoup抓取的一些基础用法,本篇内容主要是介绍BeautifulSoup模块的文档树使用以及实例。
一、遍历文档树
直接看代码吧
from bs4 import BeautifulSoup
html=' python 知识学堂 '
#上面是随便写的一个页面代码
soup=BeautifulSoup(html,'lxml')
#print(soup.prettify())
print("-------------------------------------------------分割符----------------------------------------------------")
print(soup.head) # 获取head 标签
print("-------------------------------------------------分割符----------------------------------------------------")
print(soup.a) #获取a 标签 默认是第一个
print("-------------------------------------------------分割符----------------------------------------------------")
print('contents:')
print(soup.a.contents) #tag的 .contents 属性可以将tag的子节点以列表的方式输出
print("-------------------------------------------------分割符----------------------------------------------------")
print('children:')
for child in soup.form.children: #获取标签下的一级子标签
print(child)
print("-------------------------------------------------分割符----------------------------------------------------")
print('descendants:')
for child in soup.form.descendants: #获取标签下的所有tag子孙节点进行递归循环
print(child)
print("-------------------------------------------------分割符----------------------------------------------------")
print('strings:')
for str in soup.strings: #输入标签内的字符串
print(str)
print("-------------------------------------------------分割符----------------------------------------------------")
print('stripped_strings:')
for str in soup.stripped_strings: #输入标签内的字符串 去除空字符串
print(str)
结果:
上面知识简单的举了几个获取树的节点的方式,还有很多其他的方式,比如获取父节点,兄弟节点等等。有点与jquery 遍历 DOM的概念类似。
二、搜索文档树
Beautiful Soup定义了很多搜索方法, 这里主要介绍一下比较常用的到的两个方法:find()和find_all(),其他的可以用法类似,举一反三。
2.1 过滤器
- 字符串
- 正则表达式
- 列表
- True
- 方法
from bs4 import BeautifulSoup
import re
html=' python 知识学堂 '
#上面是随便写的一个页面代码
soup=BeautifulSoup(html,'lxml')
print("-------------------------------------------------分割符----------------------------------------------------")
#最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的标签
print("字符串:")
print(soup.find_all('a'))
print("-------------------------------------------------分割符----------------------------------------------------")
#如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容.下面例子中找出所有以a开头的标签,这表示所有标签都应该被找到
print("正则表达式:")
print(soup.find_all(re.compile("^a")))
print("-------------------------------------------------分割符----------------------------------------------------")
#如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有标签和标签
print("列表:")
print(soup.find_all(['a','head']))
print("-------------------------------------------------分割符----------------------------------------------------")
#True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
print("Ture:")
print(soup.find_all(True))
结果:
2.2 find_all()
Name:可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉;
keyword 参数:如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性;
按CSS搜索:按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_ 参数搜索有指定CSS类名的tag;
string 参数:通过 string 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, string 参数接受 字符串 , 正则表达式 , 列表, True .;
limit 参数:find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果;
recursive 参数:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .
from bs4 import BeautifulSoup
html=' python 知识学堂 '
#上面是随便写的一个页面代码
soup=BeautifulSoup(html,'lxml')
#print(soup.prettify())
print("通过tag的name:")
print(soup.find_all('head')) #获取head 标签
print("通过keyword获取:")
print(soup.find_all(id="head")) #获取Id 为head的所有标签
print("通过css类名获取:")
print(soup.find_all('a',class_='mnav')) #获取所有a标签 并且class属性值为mnav
print("通过string获取:")
print(soup.find_all(string="知识")) #获取所有a标签内容为python 的所有标签,全字符匹配
print("limit参数:")
print(soup.find_all("a",limit=2)) #limit表示获取的数量
print("recursive 参数:")
print(soup.find_all("a",recursive=false)) #recursive 默认为true 表示获取当前tag的所有子孙节点,如果为false 只搜索tag直接子节点
结果:
注意只有 find_all() 和 find() 支持 recursive 参数.
find()的方法跟find_all()基本一样,唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果。
2.3 输出
- 格式化输出
- 压缩输出
- 输出格式
- get_text()
from bs4 import BeautifulSoup
html=' python 知识学堂 '
#上面是随便写的一个页面代码
soup = BeautifulSoup(html,'lxml')
#prettify() 方法将Beautiful Soup的文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行
print("格式化输出:")
print(soup.prettify())
print("-------------------------------------------------分割符----------------------------------------------------")
#如果只想得到结果字符串,不重视格式,那么可以对一个 BeautifulSoup 对象或 Tag 对象使用Python的 unicode() 或 str() 方法
print("压缩输出:")
print(str(soup))
print("-------------------------------------------------分割符----------------------------------------------------")
#Beautiful Soup输出是会将HTML中的特殊字符转换成Unicode,比如“&lquot;”
print("输出格式:")
print(str(BeautifulSoup("&&*&*",'lxml')))
print("-------------------------------------------------分割符----------------------------------------------------")
#如果只想得到tag中包含的文本内容,那么可以用 get_text() 方法,这个方法获取到tag中包含的所有文版内容包括子孙tag中的内容,并将结果作为Unicode字符串返回
print("get_text():")
print(soup.get_text())
print("-------------------------------------------------分割符----------------------------------------------------")
结果我就不贴出来了,自己执行一下就知道了。
当然还有别的很多方法,在这里就不再赘述了,可以直接参考官方
https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/
Buaautiful soup 的功能还是很强大的,这里只是简单的描述了一下爬虫常用的一些东西。
下面就来实操一下吧,还是以获取省市区为例子
三、实例
我们还是用上篇的获取省市区来举例子。
import requests
from bs4 import BeautifulSoup
import time
class Demo():
def __init__(self):
try:
base_url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/'
trlist = self.get_data(base_url, "provincetable",'provincetr') #查看页面,就知道所有的省所在的tr上都有唯一的class='provincetr'
for tr in trlist:
for td in tr:
if td.a is None:
continue
p_name = td.a.get_text()
c_url = base_url + td.a.get('href') #获取下级城市的地址
print("省:" + p_name) #获取每个省
# time.sleep(0.5)
trs = self.get_data(c_url, "citytable","citytr")
for tr in trs: #循环每个市
if tr.find_all('td')[1] is None:
continue
#c_code = tr.find_all('td')[0].string #获取城市code
c_name = tr.find_all('td')[1].string #获取城市 name
ct_url = base_url + tr.find_all('td')[1].a.get('href') #获取下级区的地址
print(p_name+"-"+c_name)
time.sleep(0.5)
trs1 = self.get_courtydata(ct_url)
if trs1 is None:
continue
for tr1 in trs1: #循环每个区
if tr1.find_all('td')[1] is None:
continue
#ct_code = tr.find_all('td')[0].string #获取区code
ct_name = tr1.find_all('td')[1].string #获取区name
print(p_name+"-"+c_name+"-"+ct_name)
except:
print("出错了")
def get_data(self, url, table_attr,attr):
response = requests.get(url)
response.encoding = 'gb2312' #编码转换
soup = BeautifulSoup(response.text, 'lxml') #使用lxml的解析器
table = soup.find('table',class_=table_attr) #查看页面元素就知道数据都在第二个 tbody
trlist = table.find_all('tr',class_=attr)
return trlist
def get_courtydata(self, url):
response = requests.get(url)
response.encoding = 'gb2312' #编码转换
soup = BeautifulSoup(response.text, 'lxml') #使用lxml的解析器
towntr=soup.find('table',class_='towntable')
if towntr is not None:
table = soup.find('table',class_='towntable')
trlist = table.find_all('tr',class_='towntr')
else:
table = soup.find('table',class_='countytable')
trlist = table.find_all('tr',class_='countytr')
return trlist
if __name__ == '__main__':
Demo()
结果
直接给大家看一下获取到的最后一个省市区的结果了,大家注意每次获取的页面信息时的时间间隔;
四、总结
本篇文章讲述了关于BeautifulSoup的一些基础的内容,主要是与爬虫相关的,关于BeautifulSoup其他功能还有很多,可以区官网上自行学习。
贴一下BeautifulSoup官网地址:
https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/