2013-02-20 2 views
4

Я довольно новичок в скручивании, и я пытаюсь сделать асинхронный клиент, который извлекает некоторые URL-адреса и сохраняет результат в отдельный файл для каждого URL-адреса. Когда я запускаю программу с ограниченным количеством серверов, скажем, 10, контур реактора правильно заканчивается, и программа завершается. Но когда я запускаю программу, например, с Alexa top 2500, программа начинает извлекать URL-адреса, но затем не заканчивается. Я установил таймаут, но он не работает, я считаю, что должен быть какой-то открытый сокет, который не вызывает никакого обратного вызова для ошибки или успеха. Моя цель - когда программа выберет страницы или истечет время ожидания для каждого сеанса, программа должна завершить работу и закрыть все дескрипторы файлов.тайм-аут витой утилиты python

Извините, но отступ кода не сохраняется во время копирования и вставки, теперь я проверил и зафиксировал его. Кодекс является минимальным, чтобы привести пример, обратите внимание, что с моей проблемой заключается в том, что реактор не останавливается, когда я запускаю программу с огромным количеством сайтов для сканирования.

#!/usr/bin/env python 

from pprint import pformat 
from twisted.internet import reactor 
import twisted.internet.defer 
import sys 
from twisted.internet.protocol import Protocol 
from twisted.web.client import Agent 
from twisted.web.http_headers import Headers 

class PrinterClient(Protocol): 
    def __init__(self, whenFinished, output): 
     self.whenFinished = whenFinished 
     self.output = output 

    def dataReceived(self, bytes): 
     #print '##### Received #####\n%s' % (bytes,) 
     self.output.write('%s' % (bytes,)) 

    def connectionLost(self, reason): 
     print 'Finished:', reason.getErrorMessage() 
     self.output.write('Finished: %s \n'%(reason.getErrorMessage())) 
     self.output.write('#########end########%s\n'%(reason.getErrorMessage())) 
     self.whenFinished.callback(None) 

def handleResponse(r, output, url): 
    output.write('############start############\n') 
    output.write('%s\n'%(url)) 
    #print "version=%s\ncode=%s\nphrase='%s'" % (r.version, r.code, r.phrase) 
    output.write("version=%s\ncode=%s\nphrase='%s'"\ 
      %(r.version, r.code, r.phrase)) 
    for k, v in r.headers.getAllRawHeaders(): 
     #print "%s: %s" % (k, '\n '.join(v)) 
     output.write("%s: %s\n" % (k, '\n '.join(v))) 
    whenFinished = twisted.internet.defer.Deferred() 
    r.deliverBody(PrinterClient(whenFinished, output)) 
    return whenFinished 

def handleError(reason): 
    print reason 
    #reason.printTraceback() 
    #reactor.stop() 

def getPage(url, output): 
    print "Requesting %s" % (url,) 
    d = Agent(reactor).request('GET', 
         url, 
    Headers({'User-Agent': ['Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_26']}), 
         None) 
    d._connectTimeout = 10 
    d.addCallback(handleResponse, output, url) 
    d.addErrback(handleError) 
    return d 

if __name__ == '__main__': 
    semaphore = twisted.internet.defer.DeferredSemaphore(500) 
    dl = list() 
    ipset = set() 
    queryset = set(['http://www.google.com','http://www.google1.com','http://www.google2.com', "up to 2500 sites"]) 
    filemap = {} 
    for q in queryset: 
     fpos = q.split('http://')[1].split(':')[0] 
     dl.append(semaphore.run(getPage, q, filemap[fpos])) 
    dl = twisted.internet.defer.DeferredList(dl) 
    dl.addCallbacks(lambda x: reactor.stop(), handleError) 
    reactor.run() 
    for k in filemap: 
     filemap[k].close() 

Спасибо. Jeppo

+0

Слишком много кода, можно ли сузить его до минимального рабочего примера, который показывает ошибку? –

+1

Не так много кода, это синтаксически недействительно. Для начала исправьте отступ. – Glyph

ответ

6

По крайней мере, две проблемы с вашим тайм-аутом.

Во-первых, единственный тайм-аут, который вы установили: _connectTimeout, и вы установили его на Deferred, возвращенном с Agent.request. Это бессмысленный атрибут, и ничто в реализации Agent, ни какая-либо часть Twisted не будет уважать его. Я думаю, вы хотели бы установить этот атрибут на экземпляр Agent вместо этого, где бы это повлияло. Тем не менее, это частный атрибут, который не предназначен для непосредственного взаимодействия. Вместо этого вы должны передать connectTimeout=10 в инициализатор Agent.

Во-вторых, этот таймаут влияет только на тайм-аут установления соединения TCP. Установка его на 10 означает, что если TCP-соединение с HTTP-сервером для определенного URL-адреса не может быть установлено менее чем за 10 секунд, попытка запроса будет провалена с ошибкой таймаута. Если соединение успешно установлено менее чем за 10 секунд, время ожидания больше не имеет значения. Если серверу требуется 10 часов, чтобы отправить вам ответ, Agent будет сидеть и ждать 10 часов. Вам нужен дополнительный тайм-аут, тайм-аут полного запроса.

Это что-то реализовать отдельно, используя reactor.callLater и, возможно, Deferred.cancel. Например,

... 
d = agent.request(...) 
timeoutCall = reactor.callLater(60, d.cancel) 
def completed(passthrough): 
    if timeoutCall.active(): 
     timeoutCall.cancel() 
    return passthrough 
d.addBoth(completed) 
... 
Смежные вопросы