【问题标题】:Scrape .aspx form with Python使用 Python 抓取 .aspx 表单
【发布时间】:2019-07-22 11:14:31
【问题描述】:

我正在尝试抓取:https://apps.neb-one.gc.ca/CommodityStatistics/Statistics.aspx,这在纸上似乎是一项简单的任务,并且有很多来自其他 SO 问题的资源。尽管如此,无论我如何更改我的请求,我都会遇到同样的错误。

我尝试了以下方法:

import requests
from bs4 import BeautifulSoup

url = "https://apps.neb-one.gc.ca/CommodityStatistics/Statistics.aspx"

with requests.Session() as s:
    s.headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36'}

    response = s.get(url)
    soup = BeautifulSoup(response.content)

     data = {
         "ctl00$MainContent$rdoCommoditySystem": "ELEC",
         "ctl00$MainContent$lbReportName": "171",
         "ctl00$MainContent$ddlFrom": "01/11/2018 12:00:00 AM",
         "ctl00$MainContent$rdoReportFormat": "Excel",
         "ctl00$MainContent$btnView": "View",
         "__EVENTVALIDATION": soup.find('input', {'name':'__EVENTVALIDATION'}).get('value',''),
         "__VIEWSTATE": soup.find('input', {'name': '__VIEWSTATE'}).get('value', ''),
         "__VIEWSTATEGENERATOR": soup.find('input', {'name': '__VIEWSTATEGENERATOR'}).get('value', '')
     }

    response = requests.post(url, data=data)

当我打印 response.contents 对象时,我收到了这条消息(tl;dr,它说 “发生系统错误。系统将提醒技术支持该问题”): p>

b'\r\n\r\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r\n\r\n<html xmlns="http://www.w3.org/1999/xhtml" >\r\n<head><title>\r\n\r\n</title></head>\r\n<body>\r\n   <form name="form1" method="post" action="Error.aspx?ErrorID=86e0c980-7832-4fc5-b5a8-a8254dd8ad69" id="form1">\r\n<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTg3NjI4NzkzNmRkaCA5IA9393/t2iMAptLYU1QiPc8=" />\r\n\r\n<input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="9D6BDE45" />\r\n    <div>\r\n        <h4>\r\n            <span id="lblError">Error</span>\r\n        </h4>\r\n        <span id="lblMessage" class="Validator"><font color="Black">System error occurred. The system will alert technical support of the problem.</font></span>\r\n    </div>\r\n    </form>\r\n</body>\r\n</html>\r\n'

我使用了其他选项,例如更改__EVENTTARGET 参数,如建议的here 那样,还将cookie 从第一个请求传递到POST 请求。检查页面的来源,我注意到表单有一个“查询”功能,需要__EVENTTARGET__EVENTARGUMENT 才能工作:

//<![CDATA[
var theForm = document.forms['aspnetForm'];
if (!theForm) {
    theForm = document.aspnetForm;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}
//]]>

但在 POST 响应的正文中,两个参数都是空的(可以在 Chrome 开发人员检查器中检查)。另一个问题是我需要下载任何格式(PDF 或 Excel)的文件,或者获取 HTML 版本,但是 .ASPX 表单不会在同一页面中呈现信息,它会打开一个新的 url:@ 987654323@ 用信息代替。

我有点迷路了,我错过了什么?

【问题讨论】:

    标签: python html web-scraping beautifulsoup python-requests


    【解决方案1】:

    通过更加小心地处理__VIEWSTATE 值,我成功地解决了这个问题。在 ASPX 表单中,页面使用__VIEWSTATE 来散列网页的状态(即用户已经选择了表单的哪些选项,或者在我们的例子中是请求的),并允许下一个请求。

    在这种情况下:

    1. 请求获取所有标题,将它们存储在payload 中,并通过更新字典添加我的第一个选择。
    2. 使用更新的__VIEWSTATE 值发出第二个请求,并在我的请求中添加更多选项。
    3. 与 2. 相同,但添加了最后一个选项。

    这将使我在使用浏览器发出请求时获得相同的 HTML 正文,但仍然没有向我显示数据,或者允许我下载文件作为最后一个请求正文的一部分。这个问题可以用selenium处理,但是我一直没有成功。 This 中的问题描述了我的问题。

    url = 'https://apps.neb-one.gc.ca/CommodityStatistics/Statistics.aspx'
    
    with requests.Session() as s:
            s.headers = {
                "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36",
                "Content-Type": "application/x-www-form-urlencoded",
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
                "Referer": "https://apps.neb-one.gc.ca/CommodityStatistics/Statistics.aspx",
                "Accept-Encoding": "gzip, deflate, br",
                "Accept-Language": "en-US,en;q=0.9"
            }
    
            response = s.get(url)
            soup = BeautifulSoup(response.content, 'html5lib')
    
            data = { tag['name']: tag['value'] 
                for tag in soup.select('input[name^=ctl00]') if tag.get('value')
                }
            state = { tag['name']: tag['value'] 
                    for tag in soup.select('input[name^=__]')
                }
    
            payload = data.copy()
            payload.update(state)
    
            payload.update({
                "ctl00$MainContent$rdoCommoditySystem": "ELEC",
                "ctl00$MainContent$lbReportName": '76',
                "ctl00$MainContent$rdoReportFormat": 'PDF',
                "ctl00$MainContent$ddlStartYear": "2008",
                "__EVENTTARGET": "ctl00$MainContent$rdoCommoditySystem$2"
            })
    
            print(payload['__EVENTTARGET'])
            print(payload['__VIEWSTATE'][-20:])
    
            response = s.post(url, data=payload, allow_redirects=True)
            soup = BeautifulSoup(response.content, 'html5lib')
    
            state = { tag['name']: tag['value'] 
                     for tag in soup.select('input[name^=__]')
                 }
    
            payload.pop("ctl00$MainContent$ddlStartYear")
            payload.update(state)
            payload.update({
                "__EVENTTARGET": "ctl00$MainContent$lbReportName",
                "ctl00$MainContent$lbReportName": "171",
                "ctl00$MainContent$ddlFrom": "01/12/2018 12:00:00 AM"
            })
    
            print(payload['__EVENTTARGET'])
            print(payload['__VIEWSTATE'][-20:])
    
            response = s.post(url, data=payload, allow_redirects=True)
            soup = BeautifulSoup(response.content, 'html5lib')
    
            state = { tag['name']: tag['value']
                     for tag in soup.select('input[name^=__]')
                    }
    
            payload.update(state)
            payload.update({
                "ctl00$MainContent$ddlFrom": "01/10/1990 12:00:00 AM",
                "ctl00$MainContent$rdoReportFormat": "HTML",
                "ctl00$MainContent$btnView": "View"
            })
    
            print(payload['__VIEWSTATE'])
    
            response = s.post(url, data=payload, allow_redirects=True)
            print(response.text)
    

    【讨论】:

      猜你喜欢
      • 2015-01-29
      • 2021-06-09
      • 2011-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-12
      相关资源
      最近更新 更多