eia_steo.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. package services
  2. import (
  3. "context"
  4. "encoding/json"
  5. "eta/eta_crawler/models"
  6. "eta/eta_crawler/utils"
  7. "fmt"
  8. "reflect"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "github.com/rdlucklib/rdluck_tools/http"
  13. )
  14. var noCrawlerName = map[string]struct{}{"Real Gross Domestic Product": {}, "Nominal U.S. Dollar Exchange Rate": {}}
  15. func SyncEiaSteoData(cont context.Context) (err error) {
  16. // err = syncEiaSteoData()
  17. eiaSteoUrls := map[string]string{
  18. "International Petroleum and Other Liquids Production、Consumption、Inventories": `https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=6&f=M&s=0&id=&linechart=PAPR_OECD~PAPR_NONOPEC&maptype=0&ctype=linechart&map=&method=getData`,
  19. "Non-OPEC Petroleum and Other Liquids Production": `https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=29&f=M&s=0&start=201901&end=202512&id=&ctype=linechart&maptype=0&method=getData`,
  20. "Total Liquid Fuels Production": `https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=7&f=M&s=0&start=201901&end=202512&maptype=0&ctype=linechart&id=&method=getData`,
  21. "Total Crude Oil Production": `https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=30&f=M&s=0&start=201901&end=202512&id=&ctype=linechart&maptype=0&method=getData`,
  22. "World Petroleum and Other Liquid Fuels Consumption": `https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=31&f=M&s=0&start=201901&end=202512&maptype=0&ctype=linechart&id=&method=getData`,
  23. "U.S. Petroleum and Other Liquids Supply、Consumption、Inventories": `https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=9&f=M&s=0&start=201901&end=202512&maptype=0&ctype=linechart&method=getData`,
  24. "Drilling Productivity Metrics": `https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=32&f=M&s=0&start=201901&end=202512&ctype=linechart&maptype=0&method=getData`,
  25. "Crude Oil and Natural Gas Production from Shale and Tight Formations": `https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=33&f=M&s=0&start=201901&end=202512&maptype=0&ctype=linechart&method=getData`,
  26. }
  27. var eiaIndexName = []string{
  28. "International Petroleum and Other Liquids Production、Consumption、Inventories",
  29. "Non-OPEC Petroleum and Other Liquids Production",
  30. "Total Liquid Fuels Production",
  31. "Total Crude Oil Production",
  32. "World Petroleum and Other Liquid Fuels Consumption",
  33. "U.S. Petroleum and Other Liquids Supply、Consumption、Inventories",
  34. "Drilling Productivity Metrics",
  35. "Crude Oil and Natural Gas Production from Shale and Tight Formations",
  36. }
  37. for _, name := range eiaIndexName {
  38. url := eiaSteoUrls[name]
  39. err = syncEiaSteoDataV2(name, url)
  40. if err != nil {
  41. fmt.Println("同步失败", err)
  42. return
  43. }
  44. }
  45. return
  46. }
  47. func syncEiaSteoData(eiaSteoUrl string) (err error) {
  48. // 获取数据
  49. //官网地址:https://www.eia.gov/outlooks/steo/data/browser/#/?v=6&f=M&s=0&start=201701&end=202312&linechart=~T3_STCHANGE_WORLD&maptype=0&ctype=linechart&map=
  50. // 这是获取数据的链接(月度的)
  51. // eiaSteoUrl := "https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=6&f=M&s=0&id=&linechart=PAPR_OECD~PAPR_NONOPEC&maptype=0&ctype=linechart&map=&method=getData"
  52. eiaSteoData, err := queryData(eiaSteoUrl)
  53. if err != nil {
  54. fmt.Println("读取失败", err)
  55. return
  56. }
  57. // 获取分类列表
  58. classifyList, err := models.GetBaseFromEiaSteoClassifyAll()
  59. if err != nil {
  60. fmt.Println("获取分类失败:", err)
  61. return
  62. }
  63. classifyMap := make(map[string]*models.BaseFromEiaSteoClassify)
  64. for _, v := range classifyList {
  65. classifyMap[v.ClassifyNameOriginal] = v
  66. }
  67. // 获取指标列表
  68. indexList, err := models.GetBaseFromEiaSteoIndexAll()
  69. if err != nil {
  70. fmt.Println("获取分类失败:", err)
  71. return
  72. }
  73. indexMap := make(map[string]*models.BaseFromEiaSteoIndex)
  74. for _, v := range indexList {
  75. indexMap[v.IndexCode] = v
  76. }
  77. var nowClassify *models.BaseFromEiaSteoClassify
  78. for _, v := range eiaSteoData.VIEWSDATA.ROWS {
  79. // 如果没有数据,那么就返回
  80. if v.HASDATA != 1 {
  81. continue
  82. }
  83. if v.LEVEL == 1 {
  84. tmpNowClassify, ok := classifyMap[v.CHARTNAME]
  85. if !ok {
  86. nowClassify = &models.BaseFromEiaSteoClassify{
  87. BaseFromEiaSteoClassifyId: 0,
  88. ClassifyName: v.CHARTNAME,
  89. ClassifyNameOriginal: v.CHARTNAME,
  90. ModifyTime: time.Now(),
  91. CreateTime: time.Now(),
  92. }
  93. // 新增指标
  94. err = nowClassify.AddBaseFromEiaSteoClassify()
  95. if err != nil {
  96. return
  97. }
  98. classifyMap[v.CHARTNAME] = nowClassify
  99. } else {
  100. nowClassify = tmpNowClassify
  101. }
  102. //continue
  103. }
  104. // 如果系列名称为空的话,那么也返回
  105. if v.SERIESID == `` {
  106. continue
  107. }
  108. if v.LEVEL > 1 && v.SERIESID == `` {
  109. continue
  110. }
  111. var eiaSteoIndex *models.BaseFromEiaSteoIndex
  112. eiaSteoIndex, ok := indexMap[v.SERIESID]
  113. if !ok {
  114. eiaSteoIndex = &models.BaseFromEiaSteoIndex{
  115. //BaseFromEiaSteoIndexId: 0,
  116. BaseFromEiaSteoClassifyId: nowClassify.BaseFromEiaSteoClassifyId,
  117. IndexCode: v.SERIESID,
  118. IndexName: v.CHARTNAME, // 不做中文名称的转换
  119. IndexNameOriginal: v.CHARTNAME,
  120. Frequency: "月度",
  121. Level: v.LEVEL,
  122. Unit: v.UNITS,
  123. Super: v.SUPER,
  124. Precision: v.PRECISION,
  125. LastHistorical: strconv.Itoa(v.LASTHISTORICAL),
  126. Description: v.DESCRIPTION,
  127. IsMappable: v.ISMAPPABLE,
  128. StartDate: time.Now(),
  129. EndDate: time.Now(),
  130. ModifyTime: time.Now(),
  131. CreateTime: time.Now(),
  132. }
  133. // 新增指标
  134. err = eiaSteoIndex.Add()
  135. if err != nil {
  136. return
  137. }
  138. indexMap[v.SERIESID] = eiaSteoIndex
  139. } else {
  140. updateCol := make([]string, 0)
  141. if eiaSteoIndex.BaseFromEiaSteoClassifyId != nowClassify.BaseFromEiaSteoClassifyId {
  142. eiaSteoIndex.BaseFromEiaSteoClassifyId = nowClassify.BaseFromEiaSteoClassifyId
  143. updateCol = append(updateCol, "BaseFromEiaSteoClassifyId")
  144. }
  145. if eiaSteoIndex.IndexName != v.CHARTNAME {
  146. eiaSteoIndex.IndexName = v.CHARTNAME
  147. updateCol = append(updateCol, "IndexName")
  148. }
  149. if eiaSteoIndex.IndexNameOriginal != v.CHARTNAME {
  150. eiaSteoIndex.IndexNameOriginal = v.CHARTNAME
  151. updateCol = append(updateCol, "IndexNameOriginal")
  152. }
  153. // Frequency: "月度",
  154. // Level: v.LEVEL,
  155. // Unit: v.UNITS,
  156. // Super: v.SUPER,
  157. // Precision: v.PRECISION,
  158. // LastHistorical: strconv.Itoa(v.LASTHISTORICAL),
  159. // Description: v.DESCRIPTION,
  160. // IsMappable: v.ISMAPPABLE,
  161. if len(updateCol) > 0 {
  162. eiaSteoIndex.Update(updateCol)
  163. }
  164. }
  165. //校验数据类型对不对
  166. valType := reflect.TypeOf(v.DATA)
  167. //if valType.String() == "map[string]interface{}"{
  168. // fmt.Println("匹配上了")
  169. //}
  170. switch valType.String() {
  171. case "[]interface {}": // 没有数据
  172. case "map[string]interface {}": // 有数据
  173. data := v.DATA.(map[string]interface{})
  174. err = models.HandleEiaSteoData(data, eiaSteoIndex)
  175. if err != nil {
  176. return
  177. }
  178. //if eiaSteoIndex.IndexCode == "PATC_OECD_EUROPE" {
  179. // os.Exit(0)
  180. //}
  181. }
  182. fmt.Println(v.CHARTNAME, v.SERIESID, "==", v.PRECISION, "===========")
  183. }
  184. return
  185. }
  186. func syncEiaSteoDataV2(eiaSteoName, eiaSteoUrl string) (err error) {
  187. defer func() {
  188. if r := recover(); r != nil {
  189. fmt.Println("异常:", r)
  190. fmt.Println("异常的eiaSteo名字", eiaSteoName)
  191. }
  192. }()
  193. // 获取数据
  194. //官网地址:https://www.eia.gov/outlooks/steo/data/browser/#/?v=6&f=M&s=0&start=201701&end=202312&linechart=~T3_STCHANGE_WORLD&maptype=0&ctype=linechart&map=
  195. // 这是获取数据的链接(月度的)
  196. // eiaSteoUrl := "https://www.eia.gov/outlooks/steo/data/browser/data/index.php?v=6&f=M&s=0&id=&linechart=PAPR_OECD~PAPR_NONOPEC&maptype=0&ctype=linechart&map=&method=getData"
  197. eiaSteoData, err := queryData(eiaSteoUrl)
  198. if err != nil {
  199. fmt.Println("读取失败", err)
  200. return
  201. }
  202. parentClassify, err := models.GetBaseFromEiaSteoClassifyByName(eiaSteoName)
  203. if err != nil && err.Error() != utils.ErrNoRow() {
  204. fmt.Println("添加eiaSteo指标异常, err: ", err.Error())
  205. return
  206. }
  207. if parentClassify == nil {
  208. tmpClassify := &models.BaseFromEiaSteoClassify{
  209. ClassifyName: eiaSteoName,
  210. ClassifyNameOriginal: eiaSteoName,
  211. Level: 1,
  212. ModifyTime: time.Now(),
  213. CreateTime: time.Now(),
  214. }
  215. // 新增指标分类
  216. err = tmpClassify.AddBaseFromEiaSteoClassify()
  217. if err != nil {
  218. return
  219. }
  220. parentClassify = tmpClassify
  221. }
  222. // 获取分类列表
  223. classifyList, err := models.GetChildBaseFromEiaSteoClassifyById(parentClassify.BaseFromEiaSteoClassifyId)
  224. if err != nil {
  225. fmt.Println("获取分类失败:", err)
  226. return
  227. }
  228. classifyMap := make(map[string]*models.BaseFromEiaSteoClassify)
  229. for _, v := range classifyList {
  230. classifyMap[v.ClassifyNameOriginal] = v
  231. }
  232. // 获取指标列表
  233. indexList, err := models.GetBaseFromEiaSteoIndexAll()
  234. if err != nil {
  235. fmt.Println("获取分类失败:", err)
  236. return
  237. }
  238. indexMap := make(map[string]*models.BaseFromEiaSteoIndex)
  239. for _, v := range indexList {
  240. indexMap[v.IndexCode] = v
  241. }
  242. var hasClassify *models.BaseFromEiaSteoClassify
  243. var noDataClassify *models.BaseFromEiaSteoClassify
  244. for i, v := range eiaSteoData.VIEWSDATA.ROWS {
  245. // 如果没有数据,那么就返回
  246. if v.HASDATA != 1 && strings.TrimSpace(v.CHARTNAME) == "" {
  247. curRow := eiaSteoData.VIEWSDATA.ROWS
  248. length := len(eiaSteoData.VIEWSDATA.ROWS)
  249. if length > i+1 {
  250. if noDataClassify != nil {
  251. if curRow[i+1].LEVEL == 1 && strings.TrimSpace(curRow[i+1].CHARTNAME) != "" && curRow[i+1].HASDATA != 1 {
  252. noDataClassify = nil
  253. }
  254. }
  255. if hasClassify != nil {
  256. if curRow[i+1].LEVEL == 1 && strings.TrimSpace(curRow[i+1].CHARTNAME) != "" && curRow[i+1].HASDATA == 1 {
  257. hasClassify = nil
  258. }
  259. }
  260. }
  261. continue
  262. }
  263. if noDataClassify == nil && hasClassify == nil && strings.TrimSpace(v.CHARTNAME) != "" && v.LEVEL == 1 {
  264. classify, ok := classifyMap[v.CHARTNAME]
  265. if !ok {
  266. tmpClassify := &models.BaseFromEiaSteoClassify{
  267. BaseFromEiaSteoClassifyId: 0,
  268. ClassifyName: v.CHARTNAME,
  269. ClassifyNameOriginal: v.CHARTNAME,
  270. ParentId: parentClassify.BaseFromEiaSteoClassifyId,
  271. Level: 2,
  272. ModifyTime: time.Now(),
  273. CreateTime: time.Now(),
  274. }
  275. classifyMap[v.CHARTNAME] = tmpClassify
  276. if v.HASDATA == 1 {
  277. hasClassify = tmpClassify
  278. } else {
  279. noDataClassify = tmpClassify
  280. }
  281. if _, ok := noCrawlerName[tmpClassify.ClassifyName]; ok {
  282. continue
  283. }
  284. // 新增指标分类
  285. err = tmpClassify.AddBaseFromEiaSteoClassify()
  286. if err != nil {
  287. return
  288. }
  289. } else {
  290. if v.HASDATA == 1 {
  291. hasClassify = classify
  292. } else {
  293. noDataClassify = classify
  294. }
  295. }
  296. }
  297. // 如果系列名称为空的话,那么也返回
  298. if v.SERIESID == `` {
  299. continue
  300. }
  301. if v.HASDATA != 1 {
  302. continue
  303. }
  304. var curClassify *models.BaseFromEiaSteoClassify
  305. if noDataClassify != nil {
  306. curClassify = noDataClassify
  307. } else {
  308. curClassify = hasClassify
  309. }
  310. if _, ok := noCrawlerName[curClassify.ClassifyName]; ok {
  311. continue
  312. }
  313. eiaSteoIndex, ok := indexMap[v.SERIESID]
  314. if !ok {
  315. eiaSteoIndex = &models.BaseFromEiaSteoIndex{
  316. //BaseFromEiaSteoIndexId: 0,
  317. BaseFromEiaSteoClassifyId: curClassify.BaseFromEiaSteoClassifyId,
  318. IndexCode: v.SERIESID,
  319. IndexName: v.CHARTNAME, // 不做中文名称的转换
  320. IndexNameOriginal: v.CHARTNAME,
  321. Frequency: "月度",
  322. Level: v.LEVEL,
  323. Unit: v.UNITS,
  324. Super: v.SUPER,
  325. Precision: v.PRECISION,
  326. LastHistorical: strconv.Itoa(v.LASTHISTORICAL),
  327. Description: v.DESCRIPTION,
  328. IsMappable: v.ISMAPPABLE,
  329. StartDate: time.Now(),
  330. EndDate: time.Now(),
  331. ModifyTime: time.Now(),
  332. CreateTime: time.Now(),
  333. }
  334. // 新增指标
  335. err = eiaSteoIndex.Add()
  336. if err != nil {
  337. return
  338. }
  339. indexMap[v.SERIESID] = eiaSteoIndex
  340. } else {
  341. updateCol := make([]string, 0)
  342. if eiaSteoIndex.BaseFromEiaSteoClassifyId != curClassify.BaseFromEiaSteoClassifyId {
  343. eiaSteoIndex.BaseFromEiaSteoClassifyId = curClassify.BaseFromEiaSteoClassifyId
  344. updateCol = append(updateCol, "BaseFromEiaSteoClassifyId")
  345. }
  346. if eiaSteoIndex.IndexName != v.CHARTNAME {
  347. eiaSteoIndex.IndexName = v.CHARTNAME
  348. updateCol = append(updateCol, "IndexName")
  349. }
  350. if eiaSteoIndex.IndexNameOriginal != v.CHARTNAME {
  351. eiaSteoIndex.IndexNameOriginal = v.CHARTNAME
  352. updateCol = append(updateCol, "IndexNameOriginal")
  353. }
  354. if len(updateCol) > 0 {
  355. eiaSteoIndex.Update(updateCol)
  356. }
  357. }
  358. //校验数据类型对不对
  359. valType := reflect.TypeOf(v.DATA)
  360. switch valType.String() {
  361. case "[]interface {}": // 没有数据
  362. case "map[string]interface {}": // 有数据
  363. data := v.DATA.(map[string]interface{})
  364. err = models.HandleEiaSteoData(data, eiaSteoIndex)
  365. if err != nil {
  366. return
  367. }
  368. }
  369. fmt.Println(v.CHARTNAME, v.SERIESID, "==", v.PRECISION, "===========")
  370. }
  371. return
  372. }
  373. // queryData 接口请求网站数据
  374. func queryData(eiaSteoUrl string) (eiaSteoData EiaSteoData, err error) {
  375. body, err := http.Get(eiaSteoUrl)
  376. if err != nil {
  377. fmt.Println("err:", err)
  378. }
  379. //utils.FileLog.Info("eia steo 报告数据:" + string(body))
  380. err = json.Unmarshal(body, &eiaSteoData)
  381. return
  382. }
  383. type EiaSteoData struct {
  384. SERIESDATA struct {
  385. DATACOLUMNS []interface{} `json:"DATACOLUMNS"`
  386. ROWS []interface{} `json:"ROWS"`
  387. } `json:"SERIESDATA"`
  388. VIEWSDATA struct {
  389. DATACOLUMNS []int `json:"DATACOLUMNS"`
  390. ROWS []struct {
  391. CHARTNAME string `json:"CHART_NAME"`
  392. DESCRIPTION string `json:"DESCRIPTION"`
  393. HASDATA int `json:"HAS_DATA"`
  394. PRECISION int `json:"PRECISION"`
  395. SERIESID string `json:"SERIES_ID"`
  396. UNITS string `json:"UNITS"`
  397. SUPER string `json:"SUPER"`
  398. LEVEL int `json:"LEVEL"`
  399. //DATA map[int]float64 `json:"DATA"`
  400. DATA interface{} `json:"DATA"`
  401. LASTHISTORICAL int `json:"LAST_HISTORICAL,omitempty"`
  402. ISMAPPABLE int `json:"IS_MAPPABLE,omitempty"`
  403. } `json:"ROWS"`
  404. } `json:"VIEWSDATA"`
  405. }
  406. // EiaSteoNameMap 中英文互换
  407. var EiaSteoNameMap = map[string]string{
  408. "PAPR_OECD": "OECD石油产量",
  409. "PAPR_US": "美国五十州石油产量",
  410. "PAPR_CA": "加拿大石油产量",
  411. "PAPR_MX": "墨西哥石油产量",
  412. "PAPR_OTHEROECD": "其他OECD石油产量",
  413. "PAPR_NONOECD": "非OECD石油产量",
  414. "PAPR_OPEC": "OPEC13国石油产量",
  415. "COPR_OPEC": "OPEC13国原油产量",
  416. "OPEC_NC": "OPEC其他石油液体产量",
  417. "PAPR_FSU": "欧亚石油产量",
  418. "PAPR_CH": "中国石油产量",
  419. "PAPR_OTHER_NONOECD": "其他非OECD石油产量",
  420. "PAPR_WORLD": "全球石油产量",
  421. "PAPR_NONOPEC": "非OPEC石油产量",
  422. "PATC_OECD": "OECD石油需求",
  423. "PATC_US": "美国五十州石油需求",
  424. "PATC_UST": "美国领地石油需求",
  425. "PATC_CA": "加拿大石油需求",
  426. "PATC_OECD_EUROPE": "欧洲石油需求",
  427. "PATC_JA": "日本石油需求",
  428. "PATC_OTHER_OECD": "其他OECD石油需求",
  429. "PATC_NON_OECD": "非OECD石油需求",
  430. "PATC_FSU": "欧亚石油需求",
  431. "PATC_NONOECD_EUROPE": "非OECD欧洲石油需求",
  432. "PATC_CH": "中国石油需求",
  433. "PATC_OTHER_ASIA": "其他亚洲石油需求",
  434. "PATC_OTHER_NONOECD": "其他非OECD石油需求",
  435. "PATC_WORLD": "全球石油需求",
  436. "T3_STCHANGE_US": "美国50州石油库存去化(需求-供给)",
  437. "T3_STCHANGE_OOECD": "其他OECD石油库存去化(需求-供给)",
  438. "T3_STCHANGE_NOECD": "非OECD石油库存去化(需求-供给)",
  439. "T3_STCHANGE_WORLD": "全球石油库存去化(需求-供给)",
  440. "PASC_US": "美国商业库存",
  441. "PASC_OECD_T3": "OECD商业库存",
  442. }