【问题标题】:How can I make Selenium run in parallel with Scrapy?如何让 Selenium 与 Scrapy 并行运行?
【发布时间】:2020-07-26 09:14:24
【问题描述】:

我正在尝试使用 Scrapy 和 Selenium 抓取一些网址。 其中一些 url 由 Scrapy 直接处理,而另一些则先由 Selenium 处理。

问题是:当 Selenium 处理一个 url 时,Scrapy 没有并行处理其他的。它等待 webdriver 完成它的工作。

我曾尝试在单独的进程中使用不同的初始化参数 run multiple spiders(使用多处理池),但我得到了 twisted.internet.error.ReactorNotRestartable。我还尝试在parse 方法中生成另一个进程。但似乎我没有足够的经验来做对。

在下面的示例中,所有 url 仅在 webdriver 关闭时打印。请指教,有没有办法让它“并行”运行

import time

import scrapy
from selenium.webdriver import Firefox


def load_with_selenium(url):
    with Firefox() as driver:
        driver.get(url)
        time.sleep(10)  # Do something
        page = driver.page_source
    return page


class TestSpider(scrapy.Spider):
    name = 'test_spider'

    tasks = [{'start_url': 'https://www.theguardian.com/', 'selenium': False},
             {'start_url': 'https://www.nytimes.com/', 'selenium': True}]

    def start_requests(self):
        for task in self.tasks:
            yield scrapy.Request(url=task['start_url'], callback=self.parse, meta=task)

    def parse(self, response):
        if response.meta['selenium']:
            response = response.replace(body=load_with_selenium(response.meta['start_url']))

        for url in response.xpath('//a/@href').getall():
            print(url)

【问题讨论】:

  • 你试过多线程吗?进程不能共享内存,所以有些东西会中断。
  • @kagronick,我试过running multiple spiders in the same process(我传递了不同的初始化参数),但它导致了完全相同的问题:Scrapy 正在等待 webdriver 完成它的工作。

标签: python selenium scrapy


【解决方案1】:

看来我找到了解决办法。

我决定使用多处理,在每个进程中运行一个蜘蛛并传递一个任务作为它的 init 参数。在某些情况下,这种方法可能不合适,但它对我有用。

我以前尝试过这种方式,但我得到了twisted.internet.error.ReactorNotRestartable 异常。是在每个进程中多次调用CrawlerProcessstart()方法引起的,不正确。 Here 我找到了一个简单而清晰的例子,它使用回调在循环中运行蜘蛛。

所以我在进程之间拆分了我的tasks 列表。然后在crawl(tasks) 方法中,我创建了一个回调链来多次运行我的蜘蛛,每次传递一个不同的任务作为它的初始化参数。

import multiprocessing

import numpy as np
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

tasks = [{'start_url': 'https://www.theguardian.com/', 'selenium': False},
         {'start_url': 'https://www.nytimes.com/', 'selenium': True}]


def crawl(tasks):
    process = CrawlerProcess(get_project_settings())

    def run_spider(_, index=0):
        if index < len(tasks):
            deferred = process.crawl('test_spider', task=tasks[index])
            deferred.addCallback(run_spider, index + 1)
            return deferred

    run_spider(None)
    process.start()


def main():
    processes = 2
    with multiprocessing.Pool(processes) as pool:
        pool.map(crawl, np.array_split(tasks, processes))


if __name__ == '__main__':
    main()

我的问题帖子中TestSpider 的代码必须相应地修改以接受任务作为初始化参数。

def __init__(self, task):
    scrapy.Spider.__init__(self)
    self.task = task

def start_requests(self):
    yield scrapy.Request(url=self.task['start_url'], callback=self.parse, meta=self.task)

【讨论】:

    猜你喜欢
    • 2021-05-09
    • 2017-06-24
    • 2023-04-03
    • 1970-01-01
    • 2019-06-23
    • 2016-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多