██████╗ ███████╗██████╗ ██╗ ██╗███████╗
██╔═══██╗██╔════╝██╔══██╗╚██╗ ██╔╝██╔════╝
██║ ██║███████╗██████╔╝ ╚████╔╝ █████╗
██║▄▄ ██║╚════██║██╔══██╗ ╚██╔╝ ██╔══╝
╚██████╔╝███████║██████╔╝ ██║ ███████╗
╚══▀▀═╝ ╚══════╝╚═════╝ ╚═╝ ╚══════╝
保命声明:笔者代码能力有限,若行文中有错漏之处欢迎大家指出。
天气数据获取
苏州
广州
Python爬虫入门抓取天气信息
Python爬取天气数据
功能介绍
访问http链接,可以以JSON格式获得当日天气、温度、天气资讯.
实现
流程图
数据获取 | 网络服务 |
---|---|
![]() |
![]() |
代码
#!/usr/local/bin/python3.7
#-*-encoding:UTF-8
'''
**main.py**
- 主要功能:定时获取天气信息,发出json文件
- 维护:qsbye
- 邮箱:[email protected]
- LICENSE:Mulan PSL v2
- 依赖第三方库:schedule,BeautifulSoup4,requests,gmssl,base64
'''
'''开始import'''
import sys #系统
import os #系统
import time #时间
from datetime import datetime #时间
import requests #获取网页
from bs4 import BeautifulSoup #解析网页
import sqlite3 #数据库
import json #json操作
import schedule #定时任务
import socket #网络
from urllib.parse import unquote,quote#解析字符串
from gmssl import sm2 #国密算法
from base64 import b64encode,b64decode #base64操作
'''结束import'''
'''开始全局变量'''
pass
'''结束全局变量'''
'''
@分类:获取数据&数据库操作
'''
class Database_Class:
'获取当日天气信息并保存到数据库'
#初始化
def __init__(self):
pass
'''
@功能:调用获取天气信息函数
'''
def Update_weather(self):
print("获取天气数据,now:",datetime.now().strftime("%Y.%m.%d %H:%M:%S"))
self.main()
'''
@功能:获取天气网天气信息
返回:字典容器
'''
def Get_weather_info(self):
res = requests.get('http://www.weather.com.cn/weather1d/101280101.shtml')
res.encoding = 'utf-8'
html = res.text
soup = BeautifulSoup(html,'html.parser')#解析文档
#tz=pytz.timezone('Asia/Shanghai')
os.environ['TZ'] = 'Asia/Shanghai' #设置时区
#获取完整信息"02月09日08时 周四 小雨 23/17°C"
weather_full_info=soup.find(id="hidden_title")['value']
#print(weather_full_info)
weathers=soup.find(class_="today clearfix",id="today").find("ul",class_="clearfix").find_all('li')
weather_tqzx=soup.find(class_="con today clearfix").find(class_="left fl").find(class_="tq_zx",id="tq_zx").find_all("dd")
my_weather_today={ \
'weather_wea':weathers[0].find('p',class_="wea").text, \
'weather_tem':weathers[0].find('p',class_="tem").text, \
'weather_wind':weathers[0].find('p',class_='win').find('span').text, \
'weather_source':"中国天气网", \
'update_time':datetime.now().strftime("%Y.%m.%d %H:%M:%S"), \
'weather_full':soup.find(class_="con today clearfix").find(class_="left fl").find(class_="today clearfix",id="today").find(id="hidden_title")['value'], \
'weather_city':soup.find(class_="con today clearfix").find(class_="left fl").find(class_="left-div").find(class_="ctop clearfix").find(class_="crumbs fl").find_all('a')[2].text \
}
my_weather_today['weather_news']=[]
for zx in weather_tqzx:
my_weather_today['weather_news'].append(zx.find('a').text)
return my_weather_today
'''
@功能:获取心知天气信息
返回:字典容器
'''
def Get_weather_info2(self):
pass
'''
@功能:存储天气信息到数据库,TODO:添加信息有效期
输入:字典
'''
def Save_to_database(self,input):
#获取今日天气
weather_today=self.Get_weather_info()
#调试:打印
#print('天气资讯:',weather_today['weather_news'])
#连接数据库
conn=sqlite3.connect('./files/weather.db')
#数据库游标
cursor=conn.cursor()
#创建表(sqlite语句)
SQL_CREATE_TABLE= \
'''
CREATE TABLE IF NOT EXISTS weather
(city text,
wea text,
temperature text,
wind text,
update_time text,
source text,
news text
);
'''
#插入数据(sqlite语句)
SQL_INSERT_DATA= \
'''INSERT INTO weather(city,wea,temperature,wind,update_time,source,news) values(?,?,?,?,?,?,?);'''
#转换列表为字符串
weather_news=','.join(weather_today['weather_news'])
data=[
(weather_today['weather_city'],weather_today['weather_wea'],weather_today['weather_tem'], \
weather_today['weather_wind'],weather_today['update_time'],weather_today['weather_source'], \
weather_news \
)
]
#通过上下文与数据库交互
with conn:
conn.execute(SQL_CREATE_TABLE)
conn.executemany(SQL_INSERT_DATA,data)
#conn.commit()
'''
@功能:查表获取天气
返回:天气数据字典
'''
def Query_database(self):
#打开数据表
try:
conn=sqlite3.connect("./files/weather.db")
except IOError:
print("Error:没有找到文件或读取文件失败")
else:
print("打开数据库成功")
#查询数据
SQL_QUERY="SELECT * FROM weather WHERE city = '广州'"
#通过列名读取数据
conn.row_factory = sqlite3.Row
with conn:
data_s=conn.execute(SQL_QUERY)
'''
data_dict={'weather_city':data_s.fetchall()[0]['city'],'weather_wea':data_s.fetchall()[0]['wea'],'weather_tem':data_s.fetchall()[0]['temperature'], \
'weather_wind':data_s.fetchall()[0]['wind'],'update_time':data_s.fetchall()[0]['update_time'],'weather_source':data_s.fetchall()[0]['source'] \
'weather_news':data_s.fetchall()[0]['news'] \
}
'''
for data in data_s:
data_dict={'weather_city':data['city'],'weather_wea':data['wea'],'weather_tem':data['temperature'], \
'weather_wind':data['wind'],'update_time':data['update_time'],'weather_source':data['source'], \
'weather_news':data['news'] \
}
return data_dict
'''
@功能:封装数据到json文件
输入:数据字典,token
输出:json
'''
def Put_data_into_json(self,data,token):
#判断数据
if(data['weather_city']!='' or data['weather_city']!=None):
#传入的是天气数据
#字典类型转换为JSON对象
data['token']=token
json_str=json.dumps(data)
#调试
print(json_str)
#写入JSON文件
with open('./files/weather.json','w') as f:
json.dump(data,f)
return json_str
'''
@功能:主函数
'''
def main(self):
self.Save_to_database(0)
self.Put_data_into_json(self.Query_database(),'1234')
'''
@分类:权限控制
TODO:⬇️
'''
class Access_Class:
pass
'''
@分类:定时任务
'''
class Task_Class:
'配置定时任务'
#初始化
def __init__(self):
pass
'''
@功能:定时任务配置
'''
def Schedule_task_init(self):
print("配置定时任务")
#每天0:15执行任务
schedule.every().day.at("00:15").do(Database_Class.Update_weather)
#添加到循环
'''
while 1:
schedule.run_pending()
'''
'''
@分类:http服务端
端口:10001
请求:http://127.0.0.1:10001/?file=now.json&key=FTudvvro3fWxMuhW&location=guangzhou
'''
class Web_Server_Class:
'服务端'
self_key="FTudvvro3fWxMuhW"
print("self_key:",self_key)
port=10001
#初始化
def __init__(self):
pass
self.s=socket.socket()
#读取json文件
with open('./files/weather.json','r') as f:
self.text=f.read()
#绑定ip与端口
self.s.bind(('0.0.0.0',Web_Server_Class.port))
#开始监听,可以处理5个请求
self.s.listen(5)
print("socker监听中...")
'''
@功能:解析请求头
'''
def Parse_request(self,request):
raw_list = request.split("\r\n")
request = {}
for index in range(1, len(raw_list)):
item = raw_list[index].split(":")
if len(item) == 2:
request.update({item[0].lstrip(' '): item[1].lstrip(' ')})
return request
'''
@功能:分离参数
返回:字典
'''
def Parse_parameters(self,input):
#分离?之后的内容
input=input.split('?')[1]
#分离空格之前内容
input=input.split(' ')[0]
print('?之后内容',input)
args=input.split('&')
query={}
for arg in args:
key,value=arg.split('=')
print('key:',key,'value:',value)
query[key]=unquote(value)
return query
'''
@功能:处理网络请求
'''
def Handle_http(self):
#while 1:
#建立连接
conn,addr=self.s.accept()
print('与',addr,'建立连接')
#解析字节流
req=conn.recv(1024).decode('utf-8')
print('接收到:',str(req),'来自:',conn.getpeername())
#解析参数
self.Parse_parameters(req)
#发送json数据(带完整http response返回格式)
response = 'HTTP/1.1 200 OK\r\nContent-Type: text/plain;charset=utf-8\r\nContent-Length: ' \
'{length}\r\n\r\n{body}'.format(length=len(self.text.encode("utf-8")), body=self.text)
conn.sendall(response.encode('utf-8'))
#发送数据,默认为utf-8编码
conn.send('connected'.encode('utf-8'))
#关闭连接
conn.close()
#不让再次连接了
#break
'''
主函数
'''
def main(self):
self.Handle_http()
'''
@功能:主函数
'''
def main():
print("进入main函数")
#初次启动先获取一次天气信息,新建文件
DatabaseClass=Database_Class()#实例化
DatabaseClass.Update_weather()
print("初始化完成")
#设置定时任务
TaskClass=Task_Class()
TaskClass.Schedule_task_init()
print("定时任务设置完成")
#启动网络服务端
WebServerClass=Web_Server_Class()
#WebServerClass.main()
print("网络服务端启动完成")
print("进入运行状态")
#主循环
while 1:
schedule.run_pending()
WebServerClass.main()
url="http://127.0.0.1:10001/?file=now.json&key=FTudvvro3fWxMuhW&location=guangzhou"
print("访问:",url)
if(__name__=='__main__'):
main()
部署
获取Pyhton脚本依赖列表
[https://zhuanlan.zhihu.com/p/69058584]
#导出依赖包
pip install pipreqs
python -m pipreqs.pipreqs ./ --encoding=utf8 --force
生成requirements.txt文件.
beautifulsoup4==4.11.2
gmssl==3.2.1
requests==2.23.0
schedule==1.1.0
制作Dockerfile
如何使用 Dockerfile 创建自定义 Docker 镜像 | Linux 中国
Dockerfile
#基础镜像
FROM python:3.7-alpine
#切换包管理器源
RUN python -m pip install pqi
RUN pqi use douban
#复制文件
WORKDIR /usr/src/app
RUN mkdir files
COPY ./requirements.txt ./
COPY ./main3.py ./
COPY ./files/weather.db ./files
COPY ./files/weather.json ./files
#安装依赖
RUN pip install --no-cache-dir -r requirements.txt
#设置容器启动时的命令
CMD [ "python", "./main3.py" ]
开始制作镜像
docker build -t qsbye/sunny-weather:v1 -f Dockerfile .
#查看镜像
docker images
注意:qsbye为笔者的dockerhub用户名,原因⬇️
docker push:denied: requested access to the resource is denied
[https://www.cnblogs.com/luckforefforts/p/13642703.html]
tag 镜像的时候,要加上自己docker hub 的用户名:
docker tag centos/php-70-centos7 shenghp/myphp
上传镜像
docker login
docker push qsbye/sunny-weather:v1
运行
docker pull qsbye/sunny-weather:v1
docker run --name=sunny_weather -p 10001:10001 --restart=always -d qsbye/sunny-weather:v1
效果
wget -O weather_now.json 'http://vpn.qsbye.cn:10001/?file=now.json&key=FTudvvro3fWxMuhW&location=guangzhou' && cat weather_now.json
weather_now.json
{"weather_city": "\u5e7f\u5dde", "weather_wea": "\u9634", "weather_tem": "\n24\u00b0C\n", "weather_wind": "3-4\u7ea7", "update_time": "2023.02.10 17:43:59", "weather_source": "\u4e2d\u56fd\u5929\u6c14\u7f51", "weather_news": " \u65e0\u7f1d\u8854\u63a5\uff01\u4e2d\u4e1c\u90e8\u53c8\u4e00\u8f6e\u5927\u8303\u56f4\u96e8\u96ea\u6765\u88ad \u4e00\u6587\u4e86\u89e3\u96e8\u96ea\u6709\u591a\u5f3a , \u54c8\u5c14\u6ee8\u964d\u96ea \u80fd\u89c1\u5ea6\u8f83\u5dee\u8def\u9762\u6e7f\u6ed1\u5f71\u54cd\u51fa\u884c , \u6d59\u6c5f\u91d1\u534e\u8349\u7eff\u6885\u7ea2 \u6ee1\u56ed\u6625\u8272\u5173\u4e0d\u4f4f , 2\u670811\u65e5\u81f313\u65e5\u6211\u56fd\u4e2d\u4e1c\u90e8\u96e8\u96ea\u964d\u6e29\u5c06\u518d\u81f3 , \u6e7f\u6f09\u6f09\uff01\u5e7f\u897f\u5357\u5b81\u5e02\u906d\u9047\u96e8\u96fe\u56de\u5357\u5929\u201c\u4e09\u8fde\u51fb\u201d ", "token": "1234"}
用[https://www.json.cn]工具解码,