stock-tracker

This commit is contained in:
C菌
2026-07-04 00:17:11 +08:00
commit 6087341a48
6463 changed files with 1929869 additions and 0 deletions
@@ -0,0 +1,6 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
Date: 2019/9/30 13:58
Desc:
"""
@@ -0,0 +1,107 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
Date: 2024/4/25 17:20
Desc: 河北省空气质量预报信息发布系统
https://110.249.223.67/publish
每日 17 时发布
等级划分
1. 空气污染指数为0-50,空气质量级别为一级,空气质量状况属于优。此时,空气质量令人满意,基本无空气污染,各类人群可正常活动。
2. 空气污染指数为51-100,空气质量级别为二级,空气质量状况属于良。此时空气质量可接受,但某些污染物可能对极少数异常敏感人群健康有较弱影响,建议极少数异常敏感人群应减少户外活动。
3. 空气污染指数为101-150,空气质量级别为三级,空气质量状况属于轻度污染。此时,易感人群症状有轻度加剧,健康人群出现刺激症状。建议儿童、老年人及心脏病、呼吸系统疾病患者应减少长时间、高强度的户外锻炼。
4. 空气污染指数为151-200,空气质量级别为四级,空气质量状况属于中度污染。此时,进一步加剧易感人群症状,可能对健康人群心脏、呼吸系统有影响,建议疾病患者避免长时间、高强度的户外锻练,一般人群适量减少户外运动。
5. 空气污染指数为201-300,空气质量级别为五级,空气质量状况属于重度污染。此时,心脏病和肺病患者症状显著加剧,运动耐受力降低,健康人群普遍出现症状,建议儿童、老年人和心脏病、肺病患者应停留在室内,停止户外运动,一般人群减少户外运动。
6. 空气污染指数大于300,空气质量级别为六级,空气质量状况属于严重污染。此时,健康人群运动耐受力降低,有明显强烈症状,提前出现某些疾病,建议儿童、老年人和病人应当留在室内,避免体力消耗,一般人群应避免户外活动。
发布单位:河北省环境应急与重污染天气预警中心 技术支持:中国科学院大气物理研究所 中科三清科技有限公司
"""
import pandas as pd
import requests
from bs4 import BeautifulSoup
def air_quality_hebei() -> pd.DataFrame:
"""
河北省空气质量预报信息发布系统-空气质量预报, 未来 6 天
http://218.11.10.130:8080/#/application/home
:return: city = "", 返回所有地区的数据; city="唐山市", 返回唐山市的数据
:rtype: pandas.DataFrame
"""
url = "http://218.11.10.130:8080/api/hour/130000.xml"
r = requests.get(url)
soup = BeautifulSoup(r.content, features="xml")
data = []
cities = soup.find_all("City")
for city in cities:
pointers = city.find_all("Pointer")
for pointer in pointers:
row = {
"City": city.Name.text if city.Name else None,
"Region": pointer.Region.text if pointer.Region else None,
"Station": pointer.Name.text if pointer.Name else None,
"DateTime": pointer.DataTime.text if pointer.DataTime else None,
"AQI": pointer.AQI.text if pointer.AQI else None,
"Level": pointer.Level.text if pointer.Level else None,
"MaxPoll": pointer.MaxPoll.text if pointer.MaxPoll else None,
"Longitude": pointer.CLng.text if pointer.CLng else None,
"Latitude": pointer.CLat.text if pointer.CLat else None,
}
polls = pointer.find_all("Poll")
for poll in polls:
poll_name = poll.Name.text if poll.Name else None
poll_value = poll.Value.text if poll.Value else None
row[f"{poll_name}_Value"] = poll_value
row[f"{poll_name}_IAQI"] = poll.IAQI.text if poll.IAQI else None
data.append(row)
df = pd.DataFrame(data)
numeric_columns = ["AQI", "Longitude", "Latitude"] + [
col for col in df.columns if col.endswith("_Value") or col.endswith("_IAQI")
]
for col in numeric_columns:
df[col] = pd.to_numeric(df[col], errors="coerce")
column_names = {
"City": "城市",
"Region": "区域",
"Station": "监测点",
"DateTime": "时间",
"Level": "空气质量等级",
"MaxPoll": "首要污染物",
"Longitude": "经度",
"Latitude": "纬度",
"SO2_Value": "二氧化硫_浓度",
"SO2_IAQI": "二氧化硫_IAQI",
"CO_Value": "一氧化碳_浓度",
"CO_IAQI": "一氧化碳_IAQI",
"NO2_Value": "二氧化氮_浓度",
"NO2_IAQI": "二氧化氮_IAQI",
"O3-1H_Value": "臭氧1小时_浓度",
"O3-1H_IAQI": "臭氧1小时_IAQI",
"O3-8H_Value": "臭氧8小时_浓度",
"O3-8H_IAQI": "臭氧8小时_IAQI",
"PM2.5_Value": "PM2.5_浓度",
"PM2.5_IAQI": "PM2.5_IAQI",
"PM10_Value": "PM10_浓度",
"PM10_IAQI": "PM10_IAQI",
}
df = df.rename(columns=column_names)
basic_columns = [
"城市",
"区域",
"监测点",
"时间",
"AQI",
"空气质量等级",
"首要污染物",
"经度",
"纬度",
]
pollutant_columns = [col for col in df.columns if col not in basic_columns]
df = df[basic_columns + sorted(pollutant_columns)]
return df
if __name__ == "__main__":
air_quality_hebei_df = air_quality_hebei()
print(air_quality_hebei_df)
@@ -0,0 +1,294 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
Date: 2024/4/2 22:40
Desc: 真气网-空气质量
https://www.zq12369.com/environment.php
空气质量在线监测分析平台的空气质量数据
https://www.aqistudy.cn/
"""
import json
import os
import re
from io import StringIO
import pandas as pd
import requests
from py_mini_racer import MiniRacer
from akshare.utils import demjson
def _get_js_path(name: str = None, module_file: str = None) -> str:
"""
获取 JS 文件的路径(从模块所在目录查找)
:param name: 文件名
:type name: str
:param module_file: 模块路径
:type module_file: str
:return: 路径
:rtype: str
"""
module_folder = os.path.abspath(os.path.dirname(os.path.dirname(module_file)))
module_json_path = os.path.join(module_folder, "air", name)
return module_json_path
def _get_file_content(file_name: str = "crypto.js") -> str:
"""
获取 JS 文件的内容
:param file_name: JS 文件名
:type file_name: str
:return: 文件内容
:rtype: str
"""
setting_file_name = file_name
setting_file_path = _get_js_path(setting_file_name, __file__)
with open(setting_file_path) as f:
file_data = f.read()
return file_data
def has_month_data(href):
"""
Deal with href node
:param href: href
:type href: str
:return: href result
:rtype: str
"""
return href and re.compile("monthdata.php").search(href)
def air_city_table() -> pd.DataFrame:
"""
真气网-空气质量历史数据查询-全部城市列表
https://www.zq12369.com/environment.php?date=2019-06-05&tab=rank&order=DESC&type=DAY#rank
:return: 城市映射
:rtype: pandas.DataFrame
"""
url = "https://www.zq12369.com/environment.php"
date = "2020-05-01"
temp_df = None
if len(date.split("-")) == 3:
params = {
"date": date,
"tab": "rank",
"order": "DESC",
"type": "DAY",
}
r = requests.get(url, params=params)
temp_df = pd.read_html(StringIO(r.text))[1].iloc[1:, :]
del temp_df["降序"]
temp_df.reset_index(inplace=True)
temp_df["index"] = temp_df.index + 1
temp_df.columns = [
"序号",
"省份",
"城市",
"AQI",
"空气质量",
"PM2.5浓度",
"首要污染物",
]
temp_df["AQI"] = pd.to_numeric(temp_df["AQI"])
return temp_df
def air_quality_watch_point(
city: str = "杭州", start_date: str = "20220408", end_date: str = "20220409"
) -> pd.DataFrame:
"""
真气网-监测点空气质量-细化到具体城市的每个监测点
指定之间段之间的空气质量数据
https://www.zq12369.com/
:param city: 调用 ak.air_city_table() 接口获取
:type city: str
:param start_date: e.g., "20190327"
:type start_date: str
:param end_date: e.g., ""20200327""
:type end_date: str
:return: 指定城市指定日期区间的观测点空气质量
:rtype: pandas.DataFrame
"""
start_date = "-".join([start_date[:4], start_date[4:6], start_date[6:]])
end_date = "-".join([end_date[:4], end_date[4:6], end_date[6:]])
url = "https://www.zq12369.com/api/zhenqiapi.php"
file_data = _get_file_content(file_name="crypto.js")
ctx = MiniRacer()
ctx.eval(file_data)
method = "GETCITYPOINTAVG"
city_param = ctx.call("encode_param", city)
payload = {
"appId": "a01901d3caba1f362d69474674ce477f",
"method": ctx.call("encode_param", method),
"city": city_param,
"startTime": ctx.call("encode_param", start_date),
"endTime": ctx.call("encode_param", end_date),
"secret": ctx.call("encode_secret", method, city_param, start_date, end_date),
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/81.0.4044.122 Safari/537.36"
}
r = requests.post(url, data=payload, headers=headers)
data_text = r.text
data_json = demjson.decode(ctx.call("decode_result", data_text))
temp_df = pd.DataFrame(data_json["rows"])
return temp_df
def air_quality_hist(
city: str = "杭州",
period: str = "day",
start_date: str = "20190327",
end_date: str = "20200427",
) -> pd.DataFrame:
"""
真气网-空气历史数据
https://www.zq12369.com/
:param city: 调用 ak.air_city_table() 接口获取所有城市列表
:type city: str
:param period: "hour": 每小时一个数据, 由于数据量比较大, 下载较慢; "day": 每天一个数据; "month": 每个月一个数据
:type period: str
:param start_date: e.g., "20190327"
:type start_date: str
:param end_date: e.g., "20200327"
:type end_date: str
:return: 指定城市和数据频率下在指定时间段内的空气质量数据
:rtype: pandas.DataFrame
"""
start_date = "-".join([start_date[:4], start_date[4:6], start_date[6:]])
end_date = "-".join([end_date[:4], end_date[4:6], end_date[6:]])
url = "https://www.zq12369.com/api/newzhenqiapi.php"
file_data = _get_file_content(file_name="outcrypto.js")
ctx = MiniRacer()
ctx.eval(file_data)
app_id = "4f0e3a273d547ce6b7147bfa7ceb4b6e"
method = "CETCITYPERIOD"
timestamp = ctx.eval("timestamp = new Date().getTime()")
p_text = json.dumps(
{
"city": city,
"endTime": f"{end_date} 23:45:39",
"startTime": f"{start_date} 00:00:00",
"type": period.upper(),
},
ensure_ascii=False,
indent=None,
).replace(' "', '"')
secret = ctx.call("hex_md5", app_id + method + str(timestamp) + "WEB" + p_text)
payload = {
"appId": "4f0e3a273d547ce6b7147bfa7ceb4b6e",
"method": "CETCITYPERIOD",
"timestamp": int(timestamp),
"clienttype": "WEB",
"object": {
"city": city,
"type": period.upper(),
"startTime": f"{start_date} 00:00:00",
"endTime": f"{end_date} 23:45:39",
},
"secret": secret,
}
need = (
json.dumps(payload, ensure_ascii=False, indent=None, sort_keys=False)
.replace(' "', '"')
.replace("\\", "")
.replace('p": ', 'p":')
.replace('t": ', 't":')
)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)"
"Chrome/100.0.4896.75 Safari/537.36",
"X-Requested-With": "XMLHttpRequest",
}
params = {"param": ctx.call("encode_param", need)}
r = requests.post(url, data=params, headers=headers)
temp_text = ctx.call("decryptData", r.text)
data_json = demjson.decode(ctx.call("b.decode", temp_text))
temp_df = pd.DataFrame(data_json["result"]["data"]["rows"])
temp_df.index = temp_df["time"]
del temp_df["time"]
temp_df = temp_df.astype(float, errors="ignore")
return temp_df
def air_quality_rank(date: str = "") -> pd.DataFrame:
"""
真气网-168 城市 AQI 排行榜
https://www.zq12369.com/environment.php?date=2020-03-12&tab=rank&order=DESC&type=DAY#rank
:param date: "": 当前时刻空气质量排名; "20200312": 当日空气质量排名; "202003": 当月空气质量排名; "2019": 当年空气质量排名;
:type date: str
:return: 指定 date 类型的空气质量排名数据
:rtype: pandas.DataFrame
"""
if len(date) == 4:
date = date
elif len(date) == 6:
date = "-".join([date[:4], date[4:6]])
elif date == "":
date = "实时"
else:
date = "-".join([date[:4], date[4:6], date[6:]])
url = "https://www.zq12369.com/environment.php"
if len(date.split("-")) == 3:
params = {
"date": date,
"tab": "rank",
"order": "DESC",
"type": "DAY",
}
r = requests.get(url, params=params)
return pd.read_html(StringIO(r.text))[1].iloc[1:, :]
elif len(date.split("-")) == 2:
params = {
"month": date,
"tab": "rank",
"order": "DESC",
"type": "MONTH",
}
r = requests.get(url, params=params)
return pd.read_html(StringIO(r.text))[2].iloc[1:, :]
elif len(date.split("-")) == 1 and date != "实时":
params = {
"year": date,
"tab": "rank",
"order": "DESC",
"type": "YEAR",
}
r = requests.get(url, params=params)
return pd.read_html(StringIO(r.text))[3].iloc[1:, :]
if date == "实时":
params = {
"tab": "rank",
"order": "DESC",
"type": "MONTH",
}
r = requests.get(url, params=params)
return pd.read_html(StringIO(r.text))[0].iloc[1:, :]
if __name__ == "__main__":
air_city_table_df = air_city_table()
print(air_city_table_df)
air_quality_watch_point_df = air_quality_watch_point(
city="杭州", start_date="20220408", end_date="20220409"
)
print(air_quality_watch_point_df)
air_quality_hist_df = air_quality_hist(
city="北京",
period="day",
start_date="20220801",
end_date="20240402",
)
print(air_quality_hist_df)
air_quality_rank_df = air_quality_rank()
print(air_quality_rank_df)
@@ -0,0 +1,413 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
Date: 2019/11/25 20:45
Desc: 空气质量接口配置文件
"""
city_chinese_list = [
"北京",
"重庆",
"福州",
"广州",
"杭州",
"昆明",
"南昌",
"南京",
"南宁",
"南通",
"宁波",
"上海",
"深圳",
"苏州",
"徐州",
"银川",
]
city_english_list = [
"beijing",
"chongqing",
"foochow",
"guangzhou",
"hangzhou",
"kunming",
"nanchang",
"nanjing",
"nanning",
"nantong",
"ningbo",
"shanghai",
"shenzhen",
"suzhou",
"xuzhou",
"yinchuan",
]
city_code_dict = {
"北京": "110000",
"天津": "120000",
"石家庄": "130100",
"唐山": "130200",
"秦皇岛": "130300",
"邯郸": "130400",
"邢台": "130500",
"保定": "130600",
"承德": "130800",
"沧州": "130900",
"廊坊": "131000",
"衡水": "131100",
"张家口": "131200",
"太原": "140100",
"大同": "140200",
"阳泉": "140300",
"长治": "140400",
"晋城": "140500",
"朔州": "140600",
"晋中": "140700",
"运城": "140800",
"忻州": "140900",
"临汾": "141000",
"吕梁": "141100",
"呼和浩特": "150100",
"包头": "150200",
"乌海": "150300",
"赤峰": "150400",
"通辽": "150500",
"鄂尔多斯": "150600",
"呼伦贝尔": "150700",
"巴彦淖尔": "150800",
"乌兰察布": "150900",
"兴安盟": "152200",
"锡林郭勒盟": "152500",
"阿拉善盟": "152900",
"沈阳": "210100",
"大连": "210200",
"瓦房店": "210281",
"鞍山": "210300",
"抚顺": "210400",
"本溪": "210500",
"丹东": "210600",
"锦州": "210700",
"营口": "210800",
"阜新": "210900",
"辽阳": "211000",
"盘锦": "211100",
"铁岭": "211200",
"朝阳": "211300",
"葫芦岛": "211400",
"长春": "220100",
"吉林": "220200",
"四平": "220300",
"辽源": "220400",
"通化": "220500",
"白山": "220600",
"松原": "220700",
"白城": "220800",
"延边州": "222400",
"哈尔滨": "230100",
"齐齐哈尔": "230200",
"鸡西": "230300",
"鹤岗": "230400",
"双鸭山": "230500",
"大庆": "230600",
"伊春": "230700",
"佳木斯": "230800",
"七台河": "230900",
"牡丹江": "231000",
"黑河": "231100",
"绥化": "231200",
"大兴安岭地区": "232700",
"上海": "310000",
"南京": "320100",
"无锡": "320200",
"江阴": "320281",
"宜兴": "320282",
"徐州": "320300",
"常州": "320400",
"溧阳": "320481",
"金坛": "320482",
"苏州": "320500",
"常熟": "320581",
"张家港": "320582",
"昆山": "320583",
"吴江": "320584",
"太仓": "320585",
"南通": "320600",
"海门": "320684",
"连云港": "320700",
"淮安": "320800",
"盐城": "320900",
"扬州": "321000",
"镇江": "321100",
"句容": "321183",
"泰州": "321200",
"宿迁": "321300",
"杭州": "330100",
"富阳": "330183",
"临安": "330185",
"宁波": "330200",
"温州": "330300",
"嘉兴": "330400",
"湖州": "330500",
"诸暨": "330681",
"金华": "330700",
"义乌": "330782",
"衢州": "330800",
"舟山": "330900",
"台州": "331000",
"丽水": "331100",
"绍兴": "331300",
"合肥": "340100",
"芜湖": "340200",
"蚌埠": "340300",
"淮南": "340400",
"马鞍山": "340500",
"淮北": "340600",
"铜陵": "340700",
"安庆": "340800",
"黄山": "341000",
"滁州": "341100",
"阜阳": "341200",
"宿州": "341300",
"六安": "341500",
"亳州": "341600",
"池州": "341700",
"宣城": "341800",
"福州": "350100",
"厦门": "350200",
"莆田": "350300",
"三明": "350400",
"泉州": "350500",
"漳州": "350600",
"南平": "350700",
"龙岩": "350800",
"宁德": "350900",
"南昌": "360100",
"景德镇": "360200",
"萍乡": "360300",
"九江": "360400",
"新余": "360500",
"鹰潭": "360600",
"赣州": "360700",
"吉安": "360800",
"宜春": "360900",
"抚州": "361000",
"上饶": "361100",
"济南": "370100",
"章丘": "370181",
"青岛": "370200",
"胶州": "370281",
"即墨": "370282",
"平度": "370283",
"胶南": "370284",
"莱西": "370285",
"淄博": "370300",
"枣庄": "370400",
"东营": "370500",
"烟台": "370600",
"莱州": "370683",
"蓬莱": "370684",
"招远": "370685",
"潍坊": "370700",
"寿光": "370783",
"济宁": "370800",
"泰安": "370900",
"威海": "371000",
"文登": "371081",
"荣成": "371082",
"乳山": "371083",
"日照": "371100",
"莱芜": "371200",
"临沂": "371300",
"德州": "371400",
"聊城": "371500",
"滨州": "371600",
"菏泽": "371700",
"郑州": "410100",
"开封": "410200",
"洛阳": "410300",
"平顶山": "410400",
"安阳": "410500",
"鹤壁": "410600",
"新乡": "410700",
"焦作": "410800",
"濮阳": "410900",
"许昌": "411000",
"漯河": "411100",
"三门峡": "411200",
"南阳": "411300",
"商丘": "411400",
"信阳": "411500",
"周口": "411600",
"驻马店": "411700",
"武汉": "420100",
"黄石": "420200",
"十堰": "420300",
"宜昌": "420500",
"襄阳": "420600",
"鄂州": "420700",
"荆门": "420800",
"孝感": "420900",
"荆州": "421000",
"黄冈": "421100",
"咸宁": "421200",
"随州": "421300",
"恩施州": "422800",
"长沙": "430100",
"株洲": "430200",
"湘潭": "430300",
"衡阳": "430400",
"邵阳": "430500",
"岳阳": "430600",
"常德": "430700",
"张家界": "430800",
"益阳": "430900",
"郴州": "431000",
"永州": "431100",
"怀化": "431200",
"娄底": "431300",
"湘西州": "433100",
"广州": "440100",
"韶关": "440200",
"深圳": "440300",
"珠海": "440400",
"汕头": "440500",
"佛山": "440600",
"江门": "440700",
"湛江": "440800",
"茂名": "440900",
"肇庆": "441200",
"惠州": "441300",
"梅州": "441400",
"汕尾": "441500",
"河源": "441600",
"阳江": "441700",
"清远": "441800",
"东莞": "441900",
"中山": "442000",
"潮州": "445100",
"揭阳": "445200",
"云浮": "445300",
"南宁": "450100",
"柳州": "450200",
"桂林": "450300",
"梧州": "450400",
"北海": "450500",
"防城港": "450600",
"钦州": "450700",
"贵港": "450800",
"玉林": "450900",
"百色": "451000",
"贺州": "451100",
"河池": "451200",
"来宾": "451300",
"崇左": "451400",
"海口": "460100",
"三亚": "460200",
"重庆": "500000",
"成都": "510100",
"自贡": "510300",
"攀枝花": "510400",
"泸州": "510500",
"德阳": "510600",
"绵阳": "510700",
"广元": "510800",
"遂宁": "510900",
"内江": "511000",
"乐山": "511100",
"南充": "511300",
"眉山": "511400",
"宜宾": "511500",
"广安": "511600",
"达州": "511700",
"雅安": "511800",
"巴中": "511900",
"资阳": "512000",
"阿坝州": "513200",
"甘孜州": "513300",
"凉山州": "513400",
"贵阳": "520100",
"六盘水": "520200",
"遵义": "520300",
"安顺": "520400",
"铜仁地区": "522200",
"黔西南州": "522300",
"毕节": "522400",
"黔东南州": "522600",
"黔南州": "522700",
"昆明": "530100",
"曲靖": "530300",
"玉溪": "530400",
"保山": "530500",
"昭通": "530600",
"丽江": "530700",
"普洱": "530800",
"临沧": "530900",
"红河州": "532522",
"文山州": "532621",
"西双版纳州": "532801",
"大理州": "532901",
"德宏州": "533103",
"怒江州": "533300",
"迪庆州": "533421",
"拉萨": "540100",
"昌都": "542100",
"山南": "542200",
"日喀则": "542300",
"那曲地区": "542400",
"阿里地区": "542500",
"林芝": "542600",
"西安": "610100",
"铜川": "610200",
"宝鸡": "610300",
"咸阳": "610400",
"渭南": "610500",
"延安": "610600",
"汉中": "610700",
"榆林": "610800",
"安康": "610900",
"商洛": "611000",
"兰州": "620100",
"嘉峪关": "620200",
"金昌": "620300",
"白银": "620400",
"天水": "620500",
"武威": "620600",
"张掖": "620700",
"平凉": "620800",
"酒泉": "620900",
"庆阳": "621000",
"定西": "621100",
"陇南": "621200",
"临夏州": "622900",
"甘南州": "623000",
"西宁": "630100",
"海东地区": "632100",
"海北州": "632200",
"黄南州": "632300",
"海南州": "632500",
"果洛州": "632600",
"玉树州": "632700",
"海西州": "632800",
"银川": "640100",
"石嘴山": "640200",
"吴忠": "640300",
"固原": "640400",
"中卫": "640500",
"乌鲁木齐": "650100",
"克拉玛依": "650200",
"吐鲁番地区": "652100",
"哈密地区": "652200",
"昌吉州": "652300",
"博州": "652700",
"库尔勒": "652800",
"阿克苏地区": "652900",
"克州": "653000",
"喀什地区": "653100",
"和田地区": "653200",
"伊犁哈萨克州": "654000",
"塔城地区": "654200",
"阿勒泰地区": "654300",
"石河子": "659001",
"五家渠": "659004",
}
@@ -0,0 +1,139 @@
function Base64() {
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", this.encode = function(a) {
var c, d, e, f, g, h, i, b = "",
j = 0;
for (a = _utf8_encode(a); j < a.length;) c = a.charCodeAt(j++), d = a.charCodeAt(j++), e = a.charCodeAt(j++), f = c >> 2, g = (3 & c) << 4 | d >> 4, h = (15 & d) << 2 | e >> 6, i = 63 & e, isNaN(d) ? h = i = 64 : isNaN(e) && (i = 64), b = b + _keyStr.charAt(f) + _keyStr.charAt(g) + _keyStr.charAt(h) + _keyStr.charAt(i);
return b
}, this.decode = function(a) {
var c, d, e, f, g, h, i, b = "",
j = 0;
for (a = a.replace(/[^A-Za-z0-9\+\/\=]/g, ""); j < a.length;) f = _keyStr.indexOf(a.charAt(j++)), g = _keyStr.indexOf(a.charAt(j++)), h = _keyStr.indexOf(a.charAt(j++)), i = _keyStr.indexOf(a.charAt(j++)), c = f << 2 | g >> 4, d = (15 & g) << 4 | h >> 2, e = (3 & h) << 6 | i, b += String.fromCharCode(c), 64 != h && (b += String.fromCharCode(d)), 64 != i && (b += String.fromCharCode(e));
return b = _utf8_decode(b)
}, _utf8_encode = function(a) {
var b, c, d;
for (a = a.replace(/\r\n/g, "\n"), b = "", c = 0; c < a.length; c++) d = a.charCodeAt(c), 128 > d ? b += String.fromCharCode(d) : d > 127 && 2048 > d ? (b += String.fromCharCode(192 | d >> 6), b += String.fromCharCode(128 | 63 & d)) : (b += String.fromCharCode(224 | d >> 12), b += String.fromCharCode(128 | 63 & d >> 6), b += String.fromCharCode(128 | 63 & d));
return b
}, _utf8_decode = function(a) {
for (var b = "", c = 0, d = c1 = c2 = 0; c < a.length;) d = a.charCodeAt(c), 128 > d ? (b += String.fromCharCode(d), c++) : d > 191 && 224 > d ? (c2 = a.charCodeAt(c + 1), b += String.fromCharCode((31 & d) << 6 | 63 & c2), c += 2) : (c2 = a.charCodeAt(c + 1), c3 = a.charCodeAt(c + 2), b += String.fromCharCode((15 & d) << 12 | (63 & c2) << 6 | 63 & c3), c += 3);
return b
}
}
function hex_md5(a) {
return binl2hex(core_md5(str2binl(a), a.length * chrsz))
}
function b64_md5(a) {
return binl2b64(core_md5(str2binl(a), a.length * chrsz))
}
function str_md5(a) {
return binl2str(core_md5(str2binl(a), a.length * chrsz))
}
function hex_hmac_md5(a, b) {
return binl2hex(core_hmac_md5(a, b))
}
function b64_hmac_md5(a, b) {
return binl2b64(core_hmac_md5(a, b))
}
function str_hmac_md5(a, b) {
return binl2str(core_hmac_md5(a, b))
}
function md5_vm_test() {
return "900150983cd24fb0d6963f7d28e17f72" == hex_md5("abc")
}
function core_md5(a, b) {
var c, d, e, f, g, h, i, j, k;
for (a[b >> 5] |= 128 << b % 32, a[(b + 64 >>> 9 << 4) + 14] = b, c = 1732584193, d = -271733879, e = -1732584194, f = 271733878, g = 0; g < a.length; g += 16) h = c, i = d, j = e, k = f, c = md5_ff(c, d, e, f, a[g + 0], 7, -680876936), f = md5_ff(f, c, d, e, a[g + 1], 12, -389564586), e = md5_ff(e, f, c, d, a[g + 2], 17, 606105819), d = md5_ff(d, e, f, c, a[g + 3], 22, -1044525330), c = md5_ff(c, d, e, f, a[g + 4], 7, -176418897), f = md5_ff(f, c, d, e, a[g + 5], 12, 1200080426), e = md5_ff(e, f, c, d, a[g + 6], 17, -1473231341), d = md5_ff(d, e, f, c, a[g + 7], 22, -45705983), c = md5_ff(c, d, e, f, a[g + 8], 7, 1770035416), f = md5_ff(f, c, d, e, a[g + 9], 12, -1958414417), e = md5_ff(e, f, c, d, a[g + 10], 17, -42063), d = md5_ff(d, e, f, c, a[g + 11], 22, -1990404162), c = md5_ff(c, d, e, f, a[g + 12], 7, 1804603682), f = md5_ff(f, c, d, e, a[g + 13], 12, -40341101), e = md5_ff(e, f, c, d, a[g + 14], 17, -1502002290), d = md5_ff(d, e, f, c, a[g + 15], 22, 1236535329), c = md5_gg(c, d, e, f, a[g + 1], 5, -165796510), f = md5_gg(f, c, d, e, a[g + 6], 9, -1069501632), e = md5_gg(e, f, c, d, a[g + 11], 14, 643717713), d = md5_gg(d, e, f, c, a[g + 0], 20, -373897302), c = md5_gg(c, d, e, f, a[g + 5], 5, -701558691), f = md5_gg(f, c, d, e, a[g + 10], 9, 38016083), e = md5_gg(e, f, c, d, a[g + 15], 14, -660478335), d = md5_gg(d, e, f, c, a[g + 4], 20, -405537848), c = md5_gg(c, d, e, f, a[g + 9], 5, 568446438), f = md5_gg(f, c, d, e, a[g + 14], 9, -1019803690), e = md5_gg(e, f, c, d, a[g + 3], 14, -187363961), d = md5_gg(d, e, f, c, a[g + 8], 20, 1163531501), c = md5_gg(c, d, e, f, a[g + 13], 5, -1444681467), f = md5_gg(f, c, d, e, a[g + 2], 9, -51403784), e = md5_gg(e, f, c, d, a[g + 7], 14, 1735328473), d = md5_gg(d, e, f, c, a[g + 12], 20, -1926607734), c = md5_hh(c, d, e, f, a[g + 5], 4, -378558), f = md5_hh(f, c, d, e, a[g + 8], 11, -2022574463), e = md5_hh(e, f, c, d, a[g + 11], 16, 1839030562), d = md5_hh(d, e, f, c, a[g + 14], 23, -35309556), c = md5_hh(c, d, e, f, a[g + 1], 4, -1530992060), f = md5_hh(f, c, d, e, a[g + 4], 11, 1272893353), e = md5_hh(e, f, c, d, a[g + 7], 16, -155497632), d = md5_hh(d, e, f, c, a[g + 10], 23, -1094730640), c = md5_hh(c, d, e, f, a[g + 13], 4, 681279174), f = md5_hh(f, c, d, e, a[g + 0], 11, -358537222), e = md5_hh(e, f, c, d, a[g + 3], 16, -722521979), d = md5_hh(d, e, f, c, a[g + 6], 23, 76029189), c = md5_hh(c, d, e, f, a[g + 9], 4, -640364487), f = md5_hh(f, c, d, e, a[g + 12], 11, -421815835), e = md5_hh(e, f, c, d, a[g + 15], 16, 530742520), d = md5_hh(d, e, f, c, a[g + 2], 23, -995338651), c = md5_ii(c, d, e, f, a[g + 0], 6, -198630844), f = md5_ii(f, c, d, e, a[g + 7], 10, 1126891415), e = md5_ii(e, f, c, d, a[g + 14], 15, -1416354905), d = md5_ii(d, e, f, c, a[g + 5], 21, -57434055), c = md5_ii(c, d, e, f, a[g + 12], 6, 1700485571), f = md5_ii(f, c, d, e, a[g + 3], 10, -1894986606), e = md5_ii(e, f, c, d, a[g + 10], 15, -1051523), d = md5_ii(d, e, f, c, a[g + 1], 21, -2054922799), c = md5_ii(c, d, e, f, a[g + 8], 6, 1873313359), f = md5_ii(f, c, d, e, a[g + 15], 10, -30611744), e = md5_ii(e, f, c, d, a[g + 6], 15, -1560198380), d = md5_ii(d, e, f, c, a[g + 13], 21, 1309151649), c = md5_ii(c, d, e, f, a[g + 4], 6, -145523070), f = md5_ii(f, c, d, e, a[g + 11], 10, -1120210379), e = md5_ii(e, f, c, d, a[g + 2], 15, 718787259), d = md5_ii(d, e, f, c, a[g + 9], 21, -343485551), c = safe_add(c, h), d = safe_add(d, i), e = safe_add(e, j), f = safe_add(f, k);
return Array(c, d, e, f)
}
function md5_cmn(a, b, c, d, e, f) {
return safe_add(bit_rol(safe_add(safe_add(b, a), safe_add(d, f)), e), c)
}
function md5_ff(a, b, c, d, e, f, g) {
return md5_cmn(b & c | ~b & d, a, b, e, f, g)
}
function md5_gg(a, b, c, d, e, f, g) {
return md5_cmn(b & d | c & ~d, a, b, e, f, g)
}
function md5_hh(a, b, c, d, e, f, g) {
return md5_cmn(b ^ c ^ d, a, b, e, f, g)
}
function md5_ii(a, b, c, d, e, f, g) {
return md5_cmn(c ^ (b | ~d), a, b, e, f, g)
}
function core_hmac_md5(a, b) {
var d, e, f, g, c = str2binl(a);
for (c.length > 16 && (c = core_md5(c, a.length * chrsz)), d = Array(16), e = Array(16), f = 0; 16 > f; f++) d[f] = 909522486 ^ c[f], e[f] = 1549556828 ^ c[f];
return g = core_md5(d.concat(str2binl(b)), 512 + b.length * chrsz), core_md5(e.concat(g), 640)
}
function safe_add(a, b) {
var c = (65535 & a) + (65535 & b),
d = (a >> 16) + (b >> 16) + (c >> 16);
return d << 16 | 65535 & c
}
function bit_rol(a, b) {
return a << b | a >>> 32 - b
}
function str2binl(a) {
var d, b = Array(),
c = (1 << chrsz) - 1;
for (d = 0; d < a.length * chrsz; d += chrsz) b[d >> 5] |= (a.charCodeAt(d / chrsz) & c) << d % 32;
return b
}
function binl2str(a) {
var d, b = "",
c = (1 << chrsz) - 1;
for (d = 0; d < 32 * a.length; d += chrsz) b += String.fromCharCode(a[d >> 5] >>> d % 32 & c);
return b
}
function binl2hex(a) {
var d, b = hexcase ? "0123456789ABCDEF" : "0123456789abcdef",
c = "";
for (d = 0; d < 4 * a.length; d++) c += b.charAt(15 & a[d >> 2] >> 8 * (d % 4) + 4) + b.charAt(15 & a[d >> 2] >> 8 * (d % 4));
return c
}
function binl2b64(a) {
var d, e, f, b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
c = "";
for (d = 0; d < 4 * a.length; d += 3)
for (e = (255 & a[d >> 2] >> 8 * (d % 4)) << 16 | (255 & a[d + 1 >> 2] >> 8 * ((d + 1) % 4)) << 8 | 255 & a[d + 2 >> 2] >> 8 * ((d + 2) % 4), f = 0; 4 > f; f++) c += 8 * d + 6 * f > 32 * a.length ? b64pad : b.charAt(63 & e >> 6 * (3 - f));
return c
}
function encode_param(a) {
var b = new Base64;
return b.encode(a)
}
function encode_secret() {
var b, a = appId;
for (b = 0; b < arguments.length; b++) a += arguments[b];
return a = a.replace(/\s/g, ""), hex_md5(a)
}
function decode_result(a) {
var b = new Base64;
return b.decode(b.decode(b.decode(a)))
}
var hexcase = 0,
b64pad = "",
chrsz = 8,
appId = "a01901d3caba1f362d69474674ce477f";
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,112 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
Date: 2024/4/29 16:00
Desc: 日出和日落数据
https://www.timeanddate.com
"""
from io import StringIO
import pandas as pd
import requests
def sunrise_city_list() -> list:
"""
查询日出与日落数据的城市列表
https://www.timeanddate.com/astronomy/china
:return: 所有可以获取的数据的城市列表
:rtype: list
"""
url = "https://www.timeanddate.com/astronomy/china"
r = requests.get(url)
city_list = []
china_city_one_df = pd.read_html(StringIO(r.text))[1]
china_city_two_df = pd.read_html(StringIO(r.text))[2]
city_list.extend([item.lower() for item in china_city_one_df.iloc[:, 0].tolist()])
city_list.extend([item.lower() for item in china_city_one_df.iloc[:, 3].tolist()])
city_list.extend([item.lower() for item in china_city_one_df.iloc[:, 6].tolist()])
city_list.extend([item.lower() for item in china_city_two_df.iloc[:, 0].tolist()])
city_list.extend([item.lower() for item in china_city_two_df.iloc[:, 1].tolist()])
city_list.extend([item.lower() for item in china_city_two_df.iloc[:, 2].tolist()])
city_list.extend([item.lower() for item in china_city_two_df.iloc[:, 3].tolist()])
city_list.extend(
[item.lower() for item in china_city_two_df.iloc[:, 4].dropna().tolist()]
)
return city_list
def sunrise_daily(date: str = "20240428", city: str = "beijing") -> pd.DataFrame:
"""
每日日出日落数据
https://www.timeanddate.com/astronomy/china/shaoxing
:param date: 需要查询的日期, e.g., “20200428”
:type date: str
:param city: 需要查询的城市; 注意输入的格式, e.g., "北京", "上海"
:type city: str
:return: 返回指定日期指定地区的日出日落数据
:rtype: pandas.DataFrame
"""
import urllib3
urllib3.disable_warnings()
if city in sunrise_city_list():
year = date[:4]
month = date[4:6]
url = f"https://www.timeanddate.com/sun/china/{city}?month={month}&year={year}"
r = requests.get(url, verify=False)
table = pd.read_html(StringIO(r.text), header=2)[1]
month_df = table.iloc[:-1,]
day_df = month_df[
month_df.iloc[:, 0].astype(str).str.zfill(2) == date[6:]
].copy()
day_df.index = pd.to_datetime([date] * len(day_df), format="%Y%m%d")
day_df.reset_index(inplace=True)
day_df.rename(columns={"index": "date"}, inplace=True)
day_df["date"] = pd.to_datetime(day_df["date"]).dt.date
return day_df
else:
raise "请输入正确的城市名称"
def sunrise_monthly(date: str = "20240428", city: str = "beijing") -> pd.DataFrame:
"""
每个指定 date 所在月份的每日日出日落数据, 如果当前月份未到月底, 则以预测值填充
https://www.timeanddate.com/astronomy/china/shaoxing
:param date: 需要查询的日期, 这里用来指定 date 所在的月份; e.g., “20200428”
:type date: str
:param city: 需要查询的城市; 注意输入的格式, e.g., "北京", "上海"
:type city: str
:return: 指定 date 所在月份的每日日出日落数据
:rtype: pandas.DataFrame
"""
import urllib3
urllib3.disable_warnings()
if city in sunrise_city_list():
year = date[:4]
month = date[4:6]
url = f"https://www.timeanddate.com/sun/china/{city}?month={month}&year={year}"
r = requests.get(url)
table = pd.read_html(StringIO(r.text), header=2)[1]
month_df = table.iloc[:-1,].copy()
month_df.index = [date[:-2]] * len(month_df)
month_df.reset_index(inplace=True)
month_df.rename(
columns={
"index": "date",
},
inplace=True,
)
return month_df
else:
raise "请输入正确的城市名称"
if __name__ == "__main__":
sunrise_daily_df = sunrise_daily(date="20240428", city="beijing")
print(sunrise_daily_df)
sunrise_monthly_df = sunrise_monthly(date="20240428", city="beijing")
print(sunrise_monthly_df)