程序设计过程里的一些心得:
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 = 1while mode != -1:try:os.system("cls")mode = 1print("----------勇敢虫虫,不怕困难!----------")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 = 1for 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.ftemperatures = 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上运行可实现如下输出:
~ 本文是笔者超级用心写的,衷心希望能给你带来些许启发! ~