2009-09-26 51 views

回答

25

作为概述,您将需要执行四项主要任务:

  • 提交请求(S)的网站,
  • 检索从网站的响应(S)
  • 解析这些反应
  • 有一定的逻辑在上面的任务进行迭代,与导航相关的(在结果列表“下一个”页)
参数

http请求和响应处理是使用Python标准库的urlliburllib2中的方法和类完成的。 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 ...

+0

如果我使用Google Chrome,那么我应该如何替换“HTTP_USER_AGENT”的值?我很抱歉,如果这个问题是愚蠢的,因为我没有做很多网络的东西。谢谢! – taocp 2013-09-17 01:14:06

+0

@taocp,一个简单的方法来知道什么'HTTP_USER_AGENT'字符串用于给定的浏览器是访问http://www.all-nettools.com/toolbox/environmental-variables-test.php此页面将告诉你浏览器发送的头部值,查找“HTTP_USER_AGENT”。实际的字符串取决于操作系统以及Chrome的特定版本和内部版本,但应该看起来像Mozilla/5.0(Windows NT 6.1; WOW64)AppleWebKit/537.36(KHTML,如Gecko)Chrome/29.0.1547.66 Safari/537.36' – mjv 2013-09-17 04:26:02

+0

非常感谢您的回复。我试着用适当的值设置到我的Chrome浏览器的代码。结果tmp.htm文件显示“找不到结果”,而当我在网站上放置“约克”时,它会返回很多结果。你知道为什么吗? – taocp 2013-09-19 00:29:41

0

”假设我们需要从相应的下拉菜单中选择“所有年份”和“所有类型”。“

做这些选择做与在最终提交的URL。

毕竟,它相当于通过urllib2发送的HTTP请求。

知道如何“从各自的下拉菜单‘所有年份’和‘所有类型’”你做以下事情。

  1. 选择“从各自的下拉菜单‘所有年份’和‘所有类型的’”

  2. 注意这实际上是提交的URL。

  3. 使用此URL中urllib2

+0

显然,网页是需要POST形式,但这个想法是一样的:注意的表单字段名称以及与“所有年份”相关的值以及所有类型的值,并使用urlib2.Request获取数据。 – mjv 2009-09-26 03:38:36

+0

我使用查尔斯网络调试代理来观看所有的http流量,当我冲浪这个网站并提交查询时,这个网址是完全静态的。它根本不包含任何参数。有一些表单数据会以某种方式传递 - 我猜 - ajax - 但我不知道如何将表单数据提交给服务器。这一切对我来说都是无法理解的。我无法通过操作url来提交查询这一事实令我困惑。 – twneale 2009-09-26 03:39:43

+0

一旦你从这个页面得到结果,如果你希望得到结果,你可以使用python模块HTMLParser或Beautifulsoup来解析html页面。此外,抓取可能会涉及更多的urlib2调用,以导航到结果的下一页。 – mjv 2009-09-26 03:41:06

4

大多数ASP.NET网站(包括您引用的网站)实际上都会使用HTTP POST动词而不是GET动词将其查询发回给自己。这就是为什么这个URL没有像你指出的那样变化。

你需要做的是看看生成的HTML并捕获所有的表单值。一定要捕获所有的表单值,因为它们中的一些用于页面验证,没有它们,您的POST请求将被拒绝。

除了验证之外,关于抓取和发布的ASPX页面与其他网络技术没有区别。

5

Selenium是使用这类任务的一大利器。您可以指定想要输入的表单值,并在几行python代码中以字符串形式检索响应页面的html。 使用Selenium可能不需要手动模拟一个有效的发布请求及其所有隐藏变量,因为我经过多次试验和错误发现。

+1

你能提供一些代码片段吗? – taocp 2013-09-12 02:19:48

+0

我成功地连接,登录和点击使用硒的链接我被困在你想从页面抓取数据的部分。由于即使点击后URI也保持不变,这会产生问题。 – 2017-01-12 10:47:31

4

其他答案中的代码很有用;如果没有它,我永远不会写我的抓取工具。

我遇到的一个问题是cookies。我在地上爬来爬去,该网站使用Cookies来记录会话ID /安全性的东西,所以我不得不添加代码让我的履带式工作:

添加此导入:

import cookielib    

初始化cookie的东西:

COOKIEFILE = 'cookies.lwp'   # the path and filename that you want to use to save your cookies in 
    cj = cookielib.LWPCookieJar()  # This is a subclass of FileCookieJar that has useful load and save methods 

安装CookieJar使得它用作默认打开处理器默认CookieProcessor

cj.load(COOKIEFILE) 
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) 
    urllib2.install_opener(opener) 

要查看该网站是否使用什么饼干:

print 'These are the cookies we have received so far :' 

    for index, cookie in enumerate(cj): 
     print index, ' : ', cookie   

这节省了饼干:

cj.save(COOKIEFILE)      # save the cookies