Python 爬取网页水务数据并实现智慧水务前端可视化

@正在学习驰骋的小马 2024-09-06 08:33:02 阅读 68

提示:本文爬取深圳市环境水务集团有限公司的公开数据作为数据样例进行数据分析与可视化。

文章目录

一、爬虫二、对爬取的数据进行数据库、excel的存储与数据处理1.代码实现

三、应用Flask框架将后端获取数据后渲染到前端四、前端Echarts的使用1.下载echarts.min.js2.放入静态文件夹下3.在要使用echarts的html网页中,导入这个文件4.在官网中找到自己心仪的图表5.下载官网中的代码到本地6.下载后用记事本打开,选取需要的部分7.将需要的部分粘贴到前端页面中,并将数据修改为后端传递的数据即可

五、最终的效果


一、爬虫

爬虫是一种强大的数据获取工具,可以帮助我们自动化地收集互联网上的各种信息,为数据分析、数据挖掘等领域提供有力的支持。被称为网络爬虫或网页蜘蛛,是一种按照一定规则自动地抓取互联网信息的程序或脚本。Python 爬虫是一种利用 Python 编程语言编写的程序,用于自动地浏览万维网并从网页中提取数据。Python 因其简洁的语法、丰富的库支持和强大的社区支持,成为了实现爬虫项目的热门选择。

基本工作流程:

发起请求:通过HTTP库(如Python的requests库)向目标网站发送请求,获取网页内容。获取响应:接收服务器的响应,通常是HTML格式的网页源代码。解析网页:使用解析库(如Python的BeautifulSoup或lxml)对网页内容进行解析,提取出需要的数据(如文本、图片链接、视频链接等)。存储数据:将提取出的数据存储到数据库或文件中,供后续处理或分析使用。

重复以上步骤:根据需要,爬虫可以不断重复上述步骤,以获取更多数据或更新数据

注意事项

遵守 robots.txt:在编写爬虫时,请确保遵守目标网站的 robots.txt

文件中的规则,以避免对网站造成不必要的负担或被视为恶意访问。合理设置请求头:为了模拟浏览器访问,你可能需要设置 User-Agent 请求头。

使用代理和延时:为了减少对目标网站服务器的压力,你可以考虑使用代理和设置请求之间的延时。处理 JavaScript 渲染的内容:如果目标网页的内容是通过 JavaScript

动态生成的,那么上述示例中的方法可能无法直接获取到这些内容。这时,你可以考虑使用像 Selenium 这样的工具来模拟浏览器行为。法律和道德问题:在编写爬虫时,请确保你的行为符合当地的法律法规,并尊重网站的版权和隐私政策。

爬虫常用的库

网络请求库

urllib:Python3自带的库,提供了基本的URL请求和网页抓取功能。它是最简单的网络请求库,但功能相对基础。

requests:第三方库,建立在urllib3之上,提供了更加人性化的API。它简化了发送HTTP请求和处理响应的过程,是最常用的Python网络请求库之一。

Selenium:自动化测试工具,通过模拟用户在浏览器中的操作来抓取动态网页内容。它支持多种浏览器,如Chrome、Firefox等,适用于需要JavaScript渲染的页面。

网页解析库

Beautiful Soup:第三方库,用于解析HTML和XML文档。它提供了从网页中提取数据的简便方法,支持多种解析器,如Python标准库中的html.parser和lxml。

lxml:第三方库,支持HTML和XML的解析,并且提供了XPath和CSS选择器的支持。它的解析效率非常高,是处理大型网页数据的理想选择。

pyquery:第三方库,类似于jQuery的Python实现,能够以jQuery的语法来操作解析HTML文档。它对于熟悉jQuery的开发者来说非常友好。

异步及并发库

asyncio:Python标准库(Python 3.4+),提供了异步I/O、时间循环、协同程序和任务等功能。结合aiohttp等异步HTTP库,可以高效地处理大量并发请求。

concurrent.futures:Python标准库,为调用异步执行提供了一个高层次的接口。它支持ThreadPoolExecutor和ProcessPoolExecutor,可以分别用于I/O密集型和CPU密集型任务的并发执行。

aiohttp:基于asyncio实现的HTTP框架,支持异步操作。它提供了与requests类似的API,但具有更高的效率和更好的并发性能。

爬虫框架

Scrapy:一个高级的Web抓取框架,用于从高度复杂的网站中抓取数据。它提供了强大的编码支持、选择器引擎和爬虫引擎,并且支持多种数据库后端和消息队列。Scrapy的学习曲线可能较陡峭,但一旦掌握,可以高效地处理各种爬取任务。

PySpider:一个由国人编写的强大的网络爬虫系统,带有强大的WebUI、脚本编辑器、任务监控器、项目管理以及结果处理器。它支持多种数据库后端和消息队列,并且可以渲染JavaScript页面。

Crawley:一个高速爬取对应网站内容的框架,支持关系和非关系数据库,数据可以导出为JSON、XML等格式。但需要注意的是,Crawley并不是一个广为人知的爬虫框架,可能在使用和社区支持方面存在限制。

其他常用库

fake-useragent:用于生成随机的User-Agent字符串,以模拟不同设备的浏览器请求,有助于绕过一些简单的反爬虫机制。

re:Python标准库,提供了正则表达式的支持。虽然它本身不是爬虫库,但在处理网页数据时经常用于文本匹配和提取。

从深圳市水务局的网站上获取污水处理数据

<code># -*- codeing = utf-8 -*-

# @Time : 2022/11/29 15:32

# @Author : 小马

# @File: GetData.py

# @Software : PyCharm

#从深圳市水务局获取到深圳各个区的污水处理数据

import urllib.request,urllib.error

import pymysql

import re

from bs4 import BeautifulSoup

import xlwt

findaddress=re.compile(r'<a href="(.*)" target="_blank">20(.*)</a>')code>

finddataset=re.compile(r'<p style="text-align: center;"><strong>(.*?)</strong></p>')code>

finddataset2020=re.compile(r'<p style="text-align: left;"><strong>(.*?)</strong></p>')code>

baseurl="http://swj.sz.gov.cn/xxgk/zfxxgkml/szswgk/tjsj/psgltj/"code>

#元特区 福田区 罗湖区 盐田区 南山区 宝安区 大鹏新区 龙宝区 龙华区 坪山区 光明区

def ask(url):

headers = {

"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Core/1.94.178.400 QQBrowser/11.2.5170.400"}

request = urllib.request.Request(url=url, headers=headers)

html = ""

try:

response = urllib.request.urlopen(request)

html = response.read().decode("utf-8")

except urllib.error.URLError as e:

if hasattr(e, "code"): # hasattr() 函数用来判断某个类实例对象是否包含指定名称的属性或方法。

print(e.code)

if hasattr(e, "reason"):

print(e.reason)

return html

def getaddress(baseurl):

addressList=[]

htmll=ask(baseurl)

soup=BeautifulSoup(htmll,"html.parser")

for item in soup("a",target="_blank"):code>

item=str(item)

add=re.findall(findaddress,item)

if(len(add)>0):

addressList.append(add[0][0])

return addressList

def get2021(): url="http://swj.sz.gov.cn/xxgk/zfxxgkml/szswgk/tjsj/psgltj/content/post_9685824.html"code>

html=ask(url)

soup=BeautifulSoup(html,"html.parser")

year="2021"code>

districtList=[]

districtname=[]

sum1=[]

sum2=[]

for i in soup.tbody:

i=str(i)

data=re.findall(finddataset,i)

if(len(data)>0):

if(len(data)==1):

districtname.append(data[0])

if(len(data)==3):

sum1.append(data[1])

sum2.append(data[2])

# for item in soup("p",style="text-align: center"):code>

# print(item)

# for i in districtname:

# print(i)

# for i in sum1:

# print(i)

# for i in sum2:

# print(i)

for i in range(0,7):

district = []

district.append(year)

district.append(districtname[i])

district.append(sum1[i])

district.append(sum2[i])

districtList.append(district)

return districtList

def get2020(): url="http://swj.sz.gov.cn/xxgk/zfxxgkml/szswgk/tjsj/psgltj/content/post_8666858.html"code>

html = ask(url)

soup = BeautifulSoup(html, "html.parser")

year = "2020"

districtList = []

districtname = []

sum1 = []

sum2 = []

for i in soup.tbody:

i = str(i)

data = re.findall(finddataset2020, i)

if (len(data) > 0):

if (len(data) == 1):

districtname.append(data[0])

if (len(data) == 3):

sum1.append(data[1])

sum2.append(data[2])

for i in range(0,2):

district = []

district.append(year)

district.append(districtname[i])

district.append(sum1[i])

district.append(sum2[i])

districtList.append(district)

district = []

district.append(year)

district.append("盐田区")

district.append(12)

district.append(3003.25)

districtList.append(district)

for i in range(4,10):

district = []

district.append(year)

district.append(districtname[i])

district.append(sum1[i])

district.append(sum2[i])

districtList.append(district)

return districtList

def get2019():

url="http://swj.sz.gov.cn/xxgk/zfxxgkml/szswgk/tjsj/psgltj/content/post_8123700.html"code>

html = ask(url)

soup = BeautifulSoup(html, "html.parser")

year = "2019"

districtList = []

districtname = []

sum1 = []

sum2 = []

for i in soup.tbody:

i = str(i)

data = re.findall(finddataset2020, i)

if (len(data) > 0):

if (len(data) == 1):

districtname.append(data[0])

if (len(data) == 3):

sum1.append(data[1])

sum2.append(data[2])

for i in range(0,7):

district = []

district.append(year)

district.append(districtname[i])

district.append(sum1[i])

district.append(sum2[i])

districtList.append(district)

return districtList

def initdb(sql):

db=pymysql.connect(host="localhost",user="root",password="root",port=3307,database="student",charset='utf8')code>

try:

cursor=db.cursor()

data=cursor.execute(sql)

print(data)

result=cursor.fetchall()

for i in result:

print(i)

db.commit()

except Exception as e:

db.rollback()

print(str(e))

finally:

db.close()

def usedb():

a=get2021()

for item in a:

item[1]='"'+item[1]+'"'

sql = "insert into sewage (year,district,scale,sewagetreatment)values(%s,%s,%s,%s)"%(item[0],item[1],item[2],item[3])

initdb(sql)

b=get2020()

for item in b:

item[1] = '"' + item[1] + '"'

sql = "insert into sewage (year,district,scale,sewagetreatment)values(%s,%s,%s,%s)" % (item[0], item[1], item[2], item[3])

initdb(sql)

c=get2019()

for item in c:

item[1] = '"' + item[1] + '"'

sql = "insert into sewage (year,district,scale,sewagetreatment)values(%s,%s,%s,%s)" % (item[0], item[1], item[2], item[3])

initdb(sql)

def getDatafromDB():

sql="select * from sewage"code>

db = pymysql.connect(host="localhost", user="root", password="root", port=3307, database="student", charset='utf8')code>

try:

cursor = db.cursor()

data = cursor.execute(sql)

result = cursor.fetchall()

db.commit()

except Exception as e:

db.rollback()

print(str(e))

finally:

db.close()

return result

def getDatafrom(sql):

db = pymysql.connect(host="localhost", user="root", password="root", port=3307, database="student", charset='utf8')code>

try:

cursor = db.cursor()

data = cursor.execute(sql)

result = cursor.fetchall()

db.commit()

except Exception as e:

db.rollback()

print(str(e))

finally:

db.close()

return result

if __name__=="__main__":

sql = "select sum(sewagetreatment) from sewage where year=2019"

sql1 = "select sum(sewagetreatment) from sewage where year=2020"

r=getDatafrom(sql)

q=getDatafrom(sql1)

print(int(r[0][0]))

print(int(q[0][0]))


二、对爬取的数据进行数据库、excel的存储与数据处理

必要性:对爬取的数据进行数据库、Excel的存储与数据处理是数据分析和挖掘过程中非常重要的一步,爬取的数据通常是从网页或其他来源临时获取的。如果不进行存储,一旦程序关闭或数据源更新,这些数据就会丢失。通过将其存储在数据库或Excel文件中,可以确保数据的持久化,即数据可以长期保存并随时访问。存储在数据库或Excel中的数据可以轻松地通过SQL查询、Excel函数或数据分析工具(如Python的pandas库)进行进一步的分析和挖掘。这有助于发现数据中的模式、趋势和关联,从而支持决策制定和业务洞察。在将数据存储到数据库或Excel之前,通常需要进行一系列的数据处理和清洗工作,如去除重复数据、处理缺失值、转换数据类型和格式等。这些工作有助于确保数据的准确性和一致性,为后续的分析和挖掘提供可靠的数据基础。

1.代码实现

# -*- codeing = utf-8 -*-

# @Time : 2022/11/9 19:46

# @Author : 小马

# @File: Methods.py

# @Software : PyCharm

import sqlite3

import xlrd

import xlwt

#从月度总水量表中获得数据并将数据插入到数据表WaterConsumption中

def CreateWaterConsumption():

dbpath = "water.db"

sql = '''

create table WaterConsumption

(

id integer primary key autoincrement,

date text,

Machine1 text,

Machine2 text,

Machine3 text,

Machine4 text,

Machine5 text,

Machine6 text,

totaltime text,

totalwater text,

totalconsumption text

)

'''

conn = sqlite3.connect(dbpath)

cur = conn.cursor()

cur.execute(sql)

conn.commit()

cur.close()

conn.close()

def searchall():

datalist = []

con = sqlite3.connect("water.db")

cur = con.cursor()

sql = "select date,Machine1,Machine2,Machine3,Machine4,Machine5,Machine6,totaltime from WaterConsumption"

data = cur.execute(sql)

for item in data:

datalist.append(item)

cur.close()

con.close()

return datalist

def searchMachine():

m1=[]

m2=[]

m3=[]

m4=[]

m5=[]

m6=[]

con = sqlite3.connect("water.db")

cur = con.cursor()

sql1 = "select Machine1,Machine2,Machine3,Machine4,Machine5,Machine6 from WaterConsumption"

data1 = cur.execute(sql1)

for item in data1:

m1.append(item[0])

m2.append(item[1])

m3.append(item[2])

m4.append(item[3])

m5.append(item[4])

m6.append(item[5])

cur.close()

con.close()

return m1,m2,m3,m4,m5,m6

def searchDateAndWater():

date=[]

wate=[]

con = sqlite3.connect("water.db")

cur = con.cursor()

sql = "select date,totaltime from WaterConsumption"

data = cur.execute(sql)

for item in data:

date.append(item[0])

wate.append(item[1])

cur.close()

con.close()

return date,wate

def ReadExcel():

conn=sqlite3.connect("water.db")

cur=conn.cursor()

data=xlrd.open_workbook("月度总用水量.xls")

sheet=data.sheet_by_name("Sheet1")

for i in range(1,sheet.nrows):

sql = '''insert into WaterConsumption(date,Machine1,Machine2,Machine3,Machine4,Machine5,Machine6,totalconsumption,totaltime,totalwater)values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'''%(sheet.row(i)[0].value, sheet.row(i)[1].value, sheet.row(i)[2].value,sheet.row(i)[3].value,sheet.row(i)[4].value,sheet.row(i)[5].value,sheet.row(i)[6].value,sheet.row(i)[7].value,sheet.row(i)[8].value,sheet.row(i)[9].value)

cur.execute(sql)

conn.commit()

cur.close()

conn.close()

#创建WaterConsumption水量消耗表格

#将注册手机号,邮箱,密码插入到数据库中

def InsertIntoTable(p,pa,e):

sql = '''insert into UserTable(phonenumber,password,email,isdeleted)values(%s,%s,%s,0)'''%(p,pa,e)

conn=sqlite3.connect("water.db")

cur=conn.cursor()

cur.execute(sql)

conn.commit()

cur.close()

conn.close()

#根据手机号查询密码和逻辑删除来实现登陆功能

def SelectFromTable(p):

sql='''select password from UserTable where phonenumber=%s and isdeleted=0'''%pcode>

conn=sqlite3.connect("water.db")

cur=conn.cursor()

pa=cur.execute(sql)

result=pa.fetchone()

conn.commit()

cur.close()

conn.close()

return result[0]

#创建数据库并建表

def CreateTableAndDataBase():

dbpath="water.db"code>

sql='''code>

create table UserTable

(

id integer primary key autoincrement,

phonenumber text,

password text,

email text,

isdeleted integer

)

'''

conn=sqlite3.connect(dbpath)

cur=conn.cursor()

cur.execute(sql)

conn.commit()

cur.close()

conn.close()

def deleteTable():

dbpath = "water.db"

sql = '''

drop table WaterConsumption

'''

conn = sqlite3.connect(dbpath)

cur = conn.cursor()

cur.execute(sql)

conn.commit()

cur.close()

conn.close()

#将数据写入excel表格

def exportTo():

data = xlrd.open_workbook("月度总用水量.xls")

sheet = data.sheet_by_name("Sheet1")

# #for i in range(1, sheet.nrows):

workbook = xlwt.Workbook(encoding="UTF-8") # 创建workbook对象 一个文件code>

worksheet = workbook.add_sheet('sheet1')

for i in range(0,10):

worksheet.write(0,i,sheet.row(0)[i].value)

for i in range(1, sheet.nrows):

for j in range(0, 10):

worksheet.write(i, j, sheet.row(i)[j].value)

workbook.save('C:/Users/DELL/Downloads/月度总用水表.xls')

def searchSumSix():

datalist=[]

sql="select sum(Machine1),sum(Machine2),sum(Machine3),sum(Machine4),sum(Machine5),sum(Machine6) from WaterConsumption"code>

dbpath = "water.db"

conn = sqlite3.connect(dbpath)

cur = conn.cursor()

data=cur.execute(sql)

for item in data:

datalist.append(item)

conn.commit()

cur.close()

conn.close()

return datalist

if __name__=='__main__':

d=searchSumSix()

print(int(d[0][0]))

print(d[0][1])

# -*- codeing = utf-8 -*-

# @Time : 2022/11/12 16:50

# @Author : 小马

# @File: Service.py

# @Software : PyCharm

#代价

import xlrd2

#将供水管网维修记录表中的信息插入到数据库中

def ReadAndAnalysisExcel():

datelist=[]

data = xlrd2.open_workbook("供水管网维修记录.xls")

sheet = data.sheet_by_name("新交叉表_供水管网")

for i in range(1,476):

if(sheet.row(i)[7].value == "是"):

date="2021-"+(sheet.row(i)[0].value)[4:6]+"-"+(sheet.row(i)[0].value)[6:8]code>

datelist.append(date)

return datelist

#其他、第三者/人为、非我司管辖范围 外腐蚀 自然老化 施工质量

def ReadAndAnalysisQuestion():

result=[]

a=0

b=0

c=0

d=0

e=0

f=0

datelist = []

data = xlrd2.open_workbook("供水管网维修记录.xls")

sheet = data.sheet_by_name("新交叉表_供水管网")

for i in range(1,sheet.nrows):

if(sheet.row(i)[3].value=="其他"):

a=a+1

elif(sheet.row(i)[3].value=="第三者/人为"):

b=b+1

elif (sheet.row(i)[3].value == "非我司管辖范围"):

c = c + 1

elif (sheet.row(i)[3].value == "外腐蚀"):

d = d + 1

elif (sheet.row(i)[3].value == "自然老化"):

e = e + 1

elif (sheet.row(i)[3].value == "施工质量"):

f = f + 1

result.append(a)

result.append(b)

result.append(c)

result.append(d)

result.append(e)

result.append(f)

return result

if __name__=='__main__':

r=ReadAndAnalysisQuestion()

print(r[1])

三、应用Flask框架将后端获取数据后渲染到前端

Flask 是一个非常适合初学者和经验丰富的开发者使用的 Web 框架,它简洁而强大,能够帮助你快速构建出高质量的 Web 应用。

Flask的安装:

pip install Flask

Flask 的特点

轻量级:Flask 核心非常简洁,易于扩展。

灵活:你可以自由地选择数据库、模板引擎等。

WSGI 兼容:Flask 遵循 WSGI(Web Server Gateway Interface)标准,可以与多种服务器兼容。

开发便捷:内置的开发服务器和调试器让开发变得简单快捷。

社区支持:由于 Flask 的流行,其社区非常活跃,提供了大量的插件和扩展。

简单实例:

from flask import Flask

app = Flask(__name__)

@app.route('/')

def hello_python():

return 'Hello, Python!'

if __name__ == '__main__':

app.run(debug=True)

具体实现

# -*- codeing = utf-8 -*-

# @Time : 2022/11/8 10:44

# @Author : 小马

# @File: app.py

# @Software : PyCharm

import GetData

from flask import Flask,render_template

from flask import request

import sqlite3

import Methods

import json

import Service

app=Flask(__name__)

@app.route('/',methods=['GET','POST'])

def login():

data=request.get_data()

if(len(data)>0):

print(data)

phonenumber = request.form.get('phonenumber')

password = request.form.get('password')

p = '"' + phonenumber + '"'

password1=Methods.SelectFromTable(p)

if(password==password1 or password is password1):

return render_template("index.html")

else:

print(password)

print(password1)

return render_template("login.html")

@app.route('/forgotpassword')

def forgot():

return render_template("forgot-password.html")

@app.route('/index')

def index():

return render_template("index.html")

@app.route('/utilities-animation')

def animation():

result=GetData.getDatafromDB()

sql = "select sum(sewagetreatment) from sewage where year=2019"

sql1 = "select sum(sewagetreatment) from sewage where year=2020"

sql2="select sum(sewagetreatment) from sewage where year=2021"code>

r = GetData.getDatafrom(sql)

q = GetData.getDatafrom(sql1)

l = GetData.getDatafrom(sql2)

return render_template("utilities-animation.html",data=result,d1=int(r[0][0])/1000,d2=int(q[0][0])/1000,d3=int(l[0][0])/1000)

@app.route('/question')

def question():

r = Service.ReadAndAnalysisQuestion()

return render_template("question.html",data=r)

@app.route('/buttons')

def buttons():

d=Methods.searchall()

return render_template("buttons.html",waterlist=d)

@app.route('/charts')

def charts():

d=Service.ReadAndAnalysisExcel()

return render_template("charts.html",dateList=d)

@app.route('/export')

def export():

Methods.exportTo()

d = Methods.searchall()

return render_template("buttons.html",waterlist=d)

@app.route('/tables')

def tables():

return render_template("tables.html")

@app.route('/cards')

def cards():

a=Methods.searchMachine()

b=Methods.searchSumSix()

b1 = int(b[0][0])

b2 = int(b[0][1])

b3 = int(b[0][2])

b4 = int(b[0][3])

b5 = int(b[0][4])

b6 = int(b[0][5])

d=Methods.searchDateAndWater()

Xdata=d[0]

Ydata=d[1]

return render_template("cards.html",Xdata=Xdata,Ydata=Ydata,m1=a[0],m2=a[1],m3=a[2],m4=a[3],m5=a[4],m6=a[5],n1=b1,n2=b2,n3=b3,n4=b4,n5=b5,n6=b6)

@app.route('/register',methods=['GET','POST'])

def register():

data = request.get_data()

print("----------")

print(data)

if(len(data)>0):

phonenumber=request.form.get('phonenumber')

password=request.form.get('password')

email=request.form.get('email')

if(len(phonenumber)>0 and len(password)>0):

p='"'+phonenumber+'"'code>

pa='"'+password+'"'code>

e='"'+email+'"'code>

Methods.InsertIntoTable(p,pa,e)

return render_template("login.html")

return render_template("register.html")

if __name__=='__main__':

app.run(debug=True)

四、前端Echarts的使用

1.下载echarts.min.js

链接:

https://github.com/apache/echarts/tree/master/dist

2.放入静态文件夹下

在这里插入图片描述

3.在要使用echarts的html网页中,导入这个文件

在这里插入图片描述

代码如下:

<code><script src="static/js/echarts.min.js"></script>code>

4.在官网中找到自己心仪的图表

在这里插入图片描述

5.下载官网中的代码到本地

在这里插入图片描述

6.下载后用记事本打开,选取需要的部分

在这里插入图片描述

7.将需要的部分粘贴到前端页面中,并将数据修改为后端传递的数据即可

后端传递的数据

<code>@app.route('/charts')

def charts():

d=Service.ReadAndAnalysisExcel()

return render_template("charts.html",dateList=d)

前端接收和调用

var datelist={ { dateList|tojson}};

五、最终的效果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

将代码的链接放到了最后,有喜欢的小伙伴在下载时给作者点一个star

智慧水务:https://gitee.com/majunlong1/IntelligentWater

在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。