一键爬取空气质量相关指数

刚刚入门python,想上手一些比较简单的爬虫项目。今天我们来爬一爬一个空气质量指数的网站来获取全国城市的空气质量指数。记得跟着我的思路来哟!

前期准备工作

首先一个舒服的pythonIDLE肯定是必须的了,小编使用的是pycharm,安装的是anaconda,不懂安装的自行去简书查查哟,这里不再赘述。
然后确定的我们要爬取的网站,这个网站是http://pm25.in/,界面是这样的:

image.png

个人觉得这个网站挺良心的,界面很舒适。那么我们现在正式上手我们的小项目吧。

爬虫开始

引入所需要的库

对于爬虫,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的结构,用于在出错时查找原因。
返回的部分结果:


image.png

html没学好,咱也不懂,咱也不敢问😭,但好歹我们迈出了第一步,成功爬到了目标网页最原始的数据👀。

  • 确定网址
    有人可能会问,网址不是已经确定了吗🐴,不是这个http://pm25.in/?。这……,不对的,我们要获取的网页是各个城市的网页,而不是人家网站的首页。
    我们先来随便点开一个城市,来分析它的网址,例如点开武汉:
    image.png

    上方那个框是武汉这个城市的网址,下方那个框是我们所需要的数据。现在运用我们的火眼金睛来找规律了,这个网址是不是在"http://pm25.in/"后面再加上武汉的拼音?那其他城市呢?
    我们再随便点击一些城市验证我们的想法:
    峨眉山

    大同

    得出结论:在"http://pm25.in/"后面加上一个城市的拼音就是我们需要查询城市的网址,把这个网址传进上面我们写好的函数就行了,是不是很简单👍。

获取城市列表

我们的目标是获取所有城市的空气质量指数,肯定不能让我们一个一个输入一个城市的拼音来进行查询啊,所以我们的下一步是获取所有城市的列表,也包括所有城市的拼音。
在这里先墙裂推荐chrome浏览器,对爬虫有非常大的帮助。

  • 网页分析
    我们知道,城市列表在目标网址的首页,点击F12分析一下它的源码。部分源码如下:


    image.png

    从中可以看出,前面是一个城市的拼音,后面是一个城市的中文名,这就是我们想要的。

  • 正则表达式
    根据我们需要的东西,编写正则表达式如下:
    '<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
  • 正则表达式
    话不多说,直接上图:


    image.png

再放大一点:


image.png

简单分析一下:我们要获取的数值在<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()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容