【python爬虫实战】进阶天气虫虫(过程复盘 & 心得分享)

程序设计过程里的一些心得:

0. 规模较大的程序,往往都是以更小的功能块搭建起来的。如此,为了提升总体程序的构建效率, 笔者发现分“两步走”会比较高效:

        A. 遇到需要反复调试的功能块,可先在另一程序中逐一单独测试某一功能块(这有助于突出模块本身的细节问题)

        B. 最后像搭积木一样将模块放回主题程序中,进行一些接口性的“磨合”

1. 见招拆招,必备的是灵活的思路

相比于上一篇初代爬虫,进阶爬虫的核心特点在于可以提供未来五天天气情况的预测,而这也是程序实现的关键难点。具体而言有如下两方面:

        A. 如何精准获取:

由于要获取的信息较多较细,观察网页html原码,或者参考编译过程中直接用find_all函数获取的结果不难发现,由于网页原码中标签的重复性较大,通过一次find_all获得的可迭代对象可谓是“鱼龙混杂”。

解决方案多层搜索,缩小目标范围:#以获取list_weather为例

weathers = soup.find_all("td",attrs={"width":"20%", "align":"left"})
list_weather = []
for weather in weathers:#预处理
    weather = weather.find("p")#找到除上述标签外还有标签"p"的
    weather = weather.find("span")#找到除上述标签外还有标签"span"的
    if weather:#排除weather为None的情况
        list_weather.append(weather.string)

AT:find_all返回的对象只可用,且可不断用“find”函数

        B.如何“完美”输出:

笔者在输出这一步碰上一个麻烦:要实现如下的规整输出,就不得不在一次for循环中将日期、气温、天气的值都输出一遍。

但是!麻烦来了,他们三者在find_all后得到的元素个数并不一样,但for循环的更新一次的依据恰恰是根据迭代一次来进行的(迭代一次,for循环进行一次),因此想要实现“优雅“输出大概没有那么容易。

解决方案:预处理

还是以上面的获取list_weather为例,实际上,之所以要专门建立一个list_weather,恰恰是为了将有效元素留在里面,最终list_weather中只留下干净的五个元素。

完整代码如下:

#请求头headers的值需根据电脑信息自行提供哈,具体方法可参考b站视频讲解哈,此处不再赘述

from bs4 import BeautifulSoup
import requests
from pypinyin import pinyin, Style
import time
import os
if __name__ == '__main__':
    headers = { }
    mode = 1
    while mode != -1:
        try:
            os.system("cls")
            mode = 1
            print("----------勇敢虫虫,不怕困难!----------")
            print("~让虫虫爬一会~")
            response = requests.get("http://qq.ip138.com/weather/", headers=headers)
            response.raise_for_status()
            soup = BeautifulSoup(response.content, "html.parser", from_encoding="utf-8")
            provinces = soup.find_all("dd")  
            # 打印所所有城市名
            dict_province = {}
            for idx, province in enumerate(provinces):
                province_text = province.get_text(strip = True)
                print(idx, province_text)
                dict_province.setdefault(idx, province_text)
            
            #接收用户选择省份
            idx_province = int(input("请选择您关注的省对应序号(虫虫只爬得动省份哈, 至于市、区,请期待一下吧~):"))
            province_chosen = dict_province[idx_province]
            # pinyin_province = ' '.join([''.join(pinyin(char, style=Style.NORMAL)) for char in province_chosen])
            pinyin_list_province = []  
            for char in province_chosen:  
                # pinyin函数返回的是一个列表,其中包含了每个音节的列表(对于多音字)  
                # 使用列表推导式将每个音节的列表转换为用空格分隔的字符串  
                pinyin_chars = ' '.join([''.join(py) for py in pinyin(char, style=Style.NORMAL)])  
                pinyin_list_province.append(pinyin_chars)  
            # 最后,使用join函数将所有汉字的拼音连接起来,用空格分隔  
            pinyin_province = ''.join(pinyin_list_province) 
            
    #省份    #访问对应省份网址
            response = requests.get(f"https://qq.ip138.com/weather/{pinyin_province}/", headers=headers)
            soup = BeautifulSoup(response.content, "html.parser")
            cities = soup.find_all("a", attrs={"class", "title"})
            temps = soup.find_all("span", attrs={"class":"temp"})
            weathers = soup.find_all("span", attrs={"class":"weather"})
            dict_cities = {}
            dict_temps = {}
            dict_weathers = {}
            idx = 1
            for city, temp, weather in zip(cities, temps, weathers):
                city_text = city.get_text(strip = True)
                print(idx, city.string)
                dict_cities.setdefault(idx, city_text)
                dict_temps.setdefault(idx, temp)
                dict_weathers.setdefault(idx, weather)
                idx += 1
            #接收用户输入城市
            while mode == 1:
                idx_city = int(input("请选择你要关注的城市对应序号:"))
                city_chosen = list(dict_cities[idx_city])
                city_chosen = ''.join(city_chosen[:len(city_chosen)-4])
                pinyin_list_city = []
                for char in city_chosen:  
                    # pinyin函数返回的是一个列表,其中包含了每个音节的列表(对于多音字)  
                    # 使用列表推导式将每个音节的列表转换为用空格分隔的字符串  
                    pinyin_chars = ' '.join([''.join(py) for py in pinyin(char, style=Style.NORMAL)])  
                    pinyin_list_city.append(pinyin_chars)  
                # 最后,使用join函数将所有汉字的拼音从列表中提取出来并连接起来,用空格分隔  
                pinyin_list_city = pinyin_list_city[:len(pinyin_list_city)]#去掉末尾“天气预报”对应拼音
                pinyin_city = ''.join(pinyin_list_city) 
                city_temp = dict_temps[idx_city]
                city_weather = dict_weathers[idx_city]
                print("\n找到啦:\n{0}市今日{1},温度{2}".format(city_chosen, city_weather.string, city_temp.string))
                time.sleep(2)
                mode = int(input("天气虫虫:看完了这座城,接下来要俺做什么嘞?(请输入:-1:结束一切程序/ 0:看看别的省(市、区)/1:看看本省的其他城市)/2:了解下当前城市未来五天天气发展情况 :"))
                if mode == 2:
                    response = requests.get(f"https://qq.ip138.com/weather/{pinyin_province}/{pinyin_city}.htm", headers=headers)
                    soup = BeautifulSoup(response.content, "html.parser")
                    dates = soup.find_all("p", attrs={"class":"date"})
                    weathers = soup.find_all("td",attrs={"width":"20%", "align":"left"})
                    # weather = weathers.f
                    temperatures = soup.find_all("td", attrs={"width":"15%", "align":"center"})
                    list_weather = []
                    for weather in weathers:#预处理
                        weather = weather.find("p")
                        weather = weather.find("span")
                        if weather:
                            list_weather.append(weather.string)
                    print(f"--------好滴~未来五天,{city_chosen}天气情况见下哦--------")
                    print("日期".ljust(len("2024-07-01")), "气温".ljust(len("20℃ ~ 28℃ ")), "天气")
                    for date, temp, wehather in zip(dates, temperatures, list_weather):
                        print(date.string, end=" | ")
                        temp = temp.find("p")
                        print(temp.string, end=" | ")
                        print(weather.string)
                    time.sleep(5)
                    mode = int(input("看完了这座城,接下来要我做什么嘞?(请输入:-1:结束一切程序/ 0:看看别的省(市、区)/1:看看本省的其他城市 :"))
                        

        except requests.RequestException as ex:
            print(f"网络错误:{ex}")

在vscode上运行可实现如下输出:

~ 本文是笔者超级用心写的,衷心希望能给你带来些许启发! ~

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值