AGMARKNET网站多页面表格数据爬取求助
我尝试从AGMARKNET网站爬取苹果在查谟和克什米尔地区的历史价格数据,目标表格分为11页,但所有页面使用同一URL。作为网页爬取和Python新手,因为网站没有公开API,只能通过爬取页面获取数据。目前用BeautifulSoup只能爬取第一页的500条数据,无法获取全部11页的完整数据,求解决方向。
目标链接:https://agmarknet.gov.in/SearchCmmMkt.aspx?Tx_Commodity=17&Tx_State=JK&Tx_District=0&Tx_Market=0&DateFrom=01-Oct-2004&DateTo=18-Oct-2022&Fr_Date=01-Oct-2004&To_Date=18-Oct-2022&Tx_Trend=2&Tx_CommodityHead=Apple&Tx_StateHead=Jammu+and+Kashmir&Tx_DistrictHead=--Select--&Tx_MarketHead=--Select--
当前代码:
import requests import pandas as pd from bs4 import BeautifulSoup # 补充原代码缺失的导入 url = 'https://agmarknet.gov.in/SearchCmmMkt.aspx?Tx_Commodity=17&Tx_State=JK&Tx_District=0&Tx_Market=0&DateFrom=01-Oct-2004&DateTo=18-Oct-2022&Fr_Date=01-Oct-2004&To_Date=18-Oct-2022&Tx_Trend=2&Tx_CommodityHead=Apple&Tx_StateHead=Jammu+and+Kashmir&Tx_DistrictHead=--Select--&Tx_MarketHead=--Select--' response = requests.get(url) # 解析HTML soup = BeautifulSoup(response.content, 'html.parser') # 定位目标表格并转为Tag对象 stat_table = soup.find_all('table', {'class': 'tableagmark_new'}) stat_table = stat_table[0] # 提取表格数据到列表 rows = [] for tr in stat_table.find_all('tr')[1:]: cells = [] tds = tr.find_all('td') if len(tds) == 0: ths = tr.find_all('th') for th in ths: cells.append(th.text.strip()) else: for td in tds: cells.append(td.text.strip()) rows.append(cells) # 转为DataFrame table = pd.DataFrame(rows)
这类ASP.NET网站的分页不依赖URL参数跳转,而是通过POST请求提交ViewState等隐藏验证字段加载新页面,具体实现步骤如下:
提取页面核心隐藏字段
第一次GET请求后,从页面中提取ASP.NET验证必需的三个字段:__VIEWSTATE、__VIEWSTATEGENERATOR、__EVENTVALIDATION,这些值每次页面刷新都会变化,必须动态获取:viewstate = soup.find('input', {'id': '__VIEWSTATE'})['value'] viewstategenerator = soup.find('input', {'id': '__VIEWSTATEGENERATOR'})['value'] eventvalidation = soup.find('input', {'id': '__EVENTVALIDATION'})['value']构造分页POST请求参数
通过浏览器开发者工具(F12)查看分页按钮的网络请求,复制Form Data中的所有参数,其中关键的分页参数是:__EVENTTARGET:分页控件的ID(比如ctl00$ContentPlaceHolder1$gvResults)__EVENTARGUMENT:分页指令(比如Page$2表示第2页)
同时需要保留原查询的商品、地区、日期等固定参数。
循环爬取所有页面
- 使用
requests.Session()保持会话,避免重复建立连接 - 初始化空DataFrame存储全量数据
- 循环遍历1到11页,每次提交POST请求后,更新隐藏字段值,解析当前页表格数据并追加到全量DataFrame中
- 添加请求延迟(如
time.sleep(1)),防止请求频率过高被封禁
- 使用
完整示例代码
import time import requests import pandas as pd from bs4 import BeautifulSoup base_url = 'https://agmarknet.gov.in/SearchCmmMkt.aspx?Tx_Commodity=17&Tx_State=JK&Tx_District=0&Tx_Market=0&DateFrom=01-Oct-2004&DateTo=18-Oct-2022&Fr_Date=01-Oct-2004&To_Date=18-Oct-2022&Tx_Trend=2&Tx_CommodityHead=Apple&Tx_StateHead=Jammu+and+Kashmir&Tx_DistrictHead=--Select--&Tx_MarketHead=--Select--' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36' } # 初始化全量数据容器 all_data = pd.DataFrame() # 建立会话并获取初始页面参数 session = requests.Session() response = session.get(base_url, headers=headers) soup = BeautifulSoup(response.content, 'html.parser') # 提取初始验证字段 viewstate = soup.find('input', {'id': '__VIEWSTATE'})['value'] viewstategenerator = soup.find('input', {'id': '__VIEWSTATEGENERATOR'})['value'] eventvalidation = soup.find('input', {'id': '__EVENTVALIDATION'})['value'] # 解析第一页数据 stat_table = soup.find_all('table', {'class': 'tableagmark_new'})[0] rows = [] for tr in stat_table.find_all('tr')[1:]: cells = [] tds = tr.find_all('td') if len(tds) == 0: ths = tr.find_all('th') for th in ths: cells.append(th.text.strip()) else: for td in tds: cells.append(td.text.strip()) rows.append(cells) df_page = pd.DataFrame(rows[1:], columns=rows[0]) all_data = pd.concat([all_data, df_page], ignore_index=True) # 循环爬取第2到11页 for page_num in range(2, 12): time.sleep(1) # 构造POST请求参数 form_data = { '__VIEWSTATE': viewstate, '__VIEWSTATEGENERATOR': viewstategenerator, '__EVENTVALIDATION': eventvalidation, '__EVENTTARGET': 'ctl00$ContentPlaceHolder1$gvResults', '__EVENTARGUMENT': f'Page${page_num}', 'ctl00$ContentPlaceHolder1$ddlCommodity': '17', 'ctl00$ContentPlaceHolder1$ddlState': 'JK', 'ctl00$ContentPlaceHolder1$ddlDistrict': '0', 'ctl00$ContentPlaceHolder1$ddlMarket': '0', 'ctl00$ContentPlaceHolder1$txtDateFrom': '01-Oct-2004', 'ctl00$ContentPlaceHolder1$txtDateTo': '18-Oct-2022', 'ctl00$ContentPlaceHolder1$ddlTrend': '2' } # 发送POST请求并解析页面 response = session.post(base_url, data=form_data, headers=headers) soup = BeautifulSoup(response.content, 'html.parser') # 更新验证字段 viewstate = soup.find('input', {'id': '__VIEWSTATE'})['value'] viewstategenerator = soup.find('input', {'id': '__VIEWSTATEGENERATOR'})['value'] eventvalidation = soup.find('input', {'id': '__EVENTVALIDATION'})['value'] # 解析当前页数据 stat_table = soup.find_all('table', {'class': 'tableagmark_new'})[0] rows = [] for tr in stat_table.find_all('tr')[1:]: cells = [] tds = tr.find_all('td') if len(tds) == 0: ths = tr.find_all('th') for th in ths: cells.append(th.text.strip()) else: for td in tds: cells.append(td.text.strip()) rows.append(cells) df_page = pd.DataFrame(rows[1:], columns=rows[0]) all_data = pd.concat([all_data, df_page], ignore_index=True) # 保存数据到CSV all_data.to_csv('agmarknet_apple_data.csv', index=False)
内容的提问来源于stack exchange,提问作者Aqib

