前言
前些天老师说想要一个人写爬虫,听到这个消息,我直接
结果老师说,你给我随便来一个爬虫,写成实验报告发给我,让我看看。
直接把我整不会了,本想装b一波,结果反被制裁。
于是我便干脆直接写个新的(之前写的不想改了),说干就干直接爬天气预报(天气网 (weather.com.cn))。
请各位打开手机,点击天气软件,ok,完结散花
呃呃,好吧,直接开始,为了弘扬爬虫精神(装b),这回我采用了模块化编程,完整代码请参考GitHub库:https://github.com/Rasrea/python-spider。
大郎,该喝汤了
首先明确目标,咱们爬的是全球的天气预报,这就说明,这有点难,所以咱们先爬某一个城市的,我投了三个色子,刚好停在666,所以咱们就先爬舟山吧,挑个简单的时间段,就最近七天吧。定位头文件、获取对应的url....,知道我为什么在题目上加一个“思路”吗?咱们只讲重点。
先上代码:
# 包含数据获取,解析与可视化
def main():
url = 'http://www.weather.com.cn/weather/101211106.shtml'
r = requests.get(url, headers=headers) # 获取HTML
# 数据清洗
grouped_weather_lst = bath_data(r)
# 收集数据,包括时间,天气,温度
date_time = [item[0] for item in grouped_weather_lst] # 时间
weather_day = [item[1] for item in grouped_weather_lst] # 天气
temperature = [item[2] for item in grouped_weather_lst] # 温度
# 数据可视化
line_picture(date_time, weather_day, temperature) # 折线图
这里面包含两个函数bath_data()、line_picture(),一个负责处理数据,一个负责可视化数据。先讲第一个bath_data()。
劳烦各位打开各自的浏览器,查看网站的源代码,你问我为什么不打开给你们演示?因为我浏览器坏了。
打开后你们应该就会发现天气数据的位置,并使用BeautifulSoup获得,这些字符串都是4个为一组,分别是时间、天气、气温、风级(这个没啥用), 因此我把它们每四个分成一个子列表,到时候存储的时候就so easy了,上代码:
# 清洗数据
def bath_data(r):
# 先用.content获取原始字节内容,防止出现乱码
page_content = r.content
bs = BeautifulSoup(page_content, 'html.parser') # 设置解析器
text_r = bs.find('ul', {'class': 't clearfix'}) # 找到天气数据
t_weather = text_r.text.split('\n') # 转换字符集
weather_lst = [i for i in t_weather if len(i) != 0] # 去除空格
grouped_weather_lst = [weather_lst[n:n + 4] for n in range(0, len(weather_lst), 4)] # 要求每四个组成一个子列表
return grouped_weather_lst
为什么说存储的时候very easy,请看main函数,仅需三行。
# 收集数据,包括时间,天气,温度
date_time = [item[0] for item in grouped_weather_lst] # 时间
weather_day = [item[1] for item in grouped_weather_lst] # 天气
temperature = [item[2] for item in grouped_weather_lst] # 温度
接下来就是可视化了,看第二个函数。
这个就相对比较简单了,我选择的是折线图,直接上代码吧
def line_picture(date_time, weather_day, temperature):
# 使用列表推导式进一步处理数据
max_tem = [int(i.split('/')[0].replace('℃', '')) for i in temperature] # 最高气温
min_tem = [int(i.split('/')[1].replace('℃', '')) if len(i.split('/')) == 2
else int(i.split('/')[0].replace('℃', '')) for i in temperature] # 最低气温 # 竖排横坐标标签, 加空行符号
day_lst = [date_time[i].replace('日', '日\n') +
'\n' + weather_day[i] for i in range(len(date_time))]
# 可视化数据
fig = plt.figure(figsize=(12, 8)) # 创建图像对象
plt.plot(range(len(day_lst)), max_tem, 's-', label='最高气温') # 最高气温,并防止横坐标出现两个点
plt.plot(range(len(day_lst)), min_tem, 'o--', label='最低气温') # 最低气温,同理
plt.xticks(range(len(day_lst)), day_lst) # 设置横坐标标签
plt.legend() # 显示图例
# 设置标签
plt.title('舟山七日天气预报', fontsize=16)
plt.xlabel('时间和天气', fontsize=14)
plt.ylabel('温度(℃)', fontsize=14)
fig.savefig('舟山七日天气预报.png') # 保存图像
plt.show()
print('运行完成!')
在可视化之前,咱们要先再处理一下数据,因为咱们爬的都是字符串,所以需要先把他们转化成数值,这段代码应该很简单,我感觉各位都看得懂,因为我浏览器坏了,所以就不多说了。
如此这般舟山近七天的数据就爬到手了,下面就是想方设法扩展到全球。
selenium:朕来了
xdm,咱们如果想要爬取一个地点的天气,当然是自己输入为好,就像这样,先记住它。
my_site = input('请输入地点:')
因为每个地点都有一个对应的url,因此咱们的思路就是找到my_site对应的网址,然后套刚才爬取舟山的代码就行了,所以咱们的重点就是获得my_site的url。
打开浏览器,可以看到每一个地区对应的url都不一样,如果要硬找其中的规律的话,我可以给你扯一天,因为我也不知道里面有什么规律。这个时候就需要selenium登场了,它在使用之前需要下载对应浏览器的driver,请移步其他大佬的博客查看。
咱们可以直接定位搜索框,在搜索框中导入my_site,然后回车,来到相应的页面,之后再导出url就行了,话不多说,上代码:
# 寻找搜索框位置
input_box = driver.find_element(By.XPATH, "//input[@type='text']")
# 自定义地址
my_site = input('请输入地点:')
input_box.send_keys(my_site)
input_box.click()
time.sleep(1)
# 打印新的URL
print(driver.current_url)
直接运行——天气网 (weather.com.cn),结果却是原来的网址,有人可能会说,用显示等待,于是我又等了10秒结果还是原来的网址,HTML也是原来的,与my_site没有半毛钱关系,what f**k?
看来用这个方法获得url是不成了,咱们换个思路。来到浏览器搜索框(浏览器突然好了),我百无聊赖地输入 ‘’舟山”
好奇心驱使我点击‘舟山-浙江’
页面跳转了!!!!
让我们分析一下输入“舟山”后的网页源代码,怎么突然多出来这么一串
其中num="101211101",这不正是舟山网址的标签('http://www.weather.com.cn/weather/101211106.shtml)吗?说明什么?当我们在搜索框输入某个城市时,网站会生成有关该城市的网址,咱们这个时候获取其中的num值,再套http://www.weather.com.cn/weather/{}.shtml模板,不就是my_site的网址了吗。
新的思路来了,咱们使用selenium首先在搜索框中输入my_site,这个时候再爬起源码(由于没有跳转页面,所以不会遇到第一个思路的情况) ,之后使用正则或其他方法获得num值,最后组合模板,这不就是my_site的网址!
说干就干,上代码:
def get_url():
# 配置浏览器,并打开网站
driver = webdriver.Chrome()
driver.get('http://www.weather.com.cn/')
# 等待网页加载
time.sleep(2)
# 寻找搜索框位置
input_box = driver.find_element(By.XPATH, "//input[@type='text']")
# 自定义地址
my_site = input('请输入地点:')
input_box.send_keys(my_site)
input_box.click()
time.sleep(1)
html = driver.page_source # 保存网站源代码
# 关闭浏览器
driver.quit()
# 获取7天数据所对应的网址
result = re.search('<li.*?num="(.*?)">', str(html)).group(1)
url = f'http://www.weather.com.cn/weather/{result}.shtml'
如此下来url就得到了,之后就是套咱们爬取舟山时的代码就ok了。
总结