Многопроцессорная обработка Python Selen
Я написал скрипт на python в сочетании с селеном, чтобы очистить ссылки различных постов с его целевой страницы и, наконец, получить заголовок каждого поста, отслеживая URL, ведущий к его внутренней странице. Хотя содержимое, которое я здесь проанализировал, является статическим, я использовал селен, чтобы увидеть, как он работает в многопроцессорной среде.
Тем не менее, мое намерение состоит в том, чтобы выполнить очистку с использованием многопроцессорной обработки. Пока я знаю, что селен не поддерживает многопроцессорность, но, похоже, я ошибался.
Мой вопрос: как я могу сократить время выполнения, используя селен, когда он выполняется для многопроцессорной обработки?
This is my try (it a working one)
:
import requests
from urllib.parse import urljoin
from multiprocessing.pool import ThreadPool
from bs4 import BeautifulSoup
from selenium import webdriver
def get_links(link):
res = requests.get(link)
soup = BeautifulSoup(res.text,"lxml")
titles = [urljoin(url,items.get("href")) for items in soup.select(".summary .question-hyperlink")]
return titles
def get_title(url):
chromeOptions = webdriver.ChromeOptions()
chromeOptions.add_argument("--headless")
driver = webdriver.Chrome(chrome_options=chromeOptions)
driver.get(url)
sauce = BeautifulSoup(driver.page_source,"lxml")
item = sauce.select_one("h1 a").text
print(item)
if __name__ == '__main__':
url = "https://stackoverflow.com/info/tagged/web-scraping"
ThreadPool(5).map(get_title,get_links(url))
Ответы
Ответ 1
Как я могу сократить время выполнения с помощью селена, когда он сделан для запуска с использованием многопроцессорной
В вашем решении много времени тратится на запуск веб-драйвера для каждого URL. Вы можете сократить это время, запустив драйвер только один раз для каждого потока:
(... skipped for brevity ...)
threadLocal = threading.local()
def get_driver():
driver = getattr(threadLocal, 'driver', None)
if driver is None:
chromeOptions = webdriver.ChromeOptions()
chromeOptions.add_argument("--headless")
driver = webdriver.Chrome(chrome_options=chromeOptions)
setattr(threadLocal, 'driver', driver)
return driver
def get_title(url):
driver = get_driver()
driver.get(url)
(...)
(...)
В моей системе это сокращает время с 1 м7 до 24,895 с, улучшив ~ 35%. Чтобы проверить себя, загрузите полный скрипт.
Примечание: ThreadPool
использует потоки, которые ограничены Python GIL. Это нормально, если по большей части задача связана с вводом/выводом. В зависимости от последующей обработки, которую вы выполняете со соскребенными результатами, вы можете вместо этого использовать multiprocessing.Pool
. Это запускает параллельные процессы, которые как группа не ограничены GIL. Остальная часть кода остается прежней.
Ответ 2
Мой вопрос: как я могу сократить время выполнения?
Selenium кажется неподходящим инструментом для просмотра веб-страниц - хотя я ценю YMMV, особенно если вам нужно смоделировать взаимодействие пользователя с веб-сайтом или есть некоторые ограничения/требования JavaScript.
Для задач очистки без особого взаимодействия у меня были хорошие результаты при использовании пакета Python с открытым исходным кодом Scrapy для крупномасштабных задач очистки. Он выполняет многопроцессорную обработку из коробки, легко писать новые сценарии и сохранять данные в файлах или базе данных - и это действительно быстро.
Ваш сценарий будет выглядеть примерно так при реализации в виде полностью параллельного паука Scrapy (обратите внимание, я не проверял это, см. Документацию по селекторам).
import scrapy
class BlogSpider(scrapy.Spider):
name = 'blogspider'
start_urls = ['https://stackoverflow.com/info/tagged/web-scraping']
def parse(self, response):
for title in response.css('.summary .question-hyperlink'):
yield title.get('href')
Для запуска поместите это в blogspider.py
и запустите
$ scrapy runspider blogspider.py
Смотрите сайт Scrapy для полного учебника.
Обратите внимание, что Scrapy также поддерживает JavaScript посредством scrapy-splash, благодаря указателю @SIM. До сих пор я не сталкивался с этим, поэтому не могу говорить об этом, кроме того, что он хорошо интегрирован с тем, как работает Scrapy.