在Eventlet页面刮刀中维护会话?

时间:2010-02-19 08:16:13

标签: python parallel-processing screen-scraping

我正在尝试抓取需要身份验证的网站(而非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)

建立会话并不简单;我必须加载登录页面,从登录表单中提取一些变量,然后发送带有auth详细信息和那些变量的POST请求。会话结束后,其余的请求都是简单的GET请求。

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

3 个答案:

答案 0 :(得分:4)

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

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

然后你使用那​​个开启者来做你需要的任何身份验证,并且所有会话状态都将保留在opener对象本身内。然后,您可以将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

答案 1 :(得分:1)

如下所示,请使用mechanize。它会处理低级细节,比如cookie管理。

但是,要使第三方库与eventlet一起工作,您需要将stdlib中的socket和ssl对象替换为异步引擎。

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

  

来自gevent import monkey; monkey.patch_all()

然后第三方图书馆才能正常工作。

这是一个example

答案 2 :(得分:0)

您可以使用the mechanize library来简化会话建设,然后使用其中一种不同的线程/多处理技术,例如threading pool recipe(首先点击谷歌,可能有点矫枉过正,请务必阅读评论)。