如何在Python中向.aspx页面提交查询
我需要从一个 .aspx 网页上抓取查询结果。
http://legistar.council.nyc.gov/Legislation.aspx
这个网址是固定的,那我该怎么在这个页面上提交查询并获取结果呢?假设我们需要从相应的下拉菜单中选择“所有年份”和“所有类型”。
一定有人知道怎么做。
5 个回答
Selenium 是一个非常好用的工具,可以用来完成这类任务。你可以指定想要输入的表单值,然后用几行 Python 代码就能获取到响应页面的 HTML 内容。
使用 Selenium,你可能不需要手动去模拟一个有效的提交请求和所有那些隐藏的变量,这一点我在经过很多次尝试后才发现。
大多数ASP.NET网站(包括你提到的那个)其实是通过HTTP POST方式把请求发回给自己,而不是用GET方式。这就是为什么你看到的URL没有变化。
你需要做的是查看生成的HTML代码,抓取所有的表单值。一定要把所有的表单值都抓取到,因为其中有些是用来进行页面验证的,如果没有这些值,你的POST请求会被拒绝。
除了验证之外,ASPX页面在抓取和发送数据方面和其他网页技术没有什么不同。
总的来说,你需要完成四个主要任务:
- 向网站提交请求,
- 从网站获取响应,
- 解析这些响应,
- 根据导航的参数,循环执行以上任务(比如翻到结果列表的“下一页”)。
处理http请求和响应是通过Python标准库中的urllib和urllib2来完成的。解析html页面可以使用Python标准库中的HTMLParser,或者其他模块,比如Beautiful Soup。
下面的代码片段展示了如何在问题中提到的网站上进行搜索请求和接收响应。这个网站是基于ASP技术的,因此我们需要确保发送多个表单字段,其中一些字段的值可能很“糟糕”,因为这些值被ASP逻辑用来维持状态和在一定程度上验证请求。实际上,提交请求时必须使用http POST方法,因为这是这个ASP应用程序所期望的。主要的难点在于识别ASP所期望的表单字段和相关值(用Python获取页面其实是简单的部分)。
这段代码是可以运行的,或者更准确地说,曾经可以运行,直到我删除了大部分VSTATE值,并可能因为添加注释而引入了一两个拼写错误。
import urllib
import urllib2
uri = 'http://legistar.council.nyc.gov/Legislation.aspx'
#the http headers are useful to simulate a particular browser (some sites deny
#access to non-browsers (bots, etc.)
#also needed to pass the content type.
headers = {
'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13',
'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml; q=0.9,*/*; q=0.8',
'Content-Type': 'application/x-www-form-urlencoded'
}
# we group the form fields and their values in a list (any
# iterable, actually) of name-value tuples. This helps
# with clarity and also makes it easy to later encoding of them.
formFields = (
# the viewstate is actualy 800+ characters in length! I truncated it
# for this sample code. It can be lifted from the first page
# obtained from the site. It may be ok to hardcode this value, or
# it may have to be refreshed each time / each day, by essentially
# running an extra page request and parse, for this specific value.
(r'__VSTATE', r'7TzretNIlrZiKb7EOB3AQE ... ...2qd6g5xD8CGXm5EftXtNPt+H8B'),
# following are more of these ASP form fields
(r'__VIEWSTATE', r''),
(r'__EVENTVALIDATION', r'/wEWDwL+raDpAgKnpt8nAs3q+pQOAs3q/pQOAs3qgpUOAs3qhpUOAoPE36ANAve684YCAoOs79EIAoOs89EIAoOs99EIAoOs39EIAoOs49EIAoOs09EIAoSs99EI6IQ74SEV9n4XbtWm1rEbB6Ic3/M='),
(r'ctl00_RadScriptManager1_HiddenField', ''),
(r'ctl00_tabTop_ClientState', ''),
(r'ctl00_ContentPlaceHolder1_menuMain_ClientState', ''),
(r'ctl00_ContentPlaceHolder1_gridMain_ClientState', ''),
#but then we come to fields of interest: the search
#criteria the collections to search from etc.
# Check boxes
(r'ctl00$ContentPlaceHolder1$chkOptions$0', 'on'), # file number
(r'ctl00$ContentPlaceHolder1$chkOptions$1', 'on'), # Legislative text
(r'ctl00$ContentPlaceHolder1$chkOptions$2', 'on'), # attachement
# etc. (not all listed)
(r'ctl00$ContentPlaceHolder1$txtSearch', 'york'), # Search text
(r'ctl00$ContentPlaceHolder1$lstYears', 'All Years'), # Years to include
(r'ctl00$ContentPlaceHolder1$lstTypeBasic', 'All Types'), #types to include
(r'ctl00$ContentPlaceHolder1$btnSearch', 'Search Legislation') # Search button itself
)
# these have to be encoded
encodedFields = urllib.urlencode(formFields)
req = urllib2.Request(uri, encodedFields, headers)
f= urllib2.urlopen(req) #that's the actual call to the http site.
# *** here would normally be the in-memory parsing of f
# contents, but instead I store this to file
# this is useful during design, allowing to have a
# sample of what is to be parsed in a text editor, for analysis.
try:
fout = open('tmp.htm', 'w')
except:
print('Could not open output file\n')
fout.writelines(f.readlines())
fout.close()
这就是获取初始页面的基本过程。如上所述,接下来需要解析页面,也就是找到感兴趣的部分并适当地收集它们,然后存储到文件、数据库或其他地方。这个工作可以通过很多方式完成:使用html解析器,或者XSLT类型的技术(实际上是将html解析为xml后),甚至对于简单的任务,可以使用正则表达式。此外,通常提取的一个项目是“下一步信息”,也就是某种链接,可以用来向服务器发送新请求以获取后续页面。
这应该能让你大致了解什么是“手动”html抓取。还有很多其他的方法,比如专用工具、Mozilla(火狐)浏览器的GreaseMonkey插件中的脚本、XSLT等等。