刚刚入门python,想上手一些比较简单的爬虫项目。今天我们来爬一爬一个空气质量指数的网站来获取全国城市的空气质量指数。记得跟着我的思路来哟!
前期准备工作
首先一个舒服的pythonIDLE肯定是必须的了,小编使用的是pycharm,安装的是anaconda,不懂安装的自行去简书查查哟,这里不再赘述。
然后确定的我们要爬取的网站,这个网站是http://pm25.in/,界面是这样的:
个人觉得这个网站挺良心的,界面很舒适。那么我们现在正式上手我们的小项目吧。
爬虫开始
引入所需要的库
对于爬虫,urllib库肯定少不了的,然后进行数据查找的话,小编使用的是正则表达式,所以还需要引入re库,我们将爬取到的数据写入.csv文件,所以还要引入csv。(不要嫌我啰嗦,我是新人😂)
代码如下:
import urllib.request
import urllib.error
import re
import csv
get网页数据
- 网页获取函数
爬虫爬虫,首先得获取到我们的网页吧,那么网页怎么获取呢?在这里,我们使用urllib,然后写个专门下载网页的函数,向函数里面传进指定的网址进行一些常规的爬虫操作。
def get_html(url):
# 获取网页数据的函数
# 模拟浏览器操作
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'}
try:
# 建立request对象
response = urllib.request.Request(url, headers=headers)
# 打开网页
response = urllib.request.urlopen(request)
# 读取网页数据,使用utf-8解码
result = response.read().decode('utf-8')
# 返回得到的网页数据
return result
except urllib.error.URLError as e:
if hasattr(e, 'reason'):
print("错误原因为:"+str(e.reason))
except urllib.error.HTTPError as e:
if hasattr(e, 'code'):
print("错误状态代码为:"+str(e.code))
这里使用了try-except的结构,用于在出错时查找原因。
返回的部分结果:
html没学好,咱也不懂,咱也不敢问😭,但好歹我们迈出了第一步,成功爬到了目标网页最原始的数据👀。
- 确定网址
有人可能会问,网址不是已经确定了吗🐴,不是这个http://pm25.in/?。这……,不对的,我们要获取的网页是各个城市的网页,而不是人家网站的首页。
我们先来随便点开一个城市,来分析它的网址,例如点开武汉:
上方那个框是武汉这个城市的网址,下方那个框是我们所需要的数据。现在运用我们的火眼金睛来找规律了,这个网址是不是在"http://pm25.in/"后面再加上武汉的拼音?那其他城市呢?
我们再随便点击一些城市验证我们的想法:
得出结论:在"http://pm25.in/"后面加上一个城市的拼音就是我们需要查询城市的网址,把这个网址传进上面我们写好的函数就行了,是不是很简单👍。
获取城市列表
我们的目标是获取所有城市的空气质量指数,肯定不能让我们一个一个输入一个城市的拼音来进行查询啊,所以我们的下一步是获取所有城市的列表,也包括所有城市的拼音。
在这里先墙裂推荐chrome浏览器,对爬虫有非常大的帮助。
-
网页分析
我们知道,城市列表在目标网址的首页,点击F12分析一下它的源码。部分源码如下:
从中可以看出,前面是一个城市的拼音,后面是一个城市的中文名,这就是我们想要的。
- 正则表达式
根据我们需要的东西,编写正则表达式如下:
'<li>.*?<a\shref="/(.*?)">(.*?)</a.*?</li>'
- 函数代码:
def get_city(url):
# 获取全部城市列表
result = get_html(url)
pattern = re.compile('<li>.*?<a\shref="/(.*?)">(.*?)</a.*?</li>', re.S)
city_list = re.findall(pattern, result)
# 获取全部城市拼音和名称
all_city = city_list[10:-1]
print("成功获取全部城市名称列表!")
# 返回城市列表
return all_city
对于为什么all_city = city_list[10:-1]
,这里简单解释一下:
方框下面才是我们想要的,而我们的正则表达式,得到的城市列表会包括热门城市和后面一些不需要的标签(我有点菜),所以我们要去掉,理解了吧😋。
返回的部分结果:
接下来我们只需要将得到的城市列表中的拼音传进前面的
get_html(url)
函数就可以了,是不是很激动😀。
获取所有城市空气质量指数
我们还是写个获取所有城市空气质量指数的函数吧,把它称为get_allcity_aqi(url, all_city)
。
在这里先思考一下,如果光是一个函数就要获取全部城市的空气质量指数,那会显得很臃肿,不妨再写个获取单个城市的空气质量指数的函数get_one_aqi(url, city)
,然后在get_allcity_aqi(url, all_city)
中设置一个循环挨个把城市拼音参数传进get_one_aqi(url, city)
不就行了?开干开干!
-
get_all_aqi(url, city)
函数代码
def get_all_aqi(url, city):
# 获取全部城市的空气质量指数
city_aqi = []
for i, city in enumerate(all_city):
if i % 10 == 0:
print("现已经获取第{}个城市空气质量指数!".format(i))
# 调用获取单个城市空气质量指数的函数
aqi = get_one_aqi(url, city)
city_aqi.append(aqi)
# 返回全部城市的空气质量指数
return city_aqi
-
正则表达式
话不多说,直接上图:
再放大一点:
简单分析一下:我们要获取的数值在<div>这个标签中,有一个value的属性下,然后就可以编写我们的正则表达式,获取这些数值。
'<div.*?span1">.*?<div.*?value">(.*?)</div>.*?<div.*?caption">(.*?)</div>'
-
get_one_aqi(url, city)
函数代码
# 获取单个城市空气指数
def get_one_aqi(url, city):
# 某个城市的拼音
pinyin = city[0]
# 某个城市的中文名
city_name = city[1]
# 目标城市的网址
url = url + pinyin
result = get_html(html)
# 设置正则表达式
pattern = re.compile('<div.*?span1">.*?<div.*?value">(.*?)</div>.*?<div.*?caption">(.*?)</div>', re.S)
item = re.findall(pattern, result)
one_city_aqi = []
one_city_aqi.append(city_name)
for i in item:
value = i[0].strip()
one_city_aqi.append(value)
# 返回单个城市的空气质量指数
return one_city_aqi
那么到了这里我们就完成了这个爬虫项目的绝大部分工作,假装获得了全部城市的空气质量指数😑。
将数据写入.csv文件
获得数据之后呢,当然是把结果写入文件啊,难不成每次想查询都要爬一次虫?爬一次全部数据要六七分钟啊!
是不是按捺不住了,直接上代码:
def save_to_csv(city_aqi):
# 将结果保存为csv文件
# 表头
header = ["city_name", "AQI", "PM2.5/1h", "PM10/1h", "CO/1h", "NO2/1h", "O3/1h", "O3/8h", "SO2/1h"]
with open('all_city_aqi.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(header)
for i, one in enumerate(city_aqi):
if i % 10 == 0:
print("已处理第{}条信息,共计{}条信息。".format(i, len(city_aqi)))
writer.writerow(one)
print("成功将全部数据写入.csv文件!")
总结一下思路:首先要获取全部城市的拼音,这样就可以拼接得到每个城市对应的网页;
然后进入这些网页,下载每个网页的数据;
再用正则表达式匹配这些数据,获得全部城市的空气相关指数;
最后将这些数据存储为.csv文件。
懂了吧?
懂了吧?
懂了吧?
运行完后,你的工程文件目录会多了个.csv文件,里面就是本次爬虫的最终数据。部分数据如下图:
city_name,AQI,PM2.5/1h,PM10/1h,CO/1h,NO2/1h,O3/1h,O3/8h,SO2/1h
阿坝州,35,17,28,0.5,7,35,68,9
阿克苏地区,68,15,50,0.45,36,90,120,8
阿拉善盟,97,52,141,0.23,4,102,132,6
阿勒泰地区,37,9,12,0.2,13,65,72,1
阿里地区,0,0,0,0.0,0,0,0,0
安康,68,35,84,0.63,30,32,74,9
安庆,62,41,58,0.76,11,114,113,6
鞍山,42,28,41,0.7,12,60,75,8
安顺,94,70,87,0.9,17,77,101,24
安阳,95,36,82,0.8,26,109,152,5
白城,111,47,169,0.5,8,79,116,9
百色,76,36,54,0.85,12,118,130,31
白山,41,27,37,0.55,15,51,80,13
白银,62,24,72,0.35,9,114,112,4
保定,109,35,62,0.52,18,108,168,23
宝鸡,65,28,77,0.64,44,41,86,5
保山,70,30,53,0.55,9,112,122,4
包头,65,25,63,0.66,35,76,116,13
巴彦淖尔,126,44,199,0.57,7,90,110,5
...
写在后面
获取数据之后,数据分析,数据可视化,随便你玩,不过小编不陪你玩了😀,自己玩去。
然后就是,这是我第一次在简书上写这种东西,写的不好或者代码太臭不要喷得太惨😱,里面也有很多改进的地方,不过我懒,我也是python入门小白!
狗头🐶奉上全部代码:
"""
作者:菜爆炸头
时间:2019/05/21
功能:AQI计算
版本:8.0
2.0新增功能:简单爬虫,得到各个城市的aqi数据。
3.0新增功能:爬取某个城市所有的空气相关指数
5.0新增功能:实现遍历城市列表,下载所有城市的空气指数
6.0新增功能:将爬取到的数据保存为.csv文件
7.0新增功能:对得到的数据进行简单清洗和分析
8.0新增功能:运用正则表达式爬虫
"""
# 导入urllib、re库
import urllib.request
import urllib.error
import re
import csv
def get_html(url):
try:
# 获取网页
request = urllib.request.Request(url)
response = urllib.request.urlopen(request)
result = response.read().decode('utf-8')
return result
except urllib.error.URLError as e:
if hasattr(e, 'reason'):
print("错误原因为:"+str(e.reason))
except urllib.error.HTTPError as e:
if hasattr(e, 'code'):
print("错误状态代码为:"+str(e.code))
def get_city(url):
# 获取城市列表
# 获取网页地址
result = get_html(url)
pattern = re.compile('<li>.*?<a\shref="/(.*?)">(.*?)</a.*?</li>', re.S)
item = re.findall(pattern, result)
# 获取全部城市拼音和名称
all_city = item[10:-1]
print("成功获取全部城市名称列表!")
return all_city
def get_all_aqi(url, all_city):
# 获取全部城市空气指数
city_aqi = []
for i, city in enumerate(all_city):
if i % 10 == 0:
print("现已获取第{}个城市空气指数!".format(i))
aqi = get_one_aqi(url, city)
city_aqi.append(aqi)
print("成功获取全部城市空气指数!")
return city_aqi
def get_one_aqi(url, city):
# 获取单个城市空气指数
# 调用下载网页函数
pinyin = city[0]
city_name = city[1]
url = url + pinyin
result = get_html(url)
# 正则表达式
pattern = re.compile('<div.*?span1">.*?<div.*?value">(.*?)</div>.*?<div.*?caption">(.*?)</div>', re.S)
item = re.findall(pattern, result)
one_city_aqi = []
one_city_aqi.append(city_name)
for i in item:
# caption = i[1].strip()
value = i[0].strip()
one_city_aqi.append(value)
return one_city_aqi
# print(one_city_aqi)
def save_to_csv(city_aqi):
# 将结果保存为csv文件
header = ["city_name", "AQI", "PM2.5/1h", "PM10/1h", "CO/1h", "NO2/1h", "O3/1h", "O3/8h", "SO2/1h"]
with open('all_city_aqi.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(header)
for i, one in enumerate(city_aqi):
if i % 10 == 0:
print("已处理第{}条信息,共计{}条信息。".format(i, len(city_aqi)))
writer.writerow(one)
print("成功将全部数据写入.csv文件!")
def main():
# 主函数
url = "http://pm25.in/"
# 获取城市名称列表
all_city = get_city(url)
#print(all_city)
# 获取全部城市空气指数
city_aqi = get_all_aqi(url, all_city)
# 将得到的空气指数写入.csv文件
save_to_csv(city_aqi)
if __name__ == "__main__":
main()