Избегайте гонок данных при парсинге веб-страниц с помощью Selenium

avatar
Matt
9 августа 2021 в 07:10
107
0
0

Я пытаюсь очистить некоторые динамические элементы с веб-страниц, используя Selenium. В конце концов, мне придется очистка страниц, исчисляемых сотнями из 1000, поэтому я пытаюсь найти способ сделать это как можно быстрее, что и привело меня к параллелизму. Я хочу записать результаты веб-скрапа в текстовый файл, но в моем коде возникают проблемы с гонкой данных. Когда я тестирую приведенный ниже код на сотне этих веб-страниц, я получаю только 60-90 из 100, записанных в файл. Я предполагаю, что это проблема гонки данных, или, может быть, я неправильно использую Selenium с параллелизмом. Ниже показано, что у меня есть до сих пор, безрезультатно пытаясь внедрить блокировки, чтобы остановить проблему гонки данных:

Текущий код указан ниже:

import time
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.chrome.options import Options

import concurrent.futures
from itertools import cycle
import threading

MAX_THREADS = 10

def createDriver():
    """Creates a Selenium Chrome instance to pull JS elements"""
    
    # Describe Chrome options
    chrome_options = Options()
    chrome_options.add_argument('--headless')

    # Create the driver
    driver = webdriver.Chrome(executable_path=r"PATH_TO_CHROMEDRIVER",
                          options=chrome_options, desired_capabilities=chrome_options.to_capabilities())
    
    return driver

def getSubmitDate(ID, driver, wait, lock):
    """Function that uses a specific webdriver to get a webpage, pull the information, and write it to a file."""
    
    # xpath for date element we want to pull
    xpath = '/html/body/div[2]/div/div/form/div/div[1]/div[1]/div[1]/p/span'
    
    # Get the webpage for the specified ID number
    driver.get(f'https://232app.azurewebsites.net/Forms/ExclusionRequestItem/{ID}')
    
    # Wait until element is visable then get it
    value = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
    
    # Lock the write and print method with the specific lock for the wait and driver elements
    # Write date to file and print out that the element has been written
    with lock:
        with open("date.txt", "a") as f:
            f.write(value.text + '\n')
            print(f'Wrote {value.text} for {ID}')


def downloadSubmitDates(IDList, driverCycles, waitCycles, lockCycles):
    """Concurrent implementation of the getSubmitDate function"""
    threads = min(MAX_THREADS, len(IDList))
    
    with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor:
        executor.map(getSubmitDate, IDList, driverCycles, waitCycles, lockCycles)
        
def main():
    """Main method that executes code"""
    
    # number of driver and lock instances
    instances = 10
    
    # Create list of ID to pull requests for
    IDList = [i for i in range(10000, 10100)]
    
    # Create list of drivers, waits, and locks to use
    drivers = [createDriver() for _ in range(10)]
    waits = [WebDriverWait(driver, timeout=10) for driver in drivers]
    locks = [threading.Lock() for _ in range(10)]
    
    # Create cycled version of the driver, wait, and locks
    driverCycles = cycle(drivers)
    waitCycles = cycle(waits)
    lockCycles = cycle(locks)
    
    # Start a time to record how long the program takes
    t0 = time.time()
    
    # Run the concurrent code
    downloadSubmitDates(IDList, driverCycles, waitCycles, lockCycles)
    
    # Close all open webdrivers
    for driver in drivers:
        driver.close()
        
    # Record finish time
    t1 = time.time()
    
    print(f'{t1 - t0} seconds to download {len(IDList)}')
    
    # Read text file to see how many records were recorded
    date_df = pd.read_csv('date.txt', header=None)
    
    print(f'Successfully pulled {date_df.shape[0]} items out of {len(IDList)}')
Источник
DisappointedByUnaccountableMod
9 августа 2021 в 17:33
0

«Отбрасывать» означает выбрасывать, как мусор, или драться. Вероятно, вы имели в виду царапать. Отредактировано.

Ответы (0)