当我使用 ThreadPool 时,程序在完成后等待关闭
When I use ThreadPool, the program waits after completion before closing
我的代码保存网站的页面并使用 bs4 获取其他页面的 url。
在我之前的问题中,有人建议我使用多线程而不是多处理,并且我得到了一段代码,起初它确实对我有帮助。然而,当我在 100 多个操作上测试它时,它在完成后没有关闭(等待 10 分钟),我注意到该程序没有保存所有页面(并且在不同的运行中它不会保存不同的页面, 120 大约 5-8)。对于 30 次操作,它会始终如一地保存所有操作,并在停止前等待大约 10 秒。也许代码等待剩余的线程?如何修复非关闭?
这是我得到的代码:
# libraries
import os
from bs4 import BeautifulSoup
from selenium import webdriver
from multiprocessing.pool import ThreadPool
import threading
# variables
url = "https://eldorado.ua/"
directory = os.path.dirname(os.path.realpath(__file__))
env_path = directory + "\chromedriver"
chromedriver_path = env_path + "\chromedriver.exe"
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 " \
"Safari/537.36 "
dict1 = {"Смартфоны и телефоны": "https://eldorado.ua/node/c1038944/",
"Телевизоры и аудиотехника": "https://eldorado.ua/node/c1038957/",
"Ноутбуки, ПК и Планшеты": "https://eldorado.ua/node/c1038958/",
"Техника для кухни": "https://eldorado.ua/node/c1088594/",
"Техника для дома": "https://eldorado.ua/node/c1088603/",
"Игровая зона": "https://eldorado.ua/node/c1285101/",
"Гаджеты и аксесуары": "https://eldorado.ua/node/c1215257/",
"Посуда": "https://eldorado.ua/node/c1039055/",
"Фото и видео": "https://eldorado.ua/node/c1038960/",
"Красота и здоровье": "https://eldorado.ua/node/c1178596/",
"Авто и инструменты": "https://eldorado.ua/node/c1284654/",
"Спорт и туризм": "https://eldorado.ua/node/c1218544/",
"Товары для дома и сада": "https://eldorado.ua/node/c1285161/",
"Товары для детей": "https://eldorado.ua/node/c1085100/"}
count = 0
threaded_data = threading.local() # Создаёт хранилище (класс) для потоков, чтобы не вызывать webdriver при каждой итерации
os.environ['PATH'] += env_path # Добавляет chromedriver в PATH
class Driver:
def __init__(self):
options = webdriver.ChromeOptions()
options.headless = True
options.add_experimental_option("excludeSwitches", ['enable-automation'])
options.add_argument(f'--user-agent={UserAgent}')
self.driver = webdriver.Chrome(executable_path=chromedriver_path, options=options)
def __del__(self):
self.driver.quit() # driver.quit(), когда переменная больше не используется (при окончании выполнения потока)
print('The driver has been quited.')
def create_driver():
the_driver = getattr(threaded_data, 'the_driver', None)
if the_driver is None:
the_driver = Driver()
setattr(threaded_data, 'the_driver', the_driver)
return the_driver.driver
def processing_brand_pages(name):
with open(f"{directory}\section_pages\{name}.html", encoding="utf-8") as file:
soup = BeautifulSoup(file.read(), "lxml")
links = soup.find_all("div", class_="title")
driver = create_driver()
for n in links:
ref = url + n.find('a').get('href')
global count
print(n.text, count)
count += 1
driver.get(ref)
try:
with open(f"{directory}\brand_pages\{name}\{n.text}.html", "w", encoding="utf-8") as file:
file.write(driver.page_source)
except Exception as ex:
print(ex)
if __name__ == "__main__":
ThreadPool(processes=6).map(processing_brand_pages, dict1.keys())
del threaded_data # Quit all the Selenium drivers
import gc
gc.collect()
我修改为不使用Class(个人喜好):
# libraries
import os
from bs4 import BeautifulSoup
from selenium import webdriver
from multiprocessing.pool import ThreadPool
import threading
# variables
url = "https://eldorado.ua/"
directory = os.path.dirname(os.path.realpath(__file__))
env_path = directory + "\chromedriver"
chromedriver_path = env_path + "\chromedriver.exe"
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 " \
"Safari/537.36 "
dict1 = {"Смартфоны и телефоны": "https://eldorado.ua/node/c1038944/",
"Телевизоры и аудиотехника": "https://eldorado.ua/node/c1038957/",
"Ноутбуки, ПК и Планшеты": "https://eldorado.ua/node/c1038958/",
"Техника для кухни": "https://eldorado.ua/node/c1088594/",
"Техника для дома": "https://eldorado.ua/node/c1088603/",
"Игровая зона": "https://eldorado.ua/node/c1285101/",
"Гаджеты и аксесуары": "https://eldorado.ua/node/c1215257/",
"Посуда": "https://eldorado.ua/node/c1039055/",
"Фото и видео": "https://eldorado.ua/node/c1038960/",
"Красота и здоровье": "https://eldorado.ua/node/c1178596/",
"Авто и инструменты": "https://eldorado.ua/node/c1284654/",
"Спорт и туризм": "https://eldorado.ua/node/c1218544/",
"Товары для дома и сада": "https://eldorado.ua/node/c1285161/",
"Товары для детей": "https://eldorado.ua/node/c1085100/"}
count = 0
threaded_data = threading.local() # Создаёт экземпляр класса (типо хранилище) для потоков, чтобы не вызывать webdriver при каждой итерации
os.environ['PATH'] += env_path # Добавляет chromedriver в PATH
def processing_brand_pages(name):
with open(f"{directory}\section_pages\{name}.html", encoding="utf-8") as file:
soup = BeautifulSoup(file.read(), "lxml")
links = soup.find_all("div", class_="title")
driver = put_driver_in_threaded_data()
for n in links:
ref = url + n.find('a').get('href')
global count
print(n.text, count)
count += 1
driver.get(ref)
try:
with open(f"{directory}\brand_pages\{name}\{n.text}.html", "w", encoding="utf-8") as file:
file.write(driver.page_source)
except Exception as ex:
print(ex)
def put_driver_in_threaded_data():
threaded_driver = getattr(threaded_data, 'driver_in_threaded_data', None)
if threaded_driver is None:
options = webdriver.ChromeOptions()
options.headless = True
options.add_experimental_option("excludeSwitches", ['enable-automation'])
options.add_argument(f'--user-agent={UserAgent}')
threaded_driver = webdriver.Chrome(executable_path=chromedriver_path, options=options)
setattr(threaded_data, 'driver_in_threaded_data', threaded_driver)
return threaded_driver
if __name__ == "__main__":
ThreadPool(processes=6).map(processing_brand_pages, dict1.keys())
del threaded_data # Quit all the Selenium drivers
import gc
gc.collect()
两者的工作方式相同,而且两者都会出现问题。
我认为你需要写:
with ThreadPool(processes=6) as pool:
pool.map(processing_brand_pages, dict1.keys())
这会强制 pool.join()
在执行下一条语句之前发生。现在编写代码时,您只是启动线程,然后继续前进。
我的代码保存网站的页面并使用 bs4 获取其他页面的 url。 在我之前的问题中,有人建议我使用多线程而不是多处理,并且我得到了一段代码,起初它确实对我有帮助。然而,当我在 100 多个操作上测试它时,它在完成后没有关闭(等待 10 分钟),我注意到该程序没有保存所有页面(并且在不同的运行中它不会保存不同的页面, 120 大约 5-8)。对于 30 次操作,它会始终如一地保存所有操作,并在停止前等待大约 10 秒。也许代码等待剩余的线程?如何修复非关闭? 这是我得到的代码:
# libraries
import os
from bs4 import BeautifulSoup
from selenium import webdriver
from multiprocessing.pool import ThreadPool
import threading
# variables
url = "https://eldorado.ua/"
directory = os.path.dirname(os.path.realpath(__file__))
env_path = directory + "\chromedriver"
chromedriver_path = env_path + "\chromedriver.exe"
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 " \
"Safari/537.36 "
dict1 = {"Смартфоны и телефоны": "https://eldorado.ua/node/c1038944/",
"Телевизоры и аудиотехника": "https://eldorado.ua/node/c1038957/",
"Ноутбуки, ПК и Планшеты": "https://eldorado.ua/node/c1038958/",
"Техника для кухни": "https://eldorado.ua/node/c1088594/",
"Техника для дома": "https://eldorado.ua/node/c1088603/",
"Игровая зона": "https://eldorado.ua/node/c1285101/",
"Гаджеты и аксесуары": "https://eldorado.ua/node/c1215257/",
"Посуда": "https://eldorado.ua/node/c1039055/",
"Фото и видео": "https://eldorado.ua/node/c1038960/",
"Красота и здоровье": "https://eldorado.ua/node/c1178596/",
"Авто и инструменты": "https://eldorado.ua/node/c1284654/",
"Спорт и туризм": "https://eldorado.ua/node/c1218544/",
"Товары для дома и сада": "https://eldorado.ua/node/c1285161/",
"Товары для детей": "https://eldorado.ua/node/c1085100/"}
count = 0
threaded_data = threading.local() # Создаёт хранилище (класс) для потоков, чтобы не вызывать webdriver при каждой итерации
os.environ['PATH'] += env_path # Добавляет chromedriver в PATH
class Driver:
def __init__(self):
options = webdriver.ChromeOptions()
options.headless = True
options.add_experimental_option("excludeSwitches", ['enable-automation'])
options.add_argument(f'--user-agent={UserAgent}')
self.driver = webdriver.Chrome(executable_path=chromedriver_path, options=options)
def __del__(self):
self.driver.quit() # driver.quit(), когда переменная больше не используется (при окончании выполнения потока)
print('The driver has been quited.')
def create_driver():
the_driver = getattr(threaded_data, 'the_driver', None)
if the_driver is None:
the_driver = Driver()
setattr(threaded_data, 'the_driver', the_driver)
return the_driver.driver
def processing_brand_pages(name):
with open(f"{directory}\section_pages\{name}.html", encoding="utf-8") as file:
soup = BeautifulSoup(file.read(), "lxml")
links = soup.find_all("div", class_="title")
driver = create_driver()
for n in links:
ref = url + n.find('a').get('href')
global count
print(n.text, count)
count += 1
driver.get(ref)
try:
with open(f"{directory}\brand_pages\{name}\{n.text}.html", "w", encoding="utf-8") as file:
file.write(driver.page_source)
except Exception as ex:
print(ex)
if __name__ == "__main__":
ThreadPool(processes=6).map(processing_brand_pages, dict1.keys())
del threaded_data # Quit all the Selenium drivers
import gc
gc.collect()
我修改为不使用Class(个人喜好):
# libraries
import os
from bs4 import BeautifulSoup
from selenium import webdriver
from multiprocessing.pool import ThreadPool
import threading
# variables
url = "https://eldorado.ua/"
directory = os.path.dirname(os.path.realpath(__file__))
env_path = directory + "\chromedriver"
chromedriver_path = env_path + "\chromedriver.exe"
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 " \
"Safari/537.36 "
dict1 = {"Смартфоны и телефоны": "https://eldorado.ua/node/c1038944/",
"Телевизоры и аудиотехника": "https://eldorado.ua/node/c1038957/",
"Ноутбуки, ПК и Планшеты": "https://eldorado.ua/node/c1038958/",
"Техника для кухни": "https://eldorado.ua/node/c1088594/",
"Техника для дома": "https://eldorado.ua/node/c1088603/",
"Игровая зона": "https://eldorado.ua/node/c1285101/",
"Гаджеты и аксесуары": "https://eldorado.ua/node/c1215257/",
"Посуда": "https://eldorado.ua/node/c1039055/",
"Фото и видео": "https://eldorado.ua/node/c1038960/",
"Красота и здоровье": "https://eldorado.ua/node/c1178596/",
"Авто и инструменты": "https://eldorado.ua/node/c1284654/",
"Спорт и туризм": "https://eldorado.ua/node/c1218544/",
"Товары для дома и сада": "https://eldorado.ua/node/c1285161/",
"Товары для детей": "https://eldorado.ua/node/c1085100/"}
count = 0
threaded_data = threading.local() # Создаёт экземпляр класса (типо хранилище) для потоков, чтобы не вызывать webdriver при каждой итерации
os.environ['PATH'] += env_path # Добавляет chromedriver в PATH
def processing_brand_pages(name):
with open(f"{directory}\section_pages\{name}.html", encoding="utf-8") as file:
soup = BeautifulSoup(file.read(), "lxml")
links = soup.find_all("div", class_="title")
driver = put_driver_in_threaded_data()
for n in links:
ref = url + n.find('a').get('href')
global count
print(n.text, count)
count += 1
driver.get(ref)
try:
with open(f"{directory}\brand_pages\{name}\{n.text}.html", "w", encoding="utf-8") as file:
file.write(driver.page_source)
except Exception as ex:
print(ex)
def put_driver_in_threaded_data():
threaded_driver = getattr(threaded_data, 'driver_in_threaded_data', None)
if threaded_driver is None:
options = webdriver.ChromeOptions()
options.headless = True
options.add_experimental_option("excludeSwitches", ['enable-automation'])
options.add_argument(f'--user-agent={UserAgent}')
threaded_driver = webdriver.Chrome(executable_path=chromedriver_path, options=options)
setattr(threaded_data, 'driver_in_threaded_data', threaded_driver)
return threaded_driver
if __name__ == "__main__":
ThreadPool(processes=6).map(processing_brand_pages, dict1.keys())
del threaded_data # Quit all the Selenium drivers
import gc
gc.collect()
两者的工作方式相同,而且两者都会出现问题。
我认为你需要写:
with ThreadPool(processes=6) as pool:
pool.map(processing_brand_pages, dict1.keys())
这会强制 pool.join()
在执行下一条语句之前发生。现在编写代码时,您只是启动线程,然后继续前进。