如何在Scrapy(Python)中实现并发中间件

7 投票
2 回答
2048 浏览
提问于 2025-04-17 21:59

编辑 2

第二种方法。目前,我放弃了使用多个实例,并把scrapy的设置调整为不使用并发请求。虽然这样速度慢,但比较稳定。我已经悬赏了。谁能帮我让这个程序能够并发运行?如果我把scrapy设置为并发运行,就会出现段错误。

class WebkitDownloader( object ):

    def __init__(self):
        os.environ["DISPLAY"] = ":99"
        self.proxyAddress = "a:b@" + PROXY_DEFAULT_HOST + ":" + str(PROXY_DEFAULT_PORT)


    def process_response(self, request, response, spider):
        self.request = request
        self.response = response
        if 'cached' not in response.flags:
            webkitBrowser = webkit.WebkitBrowser(proxy = self.proxyAddress, gui=False, timeout=0.5, delay=0.5, forbidden_extensions=['js','css','swf','pdf','doc','xls','ods','odt'])
            #print "added to queue: " + str(self.counter)
            webkitBrowser.get(html=response.body, num_retries=0)
            html = webkitBrowser.current_html()
            respcls = responsetypes.from_args(headers=response.headers, url=response.url)
            kwargs = dict(cls=respcls, body=killgremlins(html))
            response = response.replace(**kwargs)
            webkitBrowser.setPage(None)
            del webkitBrowser
        return response

编辑:

在此期间,我尝试自己回答这个问题,实施了一个队列,但出于某种原因,它并没有异步运行。基本上,当webkitBrowser.get(html=response.body, num_retries=0)在忙的时候,scrapy就会被阻塞,直到这个方法完成。新的请求不会被分配给self.queue中剩余的空闲实例。

有没有人能给我指个方向,让这个程序正常运行?

class WebkitDownloader( object ):

    def __init__(self):
        proxyAddress = "http://" + PROXY_DEFAULT_HOST + ":" + str(PROXY_DEFAULT_PORT)
        self.queue = list()
        for i in range(8):
            self.queue.append(webkit.WebkitBrowser(proxy = proxyAddress, gui=True, timeout=0.5, delay=5.5, forbidden_extensions=['js','css','swf','pdf','doc','xls','ods','odt']))

    def process_response(self, request, response, spider):

        i = 0
        for webkitBrowser in self.queue:
            i += 1
            if webkitBrowser.status == "WAITING":
                break
        webkitBrowser = self.queue[i]

        if webkitBrowser.status == "WAITING":
            # load webpage
            print "added to queue: " + str(i)
            webkitBrowser.get(html=response.body, num_retries=0)
            webkitBrowser.scrapyResponse = response

        while webkitBrowser.status == "PROCESSING":
            print "waiting for queue: " + str(i)  

        if webkitBrowser.status == "DONE":
            print "fetched from queue: " + str(i)
            #response = webkitBrowser.scrapyResponse
            html = webkitBrowser.current_html()
            respcls = responsetypes.from_args(headers=response.headers, url=response.url)
            kwargs = dict(cls=respcls, body=killgremlins(html))
            #response = response.replace(**kwargs)
            webkitBrowser.status = "WAITING"
            return response

我在scrapy的中间件中使用WebKit来渲染JavaScript。目前,scrapy被配置为一次处理1个请求(没有并发)。

我想使用并发(比如一次处理8个请求),但我需要确保8个WebkitBrowser()实例根据它们各自的处理状态接收请求(在WebkitBrowser.get()完成并准备好接收下一个请求时,立刻发送一个新的请求)。

我该如何用Python实现这个功能?这是我当前的中间件:

class WebkitDownloader( object ):

    def __init__(self):
        proxyAddress = "http://" + PROXY_DEFAULT_HOST + ":" + str(PROXY_DEFAULT_PORT)
        self.w = webkit.WebkitBrowser(proxy = proxyAddress, gui=True, timeout=0.5, delay=0.5, forbidden_extensions=['js','css','swf','pdf','doc','xls','ods','odt'])

    def process_response(self, request, response, spider):
        if not ".pdf" in response.url:
            # load webpage
            self.w.get(html=response.body, num_retries=0)
            html = self.w.current_html()
            respcls = responsetypes.from_args(headers=response.headers, url=response.url)
            kwargs = dict(cls=respcls, body=killgremlins(html))
            response = response.replace(**kwargs)

        return response 

2 个回答

1

我知道这个问题已经很老了,但我也有类似的疑问,希望我找到的信息能帮助到有同样问题的人:

  1. 如果你使用的是 scrapyjs + splash,并且你的浏览器是webkit类型的(大多数情况下是这样的,因为splash是基于webkit的),那么这可能是最简单的解决方案;

  2. 如果第一种方法不行,你可以尝试用 scrapyd 同时运行多个爬虫,或者使用 scrapy的多进程

  3. 根据你的浏览器渲染主要是等待(页面渲染)、IO密集型还是CPU密集型,你可能需要使用非阻塞的睡眠方式,比如用twisted、多线程或多进程。对于后者,继续使用scrapy的价值就会降低,你可能想要自己动手写一个简单的爬虫(例如A. Jesse Jiryu Davis和Guido van Rossum写的网络爬虫:代码文档),或者自己创建一个。

3

我不太明白你问题里的所有内容,因为我不熟悉scrapy,也不清楚是什么导致了段错误。不过,我可以回答一个问题:为什么当webkitBrowser.get在忙的时候,scrapy会被阻塞?

在你提到的“队列”例子里,我没有看到任何可以让你实现并行处理的东西。通常,我们会使用 threadingmultiprocessing 模块,这样多个任务就可以“并行”运行。你可能需要把 webkitBrowser.get 放到一个线程里,而不是直接调用它。获取网页的任务,使用Python的线程应该是比较合适的。虽然Python不能同时执行两个需要大量CPU的任务(因为有个叫GIL的东西),但它可以同时等待来自网页服务器的响应。

这里有个最近的StackOverflow问答,里面有示例代码,可能对你有帮助。

给你一个入门的想法。创建一个 队列。定义一个函数,这个函数接收这个队列作为参数,获取网页并把响应放入队列。在主程序中,在启动所有获取线程后,进入一个 while True: 循环:检查队列,处理下一个条目,如果队列是空的,就 time.sleep(.1) 等待一下。

撰写回答