代码之家  ›  专栏  ›  技术社区  ›  kbanman

在事件页面刮刀中维护会话?

  •  3
  • kbanman  · 技术社区  · 15 年前

    我正在尝试对一个需要身份验证(而不是HTTP身份验证)的站点进行一些抓取。我使用的脚本是基于 this eventlet example . 基本上,

    urls = ["https://mysecuresite.com/data.aspx?itemid=blah1",
         "https://mysecuresite.com/data.aspx?itemid=blah2",
         "https://mysecuresite.com/data.aspx?itemid=blah3"]
    
    import eventlet
    from eventlet.green import urllib2  
    
    def fetch(url):
      print "opening", url
      body = urllib2.urlopen(url).read()
      print "done with", url
      return url, body
    
    pool = eventlet.GreenPool(10)
    for url, body in pool.imap(fetch, urls):
      print "got body from", url, "of length", len(body)
    

    建立会话一点也不简单;我必须加载登录页面,从登录表单中提取一些变量,然后发送一个包含身份验证详细信息和这些变量的POST请求。在会话良好之后,其余的请求都是简单的GET请求。

    使用上述代码作为参考点,我将如何创建池其余部分将使用的会话?(我需要随后的请求同时提出)

    3 回复  |  直到 15 年前
        1
  •  4
  •   rdw    15 年前

    我不是这方面的专家,但看起来用urllib2维护会话状态的标准方法是为每个会话创建一个自定义的Opener实例。看起来是这样的:

    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
    

    然后,您可以使用这个开放器来做您必须做的任何身份验证,并且所有会话状态都将保留在开放器对象本身中。然后可以将opener对象作为并行请求的参数传递。

    下面是一个示例脚本,它为多个用户并行登录secondlife.com,并为每个用户并行发出多个页面请求。这个特定站点的登录过程很复杂,因为它需要从第一个请求捕获CSRF令牌,然后才能使用第二个请求登录。因此,登录方法非常混乱。不过,对于你感兴趣的任何网站,原则都应该是相同的。

    import eventlet
    from eventlet.green import urllib2
    import re
    
    login_url = 'https://secure-web28.secondlife.com/my/account/login.php?lang=en&type=second-life-member&nextpage=/my/index.php?lang=en'
    
    pool = eventlet.GreenPool(10)
    
    def fetch_title(opener, url):
        match = re.search(r'<title>(.*)</title>', opener.open(url).read())
        if match:
            return match.group(1)
        else:
            return "no title"
    
    def login(login_url, fullname, password):
        opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
        login_page = opener.open(login_url).read()
        csrf_token = re.search(r'<input type="hidden" name="CSRFToken" value="(.*)"/>', login_page).group(1)
        username, lastname = fullname.split()
        auth = "CSRFToken=%s&form[type]=second-life-member&form[nextpage]=/my/index.php?lang=en&form[persistent]=Y&form[form_action]=Log%%20In&form[form_lang]=en&form[username]=%s&form[lastname]=%s&form[password]=%s&submit=Submit" % (
            csrf_token, username, lastname, password)
        logged_in = opener.open(login_url, auth).read()
        return opener
    
    
    def login_and_fetch(login_url, fullname, password, page_urls):
        opener = login(login_url, fullname, password)
        # note that this deliberately uses the global pool
        pile = eventlet.GreenPile(pool)
        for url in page_urls:
            pile.spawn(fetch_title, opener, url)
    
        return pile
    
    login_urls = [login_url] *2
    usernames = [...]
    passwords = [...]
    page_urls = [['https://secure-web28.secondlife.com/my/account/?lang=en-US',
            'https://secure-web28.secondlife.com/my/community/events/index.php?lang=en-US']] * 2
    
    for user_iter in pool.imap(login_and_fetch, login_urls, usernames, passwords, page_urls):
        for title in user_iter:
            print "got title", title
    
        2
  •  1
  •   Denis    15 年前

    如下建议,使用 mechanize . 它将处理低级的细节,比如为您管理cookie。

    但是,要使第三方库与eventlet一起工作,您需要用一些在后台异步的东西替换stdlib中的socket和ssl对象。

    这在eventlet中是可行的,但这里不是很简单。 我建议使用 gevent 你要做的就是

    来自gevent import monkey;monkey.patch_all()

    然后第三方图书馆就可以工作了。

    这是一个 example .

        3
  •  0
  •   badp    15 年前

    你可以使用 the mechanize library 为了使会话建立更容易,然后使用如下不同的线程/多处理技术之一 threading pool recipe (第一次点击谷歌,可能有点过头了,确保你读了评论)。