Scrapy,在管道中发出http请求

时间:2016-07-19 19:33:58

标签: scrapy scrapy-pipeline

假设我有一个看似这样的抓取项目

{
    name: "Foo",
    country: "US",
    url: "http://..."
}

在一个管道中,我想对URL发出GET请求,并检查一些标题,如content_type和status。当标题不符合某些条件时,我想删除该项目。像

class MyPipeline(object):
    def process_item(self, item, spider):
        request(item['url'], function(response) {
           if (...) {
             raise DropItem()
           }
           return item
        }, function(error){ 
            raise DropItem()
        })

使用管道无法闻到这样的气味。你怎么看?任何想法如何实现这一目标?

蜘蛛:

import scrapy
import json

class StationSpider(scrapy.Spider):
    name = 'station'
    start_urls = ['http://...']

    def parse(self, response):
        jsonResponse = json.loads(response.body_as_unicode())
        for station in jsonResponse:
            yield station

1 个答案:

答案 0 :(得分:6)

简单方法

import requests

def process_item(self, item, spider):
    response = requests.get(item['url'])
    if r.status_code ...:
        raise DropItem()
    elif response.text ...:
        raise DropItem()
    else:
        return item

Scrapy方式

现在我认为你不应该在管道内做这件事,你应该在蜘蛛内部处理它而不是产生一个项目而是一个请求,然后产生该项目。

现在,如果您仍想在管道中包含scrapy请求,可以执行以下操作:

class MyPipeline(object):

    def __init__(self, crawler):
        self.crawler = crawler

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def process_item(self, item, spider):
        ...
        self.crawler.engine.crawl(
                    Request(
                        url='someurl',
                        callback=self.custom_callback,
                    ),
                    spider,
                )

        # you have to drop the item, and send it again after your check
        raise DropItem()
    # YES, you can define a method callback inside the same pipeline
    def custom_callback(self, response):
        ...
        yield item

检查我们是否在管道内模拟蜘蛛回调的相同行为。当你想要做一个额外的请求时,你需要找出一种总是丢弃项目的方法,并且只是通过额外的回调传递那些项目。

一种方法是发送不同类型的项目,并在管道的process_item内进行检查:

def process_item(self, item, spider):
    if isinstance(item, TempItem):
        ...
    elif isinstance(item, FinalItem):
        yield item