这次的事情是老板让做一个对话机器人,先拿天气来练练手。所以首先要有数据吧QAQ。没办法,自己动手,丰衣足食。
顺便说一句,整个天气对话机器人的的初始demo已经弄好了,在我的github里。并且初始的展示平台放到了我的腾讯云学生机上。不过目前还处于人工智障阶段,还需要进一步优化。
好了,废话说完了,下面来正经的。
首先需要搞到全国各地的主要城市地名数据。
然后根据地点数据去天气网站上爬数据
我爬取的主要是两个网站 http://pm25.in/+city 以及 https://www.tianqi.com/city
参考一些已经有的博客,beautifulsoup4以及正则我都用了,感觉还是bs4方便点。
首先爬取一些PM2.5之类的空气数据以及单日常有的数据,例如湿度啊,风速啊,紫外线啊之类的。
def get_PM25(city):
try:
#天气网站地址
url = "http://pm25.in/"+city
data = urllib.request.urlopen(url).read().decode("utf-8")
# print("城市:{}".format(city))
data_time = '<div class="live_data_time">\s{1,}<p>数据更新时间:(.*?)</p>'
datatime = re.compile(data_time, re.S).findall(data)
#先爬PM2.5的
data_pm25 = '<div class="span1">\s{1,}<div class="value">\n\s{1,}(.*?)\s{1,}</div>'
data_o3 = '<div class="span1">\s{1,}<div class ="value">\n\s{1,}(.*?)\s{1,}</div>'
pm25list = re.compile(data_pm25, re.S).findall(data)
o3list = re.compile(data_o3, re.S).findall(data)
pm25list.append(o3list[0])
#再爬一些单日数据
url = 'https://www.tianqi.com/{}/'.format(city)
data = urllib.request.urlopen(url).read().decode("utf-8")
soup = BeautifulSoup(data, 'html.parser')
li_shidu = soup.find('dd',attrs={'class':'shidu'}).find_all('b')
#数据保存
pm_list = {"AQI": pm25list[0]+"μg/m3", "PM25": pm25list[1]+"μg/m3", "PM10": pm25list[2]+"μg/m3", "CO": pm25list[3]+"mg/m3", "NO2": pm25list[4]+"μg/m3",
"SO2": pm25list[5]+"μg/m3", "O3": pm25list[6]+"μg/m3",
"humidty": li_shidu[0].string.split(":")[1],
"wind": li_shidu[1].string.split(":")[1].split()[0],
"wind_inten": li_shidu[1].string.split(":")[1].split()[1],
"ultra_ray": li_shidu[2].string.split(":")[1],
"date": datatime[0]}
return pm_list
except urllib.error.URLError as e:
print("出现URLERROR!一分钟后重试……")
if hasattr(e, "code"):
print(e.code)
if hasattr(e, "reason"):
print(e.reason)
time.sleep(60)
except Exception as e:
print(e)
time.sleep(5)
然后再爬取一周的天气数据:
def get_Weather(city):
try:
weatherlist = []
url='https://www.tianqi.com/{}/'.format(city)
data=urllib.request.urlopen(url).read().decode("utf-8")
soup = BeautifulSoup(data,'html.parser')
li_date = soup.find('ul',attrs={'class':'week'}).find_all('li')
li_shidu = soup.find('dd', attrs={'class': 'shidu'}).find_all('b')
li_weather = soup.find('ul', attrs={'class': 'txt txt2'}).find_all('li')
li_temp = soup.find('div', attrs={'class': 'zxt_shuju'}).find('ul').find_all('li')
for i in range (len(li_weather)):
slot = [];
slot.append({'日期': li_date[i].find('b').string,'星期': li_date[i].find('span').string,'天气':li_weather[i].string,'最高气温': li_temp[i].find('span').string+"℃",'最低气温': li_temp[i].find('b').string+"℃"})
weatherlist.append(slot)
return weatherlist
except urllib.error.URLError as e:
print("出现URLERROR!一分钟后重试……")
if hasattr(e,"code"):
print(e.code)
if hasattr(e,"reason"):
print(e.reason)
time.sleep(60)
# get_Weather(city)
except Exception as e:
print("Exception:"+str(e))
# time.sleep(10)
爬的时候一定要写try_catch.因为网络会出问题,或者你爬的太快人家网站把你封了,或者出一些乱七八糟的错误都有可能的。
然后就是保存的数据库。
这里我建了两个表,分别是单日数据表以及一周数据表:
城市 | 主键 |
日期 | 主键 |
一周数据 | ... |
城市 | 主键 |
单日各项数据... | ... |
... | ... |
用到的是sqllite.本来就不是啥大型数据库系统,也没啥保密的,就不搞视图,密码,索引啥的了。
查询的时候也是直接访问数据库的(话说要是正规的这么做会不会被打死0.0)
建表代码:
def create_dataDB(path):
conn = sqlite3.connect(path)
cursor = conn.cursor()
sql_weather = '''
CREATE TABLE IF NOT EXISTS weather(
date text,
city_name text,
week text,
city_pinyin text,
weather text,
high_temp text,
low_temp text,
primary key (date,city_name)
)'''
sql_pm = '''
CREATE TABLE IF NOT EXISTS pm(
city text,
date text,
AQI text,
PM25 text,
PM10 text,
CO text,
NO2 text,
SO2 text,
O3 text,
humidty text,
wind text,
wind_inten text,
ultra_ray text,
primary key(city)
)'''
cursor.execute(sql_weather)
cursor.execute(sql_pm)
cursor.close()
print("database has created!")
插入代码:
def insert_weatherdata(path,data,cp,cn):
try:
conn = sqlite3.connect(path)
cursor = conn.cursor()
for i in range(len(data)):
cursor.execute(
'''
replace into weather
(date,
city_name,
week,
city_pinyin,
weather,
high_temp,
low_temp)
values
('{}','{}','{}','{}','{}','{}','{}')
'''.format(
data[i][0]['日期'],
cn,
data[i][0]['星期'],
cp,
data[i][0]['天气'],
data[i][0]['最高气温'],
data[i][0]['最低气温']
)
)
conn.commit()
cursor.close()
print("{}一周天气数据已经成功插入!".format(cn))
except Exception as e:
print(e)
def insert_PMdata(path,data,cn):
try:
conn = sqlite3.connect(path)
cursor = conn.cursor()
cursor.execute(
'''
replace into pm
(city,
date,
AQI,
PM25,
PM10,
CO,
NO2,
SO2,
O3,
humidty,
wind,
wind_inten,
ultra_ray)
values
('{}','{}','{}','{}','{}',
'{}','{}','{}','{}','{}',
'{}','{}','{}')
'''.format(
cn,
data.get("date"),
data.get("AQI"),
data.get('PM25'),
data.get('PM10'),
data.get('CO'),
data.get('NO2'),
data.get('SO2'),
data.get('O3'),
data.get('humidty'),
data.get('wind'),
data.get('wind_inten'),
data.get('ultra_ray'),
)
)
conn.commit()
cursor.close()
print("{}PM最新数据已经成功插入!".format(cn))
except Exception as e:
print(e)
这里有一个小问题,就是数据库是可以批量插入的。但我在代码里是爬一条插一条的,以后肯定会改,不然这样也太慢了。
另外,爬虫的时候我设置了随机2-5秒爬一次(不知道是不是太久了),因为之前没设置休息时间被封IP了。。。。。
最后的结果如下:
改进:
目前这样是肯定不满意的啦。首先,数据的爬取和插入就很有问题,之后会改进,定时爬取,统一更新。然后就是开个进程自动更新数据库,不会自己更新天气的,你不是人工智障,谁是人工智障。
改进就不在这里写了,会在github上更新。