Python中类似于Perl的HTTP::Async->next_response的等效方法

3
我正在寻找一种方法来实现类似于Perl的HTTP::Async模块中next_response方法的功能。
HTTP::Async模块不会生成任何后台线程,也不使用任何回调函数。相反,每当任何人(在我的情况下是主线程)在对象上调用next_response时,已由操作系统接收到的所有数据都将被读取(阻塞,但瞬间完成,因为它只处理已经接收到的数据)。如果这是响应的结尾,则next_response返回一个HTTP::Response对象,否则返回undef。
使用此模块的示例代码如下(伪代码):
request = HTTP::Async(url)
do:
    response = request->next_response()
    if not response:
        sleep 5 # or process events or whatever
while not response

# Do things with response

据我所知,Python的urllib或http.client不支持这种样式。至于为什么我想以这种方式做:
- 这是用于嵌入式Python环境的,我无法启动线程,也不能让Python生成任何线程。 - 我只能使用一个线程,而实际上这个线程是嵌入应用程序的线程。这意味着我也不能有任何延迟的回调——应用程序决定何时运行我的Python代码。我所能做的就是请求嵌入式应用程序每50毫秒调用我选择的回调函数。 在Python中有没有办法做到这一点?
供参考,以下是我目前拥有的Perl代码,并且我希望将其移植到Python中:
httpAsync = HTTP::Async->new()

sub httpRequestAsync {
    my ($url, $callback) = @_; # $callback will be called with the response text

    $httpAsync->add(new HTTP::Request(GET => $url));

    # create_timer causes the embedding application to call the supplied callback every 50ms
    application::create_timer(50, sub {
        my $timer_result = application::keep_timer;
        my $response = $httpAsync->next_response;
        if ($response) {
            my $responseText = $response->decoded_content;
            if ($responseText) {
                $callback->($responseText);
            }
            $timer_result = application::remove_timer;
        }

        # Returning application::keep_timer will preserve the timer to be called again.
        # Returning application::remove_timer will remove the timer.
        return $timer_result;
    });
}

httpRequestAsync('http://www.example.com/', sub {
    my $responseText = $_[0];
    application::display($responseText);
});

编辑:考虑到这是针对嵌入式Python实例的,我将尽可能提供所有可行的选择(包括标准库或其他选择),因为我必须评估它们以确保它们可以在我的特定限制下运行。

1个回答

0
注意:如果您只想在调用数据接收时检索数据,只需向handle_receive添加一个标志,并将其添加到handle_receive内的睡眠块中,这样您只有在调用函数时才能获得数据。
#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
import asyncore, errno
from socket import AF_INET, SOCK_STREAM
from time import sleep

class sender():
    def __init__(self, sock_send):
        self.s = sock_send
        self.bufferpos = 0
        self.buffer = {}
        self.alive = 1

    def send(self, what):
        self.buffer[len(self.buffer)] = what

    def writable(self):
        return (len(self.buffer) > self.bufferpos)

    def run(self):
        while self.alive:
            if self.writable():
                logout = str([self.buffer[self.bufferpos]])
                self.s(self.buffer[self.bufferpos])
                self.bufferpos += 1
            sleep(0.01)

class SOCK(asyncore.dispatcher):
    def __init__(self, _s=None, config=None):
        self.conf = config
        Thread.__init__(self)

        self._s = _s

        self.inbuffer = ''
        #self.buffer = ''
        self.lockedbuffer = False
        self.is_writable = False

        self.autounlockAccounts = {}

        if _s:
            asyncore.dispatcher.__init__(self, _s)
            self.sender = sender(self.send)

        else:
            asyncore.dispatcher.__init__(self)
            self.create_socket(AF_INET, SOCK_STREAM)
            #if self.allow_reuse_address:
            #   self.set_resue_addr()

            self.bind((self.conf['SERVER'], self.conf['PORT']))
            self.listen(5)

            self.sender = None

        self.start()

    def parse(self):
        self.lockedbuffer = True

        ## Parse here
        print self.inbuffer

        self.inbuffer = ''
        self.lockedbuffer = False

    def readable(self):
        return True
    def handle_connect(self):
        pass
    def handle_accept(self):
        (conn_sock, client_address) = self.accept()
        if self.verify_request(conn_sock, client_address):
            self.process_request(conn_sock, client_address)
    def process_request(self, sock, addr):
        x = SOCK(sock, config={'PARSER' : self.conf['PARSER'], 'ADDR' : addr[0], 'NAME' : 'CORE_SUB_SOCK_('+str(addr[0]) + ')'})
    def verify_request(self, conn_sock, client_address):
        return True
    def handle_close(self):
        self.close()
            if self.sender:
                self.sender.alive = False
    def handle_read(self):
        data = self.recv(8192)
        while self.lockedbuffer:
            sleep(0.01)
        self.inbuffer += data
    def writable(self):
        return True
    def handle_write(self):
        pass

    def run(self):
            if not self._s:
            asyncore.loop()

imap = SOCK(config={'SERVER' : '', 'PORT' : 6668})
imap.run()

while 1
    sleep(1)

类似这样的东西? Asyncore套接字,当有数据接收时始终将其附加到输入缓冲区中。
您可以根据需要进行修改,我只是从另一个项目中粘贴了一段代码,恰好是线程化的 :)
最后尝试:
class EchoHandler(asyncore.dispatcher_with_send):

    def handle_read(self):
        data = self.recv(8192)
        if data:
            self.send(data)

谢谢,但正如我在问题中所说,嵌入式环境的限制阻止我启动新线程。 - Arnavion
这是一个“你能做什么”的答案,修改它,尝试一下并且摆弄它。 - Torxed
没错,那可以运行,可惜你不能使用线程,因为就像我在第一个例子中所做的那样,我只是将asyncore.loop()放入了一个线程中,这样它就是非阻塞的 :) - Torxed
有没有一种简单的方法可以在这个上面放置一个HTTP响应解析器?如果可以的话,我不想自己解析头并提取正文(当然只需要跳过一些行)。 - Arnavion
@Arnavion 我会选择BeautifulSoup来解析HTML。 soup = BeautifulSoup(self.inbuffer),然后就有一个完美的对象可以获取和处理数据。除此之外,如果不是通过http://docs.python.org/2/library/htmlparser.html手动构建,你就必须自己构建。 - Torxed
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接