Scrapy:蜘蛛中间件中的异步数据库请求?

时间:2012-03-17 18:17:03

标签: python twisted scrapy

文件前言

我有一个蜘蛛将其结果存储在数据库中。然后,为了节省一些时间和远程Web服务器资源,我决定不请求已经在数据库中的项目。阅读文档我决定自定义蜘蛛中间件是我的最佳选择。

class SkipDupeMiddleware(object):
    process_spider_output(response, result, spider):
        for r in result:
            if isinstance(r, Request) and item_in_database(r.url):
                log.msg('Skip %s' % r.url)
            else:
                yield r

其中item_in_database查询数据库。

它也很有效并节省了大量时间。

现在真正的问题:

后来我读到在非阻塞应用程序中使用阻塞数据库请求不是一个好主意。我总是在"做正确的事情" 并决定将我的所有数据库请求包装在adbapi

我发现在管道中使用adbapi的配方,但是可以在中间件级别执行此操作吗?中间件预计会返回BaseItemRequestNone,但adbapi会返回twisted' s Deferred,后来会返回{ {1}}或Request

现在我被卡住了。

4 个答案:

答案 0 :(得分:2)

您可以(并且应该)从管道process_item()方法返回Deferred。

答案 1 :(得分:1)

AFAIK,

Scrapy主要是同步的。只下载页面是异步完成的,这就是请求有回调的原因。

管道和中间件是同步的。

答案 2 :(得分:1)

默认情况下,scrapy不会通过使用Duplicate-Filter(默认情况下为enalbed)在爬网中进行重复请求。设置参数DUPEFILTER_CLAS决定使用哪个过滤器 其默认值为:'scrapy.dupefilter.RFPDupeFilter'。事实上,它只是 现在选择。 如果需要持久性功能,则应设置JOBDIR。然后scrapy将存储访问的URL并在下次运行时加载它们(进入filter的dict)。

你可以在这里得到一些提示: http://groups.google.com/group/scrapy-users/browse_thread/thread/56546e9fab7030f3

答案 3 :(得分:0)

类似的问题,我想在蜘蛛中为前几个项目做一些检查(包括数据库调用),如果检查通过则停止抓取(我想如果只有少数请求,我们可以使用阻止数据库连接)。

我想到了一个想法:如果scrapy spider能够对我们搜索的网站发出http请求,那么请使用相同的机制执行异步数据库请求。对于那些拥有REST API的数据库,如elasticsearch,它必须易于实现。

这是我为AWS S3所做的:

from types import MethodType

import botocore.session
import treq
from scrapy import Request


class BotocoreRequest(Exception):

    def __init__(self, request, *args, **kwargs):
        super(BotocoreRequest, self).__init__(*args, **kwargs)
        self.method = request.method
        # https://github.com/twisted/treq/issues/185
        self.url = request.url.replace('https://', 'http://')
        self.headers = dict(request.headers)
        self.body = request.body and request.body.read()


def _send_request(self, request_dict, operation_model):
    request = self.create_request(request_dict, operation_model)
    raise BotocoreRequest(request=request)


class ScrapyAWSClient(object):
    def __init__(self, service, access_key, secret_key, region, timeout=30):
        session = botocore.session.get_session()
        session.set_credentials(
            access_key=access_key,
            secret_key=secret_key
        )
        self.client = session.create_client(service, region_name=region)
        endpoint = self.client._endpoint
        endpoint._send_request = MethodType(_send_request, endpoint)
        self.timeout = timeout

    def request(self, method, callback, meta, **kwargs):
        try:
            getattr(self.client, method)(**kwargs)
        except BotocoreRequest as e:
            return Request(
                method=e.method,
                url=e.url,
                body=e.body,
                headers=e.headers,
                meta=meta,
                callback=callback,
                dont_filter=True
            )

蜘蛛:

class MySpider(Spider):

    def __init__(self, *args, **kwargs):
        super(MySpider, self).__init__(*args, **kwargs)
        self.client = ScrapyAWSClient(
            service='s3',
            access_key='',
            secret_key='',
            region='your-region'
        )

    def parse(self, response):
        ...
        yield self.client.request(
            method='get_object',
            Bucket='my-s3-bucket',
            Key='my-key',
            callback=self.my_parser,
            meta={
                'handle_httpstatus_list': [200, 403]
            }
        )