【Python爬虫学习实践】基于BeautifulSoup的网站解析及数据可视化
在上一次的学习实践中,我们以Tencent职位信息网站为例,介绍了在爬虫中如何分析待解析的网站结构,同时也说明了利用Xpath和lxml解析网站的一般化流程。在本节的实践中,我们将以中国天气网为例,并基于Beautiful Soup库对其进行数据解析,最后再简单说明pyecharts数据可视化。
中国天气网网址:http://www.weather.com.cn/textFC/hb.shtml
和之前的Tencent职位信息实践一样,我们先来分析一下我们所爬取的网站的结构。在中国天气网中,我们可以看到其是按照地区进行了分类,如华北、东北、华东、华中等等,并且每一个地区都对应着一个不同的页面。
接下来我们再来看看我们需要获取的数据信息。如下图所示,每一个页面的数据结构都是以省市为块的城市数据列表,而这些列表中的数据就是我们需要提取的信息。由于现在当地时间为夜晚,白天的信息也就没有显示了,因此在此以爬取夜间的天气现象和最低气温为例,来说明实践中的Beautiful Soup数据提取的一般化流程。(Ps.注意这里显示了一周的天气情况,而后面的天气信息都是完整的,后续会介绍如何爬取这些不同天的数据)
通过上述简单分析,我们不难知道其实本次的数据获取只需要得到相应的URL地址,通过请求获取其HTML源代码,再利用解析库解析出相应的数据即可。下面我们就对此分过程介绍。
Step1——分析天气URL
从之前的介绍我们知道中国天气网划分了几个不同的地区,每一个地区都对应着一个URL,现在我们就先来看看这些URL的构成法。
华北:http://www.weather.com.cn/textFC/hb.shtml
东北:http://www.weather.com.cn/textFC/db.shtml
华东:http://www.weather.com.cn/textFC/hd.shtml
华中:http://www.weather.com.cn/textFC/hz.shtml
…
经过观察分析,发现这几个URL的域名都是一样的,只是后面的path的文件名不同,因此我们可以大胆地推测其URL构成法为:’http://www.weather.com.cn/textFC/+{}.shtml’,其中{}为地区名拼音缩写
接下来,我们再通过其他地区地URL来验证我们地推测。(十多秒过后…)嗯,结果发现和我们的推测是一样的,如港澳台就是引用’gat.shtml’。如此一来,URL的获取工作就好了。(其实,天气地区个数较少,网页URL也比较少,我们也可以手动获取,不过这样代码就会显得有些冗长)
## 遍历获取各地区天气URL并传入解析函数 url = 'http://www.weather.com.cn/textFC/{}.shtml' for flag in ('hb', 'db', 'hd', 'hz', 'hn', 'xb', 'xn', 'gat'): parse_page_soup(url.format(flag))
Step2——分析数据信息结构
在获取了天气URL后,接下来就要分析我们所需提取的数据结构是怎样的。下面以华北地区为例,可以看到其省市天气数据结构如下。发现其数据都放在了一个class为conMidtab的div标签中,而这样的标签共有7个,那么又分别代表什么呢?细心观察后发现只有1个的display 样式为block,而其他的都是none。其实当我们点击其他日期的时候,我们会发现其他一个变成了block,而之前的那个变成了none,所以这些其实就是代表不同时间的天气数据,进入节点查看具体信息后我们也发现这些节点的结构都一样,也再次说明这就是用来控制显示不同日期数据的。
每一个日期信息的获取的原理都是一样的,那么接下来我们就以北京(今天)为例,进一步分析其数据组织结构。浏览源码我们可以发现在comMidtab的div标签下有5个comMidtab2的子div标签,这些其实就是代表华北地区的5个省市,每一个div为一个省市天气的列表。
接下来,在每一个子div中,我们可以找到一个table标签,而里面的tbody标签下又有若干个tr标签,扫描后可得知这些tr就是每一个省市数据列表中的行。
我们进一步观察每个tr标签的内容,可以知道前两个tr标签是列表的数据信息头,而我们真正要获取的数据为之后的tr标签。
这么一来,我们便可以结合解析库来获取相应的数据信息了。不过在解析的时候要注意一个问题,那就是并不是所有的tr标签的子结构都是一样的。如下面的北京和海淀,北京比海淀多了第一个td,从图中可以看出其实这是旁边的省市名,因此在解析时需要设定一个好的解析策略。
##数据解析 import requests from bs4 import BeautifulSoup HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36', } # 设置全局变量,用于保存提取的数据 WEATHER_ALL = [] # 解析数据 def parse_page_soup(url): response = requests.get(url, headers=HEADERS) text = response.content.decode('utf-8') soup = BeautifulSoup(text, 'html5lib') #采用html5lib的解析器 conMidtab = soup.find('div', attrs={'class': 'conMidtab'}) tables = conMidtab.find_all('table') for table in tables: trs = table.find_all('tr')[2:] #舍弃前两个信息头数据 for tr in trs: tds = tr.find_all('td') data = { 'city': list(tds[-8].stripped_strings)[0], #为统一结构,倒序取值 'weather': list(tds[-4].stripped_strings)[0], 'min_temp': int(list(tds[-2].stripped_strings)[0]) } WEATHER_ALL.append(data)
这里需要说明的是,为什么我们在解析时采用的时html5lib的解析器呢?其实,采用lxml解析会快很多,但这不安全,因为在本次的天气网中,并不是所有的天气页面都是很规范的。我们可以先试一下用lxml来解析,看看会发生什么?如下图,我们发现在解析港澳台地区的时候发生了错误,那么是为什么呢?
在错误中提示列表越界,说明没有找到对应的标签,是我们提取出问题了?其实不然,之前在html5lib解析器下都是可以的,说明问题只出在了解析方式上,而这两种解析器的区别重在容错性。查看源码我们发现,港澳台地区的网页并不是那么规范的,在数据表table标签中,其有前标签而缺少了尾标签</table>,也因此容错性不及html5lib的lxml解析就会出错,所以我们在写爬虫时要先确定好解析器种类,也可使用动态解析器。
Step3——数据可视化
在爬取获得数据后,接下来我们简单地按照最低气温排序并对前12个城市的数据进行可视化处理。
对于排序,因为我们存储体的是列表对象,可以使用内置的sort()方法,而我们存储的元素类型为字典结构并对某一属性值(最低气温)排序,因此调用方法时我们需要传入一个排序关键字参数key,并通过lambda表达式使其与字典中的属性相关联(这里也恰好解释了之前获取最低气温时为什么要存储为int类型)。
于可视化处理,我们可以借助一个名为pyecharts的工具库,使用它可以很方便地绘制各类地数据图。安装方法也很简单,使用pip安装即可(pip install pyecharts),具体的使用方法在此不在叙述,大家可以参看中文文档http://pyecharts.org/#/
## 对数据排序并按照最低气温取前12个城市制作可视化图表 from pyecharts import Bar WEATHER_ALL.sort(key=lambda data: data['min_temp']) data = WEATHER_ALL[0:12] cities = list(map(lambda x: x['city'], data)) #横轴数据列表 temps = list(map(lambda x: x['min_temp'], data)) #纵轴数据列表 chart = Bar("中国天气最低气温排行榜") #实例化图表并传入标题参数 chart.add('',cities,temps) #添加一个图表关系 chart.render('temperature.html') #绘制并存储为html文件
另外,在安装pyecharts时显示安装成功,然而实际上使用时可能会出现如下的错误,提示未找到pyecharts_snapshot模块,具体解决措施可参看中文文档(见下图)
至此,上述便是基于Beautiful Soup库解析网站的一个实践,同时也简单地介绍了数据可视化的内容。其实无论Beautiful Soup还是之前的lxml,解析的一般流程都大致相同,只是适用的环境和解析速度有所区别,在实际的爬取工作中还需要认真分析网页结构,采取合适的爬取机制和手段。