4 votes

Python + splinter + http : Erreur - httplib.ResponseNotReady

Avec splinter et Python, j'ai deux threads en cours d'exécution, chacun visitant la même URL principale mais avec des itinéraires différents, par exemple, le premier thread arrive : mainurl.com/threadone et filent deux coups : mainurl.com/threadtwo en utilisant :

from splinter import Browser
browser = Browser('chrome')

Mais je suis tombé sur l'erreur suivante :

Traceback (most recent call last):
  File "multi_thread_practice.py", line 299, in <module>
    main()
  File "multi_thread_practice.py", line 290, in main
    first_method(r)
  File "multi_thread_practice.py", line 195, in parser
    second_method(title, name)
  File "multi_thread_practice.py", line 208, in confirm_product
    third_method(current_url)
  File "multi_thread_practice.py", line 214, in buy_product
    browser.visit(url)
  File "/Users/joshua/anaconda/lib/python2.7/site-packages/splinter/driver/webdriver/__init__.py", line 184, in visit
    self.driver.get(url)
  File "/Users/joshua/anaconda/lib/python2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 261, in get
    self.execute(Command.GET, {'url': url})
  File "/Users/joshua/anaconda/lib/python2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 247, in execute
    response = self.command_executor.execute(driver_command, params)
  File "/Users/joshua/anaconda/lib/python2.7/site-packages/selenium/webdriver/remote/remote_connection.py", line 464, in execute
    return self._request(command_info[0], url, body=data)
  File "/Users/joshua/anaconda/lib/python2.7/site-packages/selenium/webdriver/remote/remote_connection.py", line 488, in _request
    resp = self._conn.getresponse()
  File "/Users/joshua/anaconda/lib/python2.7/httplib.py", line 1108, in getresponse
    raise ResponseNotReady()
httplib.ResponseNotReady

Quelle est l'erreur et comment dois-je procéder pour résoudre le problème ?

Merci d'avance et je ne manquerai pas de voter ou d'accepter la réponse.

CODE AJOUTE

import time
from splinter import Browser
import threading

browser = Browser('chrome')

start_time = time.time()

urlOne = 'http://www.practiceurl.com/one'
urlTwo = 'http://www.practiceurl.com/two'
baseUrl = 'http://practiceurl.com'

browser.visit(baseUrl)

def secondThread(url):
    print 'STARTING 2ND REQUEST: ' + str(time.time() - start_time)
    browser.visit(url)
    print 'END 2ND REQUEST: ' + str(time.time() - start_time)

def mainThread(url):
    print 'STARTING 1ST REQUEST: ' + str(time.time() - start_time)
    browser.visit(url)
    print 'END 1ST REQUEST: ' + str(time.time() - start_time)

def main():
    threadObj = threading.Thread(target=secondThread, args=[urlTwo])
    threadObj.daemon = True

    threadObj.start()

    mainThread(urlOne)

main()

2voto

Generic Snake Points 400

Pour autant que je sache, ce que tu essaies de faire n'est pas possible sur un seul navigateur. Splinter agit sur un navigateur réel, et en tant que tel, passer plusieurs commandes en même temps pose des problèmes. Il agit exactement comme un humain interagirait avec un navigateur (automatisé bien sûr). Il est possible d'ouvrir plusieurs fenêtres de navigateur, mais vous ne pouvez pas envoyer de demandes dans un autre thread sans recevoir la réponse de la demande précédente. Cela provoque une erreur CannotSendRequest. Donc, ce que je recommande (si vous devez utiliser des threads), c'est d'ouvrir deux navigateurs, puis d'utiliser des threads pour envoyer une requête dans chacun d'eux. Sinon, c'est impossible.

Ce fil de discussion porte sur le sélénium, mais les informations sont transférables. Sélénium plusieurs onglets à la fois Encore une fois, cela signifie que ce que vous voulez (je suppose) faire est impossible. Et le donneur de réponse coché en vert fait la même recommandation que moi.

J'espère que cela ne vous éloigne pas trop de la piste et vous aide.

EDIT : Juste pour montrer :

import time
from splinter import Browser
import threading

browser = Browser('firefox')
browser2 = Browser('firefox')

start_time = time.time()

urlOne = 'http://www.practiceurl.com/one'
urlTwo = 'http://www.practiceurl.com/two'
baseUrl = 'http://practiceurl.com'

browser.visit(baseUrl)

def secondThread(url):
    print 'STARTING 2ND REQUEST: ' + str(time.time() - start_time)
    browser2.visit(url)
    print 'END 2ND REQUEST: ' + str(time.time() - start_time)

def mainThread(url):
    print 'STARTING 1ST REQUEST: ' + str(time.time() - start_time)
    browser.visit(url)
    print 'END 1ST REQUEST: ' + str(time.time() - start_time)

def main():
    threadObj = threading.Thread(target=secondThread, args=[urlTwo])
    threadObj.daemon = True

    threadObj.start()

    mainThread(urlOne)

main()

Notez que j'ai utilisé firefox car je n'ai pas installé chromedriver.

Il pourrait être judicieux de définir un délai d'attente après l'ouverture des navigateurs, afin de s'assurer qu'ils sont entièrement prêts, avant que les minuteurs ne commencent.

1voto

Adonis Points 2883

@GenericSnake a raison sur ce point. Pour en rajouter un peu, je vous suggère fortement de remanier votre code afin d'utiliser la fonction bibliothèque multiprocesseur principalement parce que l'implémentation du threading utilise le GIL :

Dans CPython, en raison du verrouillage global de l'interpréteur, un seul thread peut être utilisé. exécuter du code Python à la fois (même si certaines bibliothèques orientées vers la performances peuvent surmonter cette limitation). Si vous voulez que votre application fasse un meilleur usage des ressources de calcul des machines multi-cœurs, il est conseillé d'utiliser le multiprocessing. Toutefois, Cependant, le threading reste un modèle approprié si vous souhaitez exécuter plusieurs tâches liées aux E/S simultanément.

En fait, ce qui est bien avec le multiprocessing, c'est que vous pouvez remanier votre code pour éviter la duplication des méthodes. secondThread y mainThread par exemple de cette façon (une dernière chose, n'oubliez pas de nettoyer les ressources que vous utilisez, telles que browser.quit() pour fermer le navigateur une fois que vous avez terminé) :

import time
from splinter import Browser
from multiprocessing import Process
import os

os.environ['PATH'] = os.environ[
                         'PATH'] + "path/to/geckodriver" + "path/to/firefox/binary"

start_time = time.time()

urlOne = 'http://pythoncarsecurity.com/Support/FAQ.aspx'
urlTwo = 'http://pythoncarsecurity.com/Products/'

def url_visitor(url):
    print("url called: " + url)
    browser = Browser('firefox')
    print('STARTING  REQUEST TO: ' + url + " at "+ str(time.time() - start_time))
    browser.visit(url)
    print('END REQUEST TO: ' + url + " at "+ str(time.time() - start_time))   

def main():
    p1 = Process(target=url_visitor, args=[urlTwo])
    p2 = Process(target=url_visitor, args=[urlOne])
    p1.start()
    p2.start()
    p1.join() #join processes to the main process to see the output
    p2.join()

if __name__=="__main__":
    main()

Cela nous donne le résultat suivant (le timing dépendra de votre système) :

url called: http://pythoncarsecurity.com/Support/FAQ.aspx
url called: http://pythoncarsecurity.com/Products/
STARTING  REQUEST TO: http://pythoncarsecurity.com/Support/FAQ.aspx at 10.763000011444092
STARTING  REQUEST TO: http://pythoncarsecurity.com/Products/ at 11.764999866485596
END REQUEST TO: http://pythoncarsecurity.com/Support/FAQ.aspx at 16.20199990272522
END REQUEST TO: http://pythoncarsecurity.com/Products/ at 16.625999927520752

Modifier : Le problème avec le multi threading et Selenium est qu'une instance de navigateur n'est pas thread safe, la seule façon que j'ai trouvée pour contourner ce problème est d'acquérir un verrou sur l'instance du navigateur. url_visitor Cependant, dans ce cas, vous perdez l'avantage du multithreading. C'est pourquoi je pense que l'utilisation de plusieurs navigateurs est beaucoup plus bénéfique (même si je suppose que vous avez des exigences très spécifiques), voir le code ci-dessous :

import time
from splinter import Browser
import threading
from threading import Lock
import os

os.environ['PATH'] = os.environ[
                         'PATH'] + "/path/to/chromedriver"

start_time = time.time()

urlOne = 'http://pythoncarsecurity.com/Support/FAQ.aspx'
urlTwo = 'http://pythoncarsecurity.com/Products/'
browser = Browser('chrome')
lock = threading.Lock()#create a lock for the url_visitor method

def init():
    browser.visit("https://www.google.fr")
    driver = browser.driver
    driver.execute_script("window.open('{0}', '_blank');") #create a new tab
    tabs = driver.window_handles

def url_visitor(url, tabs):
    with lock:
        if tabs != 0:
            browser.driver.switch_to_window(browser.driver.window_handles[tabs])
        print("url called: " + url)
        print('STARTING  REQUEST TO: ' + url + " at "+ str(time.time() - start_time))
        browser.visit(url)
        print('END REQUEST TO: ' + url + " at "+ str(time.time() - start_time))
        browser.quit()

def main():
    p1 = threading.Thread(target=url_visitor, args=[urlTwo, 0])
    p2 = threading.Thread(target=url_visitor, args=[urlOne, 1])
    p1.start()
    p2.start()

if __name__=="__main__":
    init() #create a browser with two tabs
    main()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X