Python 3.x 如何使selenium线程运行(每个线程都有自己的驱动程序)
我有一个Python3脚本,它需要向多个不同的网站发出数千个请求,并检查它们的源代码是否传递了一些预定义的规则 我使用selenium发出请求,因为我需要在JS完成它的执行后获取源代码,但由于需要检查大量URL,我尝试让它同时运行多个线程。每个线程创建并维护一个webdriver实例以发出请求。问题是,过了一段时间,所有线程都停止运行,只剩下一个线程完成所有工作。以下是我的代码的相关部分:Python 3.x 如何使selenium线程运行(每个线程都有自己的驱动程序),python-3.x,multithreading,selenium,Python 3.x,Multithreading,Selenium,我有一个Python3脚本,它需要向多个不同的网站发出数千个请求,并检查它们的源代码是否传递了一些预定义的规则 我使用selenium发出请求,因为我需要在JS完成它的执行后获取源代码,但由于需要检查大量URL,我尝试让它同时运行多个线程。每个线程创建并维护一个webdriver实例以发出请求。问题是,过了一段时间,所有线程都停止运行,只剩下一个线程完成所有工作。以下是我的代码的相关部分: def get_browser(use_firefox = True): if use_firef
def get_browser(use_firefox = True):
if use_firefox:
options = FirefoxOptions()
options.headless = True
browser = webdriver.Firefox(options = options)
browser.implicitly_wait(4)
return browser
else:
chrome_options = ChromeOptions()
chrome_options.add_argument("--headless")
browser = webdriver.Chrome(chrome_options=chrome_options)
browser.implicitly_wait(4)
return browser
def start_validations(urls, rules, results, thread_id):
try:
log("thread %s started" % thread_id, thread_id)
browser = get_browser(thread_id % 2 == 1)
while not urls.empty():
url = "http://%s" % urls.get()
try:
log("starting %s" % url, thread_id)
browser.get(url)
time.sleep(0.5)
WebDriverWait(browser, 6).until(selenium_wait_reload(4))
html = browser.page_source
result = check_url(html, rules)
original_domain = url.split("://")[1].split("/")[0].replace("www.","")
tested_domain = browser.current_url.split("://")[1].split("/")[0].replace("www.","")
redirected_url = "" if tested_domain == original_domain else browser.current_url
results.append({"Category":result, "URL":url, "Redirected":redirected_url})
log("finished %s" % url, thread_id)
except Exception as e:
log("couldn't test url %s" % url, thread_id )
log(str(e), thread_id)
results.append({"Category":"Connection Error", "URL":url, "Redirected":""})
browser.quit()
time.sleep(2)
browser = get_browser(thread_id % 2 == 1)
except Exception as e:
log(str(e), thread_id)
finally:
log("closing thread", thread_id)
browser.quit()
def calculate_progress(urls):
progress_folder ="%sprogress/" % WEBROOT
if not os.path.exists(progress_folder):
os.makedirs(progress_folder)
initial_size = urls.qsize()
while not urls.empty():
current_size = urls.qsize()
on_queue = initial_size - current_size
progress = '{0:.0f}'.format((on_queue / initial_size * 100))
for progress_file in os.listdir(progress_folder):
file_path = os.path.join(progress_folder, progress_file)
if os.path.isfile(file_path) and not file_path.endswith(".csv"):
os.unlink(file_path)
os.mknod("%s%s" % (progress_folder, progress))
time.sleep(1)
if __name__ == '__main__':
while True:
try:
log("scraper started")
if os.path.isfile(OUTPUT_FILE):
os.unlink(OUTPUT_FILE)
manager = Manager()
rules = fetch_rules()
urls = manager.Queue()
fetch_urls()
results = manager.list()
jobs = []
p = Process(target=calculate_progress, args=(urls,))
jobs.append(p)
p.start()
for i in range(THREAD_POOL_SIZE):
log("spawning thread with id %s" % i)
p = Process(target=start_validations, args=(urls, rules, results, i))
jobs.append(p)
p.start()
time.sleep(2)
for j in jobs:
j.join()
save_results(results, OUTPUT_FILE)
log("scraper finished")
except Exception as e:
log(str(e))
正如你所看到的,首先我认为我只能有一个浏览器实例,所以我尝试至少并行运行firefox和chrome,但这仍然只剩下一个线程来完成所有工作
有时,驱动程序崩溃,线程停止工作,即使它在try/catch块中,所以每次发生这种情况时,我都开始获取浏览器的新实例,但它仍然不工作。我还尝试在创建驱动程序的每个实例之间等待几秒钟,但仍然没有结果
以下是其中一个日志文件的粘贴箱:
我注意到的一件奇怪的事情是,几乎每次唯一保持运行的线程都是最后一个生成的线程(id为3)
谢谢你的时间和帮助
编辑:
[1] 以下是完整的代码:
[2] 自定义selenium等待条件:允许我诅咒SO吗?我解决了这个问题,我认为这个答案不应该存在,因为没有其他人会从中受益。问题是我创建的自定义等待条件。此类位于编辑2中添加的粘贴箱中,但为了方便起见,我也将其添加到此处:
import time
class selenium_wait_reload:
def __init__(self, desired_repeating_sources):
self.desired_repeating_sources = desired_repeating_sources
self.repeated_pages = 0
self.previous_source = None
def __call__(self, driver):
while True:
current_source = driver.page_source
if current_source == self.previous_source:
self.repeated_pages = self.repeated_pages +1
if self.repeated_pages >= self.desired_repeating_sources:
return True
else:
self.previous_source = current_source
self.repeated_pages = 0
time.sleep(0.3)
这个类的目标是让selenium等待,因为JS可能正在加载额外的DOM
所以,这个类让selenium等待一小段时间并检查代码,再等待一小段时间并再次检查代码。该类重复此操作,直到源代码连续三次相同
问题是有些页面有js carrousel,所以源代码从来都不一样。我认为在这样的情况下,WebDriverWait第二个参数会使它崩溃,并出现timeoutexception。我错了。看起来你在操纵URL。你必须使用硒吗?我这样问是因为他们打开了浏览器,这会消耗大量内存和其他开销。你能改用请求库吗?是的,很遗憾我不得不。。。我的第一次尝试只是使用请求,但我需要在页面中的JS完成对DOM的操作(即发出ajax请求以显示某些内容,或重定向到另一个页面)后获取源代码。您可以在请求库中允许重定向。还可以发出ajax请求。是的,如果我知道有什么要求,这是可能的。还有一些网站会测试是否启用了JS,如果禁用了JS,则会为您提供不同的页面。但是,如果您可以共享一个片段来复制数千个不同网站发出的请求,我很乐意使用该解决方案,那么您可以将完整代码托管在我们可以看到的地方吗?我想用iTunes把我的手弄脏WebDriverWait@CoreyGoldberg你对预期的情况有什么建议吗?尽管我做了很多研究,但人们都说这不是一项琐碎的任务,我尝试的一切(包括滚动我自己的自定义等待条件)都失败了,所以我决定等待一个固定的X秒……你的答案与所问的问题完全无关。不是,也许你误解了什么。问题基本上是这样的:“为什么这个脚本直到最后才运行它的所有线程?”答案是“因为线程运行的代码让它永远等待。这个代码就是答案中的类”您的解决方案是对问题中没有的代码的修复。