如何在Scrapy(Python)中实现并发中间件
编辑 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 个回答
我知道这个问题已经很老了,但我也有类似的疑问,希望我找到的信息能帮助到有同样问题的人:
如果你使用的是 scrapyjs + splash,并且你的浏览器是webkit类型的(大多数情况下是这样的,因为splash是基于webkit的),那么这可能是最简单的解决方案;
如果第一种方法不行,你可以尝试用 scrapyd 同时运行多个爬虫,或者使用 scrapy的多进程;
根据你的浏览器渲染主要是等待(页面渲染)、IO密集型还是CPU密集型,你可能需要使用非阻塞的睡眠方式,比如用twisted、多线程或多进程。对于后者,继续使用scrapy的价值就会降低,你可能想要自己动手写一个简单的爬虫(例如A. Jesse Jiryu Davis和Guido van Rossum写的网络爬虫:代码和 文档),或者自己创建一个。
我不太明白你问题里的所有内容,因为我不熟悉scrapy,也不清楚是什么导致了段错误。不过,我可以回答一个问题:为什么当webkitBrowser.get在忙的时候,scrapy会被阻塞?
在你提到的“队列”例子里,我没有看到任何可以让你实现并行处理的东西。通常,我们会使用 threading
或 multiprocessing
模块,这样多个任务就可以“并行”运行。你可能需要把 webkitBrowser.get
放到一个线程里,而不是直接调用它。获取网页的任务,使用Python的线程应该是比较合适的。虽然Python不能同时执行两个需要大量CPU的任务(因为有个叫GIL的东西),但它可以同时等待来自网页服务器的响应。
这里有个最近的StackOverflow问答,里面有示例代码,可能对你有帮助。
给你一个入门的想法。创建一个 队列。定义一个函数,这个函数接收这个队列作为参数,获取网页并把响应放入队列。在主程序中,在启动所有获取线程后,进入一个 while True:
循环:检查队列,处理下一个条目,如果队列是空的,就 time.sleep(.1)
等待一下。