python爬取中国空气质量在线监测平台分析数据【已更新】

**本文介绍如何爬取诸如北京等城市的空气污染物浓度数据,并附有完整代码,统统解决你们找不到数据的科研问题!干货满满!!!
2021年1月12日更新
看了很多小伙伴的评论,发现我的代码被官方给“制裁”了【设置反扒了】,前段时间一直在准备秋招和毕设就没有时间反反扒(解决不能爬去的问题),如今捣鼓了一下午,终于还是搞定了!

刚刚爬取的程序结果如下:在这里插入图片描述

这是我刚刚更新的本地数据库的截图!在这里插入图片描述

好了,言归正传,看一下你们需要的数据应该是长这样的
在这里插入图片描述
这个网站有我们需要的空气污染物数据,时间跨度为2014年1月至最新日期,完美!
那么如何才能不费吹灰之力的获取到这些数据呢[实际上我第三次爬取该网站才成功>_<]?
先介绍我在爬取过程遇到的坑!让你觉得这并不是一件容易的事,哈哈哈!
困难1:
打开开发者工具(F12),如图:
在这里插入图片描述
这是我第一次想要爬该网站数据的时候,遇到的反爬虫情况!这是完全不给我机会呗,连网页源代码都不让我看呗! 【放弃】
最终处理方法有两个:
第一:换浏览器,如QQ浏览器,火狐浏览器
【更新】:火狐浏览器目前也不行了,也被官方设置反扒了
第二:采用断点方式(这里不具体介绍了,尽管我一开始采用的是断点)
在这里插入图片描述
第三:直接在网站上输入:view-source:网址即可 【推荐】
困难2:
在获取到网页源代码之后,你会发现你想要获取的数据就在tr标签里,但是你通过Beautifulsoup、Xpath等各种方式,都无法获取数据标签,最终他只会给你返回方框的代码,而你想要的椭圆形的代码就是不给你! 【放弃】

在这里插入图片描述
原因: 首先我们提取标签的代码是没问题的,这是因为这样的 表格数据 是通过js渲染得到的,你所需要的数据是通过ajax请求的数据包中响应数据是经过加密的密文数据…然后你就需要进行js解密,反正过程还是挺复杂的,这让没学过js的我无能为力!
解决方案:
既然是通过ajax请求获取的数据,何不从另一个角度思考-----selenium
准备工具: Firefox浏览器并配置好他的环境 ,过程并不难,百度一下即可,这一步如果有困难的可评论在下方
这里之所以用火狐而不是Chrome,原因也是Chrome无法打开网页源代码
【更新】:火狐目前也不行了
目前正确的做法是“伪装浏览器”,因为很多网站(如淘宝等)都设置了对selenium的检测,防止他们的网站被恶意爬取,检测基本原理是检测当前浏览器窗口下的 window.navigator 对象是否包含 webdriver 这个属性。因为在正常使用浏览器的情况下,这个属性是 undefined,然而一旦我们使用了 Selenium,Selenium 会给 window.navigator 设置 webdriver 属性。很多网站就通过 JavaScript 判断如果 webdriver 属性存在,那就直接屏蔽。具体的原理我就不介绍了,直接贴代码:

from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
browser = webdriver.Chrome(options=option)
browser.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
   'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'
})
option = webdriver.ChromeOptions()
option.add_argument("start-maximized")
option.add_argument("--disable-blink-features=AutomationControlled")
option.add_experimental_option("excludeSwitches", ["enable-automation"])
option.add_experimental_option("useAutomationExtension", False)
browser = webdriver.Chrome(options=option)  
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{
        'source':'''Object.defineProperty(navigator, 'webdriver', {
        get: () =>false'''
    })

感兴趣的可以参考https://zhuanlan.zhihu.com/p/191033198
接下来对代码进行讲解:
第一步:

def get_date(url):
    response = requests.get(url)
    dates = []
    try:
        if response.status_code ==200:
            response = response.text
            soup = BeautifulSoup(response, 'lxml')
            dates_ = soup.find_all('li')
            for i in dates_:
                if i.a:  # 去除空值
                    li = i.a.text  # 提取li标签下的a标签
                    date = re.findall('[0-9]*', li)  # ['2019', '', '12', '', '']
                    year = date[0]
                    month = date[2]
                    if month and year:  # 去除不符合要求的内容
                        date_new = '-'.join([year, month])
                        dates.append(date_new)
            return dates
    except:
        print('数据获取失败!')

这个代码是用来获取网页中某一个城市目前所有时间,其返回结果如图:
在这里插入图片描述
之所以日期用这样的格式是因为该网站的url链接形式:
https://www.aqistudy.cn/historydata/daydata.php?city=北京&month=2020-07
他可以被分解为 base_url+city+month
其中,base_url = ‘https://www.aqistudy.cn/historydata/daydata.php?city=’
第二步:
在得到每个月份的url链接后,接下来就是爬取数据了,这里给大家分享一个函数,也是整个代码的 核心 -------- pandas.read_html()
这个函数专门是用来解决像表格型数据的获取的,百试百灵!
代码如下:

def spider(url):
    browser.get(url)
    df = pd.read_html(browser.page_source, header=0)[0]  # 返回第一个Dataframe
    time.sleep(1)
    if not df.empty:
    	# print(dfs)
   	    # df.to_csv('data.csv',mode='a',index=None)
        return df
    else:
        return spider(url)  # 防止网络还没加载出来就爬取下一个url

整个代码也很简单,如果是仅仅获取到数据,并保存到csv文件的话,到这一步基本就结束了,只需要print一下即可,但是我这里是需要将获取到的数据保存到本地数据库中,并定时进行更新与维护![是不是觉得这都可以作为一个小项目写到简历里啦]
主体代码如下:

for ct in range(len(city)):
	list_data = []
	list_row = []
    for date in dates:
        url = base_url + city[ct] + '&month=' + date
        df = spider(url)
        time.sleep(1)
        df['city'] = city[ct]  # 添加一列
        for i in range(0, df.shape[0]):  # 行
            for j in range(df.shape[1]):  # 列
                data = df.iloc[i, j]
                list_row.append(data)
            list_data.append(list_row)
            list_row = []
    for n in range(len(list_data)):
        sql = 'insert ignore into aqidata (DATE,AQI,GRADE,PM25,PM10,SO2,CO,NO2,O3_8h,CITY)' \
              ' VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
        x = cursor.execute(sql, (list_data[n][0], float(list_data[n][1]), list_data[n][2],
        float(list_data[n][3]),float(list_data[n][4]),float(list_data[n][5]),float(list_data[n][6]),
                    float(list_data[n][7]),float(list_data[n][8]),list_data[n][9]))
        conn.commit()
cursor.close()  # 关闭cursor
conn.close()  # 关闭连接
browser.close()

上述代码可以实现全国各地每个时间段的数据的爬取与存储,只要你网站上有的数据我都可以爬下来,聪明的小伙伴根据这三块代码就可以实时的获取到全部数据啦!
在这里插入图片描述
注意:
如果想存储到数据库的话,需要提前建立数据库以及表(生怕你们跑代码出问题):
在这里插入图片描述
有了我这代码,还愁花钱买数据吗?根本不可能好吧!
彩蛋
彩蛋

(1)既然数据已经获取到啦,那么如何进行定时更新与维护呢?
其实可以参考我的这篇博客后半部分
—>传送门
(2)既然污染物浓度数据都获取到了,有没有获取气象因子的数据方法呢?——— 有!
参考我的这篇博客----->传送门

综上就是这次博客讲解的全部内容,全部都是干货!如果对代码有疑惑或者不理解的地方,欢迎留言评论!如果觉得博客对你有帮助的话就点赞收藏吧!
如果需要完整数据的话,可以评论/私信我哈【有偿】
完整代码:

# coding=utf-8
from selenium import webdriver
import pymysql
import pandas as pd
import time
import requests
import re
from bs4 import BeautifulSoup
from sqlalchemy.exc import IntegrityError

def get_date(url):
    response = requests.get(url)
    dates = []
    try:
        if response.status_code ==200:
            response = response.text
            soup = BeautifulSoup(response, 'lxml')
            dates_ = soup.find_all('li')
            for i in dates_:
                if i.a:  # 去除空值
                    li = i.a.text  # 提取li标签下的a标签
                    date = re.findall('[0-9]*', li)  # ['2019', '', '12', '', '']
                    year = date[0]
                    month = date[2]
                    if month and year:  # 去除不符合要求的内容
                        date_new = '-'.join([year, month])
                        dates.append(date_new)
            return dates
    except:
        print('数据获取失败!')
def spider(url):
    browser.get(url)
    df = pd.read_html(browser.page_source, header=0)[0]  # 返回第一个Dataframe
    time.sleep(1.5)
    if not df.empty:
        # print(df)
        # df.to_csv('data.csv', mode='a', index=None)
        print(url+'数据爬取已完成')
        return df
    else:
        return spider(url)  # 防止网络还没加载出来就爬取下一个url
if __name__ == '__main__':
    url = 'https://www.aqistudy.cn/historydata/monthdata.php?city=%E5%8C%97%E4%BA%AC'
    base_url = 'https://www.aqistudy.cn/historydata/daydata.php?city='
    # 声明浏览器对象
    option = webdriver.ChromeOptions()
    option.add_argument("start-maximized")
    option.add_argument("--disable-blink-features=AutomationControlled")
    option.add_experimental_option("excludeSwitches", ["enable-automation"])
    option.add_experimental_option("useAutomationExtension", False)
    browser = webdriver.Chrome(options=option)
    browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{
        'source':'''Object.defineProperty(navigator, 'webdriver', {
        get: () =>false'''
    })
    city = [
        '北京',
            ]
    conn = pymysql.connect(host='localhost', user='root', db='weatherdata', passwd='12345678', charset='utf8')  # 连接数据库
    cursor = conn.cursor()  # 获取cursor游标
    dates = get_date(url)[1:]
    print(dates)
    list_data = []
    list_row = []
    for ct in range(len(city)):
        for date in dates:
            url = base_url + city[ct] + '&month=' + date
            df = spider(url)
            # print(df)
            time.sleep(1.5)
            df['city'] = city[ct]  # 添加一列
            for i in range(0, df.shape[0]):  # 行
                for j in range(df.shape[1]):  # 列
                    data = df.iloc[i, j]
                    list_row.append(data)
                list_data.append(list_row)
                list_row = []
            # print(list_data)
        for n in range(len(list_data)):
            sql = 'insert ignore into aqidata (DATE,AQI,GRADE,PM25,PM10,SO2,CO,NO2,O3_8h,CITY)' \
                  ' VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
            try:
                x = cursor.execute(sql, (list_data[n][0], float(list_data[n][1]), list_data[n][2],
                float(list_data[n][3]),float(list_data[n][4]),float(list_data[n][5]),float(list_data[n][6]),
                        float(list_data[n][7]),float(list_data[n][8]),list_data[n][9]))
            except IntegrityError:
                print('IntegrityError happened!')
            conn.commit()
    cursor.close()  # 关闭cursor
    conn.close()  # 关闭连接
    browser.close()
    aqidata = pd.DataFrame(list_data,
                columns=['日期', 'AQI', '质量等级', 'PM2.5', 'PM10', 'SO2', 'CO', 'NO2', 'O3_8h', 'city'])
    print('所有数据爬取已完成!\n', aqidata)
  • 60
    点赞
  • 257
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 116
    评论
空气质量检测网的部分城市的历年每天质量数据 思路----------------------------------------- 从某城市的空气质量网页获某市每月的链接,再每个月的表格数据。连云港市:https://www.aqistudy.cn/historydata/daydata.php?city=连云港 连云港2014年5月的空气质量:https://www.aqistudy.cn/historydata/daydata.php?city=连云港&month=2014-05 遇到的问题----------------------------------------- 获的网页中的表格数据隐藏,尝试requests无法获。判断可能是动态加载的网页 尝试----------------------------------------- 1. 通过XHR,js查找隐藏数据的加载网页,没有找到。 2. 使用phantomjs.get() result=pd.read_html ,可以获得隐藏的表格数据,但是并不稳定,只是偶尔出现加载的表格数据,无法大规模的获 解决方法----------------------------------------- 查找资料得知这个网站的表格数据在Console里的items中, 使用selenium的webdriver.firefox(),driver.execute_script("return items") 数据可获得。 仍遇到的问题:----------------------------------------- 一个网页可获得数据,但是连续的获网页,会出现两个错误。 1.Message: ReferenceError: items is not defined 2.connection refused 解决方法: 1.connection refused问题,可能是网页开太多,使用driver.quit() 2. 如果 execute_script 还是出错,可尝试pd.read_html获信息。之前用phantomjs获的时候输出空的表格,可能由于加载不够,用 Waite直到table出现之后再获网页 Element=wait.until(EC.element_to_be_clickable((By.XPATH,"/html/body/div[3]/div[1]/div[1]/table/tbody"))) 3.之后出现偶尔出现输出为空,使用循环,如果输出表格为空,再重新获。 if len(result)>1: filename = str(month) + '.xls' result.to_excel('E:\python\案例程序\data\\' + filename) print('成功存入'+filename) driver.quit() else: driver.quit() return getdata(monthhref,month)
评论 116
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jancy、

知识付费,支持一下~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值