首页 > 解决方案 > 多线程冻结程序

问题描述

我有一个 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:\Users\Bassel Attia\Documents\Trading Core\1.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()

标签: pythonweb-scraping

解决方案


不幸的是,通过检查您提供的代码,我无法重现该问题,但有些事情可能会出现问题:

  • 文件 currentStock.png 被删除两次(我很惊讶它不会在您第二次尝试删除它时引发异常)
  • 您不断覆盖相同的“screenshot.png”文件
  • args 不是列表

如果这有帮助,这是一个最小的工作示例:

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()

推荐阅读