本文介绍如何爬取诸如北京等城市的空气污染物浓度数据,并附有完整代码,统统解决你们找不到数据的科研问题!干货满满!!!
先给你们看一下我爬取数据保存在本地数据库的截图!【共2423条日数据】
好了,言归正传,看一下你们需要的数据应该是长这样的
同样也包含最新数据
这个网站有我们需要的空气污染物数据,时间跨度为2014年1月至最新日期,完美!
那么如何才能不费吹灰之力的获取到这些数据呢[实际上我第三次爬取该网站才成功>_<]?
先介绍我在爬取过程遇到的坑!让你觉得这并不是一件容易的事,哈哈哈!
困难1:
打开开发者工具(F12),如图:
这是我第一次想要爬该网站数据的时候,遇到的反爬虫情况!这是完全不给我机会呗,连网页源代码都不让我看呗! 【放弃】
最终处理方法有两个:
第一:换浏览器,反正Chrome浏览器是不行的(如QQ浏览器,火狐浏览器) 【推荐】
第二:采用断点方式(这里不具体介绍了,尽管我一开始采用的是断点)
第三:直接在网站上输入:view-source:网址即可
困难2:
在获取到网页源代码之后,你会发现你想要获取的数据就在tr标签里,但是你通过Beautifulsoup、Xpath等各种方式,都无法获取数据标签,最终他只会给你返回方框的代码,而你想要的椭圆形的代码就是不给你! 【放弃】
原因: 首先我们提取标签的代码是没问题的,这是因为这样的 表格数据 是通过js渲染得到的,你所需要的数据是通过ajax请求的数据包中响应数据是经过加密的密文数据…然后你就需要进行js解密,反正过程还是挺复杂的,这让没学过js的我无能为力!
解决方案:
既然是通过ajax请求获取的数据,何不从另一个角度思考-----selenium
准备工具: Firefox浏览器并配置好他的环境 ,过程并不难,百度一下即可,这一步如果有困难的可评论在下方
这里之所以用火狐而不是Chrome,原因也是Chrome无法打开网页源代码
接下来对代码进行讲解:
第一步:
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
# print(dfs)
# df.to_csv('data.csv',mode='a',index=None)
time.sleep(1)
if not df.empty:
return df
else:
return spider(url) # 防止网络还没加载出来就爬取下一个url
整个代码也很简单,如果是仅仅获取到数据,并保存到csv文件的话,到这一步基本就结束了,只需要print一下即可,但是我这里是需要将获取到的数据保存到本地数据库中,并定时进行更新与维护![是不是觉得这都可以作为一个小项目写到简历里啦]
主体代码如下:
list_data = []
list_row = []
for ct in range(len(city)):
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()
# list_data=[]
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
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
# print(dfs)
# df.to_csv('data.csv',mode='a',index=None)
time.sleep(1)
if not df.empty:
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.FirefoxOptions()
option.add_argument('headless') # 设置option
browser = webdriver.Firefox(firefox_options=option)
city = [
'北京',
] # 可添加多个城市
conn = pymysql.connect(host='localhost', user='root', db='weatherdata', passwd='********', charset='utf8') # 连接数据库
cursor = conn.cursor() # 获取cursor游标
dates = get_date(url)
list_data = []
list_row = []
for ct in range(len(city)):
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()
# list_data=[]
cursor.close() # 关闭cursor
conn.close() # 关闭连接
browser.close()
print(list_data)