Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

DL-crawler avec Beautifulsoup,request,multiprocessing et timeit

+3 votes

Je voudrai un code souple, qui s'adapte à plusieurs type de page, et qui dl les liens.
Le code doit compter le nombre de liens, pour en faire des slices de longueurs équivalentes pour ne pas desequilibrer les threads ; et faire un timeit global pour comparer l' impact du multiprocessing.

Voici un début de code avant de caler.
Notez que l' URL-exemple ici est particulier car pour télécharger les fichiers ils portent tous le même nom "Download", j'ai pas d'autre exemple sous la main. Si vous avez une autre page mieux adaptée à la démonstration, ça m' ira ;-)

#!/usr/bin/env python3
#-*- coding: utf-8 -*-

URL_accueil = 'https://openclassrooms.com/old-courses-pdf'

from bs4 import BeautifulSoup
import requests
from tqdm import tqdm
from time import time, sleep
import cProfile
import timeit
import multiprocessing as mp

def pdf_downloader():

    page_accueil = requests.get(URL_accueil)
    soup = BeautifulSoup(page_accueil.text, "lxml")

    listPDF = []

    for a in soup.find_all('a', href=True):
        print("Found the URL:", a['href'])
        listPDF.append(a['href'])


    print("\n\n\n")
    print(listPDF)
    print("\n\n\n")

    minilist = listPDF[10:30]

    ErrorList = []

    for pdf in minilist:
        curentURL = URL_accueil + pdf
        curentCONTENU = requests.get(curentURL)

        try:
            with open(pdf, "wb") as handle:
                for data in tqdm(curentCONTENU.iter_content()):
                    handle.write(data)

        except FileNotFoundError:
            ErrorList.append(pdf)

    print("   \n Liste d' erreurs au cours des telechargements :  ",ErrorList)

"""
if __name__=='__main__':
    cProfile.run('pdf_downloader()')

# print(timeit.timeit("pdf_downloader()", setup="from __main__ import pdf_downloader", number=1))


def foo(q):
    q.put('hello')
"""

if __name__ == '__main__':
    mp.set_start_method('spawn')
    q = mp.Queue()
    #p = mp.Process(target=pdf_downloader, args=(q,))
    p = mp.Process(target=pdf_downloader)
    p.start()
    print(q.get())
    p.join()
demandé 2-Mar-2016 par buffalo974

juste un commentaire en passant, si on veut que d'autres puissent aider / maintenir du code des autres, mieux vaut respecter les règles que tout le monde suit, sinon on ne va meme pas chercher à comprendre parce que ca sera trop "ésothérique" et qu'on en aura la flemme. Là on a même du melange anglais / français dans es noms de variables.

donc pour cela tout est là http://pep8.org/

Question, pourquoi tu n'utilise pas Scrapy ?
Tu peux analyser plusieurs pages en même temps et récemment un système de "file pipelines" qui a l'air de correspondre à tes besoins.

ok, je vais essayer de nettoyer ça avec Scrapy et pylint !

pour moi : PEP8 en français

C'est quoi la question ?

C'est pour aspirer en une seule étape tous les PDF téléchargeables sur une page.

Y a toujours pas de question là. Je vois ce que tu veux faire avec ton programme, mais je vois pas la question à laquelle tu veux qu'on réponde.

1 Réponse

+2 votes

Il faut d'abord comprendre que ce qui demande principalement de la concurrence ici, c'est l'aspect IO, donc plusieurs solutions sont possibles, en particulier :

  • des threads
  • utiliser de l'IO asynchrone

Par ailleurs, il est inutile et contre-productif de diviser le travail entre les threads en fonction du nombre d'objets. Mieux vaut laisser les threads se débrouiller, soit dès qu'un thread finit de télécharger un fichier il passe au suivant.

Voici une ébauche de code, largement inspirée de la doc :

from bs4 import BeautifulSoup
import requests
from queue import Queue
from threading import Thread

NB_THREADS = 16
queue = Queue()
ErrorList = []
URL_accueil = 'https://openclassrooms.com/old-courses-pdf'

def worker():
    while True:
        pdf = queue.get()
        if pdf is None:
            break

        curentURL = URL_accueil + pdf
        curentCONTENU = requests.get(curentURL)

        try:
            with open(pdf, "wb") as handle:
                for data in curentCONTENU.iter_content():
                    handle.write(data)
        except FileNotFoundError:
            ErrorList.append(pdf)

        queue.task_done()

# main thread
def pdf_downloader():
    # start workers
    threads = []
    for i in range(NB_THREADS):
        t = Thread(target=worker)
        t.start()
        threads.append(t)

    page_accueil = requests.get(URL_accueil)
    soup = BeautifulSoup(page_accueil.text, "lxml")

    for a in soup.find_all('a', href=True):
        print("Found the URL:", a['href'])
        queue.put(a['href'])

    queue.join()

    # stop workers
    for i in range(NB_THREADS):
        queue.put(None)
    for t in threads:
        t.join()

    print("   \n Liste d' erreurs au cours des telechargements :  ", ErrorList)


if __name__ == '__main__':
    # lance le gestionnaire
    pdf_downloader()

Une fois que le mécanisme est bien saisi, tu peux aller voir ce qui est possible avec concurrent.futures.

EDIT: je viens de voir que ton code pour le worker est incorrect, je te laisse avancer là dessus.

répondu 4-Mar-2016 par yoch (2,312 points)
edité 4-Mar-2016 par yoch
...