農產品價格報告爬蟲使用說明

農產品價格報告爬蟲使用說明

# **************************************************************************
# *                                                                          *
# *                      農產品價格報告爬蟲                                   *
# *                                                                          *
# *                        作者: xiaohai                                     *
# *                        版本: v1.0.0                                      *
# *                        日期: 2024-12-05                                  *
# *                                                                          *
# *        功能說明:                                                         *
# *            1. 日度報告                                                   *
# *               - 生成今日分析報告                                         *
# *               - 生成指定日期報告                                         *
# *               - 包含價格指數、分品類分析等                                *
# *                                                                          *
# *            2. 周度報告                                                   *
# *               - 生成本周分析報告                                         *
# *               - 生成指定周報告                                           *
# *               - 匯總周內價格變化                                         *
# *                                                                          *
# *            3. 價格走勢                                                   *
# *               - 農產品價格200指數走勢                                    *
# *               - 豬肉價格全國走勢                                         *
# *               - 豬肉價格區域走勢                                         *
# *               - 糧油價格指數走勢                                         *
# *                                                                          *
# *            4. 數據導出                                                   *
# *               - 支持Excel格式導出                                        *
# *               - 包含多個數據分類                                         *
# *               - 支持時間范圍選擇                                         *
# *                                                                          *
# *        : 農業農村部市場信息中心                                   *
# *        版權聲明: 僅用于學習交流                                          *
# *                                                                          *
# **************************************************************************import os
import json
import logging
import requests
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import pandas as pd
import warnings
import urllib3
import sys
import subprocess
import pkg_resources
from bs4 import BeautifulSoup
import re
import time# 禁用SSL警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
warnings.filterwarnings('ignore')# 配置常量
VERSION = 'v1.0.0'
AUTHOR = 'xiaohai'
DATA_SOURCE = '農業農村部市場信息中心'# API配置
API_BASE_URL = 'https://ncpscxx.moa.gov.cn'
API_ENDPOINTS = {'price_index': '/product/common-price-index/getIndexList','variety_list': '/product/sys-variety/selectList','price_trend': '/product/price-info/getPriceInfoList','market_list': '/product/sys-market/selectList','daily_price': '/product/price-info/getDailyPrice','analysis_report': '/product/analysis-report/pageList'
}# 輸出目錄配置
OUTPUT_DIRS = {'base': 'reports','daily': 'reports/daily','weekly': 'reports/weekly'
}# 圖表樣式配置
CHART_STYLE = {'figure': {'figsize': (12, 6),'facecolor': '#f8fcfa'},'grid': {'linestyle': '--','alpha': 0.3,'color': 'gray'},'line': {'marker': 'o','markersize': 4,'linewidth': 2},'colors': {'blue': '#40a9ff','green': '#73d13d','orange': '#ffa940','red': '#ff4d4f','purple': '#9254de','cyan': '#36cfc9'}
}def check_and_install_packages():"""檢查并安裝所需的包"""required_packages = {'requests': 'requests',      # HTTP請求'pandas': 'pandas',          # 數據處理'matplotlib': 'matplotlib',  # 繪圖支持'urllib3': 'urllib3',        # HTTP客戶端'openpyxl': 'openpyxl',     # Excel支持'colorama': 'colorama'       # 控制臺顏色}print("\n" + "="*50)print("檢查并安裝依賴包...")print("="*50)try:import coloramacolorama.init()success_mark = colorama.Fore.GREEN + "?" + colorama.Style.RESET_ALLerror_mark = colorama.Fore.RED + "?" + colorama.Style.RESET_ALLexcept ImportError:success_mark = "?"error_mark = "?"all_success = Truefor package, import_name in required_packages.items():try:pkg_resources.require(package)print(f"{success_mark} {package:15} 已安裝")except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict):print(f"{error_mark} {package:15} 未安裝,正在安裝...")try:subprocess.check_call([sys.executable, "-m", "pip", "install", "--disable-pip-version-check","--no-cache-dir",package], stdout=subprocess.DEVNULL)print(f"{success_mark} {package:15} 安裝成功")except Exception as e:print(f"{error_mark} {package:15} 安裝失敗: {str(e)}")all_success = Falseprint("\n依賴包檢查" + ("全部完成" if all_success else "存在問題"))print("="*50 + "\n")if not all_success:print("某些依賴包安裝失敗,程序能無法正常運行!")if input("是否繼續運行?(y/n): ").lower() != 'y':sys.exit(1)class ReportCrawler:"""農產品價格報告爬蟲"""def __init__(self):# 禁用SSL警告warnings.filterwarnings('ignore')# 基礎配置self._setup_directories()self._setup_logger()self._setup_api()def _setup_directories(self):"""創建輸出目錄"""self.output_dir = "reports"self.daily_dir = os.path.join(self.output_dir, "daily")self.weekly_dir = os.path.join(self.output_dir, "weekly")for d in [self.output_dir, self.daily_dir, self.weekly_dir]:if not os.path.exists(d):os.makedirs(d)def _setup_logger(self):"""配置日志"""log_file = os.path.join("logs", f"crawler_{datetime.now().strftime('%Y%m%d')}.log")os.makedirs("logs", exist_ok=True)formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s',datefmt='%Y-%m-%d %H:%M:%S')# 文件處理器file_handler = logging.FileHandler(log_file, encoding='utf-8')file_handler.setFormatter(formatter)# 制臺處理器console_handler = logging.StreamHandler()console_handler.setFormatter(formatter)# 配置日志器self.logger = logging.getLogger(__name__)self.logger.setLevel(logging.INFO)self.logger.addHandler(file_handler)self.logger.addHandler(console_handler)def _setup_api(self):"""配置API"""self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36','Origin': 'https://ncpscxx.moa.gov.cn','Referer': 'https://ncpscxx.moa.gov.cn/','Accept': 'application/json, text/plain, */*','Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8','Content-Type': 'application/json;charset=UTF-8'}def show_menu(self):"""顯示功能菜單"""menu = """
農產品價格報告爬蟲系統
====================
1. 成今日分析報告
2. 生成本周分報告
3. 生成指定日期報告
4. 生成指定周報告
5. 生成價格指數走勢圖
6. 生成豬肉價格走勢圖
7. 生成區域價格走勢圖
8. 生成糧油價格走勢圖
9. 導出Excel數據
0. 退出系統請輸入���能編號(0-9): """print("\n" + "="*50)  # 添加分隔線choice = input(menu)print("="*50 + "\n")  # 添加分隔線return choicedef run(self):"""運行系統"""while True:choice = self.show_menu()if choice == "0":print("感謝使用,再見!")breakelif choice == "1":print("正在生成今日分析報告...")self.generate_daily_report(datetime.now())elif choice == "2":print("正在生成本周分析報告...")today = datetime.now()self.generate_weekly_report(today.year, int(today.strftime("%W")))elif choice == "3":date_str = input("請輸入日期(格式:YYYY-MM-DD): ")try:date = datetime.strptime(date_str, "%Y-%m-%d")self.generate_daily_report(date)except:print("日期格式錯誤!")elif choice == "4":year = int(input("請輸入年份: "))week = int(input("請輸入周數(1-52): "))self.generate_weekly_report(year, week)elif choice == "5":days = int(input("請輸入要查看的天數: "))end = datetime.now()start = end - timedelta(days=days)self.plot_index_trend(start, end)elif choice == "6":days = int(input("請輸入要查看的天數: "))end = datetime.now()start = end - timedelta(days=days)self.plot_pig_price_trend(start, end)elif choice == "7":days = int(input("請輸入要查看的天數: "))end = datetime.now()start = end - timedelta(days=days)self.plot_pig_price_region_trend(start, end)elif choice == "8":days = int(input("請輸入要查看的天數: "))end = datetime.now()start = end - timedelta(days=days)self.plot_grain_price_trend(start, end)elif choice == "9":days = int(input("請輸入要導天數: "))end = datetime.now()start = end - timedelta(days=days)self.export_data(start, end)else:print("無效的選擇,請重試!")input("\n按回車鍵繼續...")def _make_request(self, url, method='get', params=None, data=None):"""發送HTTP請求Args:url: 請求URLmethod: 請求方法,支持 'get'/'post'params: URL參數data: POST數據Returns:Response對象或None(請求失敗)"""try:headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}if method.lower() == 'get':response = requests.get(url,params=params,headers=headers,verify=False,timeout=10)else:response = requests.post(url,params=params,json=data,  # 添加json參數支持headers=headers,verify=False,timeout=10)response.raise_for_status()return responseexcept requests.exceptions.RequestException as e:self.logger.error(f"請求失敗: {str(e)}")return Nonedef fetch_daily_report(self, date):"""獲取日度價格報告"""try:url = f"{API_BASE_URL}/api/FarmDaily/list"data = {"daylyDate": date.strftime("%Y-%m-%d")}response = self._make_request(url, method='post', data=data)if not response:return Nonedata = response.json()if data.get("code") == 200 and data.get("content",{}).get("list"):# 找到指定日期的報告target_date = date.strftime("%Y-%m-%d")for report in data["content"]["list"]:if report["daylyDate"].startswith(target_date):# 提取所需數據return {"conclusion": report["counclesion"],"indexConclusion": report["indexConclusion"],"animalConclusion": report["animalConclusion"],"aquaticConclusion": report["aquaticConclusion"],"vegetablesConclusion": report["vegetablesConclusion"],"fruitsConclusion": report["fruitsConclusion"],"content": report["countent"],"incOrReduRange": report["incOrReduRange"]}self.logger.warning(f"未找到{target_date}的報告")return Noneself.logger.warning(f"獲取數據失敗: {data.get('message', '未知錯誤')}")return Noneexcept Exception as e:self.logger.error(f"獲取日度報告出錯: {str(e)}")return Nonedef _extract_conclusions(self, report):"""從報告中提取各類結論"""try:return {"index": report.get("indexConclusion", ""),"animal": report.get("animalConclusion", ""),"aquatic": report.get("aquaticConclusion", ""),"vegetables": report.get("vegetablesConclusion", ""),"fruits": report.get("fruitsConclusion", ""),"range": report.get("incOrReduRange", "")}except Exception as e:self.logger.error(f"提取論出錯: {str(e)}")return {}def fetch_index_data(self, start_date, end_date):"""獲取價格指數數據"""try:url = "https://pfsc.agri.cn/price_portal/pi-info-day/getPortalPiInfoDay"response = requests.post(url, headers=self.headers, verify=False)data = response.json()if data["code"] == 200:result = []for item in data["content"]:pub_date = datetime.strptime(item["publishDate"], "%Y-%m-%d")if start_date <= pub_date <= end_date:result.append({"日期": item["publishDate"],"農產品批發價格200指數": item["agriculture"],"糧油指數": item["grainAndOil"],"籃子數": item["vegetableBasket"]})return resultreturn Noneexcept Exception as e:self.logger.error(f"獲取指數數據失敗: {str(e)}")return Nonedef fetch_pig_price_data(self, start_date, end_date):"""獲取豬肉價格數據"""try:url = f"{API_BASE_URL}{API_ENDPOINTS['variety_list']}"params = {'pid': 'MH'}  # 豬肉品類IDresponse = self._make_request(url, method='post', params=params)if not response:return Nonedata = response.json()if data.get("code") == 200 and data.get("data"):# 轉換數據格式result = []for item in data["data"]:if start_date <= datetime.strptime(item["date"], "%Y-%m-%d") <= end_date:result.append({"日期": item["date"],"全國": float(item["national"]),"東北": float(item["northEast"]),"華北": float(item["northChina"]),"華東": float(item["eastChina"]),"華中": float(item["centralChina"]),"華南": float(item["southChina"]),"西南": float(item["southWest"])})return resultself.logger.warning(f"獲取數據失敗: {data.get('message', '未知錯誤')}")return Noneexcept Exception as e:self.logger.error(f"獲取豬肉價格數據失敗: {str(e)}")return Nonedef fetch_grain_price_data(self, start_date, end_date):"""獲取糧油價格數據"""try:url = f"{API_BASE_URL}{API_ENDPOINTS['variety_list']}"params = {'pid': 'TL'}  # 糧油品類IDresponse = self._make_request(url, method='post', params=params)if not response:return Nonedata = response.json()if data.get("code") == 200 and data.get("data"):# 轉換數據格式result = []for item in data["data"]:if start_date <= datetime.strptime(item["date"], "%Y-%m-%d") <= end_date:result.append({"日期": item["date"],"通義糧價指數": float(item["grainPriceIndex"]),"通義糧市指數": float(item["grainMarketIndex"]),"通義糧市第1號": float(item["grainMarketNo1"]),"通義糧天指數": float(item["grainDayIndex"]),"通義���指": float(item["grainIndex"]),"通義糧天指數(干糧)": float(item["grainDayDryIndex"])})return resultself.logger.warning(f"獲取數據失敗: {data.get('message', '未知錯誤')}")return Noneexcept Exception as e:self.logger.error(f"獲取糧油價格數據失敗: {str(e)}")return Nonedef generate_daily_report(self, date):"""生成每日分析報告"""try:report_data = self.fetch_daily_report(date)if not report_data:self.logger.warning(f"未獲取到 {date.strftime('%Y-%m-%d')} 的報告數據")returnreport_file = os.path.join(self.daily_dir,f"{date.strftime('%Y年%m月%d日')}_價格分析報告.md")# 使用更清晰模板格式content = f"""# {date.strftime('%Y年%m月%d日')} 農產品價格分析報告## 一、價格指數變化
{report_data["indexConclusion"]}## 二、分品類分析### 1. 畜禽產品
{report_data["animalConclusion"]}### 2. 水產品
{report_data["aquaticConclusion"]}### 3. 蔬菜
{report_data["vegetablesConclusion"]}### 4. 水果
{report_data["fruitsConclusion"]}## 三、價格波動情況
{report_data["incOrReduRange"]}## 四、數據說明
- 數據來源: {report_data["source"]}
- 生成時間: {datetime.now().strftime('%Y年%m月%d日 %H:%M:%S')}
- 價格單位: 元/斤
- 漲跌幅: 與上一交易日相比---
*注: 本報告由系統自動生成,僅供參考。*
"""with open(report_file, "w", encoding="utf-8") as f:f.write(content)self.logger.info(f"分析報告已生成: {report_file}")except Exception as e:self.logger.error(f"生成分析報告失敗: {str(e)}")def generate_weekly_report(self, year, week):"""生成周度匯總報告"""try:start_date = datetime.strptime(f'{year}-W{week:02d}-1', '%Y-W%W-%w')end_date = start_date + timedelta(days=6)print(f"\n正在生成第{week}周報告...")print(f"時間范圍: {start_date.strftime('%Y-%m-%d')}{end_date.strftime('%Y-%m-%d')}")print("="*50)# 獲周內所有報告reports = []current = start_datetotal_days = (end_date - start_date).days + 1for i in range(total_days):print(f"\r進度: {i+1}/{total_days} ", end="")report = self.fetch_daily_report(current)if report:reports.append(report)current += timedelta(days=1)if not reports:self.logger.warning("本周無可用數據")return# 計算周度匯總數據weekly_summary = self._calculate_weekly_summary(reports)report_file = os.path.join(self.weekly_dir,f"{year}年第{week:02d}周_{start_date.strftime('%m月%d日')}-{end_date.strftime('%m月%d日')}_價格分析報告.md")with open(report_file, "w", encoding="utf-8") as f:f.write(f"""# {year}年第{week:02d}周農產品價格分析報告
({start_date.strftime('%Y年%m月%d日')}{end_date.strftime('%Y年%m月%d日')})## 一、本周價格概況
{weekly_summary['overview']}## 二、價格指數變化
- 周初: {weekly_summary['index_start']}
- 周末: {weekly_summary['index_end']}
- 度變化: {weekly_summary['index_change']}## 三、分品類周度分析
### 1. 畜禽產品
{weekly_summary['animal_summary']}### 2. 水產品
{weekly_summary['aquatic_summary']}### 3. 蔬菜
{weekly_summary['vegetables_summary']}### 4. 水果
{weekly_summary['fruits_summary']}## 四、日度價格詳情
""")for report in reports:pub_date = datetime.strptime(report['daylyDate'][:10], '%Y-%m-%d')f.write(f"""### {pub_date.strftime('%Y年%m月%d日')}
1. 價格指數: {report.get('indexConclusion', '暫無數據')}
2. 畜禽產品: {report.get('animalConclusion', '暫無數據')}
3. 水產品: {report.get('aquaticConclusion', '暫無數據')}
4. 蔬菜: {report.get('vegetablesConclusion', '暫無數據')}
5. 水果: {report.get('fruitsConclusion', '暫無數據')}""")f.write(f"""## 五、數據說明
- 數據來源: {reports[0]["source"]}
- 生成時間: {datetime.now().strftime('%Y年%m月%d日 %H:%M:%S')}
- 價格單位: 元/公斤
- 跌幅: 與上期相比---
*注: 本報告由系統自動生成,僅供參考。*""")print("\n報告生成完成!")self.logger.info(f"周度報告已生成: {report_file}")except Exception as e:self.logger.error(f"生成周度報告失敗: {str(e)}")def _calculate_weekly_summary(self, reports):"""計算周度匯總數據"""summary = {'overview': '','index_start': reports[0].get('indexConclusion', '暫無數據'),'index_end': reports[-1].get('indexConclusion', '暫無數據'),'index_change': '','animal_summary': '','aquatic_summary': '','vegetables_summary': '','fruits_summary': ''}# 計算價格指數變化try:start_index = float(reports[0]['indexConclusion'].split('為')[1].split(',')[0])end_index = float(reports[-1]['indexConclusion'].split('為')[1].split(',')[0])change = end_index - start_indexsummary['index_change'] = f"{'上升' if change >= 0 else '下降'}{abs(change):.2f}個點"except:summary['index_change'] = '數據異常'# 生成概述summary['overview'] = f"本周農產品批發價格200指數從{summary['index_start']},到{summary['index_end']},整體{summary['index_change']}。"# 其他品類匯總...return summarydef plot_index_trend(self, start_date, end_date):"""繪制價格指數走勢圖"""try:data = self.fetch_index_data(start_date, end_date)if not data:returnplt.figure(figsize=(12, 6), facecolor='#f8fcfa')ax = plt.gca()ax.set_facecolor('#f8fcfa')dates = [item["日期"] for item in data]indices = [("農品批發價格200指數", "#ffa940"),("菜籃子指", "#73d13d"),("糧油指數", "#40a9ff")]for name, color in indices:values = [item[name] for item in data]plt.plot(dates, values, color=color, marker='o',markersize=4, linewidth=2, label=name)plt.title('農業農村部"農產品批發價格200指數"日度走勢圖',pad=20, fontsize=12, loc='left')plt.grid(True, linestyle='--', alpha=0.3)plt.xticks(rotation=45)plt.legend(loc='upper right', frameon=False)plt.tight_layout()plt.savefig(os.path.join(self.output_dir, "價格指數走勢圖.png"),dpi=300,bbox_inches='tight')plt.close()self.logger.info("價格指數走勢已生成")except Exception as e:self.logger.error(f"生成價格指數走勢圖失敗: {str(e)}")def plot_pig_price_trend(self, start_date, end_date):"""繪制豬肉價格走勢圖"""try:data = self.fetch_pig_price_data(start_date, end_date)if not data:returnplt.figure(figsize=(12, 6), facecolor='#f8fcfa')ax = plt.gca()ax.set_facecolor('#f8fcfa')dates = [item["日期"] for item in data]values = [item["全國"] for item in data]plt.plot(dates, values, color='#40a9ff', marker='o',markersize=4, linewidth=2)plt.fill_between(dates, values, color='#e6f7ff', alpha=0.5)plt.title('"瘦肉型白條豬肉出廠價格指數"全國走勢圖',pad=20, fontsize=12, loc='left')plt.grid(True, linestyle='--', alpha=0.3)plt.xticks(rotation=45)plt.tight_layout()plt.savefig(os.path.join(self.output_dir, "豬肉價格走勢圖.png"),dpi=300,bbox_inches='tight')plt.close()self.logger.info("豬肉價格走勢圖已生成")except Exception as e:self.logger.error(f"生成豬肉價格走勢圖失敗: {str(e)}")def plot_pig_price_region_trend(self, start_date, end_date):"""繪制豬肉分區域價格走勢圖"""try:data = self.fetch_pig_price_data(start_date, end_date)if not data:returnplt.figure(figsize=(12, 6), facecolor='#f8fcfa')ax = plt.gca()ax.set_facecolor('#f8fcfa')dates = [item["日期"] for item in data]regions = [("東北", "#40a9ff"),("華南", "#73d13d"),("華", "#ffa940"),("華中", "#ff4d4f"),("華東", "#9254de"),("西南", "#36cfc9")]for region, color in regions:values = [item[region] for item in data]plt.plot(dates, values, color=color, marker='o',markersize=4, linewidth=2, label=region)plt.title('"瘦肉型條豬肉出廠價格指數"區域走勢圖',pad=20, fontsize=12, loc='left')plt.grid(True, linestyle='--', alpha=0.3)plt.xticks(rotation=45)plt.legend(loc='upper right', frameon=False)plt.tight_layout()plt.savefig(os.path.join(self.output_dir, "豬肉價格區域走勢圖.png"),dpi=300,bbox_inches='tight')plt.close()self.logger.info("豬肉價格區域走勢圖已生成")except Exception as e:self.logger.error(f"生成豬肉價格區域走勢圖失敗: {str(e)}")def plot_grain_price_trend(self, start_date, end_date):"""繪制糧油價格走勢圖"""try:data = self.fetch_grain_price_data(start_date, end_date)if not data:returnplt.figure(figsize=(12, 6), facecolor='#f8fcfa')ax = plt.gca()ax.set_facecolor('#f8fcfa')dates = [item["日期"] for item in data]indices = [("通義糧價指數", "#40a9ff"),("通義糧市指數", "#73d13d"),("通義糧市第1號", "#ffa940"),("通義糧天指數", "#ff4d4f"),("通義糧指", "#9254de"),("通義糧天指數(干糧)", "#36cfc9")]for name, color in indices:values = [item[name] for item in data]plt.plot(dates, values, color=color, marker='o',markersize=4, linewidth=2, label=name)plt.title('中國通義糧油發價格指數走勢圖',pad=20, fontsize=12, loc='left')plt.grid(True, linestyle='--', alpha=0.3)plt.xticks(rotation=45)plt.legend(loc='upper right', frameon=False)plt.tight_layout()plt.savefig(os.path.join(self.output_dir, "糧油價格指數走勢圖.png"),dpi=300,bbox_inches='tight')plt.close()self.logger.info("糧油價格指數走勢圖已生成")except Exception as e:self.logger.error(f"生成糧油價格指數走勢失敗: {str(e)}")def export_data(self, start_date, end_date, format='excel'):"""導出數據Args:start_date: 開始日期end_date: 結束日期format: 導出格式,支持 'excel'/'csv'/'json'"""try:# 獲取數據data = {'index': self.fetch_index_data(start_date, end_date),'pig': self.fetch_pig_price_data(start_date, end_date),'grain': self.fetch_grain_price_data(start_date, end_date)}if not any(data.values()):return# 根據格式導出if format == 'excel':self._export_excel(data, start_date, end_date)elif format == 'csv':self._export_csv(data, start_date, end_date)elif format == 'json':self._export_json(data, start_date, end_date)else:self.logger.error(f"不支持的導出格式: {format}")except Exception as e:self.logger.error(f"導出數據失敗: {str(e)}")def _clean_text(self, text):"""清理文本內容"""if not text:return ""# 去除多余空白字符text = ' '.join(text.split())# 修復可能的標點符號問題text = text.replace('。。', '。').replace(',。', '。').replace(';。', '。')# 修復中文編碼text = text.encode('utf-8').decode('utf-8', 'ignore')return textdef _validate_report_data(self, report):"""驗證報告數據完整性"""required_fields = ["indexConclusion","animalConclusion","aquaticConclusion","vegetablesConclusion","fruitsConclusion"]is_valid = Truefor field in required_fields:if not report.get(field):self.logger.warning(f"報告缺少 {field} 數據")is_valid = Falsereport[field] = "暫無數據"return is_validdef _export_excel(self, data, start_date, end_date):"""導出Excel數據"""try:filename = f"價格數據_{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}.xlsx"filepath = os.path.join(self.output_dir, filename)with pd.ExcelWriter(filepath) as writer:# 導出價格指數if data.get('index'):df_index = pd.DataFrame(data['index'])df_index.to_excel(writer, sheet_name='價格指數', index=False)# 導出豬肉價格if data.get('pig'):df_pig = pd.DataFrame(data['pig'])df_pig.to_excel(writer, sheet_name='豬肉價格', index=False)# 導出糧油價格if data.get('grain'):df_grain = pd.DataFrame(data['grain'])df_grain.to_excel(writer, sheet_name='糧油價格', index=False)self.logger.info(f"���據已導出至: {filepath}")return Trueexcept Exception as e:self.logger.error(f"導出Excel失敗: {str(e)}")return Falsedef fetch_all_categories(self):"""獲取所有品類數據"""categories = {'MH': '豬肉','SC': '蔬菜','SG': '水果','TL': '糧油','SC': '水產','DJ': '蛋雞','NR': '牛肉','YR': '羊肉'}result = {}for code, name in categories.items():try:url = f"{API_BASE_URL}{API_ENDPOINTS['variety_list']}"params = {'pid': code}response = self._make_request(url, method='post', params=params)if response and response.json().get("code") == 200:result[name] = response.json().get("data", [])except Exception as e:self.logger.error(f"獲取{name}品類數據失敗: {str(e)}")return resultdef fetch_market_prices(self, market_id=None, variety_id=None, start_date=None, end_date=None):"""獲取市場價格數據"""try:url = f"{API_BASE_URL}{API_ENDPOINTS['daily_price']}"params = {'marketId': market_id,'varietyId': variety_id,'startDate': start_date.strftime("%Y-%m-%d") if start_date else None,'endDate': end_date.strftime("%Y-%m-%d") if end_date else None}response = self._make_request(url, method='post', params=params)if response and response.json().get("code") == 200:return response.json().get("data", [])return Noneexcept Exception as e:self.logger.error(f"獲取市場價格數據失敗: {str(e)}")return Nonedef fetch_analysis_reports(self, page=1, page_size=10):"""獲取分析報告列表"""try:url = f"{API_BASE_URL}{API_ENDPOINTS['analysis_report']}"params = {'pageNum': page,'pageSize': page_size}response = self._make_request(url, method='post', params=params)if response and response.json().get("code") == 200:return response.json().get("data", {}).get("list", [])return Noneexcept Exception as e:self.logger.error(f"獲取分析報告失敗: {str(e)}")return Nonedef crawl_all_data(self, start_date, end_date):"""爬取所有數據"""try:# 獲取所有品類categories = self.fetch_all_categories()# 獲取所有市場markets_response = self._make_request(f"{API_BASE_URL}{API_ENDPOINTS['market_list']}", method='post')markets = markets_response.json().get("data", []) if markets_response else []# 存儲結果results = {'categories': categories,'markets': markets,'prices': {},'reports': []}# 獲取每個品類的價格數據for category, varieties in categories.items():results['prices'][category] = {}for variety in varieties:variety_id = variety.get('id')if variety_id:prices = self.fetch_market_prices(variety_id=variety_id,start_date=start_date,end_date=end_date)results['prices'][category][variety.get('name')] = prices# 獲取分析報告page = 1while True:reports = self.fetch_analysis_reports(page=page)if not reports:breakresults['reports'].extend(reports)page += 1return resultsexcept Exception as e:self.logger.error(f"爬取所有數據失敗: {str(e)}")return Nonedef fetch_weekly_report_content(self, report_id=None):"""獲取周度報告內容"""try:url = f"{API_BASE_URL}/product/analysis-report/getReportContent"params = {'id': report_id} if report_id else Noneresponse = self._make_request(url, method='post', params=params)if not response:return None# 解析HTML內容soup = BeautifulSoup(response.text, 'html.parser')# 提取報告基本信息title = soup.find('h1', class_='report-title').text.strip()date = soup.find('div', class_='report-date').text.strip()source = soup.find('div', class_='report-source').text.strip()# 提取報告主體內容content = soup.find('div', class_='report-content')# 提取表格數據tables = []for table in content.find_all('table'):df = pd.read_html(str(table))[0]tables.append(df.to_dict('records'))# 提取文本內容paragraphs = []for p in content.find_all('p'):text = p.text.strip()if text:paragraphs.append(text)return {'title': title,'date': date,'source': source,'content': {'text': paragraphs,'tables': tables}}except Exception as e:self.logger.error(f"獲取報告內容失敗: {str(e)}")return Nonedef crawl_all_reports(self, start_date=None, end_date=None):"""爬取所有報告"""try:reports = []page = 1while True:# 獲取報告列表report_list = self.fetch_analysis_reports(page=page)if not report_list:break# 過濾日期范圍if start_date or end_date:filtered_reports = []for report in report_list:report_date = datetime.strptime(report['publishDate'], '%Y-%m-%d')if start_date and report_date < start_date:continueif end_date and report_date > end_date:continuefiltered_reports.append(report)report_list = filtered_reports# 獲取每個報告的詳細內容for report in report_list:report_content = self.fetch_weekly_report_content(report['id'])if report_content:reports.append({'meta': report,'content': report_content})# 添加延時避免請求過快time.sleep(1)page += 1return reportsexcept Exception as e:self.logger.error(f"爬取報告失敗: {str(e)}")return Nonedef save_reports(self, reports, output_dir='reports'):"""保存報告到文件"""try:if not os.path.exists(output_dir):os.makedirs(output_dir)for report in reports:# 生成文件名date = datetime.strptime(report['meta']['publishDate'], '%Y-%m-%d')filename = f"{date.strftime('%Y%m%d')}_{report['meta']['id']}.json"filepath = os.path.join(output_dir, filename)# 保存為JSON文件with open(filepath, 'w', encoding='utf-8') as f:json.dump(report, f, ensure_ascii=False, indent=2)return Trueexcept Exception as e:self.logger.error(f"保存報告失敗: {str(e)}")return Falseif __name__ == "__main__":try:# 檢查并安裝依賴包check_and_install_packages()# 運行爬蟲crawler = ReportCrawler()crawler.run()except KeyboardInterrupt:print("\n程序已被用中斷")sys.exit(0)except Exception as e:print(f"\n程序運行出錯: {str(e)}")sys.exit(1) 

一、功能介紹

本程序用于爬取農業農村部發布的農產品價格監測報告,包括以下功能:

1. 日度報告

  • 生成今日分析報告
  • 生成指定日期報告
  • 包含價格指數、分品類分析等

2. 周度報告

  • 生成本周分析報告
  • 生成指定周報告
  • 匯總周內價格變化

3. 價格走勢

  • 農產品價格200指數走勢
  • 豬肉價格全國走勢
  • 豬肉價格區域走勢
  • 糧油價格指數走勢

4. 數據導出

  • 支持Excel格式導出
  • 包含多個數據分類
  • 支持時間范圍選擇

二、使用說明

1. 環境要求

  • Python 3.7+
  • 依賴包會自動安裝:
    • requests: HTTP請求
    • pandas: 數據處理
    • matplotlib: 繪圖支持
    • urllib3: HTTP客戶端
    • openpyxl: Excel支持
    • colorama: 控制臺顏色

2. 運行方法

python
直接運行程序
python report_crawler.py

3. 功能菜單

農產品價格報告爬蟲系統

生成今日分析報告
生成本周分析報告
生成指定日期報告
生成指定周報告
生成價格指數走勢圖
生成豬肉價格走勢圖
生成區域價格走勢圖
生成糧油價格走勢圖
導出Excel數據
退出系統

4. 輸出文件

  • reports/daily: 日度分析報告
  • reports/weekly: 周度分析報告
  • reports: 價格走勢圖和Excel數據

三、數據來源

  • 農業農村部市場信息中心
  • 數據更新頻率: 每日14:00

四、注意事項

  1. 首次運行會自動檢查并安裝依賴包
  2. 所有數據僅供學習交流使用
  3. 建議使用時設置合理的時間范圍
  4. 如遇到問題可查看日志文件

五、更新記錄

v1.0.0 (2024-12-05)

  • 實現基礎數據爬取功能
  • 支持生成分析報告
  • 支持繪制價格走勢圖
  • 支持導出Excel數據

六、聯系方式

作者: xiaohai
僅用于學習交流

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/67141.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67141.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67141.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

3.4 Go函數作用域(標識符)

作用域標識符 簡單來說&#xff0c;作用域指的是標識符可以起作用的范圍&#xff0c;即其可見范圍。將標識符的可見性限制在一定范圍內&#xff0c;這個范圍就是作用域。 把標識符約束在一定的可見范圍內&#xff0c;這個范圍就是作用域。 1. 宇宙塊 特點&#xff1a;預定義…

kaggle比賽入門 - House Prices - Advanced Regression Techniques(第二部分)

本文承接上一篇 1. 分析住宅類型&#xff08;BldgType&#xff09;的分布以及它們與銷售價格&#xff08;SalePrice&#xff09;的關系 # 1. distribution of dwelling types and their relation to sale prices # BldgType: Type of dwellingdwelling_types df[BldgType].v…

使用shell命令安裝virtualbox的虛擬機并導出到vagrant的Box

0. 安裝virtualbox and vagrant [rootolx79vagrant ~]# cat /etc/resolv.conf #search 114.114.114.114 nameserver 180.76.76.76-- install VirtualBox yum install oraclelinux-developer-release-* wget https://yum.oracle.com/RPM-GPG-KEY-oracle-ol7 -O /etc/pki/rpm-g…

【數據結構】空間復雜度

目錄 一、引入空間復雜度的原因 二、空間復雜度的分析 ? 2.1 程序運行時內存大小 ~ 程序本身大小 ? 2.2 程序運行時內存大小 ~ 算法運行時內存大小 ? 2.3 算法運行時內存大小 ? 2.4 不考慮算法全部運行空間的原因 三、空間復雜度 ? 3.1空間復雜度的定義 ? 3.2 空…

MySQL--》深度解析InnoDB引擎的存儲與事務機制

目錄 InnoDB架構 事務原理 MVCC InnoDB架構 從MySQL5.5版本開始默認使用InnoDB存儲引擎&#xff0c;它擅長進行事務處理&#xff0c;具有崩潰恢復的特性&#xff0c;在日常開發中使用非常廣泛&#xff0c;其邏輯存儲結構圖如下所示&#xff0c; 下面是InnoDB架構圖&#xf…

Redis高階5-布隆過濾器

Redis布隆過濾器 ? 由一個初始值都為零的bit數組和多個哈希函數構成&#xff0c;用來快速判斷集合中是否存在某個元素 目的減少內存占用方式不保存數據信息&#xff0c;只是在內存中做一個是否存在的標記flag 布隆過濾器&#xff08;英語&#xff1a;Bloom Filter&#xff0…

DeepSeek學術題目選擇效果怎么樣?

論文選題 一篇出色的論文背后&#xff0c;必定有一個“智慧的選題”在撐腰。選題足夠好文章就能順利登上高水平期刊&#xff1b;選題不行再精彩的寫作也只能“當花瓶”。然而許多寶子們常常忽視這個環節&#xff0c;把大量時間花在寫作上&#xff0c;選題時卻像抓鬮一樣隨便挑一…

第五節 MATLAB命令

本節的內容將提供常用的一些MATLAB命令。 在之前的篇章中我們已經知道了MATLAB數值計算和數據可視化是一個交互式程序&#xff0c;在它的命令窗口中您可以在MATLAB提示符“>>”下鍵入命令。 MATLAB管理會話的命令 MATLAB提供管理會話的各種命令。如下表所示&#xff1a;…

Docker核心命令與Yocto項目的高效應用

隨著軟件開發逐漸向分布式和容器化方向演進&#xff0c;Docker 已成為主流的容器化技術之一。它通過標準化的環境配置、資源隔離和高效的部署流程&#xff0c;大幅提高了開發和構建效率。Yocto 項目作為嵌入式 Linux 系統構建工具&#xff0c;與 Docker 的結合進一步增強了開發…

Qt 5.14.2 學習記錄 —— ?? QFile和多線程

文章目錄 1、QFile1、打開2、讀寫3、關閉4、程序5、其它功能 2、多線程1、演示2、鎖 3、條件變量和信號量 1、QFile Qt有自己的一套文件體系&#xff0c;不過Qt也可以使用C&#xff0c;C&#xff0c;Linux的文件操作。使用Qt的文件體系和Qt自己的一些類型更好配合。 管理寫入讀…

【全棧】SprintBoot+vue3迷你商城-擴展:vue3項目創建及目錄介紹

【全棧】SprintBootvue3迷你商城-擴展&#xff1a;vue3項目創建及目錄介紹 往期的文章都在這里啦&#xff0c;大家有興趣可以看一下 【全棧】SprintBootvue3迷你商城&#xff08;1&#xff09; 【全棧】SprintBootvue3迷你商城&#xff08;2&#xff09; 【全棧】SprintBootvu…

使用Aardio庫在Python中創建桌面應用:簡單指南

引言 隨著軟件開發需求的不斷增長&#xff0c;開發者們需要更加靈活和高效的工具來快速構建應用程序。Python以其簡潔易讀的語法和強大的社區支持而聞名&#xff0c;但在創建圖形用戶界面&#xff08;GUI&#xff09;時&#xff0c;可能會遇到一些挑戰。Aardio作為一種輕量級的…

多版本并發控制:MVCC的作用和基本原理

多版本并發控制&#xff1a;MVCC的作用和基本原理 1、MVCC簡介1.1 快照讀與當前讀的區別1.1.1 快照讀1.1.2 當前讀 1.2 數據庫的讀寫問題1.3 MVCC的作用 2、MVCC實現原理之ReadView2.1 什么是ReadView2.2 ReadView的設計思路2.3 MVCC整體操作流程 1、MVCC簡介 1.1 快照讀與當前…

神經網絡|(二)sigmoid神經元函數

【1】引言 在前序學習進程中&#xff0c;我們已經了解了基本的二元分類器和神經元的構成&#xff0c;文章學習鏈接為&#xff1a; 神經網絡|(一)加權平均法&#xff0c;感知機和神經元-CSDN博客 在此基礎上&#xff0c;我們認識到神經元本身在做二元分類&#xff0c;是一種非…

Qt中QVariant的使用

1.使用QVariant實現不同類型數據的相加 方法&#xff1a;通過type函數返回數值的類型&#xff0c;然后通過setValue來構造一個QVariant類型的返回值。 函數&#xff1a; QVariant mainPage::dataPlus(QVariant a, QVariant b) {QVariant ret;if ((a.type() QVariant::Int) &a…

BAHD酰基轉移酶對紫草素的手性催化-文獻精讀105

Two BAHD Acyltransferases Catalyze the Last Step in the Shikonin/Alkannin Biosynthetic Pathway 兩個BAHD酰基轉移酶催化了紫草素/左旋紫草素生物合成途徑中的最后一步 一個BAHD酰基轉移酶專門催化紫草素的酰基化&#xff0c;而另一個BAHD酰基轉移酶則僅催化紫草素的對映…

Avalonia+ReactiveUI跨平臺路由:打造絲滑UI交互的奇幻冒險

一、引言 在當今數字化時代&#xff0c;跨平臺應用開發已成為大勢所趨。開發者們迫切需要一種高效、靈活的方式&#xff0c;能夠讓應用程序在不同操作系統上無縫運行&#xff0c;為用戶提供一致的體驗。Avalonia 和 ReactiveUI 的組合&#xff0c;宛如一對天作之合的舞者&…

CLion開發Qt桌面

IDE&#xff1a;CLion Qt Qt版本&#xff1a;5.12 學習正點原子的嵌入式Linux開發板時&#xff0c;使用Qt Creator寫代碼不是很方便&#xff0c;遂嘗試使用CLion搭建Qt開發環境。 一、CLion的Qt環境搭建 1&#xff0c;配置工具鏈 找到Qt的安裝目錄&#xff0c;此處為E:\Tools\…

【學術會議-第五屆機械設計與仿真國際學術會議(MDS 2025) 】前端開發:技術與藝術的完美融合

重要信息 大會官網&#xff1a;www.icmds.net 大會時間&#xff1a;2025年02月28日-03月02日 大會地點&#xff1a;中國-大連 會議簡介 2025年第五屆機械設計與仿真國際學術會議&#xff08;MDS 2025) 將于2025年02月28-3月02日在中國大連召開。MDS 2025將圍繞“機械設計”…

《DeepSeek R1:開源大模型的破局者》

驚爆&#xff01;中國開源大模型震撼登場 在人工智能領域的激烈競爭中&#xff0c;一場震撼全球的技術革命正悄然發生。2025 年 1 月 20 日晚&#xff0c;一家來自中國的人工智能初創公司 ——DeepSeek&#xff08;深度求索&#xff09;&#xff0c;如同一顆耀眼的新星&#x…