Программа зависает при многопоточности

#python #web-scraping

Вопрос:

У меня есть программа на python, в которой есть функция, takeScreenshot которая делает снимок экрана с 10 введенными веб-страницами. Я хочу использовать потоковую обработку, чтобы часть веб-очистки при создании снимка экрана выполнялась в фоновом режиме, пока программа продолжает вводить дополнительные веб-страницы. После того, как вы сделаете 10 скриншотов, они должны быть отображены в программе.

Вопрос в том, как заставить программу отображать их после завершения последнего takeScreenshot потока (десятого потока), чтобы не вызвать ошибку? Другими словами, как убедиться, что все потоки завершены? Я попытался составить список всех запущенных потоков и создать их .join() после ввода последней веб-страницы (в последнем цикле). Однако это приводит к зависанию программы после ввода последней веб-страницы.

 threads=[]
n=0
while n<10:
   webpage = input("Enter the webpage")
   thread = threading.Thread(target = takeScreenshot, args = webpage)
   thread.start()
   threads.append(thread)
   if n==9:
      for thread in threads:
         thread.join()
   n  
 

Я попытался исследовать больше, поэтому обнаружил, что программа зависает в последнем цикле, когда она устанавливает атрибут класса, равный снимку экрана: self.graph = PhotoImage(file='screenshot.png') . Обратите внимание, что снимок экрана последней веб-страницы обычно загружается, поэтому ошибка не связана с отсутствием снимка экрана. Предыдущая строка кода включена в takeScreenshot функцию.

Вот takeScreenshot метод (это часть класса, называемого scraping :

 ublockPath = r'C:UsersBassel AttiaDocumentsTrading Core1.37.2_0'
chromeOptions = Options()
chromeOptions.add_argument("--log-level=3")
chromeOptions.add_argument('load-extension='   ublockPath)
self.driver = webdriver.Chrome(ChromeDriverManager().install(), 
chrome_options=chromeOptions)   
    
self.driver.get(webpage)
self.driver.get_screenshot_as_file('screenshot.png')
self.image = Image.open('screenshot.png')

#crop screenshot
area = (20, 290, 1250, 800)
croppedImage=self.image.crop(area)
os.remove('currentStock.png')
croppedImage.save('screenshot.png')
self.image = Image.open('screenshot.png')

#resizeImage
newHeight = 300
newWidth = int(newHeight / self.image.height * self.image.width)
resizedImage = self.image.resize((newWidth, newHeight))
os.remove('currentStock.png')
resizedImage.save('screenshot.png')
self.image = Image.open('screenshot.png')
self.image.close()

self.driver.quit()
 

Комментарии:

1. Я тебя не понимаю. Не могли бы вы поподробнее, пожалуйста?

2. Я бы посоветовал вам использовать a multiprocessing.pool.ThreadPool , потому что вы можете отправить ему все задачи, которые хотите, а затем вызвать его join() метод, чтобы дождаться их завершения. Или вы могли бы сделать что-то подобное с concurrent.futures.ThreadPoolExecutor помощью, как предложено в связанной документации.

3. Фрагмент кода, который вы отправили, кажется мне правильным. .join() ждет завершения потока. Я уверен, что ошибка в функции takeScreenshot

4. @alec Я обновил пост, чтобы включить takeScreenshot функцию. А ты как думаешь?

Ответ №1:

Изучив предоставленный вами код, к сожалению, я не смог воспроизвести проблему, но есть некоторые вещи, которые могут быть проблематичными:

  • файл currentStock.png удаляется дважды (и я удивлен, что при второй попытке его удаления не возникает исключение).
  • вы продолжаете перезаписывать один и тот же файл «скриншот.png»
  • аргументы-это не список

Если это может помочь, вот минимальный рабочий пример:

 import os, io, threading, uuid
from PIL import Image
from selenium import webdriver


def screen(wid, webpage):
    opts = webdriver.FirefoxOptions()
    opts.add_argument('--headless')
    print(wid, 'starting webdriver')
    driver = webdriver.Firefox(options=opts)
    driver.get(webpage)
    print(wid, 'taking screenshot')

    image_data = driver.get_screenshot_as_png()
    image = Image.open(io.BytesIO(image_data))

    area = (20, 290, 1250, 800)
    cropped = image.crop(area)

    h = 300
    w = int(h / cropped.height * cropped.width)
    resized = cropped.resize((w, h))

    if not os.path.isdir('screen'):
        os.mkdir('screen')
    fname = os.path.join('screen', f'{uuid.uuid4()}.png')

    resized.save(fname)
    print(wid, f'screenshot saved to {fname}')
    driver.quit()


def main():
    threads = []
    for wid in range(4):
        webpage = input('Webpage: ')
        thread = threading.Thread(target = screen, args = [wid, webpage])
        thread.start()
        threads.append(thread)

    for i, thread in enumerate(threads):
        thread.join()
        print('joined thread : ', i)

if __name__ == '__main__':
    main()