Archive

Posts Tagged ‘python’

记一个subclass gevent.Greenlet遇到的问题

January 22nd, 2014 No comments

打算子类化gevent.Greenlet,大致样子如下:


from gevent import Greenlet
class Runner( Greenlet ):
  def __init__(self):
      Greenlet.__init__(self)
  def _run( self ):
      self.run() # invoke the real run

class Downloader( Runner ):
  def __init__(self):
      Runner.__init__(self)
  def run(self):
      print "processing ..."

d = Downloader()
d.start()
d.join()

结果一运行就出错了,

processing ...
Traceback (most recent call last):
  File "test.py", line 30, in <module>
    d.join()
  File "/usr/local/lib/python2.7/dist-packages/gevent/greenlet.py", line 290, in join
    result = self.parent.switch()
  File "/usr/local/lib/python2.7/dist-packages/gevent/hub.py", line 331, in switch
    return greenlet.switch(self)
gevent.hub.LoopExit: This operation would block forever

一时间没有搞懂是怎么回事,于是google了一下,但是看起来别人的问题和我这个不相关。
于是看了一下greenlet.py里面的Greenlet的时间,结果发现一个问题Greenlet里面有一个run函数。所以我这里的Downloader申明run函数以后覆盖了Greenlet本身的run,结果就出现了你看到的这个错误。 所以解决方法也很简单,就是不要用run这个名字。

Categories: programming Tags: ,

使用Twisted的HTTPDownloader通过socks代理下载网页

January 15th, 2012 No comments

心血来潮想要学习一下使用twsited库如何下载网页内容。同时为了能下载一下在天朝不可见的网页,我这里需要使用ssh代理。我在网上下载了一个为twsited制作的socks5的客户端。但是使用的时候发现有的时候和twsited.web.client.HTTPDownloader一起使用会出现问题。不得以查看了一下twisted.web.client.HTTPPageGetter的代码。发现当第一次GET请求但是服务器返回301的时候,HTTPPageGetter不会使用socks代理的地址去创建一个新的连接。所以我做了如下的改动。

首先,在socks5.py的ClientFactory的初始化函数中对otherFactory加入一个指向自己的引用(习惯了这种叫法)。

class ClientFactory (protocol.ClientFactory):
    def __init__(self, sockshost, socksport, host, port, otherFactory,
        method="CONNECT", login=None, password=None, timeout=60,
        readableID=None):
        ...
        self.sockshost      = sockshost
        self.socksport      = socksport
        self.host           = host
        self.port           = port
        self.method         = method
        self.login          = login
        self.password       = password
        self.otherFactory   = otherFactory
        otherFactory.otherFactory = self #加入一个指向自己的引用,这样就可以在HTTPPageGetter的派生类中使用自己的属性
        self.timeout        = timeout
        self.readableID     = readableID

然后就是从HTTPPageDownloader派生一个类,并且overwrite其中的handleStatus_301方法,主要是在建立一个新的连接的时候使用socks5.ClientFactory而不是默认是的HTTPDownloader。如下:

class SocksPageDownloader(client.HTTPPageDownloader):
    def handleStatus_301(self):
        l = self.headers.get('location')
        if not l:
            self.handleStatusDefault()
            return
        url = l[0]
        if self.followRedirect:
            scheme, host, port, path = client._parse(url, defaultPort=self.factory.otherFactory.port )
            self.factory._redirectCount += 1
            if self.factory._redirectCount >= self.factory.redirectLimit:
                err = error.InfiniteRedirection(self.status,'Infinite redirection detected',location=url)
                self.factory.noPage(failure.Failure(err))
                self.quietLoss = True
                self.transport.loseConnection()
                return
            self._completelyDone = False
            self.factory.setURL(url)

            sockhost = self.factory.otherFactory.sockshost#使用socks server的地址进行连接
            sockport = self.factory.otherFactory.socksport

            self.factory.otherFactory.host = host#修改sock5.ClientFactory的属性,以便连接到新的地址上

            if self.factory.scheme == 'https':
                from twisted.internet import ssl
                contextFactory = ssl.ClientContextFactory()
                reactor.connectSSL(sockhost, sockport,self.factory.otherFactory, contextFactory)
            else:#在connectTCP里面传入的factory是socks5.ClientFactory
                reactor.connectTCP( sockhost, sockport,self.factory.otherFactory)
        else:
            self.handleStatusDefault()
            self.factory.noPage( failure.Failure(error.PageRedirect(self.status, self.message, location = url)))
        self.quietLoss = True
        self.transport.loseConnection()

另外建立一个HTTPDownloader的配生类并且修改其中的protocol为SockPageDownloader即可

class SocksDownloaderFactory(client.HTTPDownloader):
    protocol = SockPageDownloader

下面是一段测试代码

def download():
    hd = SockDownloaderFactory("http://www.twitter.com","./foo")
    cf = socks5.ClientFactory("127.0.0.1",7070,"www.twitter.com",80,hd)
    reactor.connectTCP("127.0.0.1",7070,cf)#127.0.0.1:7070是socks代理的地址
    reactor.run()

如此就可以使用twsited的HTTPDownloader通过socks5代理下载网页数据了

downloadviasocks.tar

Categories: programming Tags: ,