python爬虫(十)BeautifulSoup案例分析-爬取全国天气状况

爬取全国天气

需求

从网站里爬取全国城市对应的最低温度,保存到csv文件里。
先要找到数据在哪,在页面点右键,检查网页源码,搜索“北京”,如下图
在这里插入图片描述
可以看到需要的数据在网页源码中,基本确定是静态加载出来的。我们向网页对应的url发起请求,就可以得到一串网页源码,用我们学到的方法进行解析就可以了,这样的话就可以确定目标url。

目标url

http://www.weather.com.cn/textFC/hb.shtml

页面分析

在页面表格城市“北京”处,点击右键,可以看到光标定位到<td 下的<a标签里,刚已经基本确定数据在网页源码中,虽然elements里的数据跟网页源码中有所区别,我们可以借助elements来进行页面分析和数据分析,定位的“北京”是在<td标签里面的,在往上折叠会发现他们都是存放于<tr标签中,前两个<tr代表的是表头,下面的每个<tr标签代表一个城市的数据,而所有的<tr标签都存放于<tbody标签里。<tbody是后来由网页渲染出来的,而在网页源码中搜索不到<tbody,不存在于网页源码中。<tbody标签存放于<table标签中,光标位于<table标签时,整个北京的数据都会被选中,再往上光标放到<div class="conMidtab2"时,不同的省/直辖市都会被选中;当光标定位到<div class="conMidtab"时,所有省/直辖市的表格都会被选中,而多个<div class="conMidtab"代表不同日期的天气情况。
从根目录往下展开,可以看到"海淀"位于,table --> tbody --> 第四个tr的文本(前两个tr是表头)的第一个td标签里;最低气温的20位于tr标签下倒数第二个td标签的文本里;我们可以用find找到第一个<div class="conMidtab"爬取第一天的天气,再用findall找到<div class="conMidtab"里所有<table标签,找到每个表格,在用findall找到所有的tr标签(过滤掉前两个tr),下面的每个tr代表的是一个城市的数据,而每个tr中的第一个td代表的是城市,倒数第二个td代表的是最低温度。
总结:

  1. 找到class="conMidtab"的div标签 里面存放了当天所有的的天气数据 用find
  2. 找到table标签 一个table对应一张表格 也就是一个省或者是直辖市的天气数据
  3. 找到table标签下面的所有tr 但是要过滤掉前两个tr标签(表头)
  4. 找到tr标签下面的所有的td 其中第一个td标签是城市名 倒数td第二个存放的是最低温
  5. 保存到csv文件中

工具:requests(发请求) bs4(解析数据) csv(保存数据)

实现步骤

import requests
from bs4 import BeautifulSoup
import csv
import time

# 发送请求,获取相应
def req_data(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62'
    }
    res = requests.get(url, headers=headers)
    html = res.content.decode('utf-8')
    return html

# 解析数据
def parse_data(html):
    soup = BeautifulSoup(html, 'lxml')
    # 1.找到class_="conMidtab"的div标签,只找一个
    div_class = soup.find(class_="conMidtab")
    # 2. 找到table标签,有好几个表格
    lis = []
    tables =div_class.find_all('table')
    for table in tables:
        # print(table)
        # 3.找到所有的tr标签,过滤掉前两个

        tr_tags = table.find_all('tr')[2:]

        for tr in tr_tags:
            d = {}
        #     print(tr)
        #     print('*'*50)
            # 4.在所有的tr标签下取出td标签
            tds = tr.find_all('td')
            # 其中第一个td标签是城市名,倒数td第二个存放的是最低温
            # 第一个td标签有两个文本,第一个是空格,所以用tds[0].stripped_strings) 返回的是生成器对象
            cities = list(tds[0].stripped_strings)[0]
            temps = list(tds[-2].stripped_strings)[0]
            
            # print(cities, temps)
            d['city'] = cities
            d['temp'] = temps
            lis.append(d)
    # print(lis)
    return lis

# 保存数据
def save_csv(lis, header):
    with open('weather.csv', 'a', encoding='utf-8', newline="") as f:
        writ = csv.DictWriter(f, header)
        if i == 0:   # 利用全局变量i
            writ.writeheader()  # 只在第一页打印表头
        writ.writerows(lis)

if __name__ == '__main__':
    # 把几个网页中不同的地方放到列表中,进行遍历取出,实现翻页处理
    lis1 = ['hb', 'db', 'hd', 'hz', 'hn', 'xb', 'xn', 'gat']
    global i  # 定义i为全局变量,实现去除重复表头
    for i in lis1:
        time.sleep(5)
        url = f'http://www.weather.com.cn/textFC/{i}.shtml'
        h = req_data(url)
        lis = parse_data(h)
        header = ('city', 'temp')
        save_csv(lis, header)

程序运行完成之后,会出现每个表格(省/直辖市)的第一条数据显示的城市是省/直辖市的名字,比如河北省,第一条数据显示的是河北,而不是石家庄;山西省第一条数据显示的是山西,而不是大同。在网页源码中我们可以看到,经过过滤表头后,第一个td显示的是省/直辖市的名字,第二个td显示的是城市名字,所以要对每个表格判断一下,是否为第一条数据(城市),如果是就返回第二个td里的内容(因为第一个td显示的是省/直辖市),如果不是还是按照以前页面分析的返回第一个td标签(城市名),可以用enumerate()获取的索引值对获取到的td标签进行判断。

a = ['x', 'y', 'z']
# print(enumerate(a))  # 返回enumerate对象,<enumerate object at 0x000002AFB0E896C0>
# for i in enumerate(a):
#     print(i)  # 返回两个数据,第一个数据是元素的索引,第二个数据是元素本身

for index, i in enumerate(a):
    print(index, i)
    # index接收到的是元素的索引值,0,1,2
    # i接收的是元素,x,y,z

可以对def parse_data()函数中for tr in tr_tags进行一下判断

         for index, tr in enumerate(tr_tags):
            d = {}
        #     print(tr)
        #     print('*'*50)
            # 4.在所有的tr标签下取出td标签
            tds = tr.find_all('td')
            # 其中第一个td标签是城市名,倒数td第二个存放的是最低温
            # 第一个td标签有两个文本,第一个是空格,所以用tds[0].stripped_strings) 返回的是生成器对象
            if index == 0:
                cities = list(tds[1].stripped_strings)[0]
            else:
                cities = list(tds[0].stripped_strings)[0]
            temps = list(tds[-2].stripped_strings)[0]

在最后一页,港澳台页面,显示的数据会有异常
在这里插入图片描述
这个时候回到网页源码中,查找<table标签,可以看到其实<table标签其实是不完整的,只有开头,没有结尾,这个时候就要把网页的解析器"lxml"换为"html5lib",这个解析器可以完善标签的内容,对所有残缺的标签进行补全,就是运行比较慢。

soup = BeautifulSoup(html, 'html5lib')

在这里插入图片描述
如果出现下图所示的内容 说明没有安装"html5lib",需要用pip install 安装一下就可以了
在这里插入图片描述
完整版代码如下:

import requests
from bs4 import BeautifulSoup
import csv
import time

# 发送请求,获取相应
def req_data(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62'
    }
    res = requests.get(url, headers=headers)
    html = res.content.decode('utf-8')
    return html

# 解析数据
def parse_data(html):
    soup = BeautifulSoup(html, 'html5lib')
    # 1.找到class_="conMidtab"的div标签,只找一个
    div_class = soup.find(class_="conMidtab")
    # 2. 找到table标签,有好几个表格
    lis = []
    tables =div_class.find_all('table')
    for table in tables:
        # print(table)
        # 3.找到所有的tr标签,过滤掉前两个

        tr_tags = table.find_all('tr')[2:]

        for index, tr in enumerate(tr_tags):
            d = {}
        #     print(tr)
        #     print('*'*50)
            # 4.在所有的tr标签下取出td标签
            tds = tr.find_all('td')
            # 其中第一个td标签是城市名,倒数td第二个存放的是最低温
            # 第一个td标签有两个文本,第一个是空格,所以用tds[0].stripped_strings) 返回的是生成器对象
            if index == 0:
                cities = list(tds[1].stripped_strings)[0]
            else:
                cities = list(tds[0].stripped_strings)[0]
            temps = list(tds[-2].stripped_strings)[0]

            # print(cities, temps)
            d['city'] = cities
            d['temp'] = temps
            lis.append(d)
    # print(lis)
    return lis

# 保存数据
def save_csv(lis, header):
    with open('weather.csv', 'a', encoding='utf-8', newline="") as f:
        writ = csv.DictWriter(f, header)
        if i == 0:   # 利用全局变量i
            writ.writeheader()  # 只在第一页打印表头
        writ.writerows(lis)

if __name__ == '__main__':
    # 把几个网页中不同的地方放到列表中,进行遍历取出,实现翻页处理
    lis1 = ['hb', 'db', 'hd', 'hz', 'hn', 'xb', 'xn', 'gat']
    global i  # 定义i为全局变量,实现去除重复表头
    for i in lis1:
        time.sleep(5)
        url = f'http://www.weather.com.cn/textFC/{i}.shtml'
        h = req_data(url)
        lis = parse_data(h)
        header = ('city', 'temp')
        save_csv(lis, header)
  • 10
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值