|
@@ -4,6 +4,9 @@ import (
|
|
|
"context"
|
|
|
"eta/eta_data_analysis/utils"
|
|
|
"fmt"
|
|
|
+ "github.com/chromedp/cdproto/cdp"
|
|
|
+ "github.com/xuri/excelize/v2"
|
|
|
+ "io"
|
|
|
"log"
|
|
|
"os"
|
|
|
"path/filepath"
|
|
@@ -13,10 +16,10 @@ import (
|
|
|
"github.com/chromedp/chromedp"
|
|
|
)
|
|
|
|
|
|
-const downloadDir = "./downloads"
|
|
|
-
|
|
|
// 定义选择器
|
|
|
var (
|
|
|
+ downloadDir = "D:\\download"
|
|
|
+ defaultDir = "C:\\Users\\Guo Mengyuan\\Downloads"
|
|
|
rzdLoginPath = "https://clients.rystadenergy.com/clients/"
|
|
|
|
|
|
clientSearchLink = `div.d-none.d-lg-flex.flex-grow-1 a[href="/clients/search/"]`
|
|
@@ -39,57 +42,6 @@ var (
|
|
|
scenarioTabSelector = tabSelectorBase + `:contains("Scenario")`
|
|
|
)
|
|
|
|
|
|
-// 函数用于设置查询时间范围
|
|
|
-func setQueryTime(ctx context.Context, year string) error {
|
|
|
- // 在这里可以直接操作 iframe 中的元素
|
|
|
- var inputCount int
|
|
|
- if err := chromedp.Run(ctx,
|
|
|
- chromedp.WaitVisible(`#reportContainer`, chromedp.ByQuery),
|
|
|
- // 获取 reportContainer 下的第一个 iframe 的内容文档
|
|
|
- chromedp.ActionFunc(func(ctx context.Context) error {
|
|
|
- // 获取 iframe 的内容文档
|
|
|
- var iframeSrc string
|
|
|
- // 获取 iframe 的 src
|
|
|
- err := chromedp.Evaluate(`document.querySelector('#reportContainer iframe').src`, &iframeSrc).Do(ctx)
|
|
|
- if err != nil {
|
|
|
- return fmt.Errorf("获取 iframe ID 或 src 失败: %v", err)
|
|
|
- }
|
|
|
- // 在 iframe 的上下文中操作
|
|
|
-
|
|
|
- return chromedp.Run(ctx,
|
|
|
- // 等待 iframe 可见
|
|
|
- chromedp.WaitVisible(`iframe[src="`+iframeSrc+`"]`, chromedp.ByQuery),
|
|
|
- chromedp.Sleep(5*time.Second),
|
|
|
- // 在 iframe 中执行操作
|
|
|
- chromedp.ActionFunc(func(ctx context.Context) error {
|
|
|
- // 选择器
|
|
|
- selector := `div.landingContainer`
|
|
|
- // 获取元素数量
|
|
|
- if err := chromedp.Evaluate(`document.querySelectorAll("`+selector+`").length`, &inputCount).Do(ctx); err != nil {
|
|
|
- return fmt.Errorf("检查输入框失败: %v", err)
|
|
|
- }
|
|
|
-
|
|
|
- if inputCount == 0 {
|
|
|
- return fmt.Errorf("没有找到匹配的 div.landingContainer 标签")
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
- }),
|
|
|
- )
|
|
|
- }),
|
|
|
- ); err != nil {
|
|
|
- log.Fatal(err)
|
|
|
- }
|
|
|
-
|
|
|
- /*return chromedp.Run(ctx,
|
|
|
- chromedp.Sleep(3*time.Second),
|
|
|
- chromedp.WaitVisible(dateSlicerInputSelector, chromedp.ByQuery),
|
|
|
- chromedp.SetValue(dateSlicerInputSelector, year, chromedp.ByQuery),
|
|
|
- chromedp.SendKeys(dateSlicerInputSelector, "\n"), // 回车查询
|
|
|
- )*/
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
// 函数用于点击下载按钮
|
|
|
func clickDownload(ctx context.Context) error {
|
|
|
return chromedp.Run(ctx, chromedp.Click(downloadButtonSelector, chromedp.ByQuery))
|
|
@@ -105,11 +57,41 @@ func downloadData(ctx context.Context) error {
|
|
|
chromedp.Click(clientSearchLink, chromedp.ByQuery),
|
|
|
chromedp.WaitVisible(`input[class="ais-SearchBox-input rounded border py-2 px-3 shadow-sm font-size-14 w-100"]`, chromedp.ByQuery),
|
|
|
chromedp.SetValue(`input[class="ais-SearchBox-input rounded border py-2 px-3 shadow-sm font-size-14 w-100"]`, "oil demand signals weekly report", chromedp.ByQuery),
|
|
|
- chromedp.Click(`div.ais-InfiniteHits img[src="/Static/img/icons/xls.png"]`, chromedp.ByQuery),
|
|
|
+ //chromedp.Click(`div.ais-InfiniteHits li a:has(img[src="/Static/img/icons/xls.png"])`, chromedp.ByQuery),
|
|
|
); err != nil {
|
|
|
return fmt.Errorf("下载 Analytics Library 数据错误: %v", err)
|
|
|
}
|
|
|
|
|
|
+ xpath := `//div[@id='search-page-hits']//li//a[.//div//span[@class='align-middle' and text()='Data']]`
|
|
|
+ var inputCount int
|
|
|
+ var nodes []*cdp.Node // 使用 *cdp.Node
|
|
|
+ if err := chromedp.Run(ctx,
|
|
|
+ chromedp.ActionFunc(func(ctx context.Context) error {
|
|
|
+
|
|
|
+ // 获取匹配的节点
|
|
|
+ if err := chromedp.Nodes(xpath, &nodes, chromedp.BySearch).Do(ctx); err != nil {
|
|
|
+ return fmt.Errorf("检查节点失败: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取节点数量
|
|
|
+ inputCount = len(nodes)
|
|
|
+ fmt.Printf("找到 %d 个匹配的元素\n", inputCount)
|
|
|
+
|
|
|
+ if inputCount > 0 {
|
|
|
+ // 点击第一个节点
|
|
|
+ return chromedp.MouseClickNode(nodes[0]).Do(ctx) // 使用 []cdp.NodeID
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }),
|
|
|
+ chromedp.Sleep(10*time.Second),
|
|
|
+ ); err != nil {
|
|
|
+ return fmt.Errorf("下载 Analytics Library 数据错误: %v", err)
|
|
|
+ }
|
|
|
+ // 解析文件移动到目标目录
|
|
|
+ if err := waitAndRenameDownloadedFile("Oil_Demand_Signals_Weekly_Report_"+utils.GetCurrentYearMonth()+".xlsx", downloadDir); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
// Cube Dashboards: Supply Revision Analysis
|
|
|
if err := chromedp.Run(ctx,
|
|
|
chromedp.WaitVisible(`div.d-none.d-lg-flex.flex-grow-1`, chromedp.ByQuery),
|
|
@@ -141,21 +123,13 @@ func downloadData(ctx context.Context) error {
|
|
|
); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- if err := setQueryTime(ctx, "2020"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
if err := clickDownload(ctx); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- if err := waitAndRenameDownloadedFile("Supply_Revision_Analysis_2020.xlsx"); err != nil {
|
|
|
+ if err := waitAndRenameDownloadedFile("Supply_Revision_Analysis_2020.xlsx", downloadDir); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- // Oil Demand Analysis
|
|
|
- if err := downloadOilDemandAnalysis(ctx); err != nil {
|
|
|
- return fmt.Errorf("下载 Oil Demand Analysis 错误: %v", err)
|
|
|
- }
|
|
|
-
|
|
|
// Oil Supply Analysis
|
|
|
if err := chromedp.Run(ctx,
|
|
|
chromedp.Click(`a[href="/clients/subscription/"]`, chromedp.ByQuery),
|
|
@@ -163,58 +137,10 @@ func downloadData(ctx context.Context) error {
|
|
|
); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- if err := setQueryTime(ctx, "2010"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
if err := clickDownload(ctx); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- if err := waitAndRenameDownloadedFile("Oil_Supply_Analysis_2010.xlsx"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-// 下载 Oil Demand Analysis 的所有标签数据
|
|
|
-func downloadOilDemandAnalysis(ctx context.Context) error {
|
|
|
- // 下载 "Continent" 标签的数据
|
|
|
- if err := downloadOilDemandByTab(ctx, continentTabSelector, "2015", "Oil_Demand_Continent_2015.xlsx"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // 下载 "Region" 标签的数据
|
|
|
- if err := downloadOilDemandByTab(ctx, regionTabSelector, "2015", "Oil_Demand_Region_2015.xlsx"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // 下载 "Country" 标签的数据
|
|
|
- if err := downloadOilDemandByTab(ctx, countryTabSelector, "2015", "Oil_Demand_Country_2015.xlsx"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // 下载 "Product_Category" 标签的数据
|
|
|
- if err := downloadOilDemandByTab(ctx, productCategoryTabSelector, "2015", "Oil_Demand_Product_Category_2015.xlsx"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // 下载 "Product_Detail" 标签的数据
|
|
|
- if err := downloadOilDemandByTab(ctx, productDetailTabSelector, "2015", "Oil_Demand_Product_Detail_2015.xlsx"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // 下载 "Sector_Category" 标签的数据
|
|
|
- if err := downloadOilDemandByTab(ctx, sectorCategoryTabSelector, "2015", "Oil_Demand_Sector_Category_2015.xlsx"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // 下载 "Sector_Detail" 标签的数据
|
|
|
- if err := downloadOilDemandByTab(ctx, sectorDetailTabSelector, "2015", "Oil_Demand_Sector_Detail_2015.xlsx"); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- // 下载 "Scenario" 标签的数据
|
|
|
- if err := downloadOilDemandByTab(ctx, scenarioTabSelector, "2015", "Oil_Demand_Scenario_2015.xlsx"); err != nil {
|
|
|
+ if err := waitAndRenameDownloadedFile("Oil_Supply_Analysis_2010.xlsx", downloadDir); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
@@ -237,11 +163,6 @@ func downloadOilDemandByTab(ctx context.Context, tabSelector string, year string
|
|
|
return fmt.Errorf("等待页面加载失败: %v", err)
|
|
|
}
|
|
|
|
|
|
- // 设置时间范围
|
|
|
- if err := setQueryTime(ctx, year); err != nil {
|
|
|
- return fmt.Errorf("设置查询时间失败: %v", err)
|
|
|
- }
|
|
|
-
|
|
|
// 点击下载按钮
|
|
|
if err := clickDownload(ctx); err != nil {
|
|
|
return fmt.Errorf("点击下载按钮失败: %v", err)
|
|
@@ -253,7 +174,7 @@ func downloadOilDemandByTab(ctx context.Context, tabSelector string, year string
|
|
|
}
|
|
|
|
|
|
// 下载完成后,重命名文件
|
|
|
- if err := waitAndRenameDownloadedFile(fileName); err != nil {
|
|
|
+ if err := waitAndRenameDownloadedFile(fileName, downloadDir); err != nil {
|
|
|
return fmt.Errorf("重命名文件失败: %v", err)
|
|
|
}
|
|
|
|
|
@@ -261,30 +182,81 @@ func downloadOilDemandByTab(ctx context.Context, tabSelector string, year string
|
|
|
}
|
|
|
|
|
|
// 等待下载文件并重命名
|
|
|
-func waitAndRenameDownloadedFile(newFileName string) error {
|
|
|
+func waitAndRenameDownloadedFile(newFileName, targetDir string) error {
|
|
|
// 等待一段时间以确保文件下载完成
|
|
|
- time.Sleep(5 * time.Second) // 可能需要根据实际情况调整
|
|
|
+ time.Sleep(60 * time.Second) // 可能需要根据实际情况调整
|
|
|
|
|
|
// 查找下载目录中的文件
|
|
|
- files, err := filepath.Glob(filepath.Join(downloadDir, "*.xlsx"))
|
|
|
+ files, err := filepath.Glob(filepath.Join(defaultDir, "*.xlsx"))
|
|
|
if err != nil {
|
|
|
return fmt.Errorf("查找文件时出错: %v", err)
|
|
|
}
|
|
|
|
|
|
- // 重命名最新的文件
|
|
|
+ // 如果没有找到文件,返回错误
|
|
|
+ if len(files) == 0 {
|
|
|
+ return fmt.Errorf("未找到任何下载的文件")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找到最新的文件
|
|
|
+ var latestFile string
|
|
|
+ var latestTime time.Time
|
|
|
+
|
|
|
for _, file := range files {
|
|
|
- if err := os.Rename(file, filepath.Join(downloadDir, newFileName)); err != nil {
|
|
|
+ info, err := os.Stat(file)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("获取文件信息时出错: %v", err)
|
|
|
+ }
|
|
|
+ if info.ModTime().After(latestTime) {
|
|
|
+ latestTime = info.ModTime()
|
|
|
+ latestFile = file
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 目标文件的完整路径
|
|
|
+ targetFilePath := filepath.Join(targetDir, newFileName)
|
|
|
+
|
|
|
+ // 重命名并移动到目标目录
|
|
|
+ if latestFile != "" {
|
|
|
+ if err := moveFile(latestFile, targetFilePath); err != nil {
|
|
|
return fmt.Errorf("重命名文件时出错: %v", err)
|
|
|
}
|
|
|
// 打印重命名后的文件名
|
|
|
- fmt.Printf("文件重命名为: %s\n", newFileName)
|
|
|
- break // 只重命名第一个找到的文件
|
|
|
+ fmt.Printf("文件重命名并移动到: %s\n", targetFilePath)
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func main() {
|
|
|
+func moveFile(source, destination string) error {
|
|
|
+ // 复制文件
|
|
|
+ srcFile, err := os.Open(source)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("打开源文件时出错: %v", err)
|
|
|
+ }
|
|
|
+ defer srcFile.Close()
|
|
|
+
|
|
|
+ dstFile, err := os.Create(destination)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("创建目标文件时出错: %v", err)
|
|
|
+ }
|
|
|
+ defer dstFile.Close()
|
|
|
+
|
|
|
+ if _, err := io.Copy(dstFile, srcFile); err != nil {
|
|
|
+ return fmt.Errorf("复制文件时出错: %v", err)
|
|
|
+ }
|
|
|
+ time.Sleep(60 * time.Second)
|
|
|
+
|
|
|
+ // 删除源文件
|
|
|
+ if err := os.Remove(source); err != nil {
|
|
|
+ return fmt.Errorf("删除源文件时出错: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// 解析网页数据,下载文件
|
|
|
+// func main() {
|
|
|
+func resolverNet() {
|
|
|
// 创建下载目录
|
|
|
if err := os.MkdirAll(downloadDir, os.ModePerm); err != nil {
|
|
|
fmt.Printf("创建下载目录时出错: %v\n", err)
|
|
@@ -296,9 +268,11 @@ func main() {
|
|
|
chromedp.Flag("headless", false),
|
|
|
chromedp.Flag("disable-blink-features", "AutomationControlled"),
|
|
|
chromedp.UserAgent(`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36`),
|
|
|
- chromedp.Flag("download.default_directory", downloadDir),
|
|
|
- chromedp.Flag("download.prompt_for_download", false), // 不弹出下载对话框
|
|
|
- chromedp.Flag("safebrowsing.enabled", true), // 启用安全浏览
|
|
|
+ // 设置了但并不生效,直接从默认下载路径读取过来
|
|
|
+ //chromedp.Flag("download.default_directory", downloadDir),
|
|
|
+ //chromedp.Flag("download.prompt_for_download", false), // 不弹出下载对话框
|
|
|
+ chromedp.Flag("safebrowsing.enabled", true), // 启用安全浏览
|
|
|
+ //chromedp.UserDataDir(filepath.Join(downloadDir, "user-data")),
|
|
|
}
|
|
|
|
|
|
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), options...)
|
|
@@ -312,6 +286,12 @@ func main() {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ // 设置下载行为
|
|
|
+ /*if err := setDownloadBehavior(ctx); err != nil {
|
|
|
+ fmt.Printf("设置下载路径时出错: %v\n", err)
|
|
|
+ return
|
|
|
+ }*/
|
|
|
+
|
|
|
// 登录操作
|
|
|
if err := login(ctx); err != nil {
|
|
|
fmt.Printf("登录错误: %v\n", err)
|
|
@@ -327,6 +307,65 @@ func main() {
|
|
|
fmt.Println("数据下载完成")
|
|
|
}
|
|
|
|
|
|
+// 解析本地文件
|
|
|
+// func fileResolver() {
|
|
|
+func main() {
|
|
|
+ var fileName string
|
|
|
+
|
|
|
+ // 解析Oil_Demand_Signals_Weekly_Report_表格
|
|
|
+ fileName = "Oil_Demand_Signals_Weekly_Report_" + utils.GetCurrentYearMonth() + ".xlsx"
|
|
|
+ filePath := filepath.Join(downloadDir, fileName)
|
|
|
+
|
|
|
+ // 打开 Excel 文件
|
|
|
+ f, err := excelize.OpenFile(filePath)
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("无法打开 Excel 文件: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取所有工作表
|
|
|
+ sheetNames := f.GetSheetList()
|
|
|
+ for _, sheetName := range sheetNames {
|
|
|
+ fmt.Printf("读取工作表: %s\n", sheetName)
|
|
|
+
|
|
|
+ // 获取工作表的最大行数
|
|
|
+ maxRow, err := f.GetRows(sheetName) // 直接获取所有行数据
|
|
|
+ if err != nil {
|
|
|
+ log.Fatalf("获取工作表数据时出错: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 遍历行并打印内容
|
|
|
+ for _, row := range maxRow {
|
|
|
+ for _, cell := range row {
|
|
|
+ fmt.Printf("%s ", cell)
|
|
|
+ }
|
|
|
+ fmt.Println()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// setDownloadBehavior 设置下载路径
|
|
|
+func setDownloadBehavior(ctx context.Context) error {
|
|
|
+ return chromedp.Run(ctx,
|
|
|
+ chromedp.ActionFunc(func(ctx context.Context) error {
|
|
|
+ // 使用 chromedp.Exec 提交下载行为
|
|
|
+ var result interface{}
|
|
|
+ if err := chromedp.Evaluate(`(function() {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ chrome.page.setDownloadBehavior({
|
|
|
+ behavior: 'allow',
|
|
|
+ downloadPath: '`+downloadDir+`'
|
|
|
+ }, function() {
|
|
|
+ resolve();
|
|
|
+ });
|
|
|
+ });
|
|
|
+ })()`, &result).Do(ctx); err != nil {
|
|
|
+ return fmt.Errorf("设置下载行为失败: %v", err)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }),
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
func login(ctx context.Context) error {
|
|
|
|
|
|
return chromedp.Run(ctx,
|