J'ai créé un robot Web qui itère sur un site Web, par exemple example.com/?id=int
où int
est un entier. la fonction obtient le résultat en html brut à l'aide de la bibliothèque requests
puis le transmet à parseAndWrite
pour extraire un div
et enregistrer sa valeur dans une base de données sqlite:
def archive(initial_index, final_index): while True: try: for i in range(initial_index, final_index): res = requests.get('https://www.example.com/?id='+str(i)) parseAndWrite(res.text) print(i, ' archived') except requests.exceptions.ConnectionError: print("[-] Connection lost. ") continue except: exit(1) break archive(1, 10000)
Mon problème est qu'après un certain temps, la boucle ne continue pas jusqu'à 10000
mais se répète à partir d'une valeur aléatoire, ce qui entraîne de nombreux enregistrements en double dans le base de données. Quelle est la cause de cette incohérence?
3 Réponses :
Je pense que vos deux boucles sont imbriquées dans le mauvais ordre. La boucle externe while
est censée réessayer toutes les URL qui causent des erreurs de connexion, mais vous l'avez placée en dehors de la boucle for
les itérations sur les numéros d'URL. Cela signifie que vous commencez toujours à partir de l'index initial chaque fois qu'une erreur se produit.
Essayez de permuter les boucles, et vous ne répéterez qu'une seule URL jusqu'à ce que cela fonctionne:
def archive(initial_index, final_index): for i in range(initial_index, final_index): while True: try: res = requests.get('https://www.example.com/?id='+str(i)) parseAndWrite(res.text) print(i, ' archived') except requests.exceptions.ConnectionError: print("[-] Connection lost. ") continue except: exit(1) break archive(1, 10000)
Si une erreur de connexion se produit, vous redémarrez à initial_index
. Au lieu de cela, vous pouvez réessayer l'index actuel encore et encore, jusqu'à ce que la connexion réussisse:
def archive(initial_index, final_index): for i in range(initial_index, final_index): while True: try: response = requests.get(f'https://www.example.com/?id={i}') parseAndWrite(response.text) print(f'{i} archived') except requests.exceptions.ConnectionError: print("[-] Connection lost. ") else: break archive(1, 10000)
Une règle générale pour une instruction try
est d'exécuter le moins de code possible dans une instruction; ne mettez que le code que vous attendez produira l'erreur que vous voulez y détecter; tout autre code est placé avant ou après l'instruction.
N'attrapez pas les erreurs dont vous ne savez pas quoi faire. Quitter le programme est rarement la bonne chose à faire; cela se produira de toute façon si personne d'autre ne détecte l'exception, alors donnez à votre appelant la possibilité de la gérer.
Et enfin, ne construisez pas d'URL vous-même; laissez la bibliothèque requests
faire cela pour vous. L'URL de base est http://www.example.com
; le paramètre id
et sa valeur peuvent être passés via un dict
à requests.get
.
Votre boucle externe va itérer sur les différents paramètres utilisés pour construire l'URL; la boucle interne essaiera la demande jusqu'à ce qu'elle réussisse. Une fois la boucle interne terminée, vous pouvez utiliser la réponse pour appeler parseAndWrite
.
def archive(initial_index, final_index): base_url = 'https://www.example.com/' for i in range(initial_index, final_index + 1): while True: try: res = requests.get(base_url, params={'id': i}) except requests.exception.ConnectionError: print("[-] Connection lost, trying again") continue else: break parseAndWrite(res.text) print('{} archived'.format(i)) archived(1, 10000)
Vous pouvez également envisager de laisser requêtes code > gérez les tentatives pour vous. Voir Puis-je définir max_retries pour les demandes.request? pour commencer.
À toute erreur de connexion, votre redémarrage à partir de 1.
@Daniel, ah! comment n'ai-je pas pu remarquer ce problème stupide ...
Placez le
try / except
autour du corps de la bouclefor
plutôt qu'autour de la boucle entière. Ensuite, vous pouvez vous débarrasser de la bouclewhile True
.Sans rapport, mais vous devriez probablement laisser les autres exceptions se propager hors de votre fonction plutôt que de quitter immédiatement. Laisser celui qui a appelé
archive
décider quoi faire en cas d'erreur inattendue.