我有一个python 3脚本,该脚本需要向多个不同的网站发出数千个请求,并检查其源代码是否通过了一些预定义的规则。
我正在使用selenium发出请求,因为我需要在JS完成执行后获取源代码,但是由于需要检查的URL数量很多,我试图使其同时运行多个线程。每个线程都会创建并维护一个Webdriver实例来发出请求。问题是一段时间后,所有线程都变为静默状态,然后停止执行,只剩下一个线程来完成所有工作。这是我的代码的相关部分:
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))
如您所见,首先我以为我只能拥有一个浏览器实例,所以我尝试至少在paralel中运行firefox和chrome,但这仍然只剩下一个线程来完成所有工作。
有时,即使驱动程序处于try / catch块中,该驱动程序仍会发狂并且该线程停止工作,因此,每次发生这种情况时,我都开始获取浏览器的新实例,但仍然无法正常工作。我还尝试在创建驱动程序的每个实例之间等待几秒钟,但仍然没有结果
这是其中一个日志文件的粘贴框:
我注意到的一件奇怪的事是,几乎每次保持运行状态的唯一线程都是最后一个生成的线程(id为3)。
感谢您的时间和帮助!
编辑:
[1]这是完整的代码:https://pastebin.com/fvVPwPVb
[2]自定义硒等待条件:https://pastebin.com/Zi7nbNFk
答案 0 :(得分:0)
我可以这样诅咒吗?我解决了这个问题,我不认为这个答案应该存在,因为没有其他人会从中受益。问题是我创建的自定义等待条件。此类位于编辑2中添加的pastebin中,但为了方便起见,我还将在此处添加它:
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)
此类的目的是让硒等待,因为JS可能正在加载其他DOM。
因此,此类使selenium等待一小会儿并检查代码,再次等待一会儿并再次检查代码。类重复此操作,直到源代码连续3次相同为止。
问题在于,有些页面包含js carrousel,因此源代码永远都不相同。我认为在这种情况下,WebDriverWait的第二个参数会使它崩溃并带有timeoutexception。我错了。