首页 > 解决方案 > 使用 splinter/selenium 加速网页解析

问题描述

我正在尝试实现以下目标。

  1. 使用 splinter 访问 URL
  2. 将浏览器实例传递给使用多进程( multiprocessing.Process )的所有方法
  3. 所有方法都在线程中执行,减少了总时间

示例代码如下所示。

from splinter import Browser
from multiprocessing import Process, Queue, current_process, freeze_support

#
# Function run by worker processes
#

def worker(input, output):
    for func, args in iter(input.get, 'STOP'):
        result = calculate(func, args)
        output.put(result)

#
# Function used to calculate result
#

def calculate(func, args):
    print(args)
    result = func(*args)
    return '%s says that %s%s = %s' % \
        (current_process().name, func.__name__, args, result)


def get_meta_tag_title(browser):
    return browser.find_by_xpath('//title')[0]['text']

def get_meta_tag_description(browser):
    return browser.find_by_xpath('//description')[0]['text']

#
#
#

def test():
    browser = Browser(headless=True)
    browser.visit('https://example.com')
    NUMBER_OF_PROCESSES = 2
    TASKS1 = [(get_meta_tag_title, (browser)), (get_meta_tag_description, (browser))]

    # Create queues
    task_queue = Queue()
    done_queue = Queue()


    # Submit tasks
    for task in TASKS1:
        task_queue.put(task)

    # Start worker processes
    for i in range(NUMBER_OF_PROCESSES):
        Process(target=worker, args=(task_queue, done_queue)).start()

    # Get and print results
    print('Unordered results:')
    for i in range(len(TASKS1)):
        print('\t', done_queue.get())

    browser.quit()


if __name__ == '__main__':
    freeze_support()
    test()

我的问题是——

  1. 这是正确的方法吗?,使用浏览器实例获取所有元素信息/值是否是线程安全的
  2. 将浏览器实例添加到队列会导致TypeError: cannot serialize '_io.BufferedWriter' object我应该使用池吗?

标签: pythonmultithreadingthread-safetypython-multithreadingsplinter

解决方案


Splinter 基于 selenium WebDriver,它不是线程安全的,因此您可能不应该在生产中的单独线程中使用它。实际上,它可能会起作用,因为您正在执行的操作是只读的。不使用线程的另一个可能原因是您尝试并行化的操作是 CPU 密集型的,并且在大多数此类情况下,由于 GIL,python 线程不会提高性能。

另一方面,多处理确实可以帮助您并行处理 CPU 密集型工作。但是将浏览器对象传递给每个工作进程(就像您所做的那样)将不起作用,因为浏览器拥有打开的文件、套接字,并且拥有自己的有状态驱动程序实例。这些不能被轻易复制并传递给另一个进程。相反,您可以做的是在每个工作人员中创建一个浏览器对象,导航到该页面并随后在其上执行您的任务。

编辑

要进一步缩短执行时间,您可以尝试:
  1. 使用除 之外的定位器方法find_by_xpath,它本质上很慢,因为它遍历整个 DOM。
  2. 使用requests库来获取 html 内容并lxml解析它而不是使用 selenium,因为 webdriver 可能会导致一些严重的开销。

推荐阅读