首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >单个项目与多个项目

单个项目与多个项目
EN

Stack Overflow用户
提问于 2019-09-09 21:19:40
回答 2查看 2.1K关注 0票数 4

我在如何储存我所有的蜘蛛上遇到了一个两难的问题。这些蜘蛛将通过命令行调用和从stdin读取的项被发送到Apache NiFi中。我还计划让这些蜘蛛的子集在单独的web服务器上使用scrapyrt返回单个项的结果。我将需要创建蜘蛛跨越许多不同的项目与不同的项目模型。它们都有类似的设置(比如使用相同的代理)。

我的问题是,什么是构建我的项目的最佳方式?

  1. 将所有蜘蛛放置在同一个存储库中。提供了一种简单的方法,可以为项目加载器和项目管道创建基类。
  2. 每个项目的组蜘蛛,我正在为不同的存储库工作。的优点是允许项目成为每个项目的焦点,并且不会变得太大。无法共享公共代码、设置、蜘蛛监视器(spidermon)和基类。这感觉是最干净的,尽管有一些重复。
  3. 包只包括我计划在NiFi回购中使用的非实时蜘蛛和在另一个回购中使用的实时蜘蛛。的优势在于,我将蜘蛛与实际使用它们的项目保持在一起,但仍然集中/卷积使用哪种蜘蛛与哪个项目一起使用。

这似乎是正确的答案是#2。与特定程序相关的蜘蛛应该在他们自己的项目中,就像你为项目A创建web服务时一样,你不会说哦,我只需要把项目B的所有服务端点都扔到同一个服务中,因为这是我所有服务都会存在的地方,尽管有些设置可能会被复制。可以说,某些共享代码/类可以通过单独的包共享。

你认为如何?为了最大限度地提高可重用性,你们是如何构建自己的项目的呢?你在什么地方划清了同一个项目和单独项目的界限?它是基于您的项目模型还是数据源?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-09-16 00:03:31

谷歌集团线程名为"针对不同来源的单个Scrapy项目与多个项目“的雅各布建议:

蜘蛛是否应该进入同一个项目,主要取决于它们抓取的数据类型,而不是数据的来源。 假设您正在从所有目标站点中刮取用户配置文件,那么您可能有一个项目管道来清理和验证用户化身,并将它们导出到您的" avatars“数据库中。把所有的蜘蛛都放在同一个项目中是有意义的。毕竟,它们都使用相同的管道,因为不管数据是从哪里刮来的,数据总是具有相同的形状。另一方面,如果您正在从Stack溢出、Wikipedia中的用户配置文件以及Github中的问题中抓取问题,并且您以不同的方式验证/处理/导出所有这些数据类型,那么将这些蜘蛛放到单独的项目中就更有意义了。 换句话说,如果您的蜘蛛具有共同的依赖关系(例如,它们共享项定义/管道/中间件),那么它们可能属于同一个项目;如果每个蜘蛛都有各自的特定依赖项,则它们可能属于单独的项目。

Pablo是Scrapy的开发人员之一,他在另一个帖子"刮痕蜘蛛与工程“中回答道:

...recommend将所有蜘蛛保存在同一个项目中,以提高代码的可重用性(公共代码、辅助函数等)。 我们有时会在蜘蛛名称上使用前缀,比如film_spider1、film_spider2 actor_spider1、actor_spider2等等。有时我们还会编写爬行器,因为当爬行的页面上有很大的重叠时,这样做更有意义。

票数 4
EN

Stack Overflow用户

发布于 2019-09-13 07:10:10

首先,当我编写像'/path'这样的路径时,这是因为我是一个Ubuntu用户。如果您是Windows用户,请调整它。那是文件管理系统的问题。

轻例

让我们想象一下,您想要2不同的网站或更多。第一个是泳装零售网站。第二个是关于天气的。您想要,因为您希望观察泳衣价格和天气之间的联系,以便预测较低的购买价格。

注意,在pipelines.py中,我将使用mongo集合,因为这是我所使用的,我暂时不需要SQL。如果您不知道mongo,请考虑集合相当于关系数据库中的表。

scrapy项目可能如下所示:

spiderswebsites.py,在这里你可以写你想要的蜘蛛的数量。

代码语言:javascript
复制
import scrapy
from ..items.py import SwimItem, WeatherItem
#if sometimes you have trouble to import from parent directory you can do
#import sys
#sys.path.append('/path/parentDirectory')

class SwimSpider(scrapy.Spider):
    name = "swimsuit"
    start_urls = ['https://www.swimsuit.com']
    def parse (self, response):
        price = response.xpath('span[@class="price"]/text()').extract()
        model = response.xpath('span[@class="model"]/text()').extract()
        ... # and so on
        item = SwimItem() #needs to be called -> ()
        item['price'] = price
        item['model'] = model
        ... # and so on
        return item

class WeatherSpider(scrapy.Spider):
    name = "weather"
    start_urls = ['https://www.weather.com']
    def parse (self, response):
        temperature = response.xpath('span[@class="temp"]/text()').extract()
        cloud = response.xpath('span[@class="cloud_perc"]/text()').extract()
        ... # and so on
        item = WeatherItem() #needs to be called -> ()
        item['temperature'] = temperature
        item['cloud'] = cloud
        ... # and so on
        return item

items.py,您可以在这里编写您想要的项目模式的数量。

代码语言:javascript
复制
import scrapy
class SwimItem(scrapy.Item):
    price = scrapy.Field()
    stock = scrapy.Field()
    ...
    model = scrapy.Field()

class WeatherItem(scrapy.Item):
    temperature = scrapy.Field()
    cloud = scrapy.Field()
    ...
    pressure = scrapy.Field()

pipelines.py,在这里我使用Mongo

代码语言:javascript
复制
import pymongo
from .items import SwimItem,WeatherItem
from .spiders.spiderswebsites import SwimSpider , WeatherSpider

class ScrapePipeline(object):

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod #this is a decorator, that's a powerful tool in Python
    def from_crawler(cls, crawler):
        return cls(
        mongo_uri=crawler.settings.get('MONGODB_URL'),
        mongo_db=crawler.settings.get('MONGODB_DB', 'defautlt-test')
        )
    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]
        
    def close_spider(self, spider):
         self.client.close()

    def process_item(self, item, spider):
        if isinstance(spider, SwimItem):
            self.collection_name = 'swimwebsite'
        elif isinstance(spider, WeatherItem):
            self.collection_name = 'weatherwebsite'
        self.db[self.collection_name].insert(dict(item))

因此,当您查看我的示例的项目时,您会发现项目完全不依赖于项目的模式,因为您可以在相同的项目中使用几种类型的项。在上面的模式中,如果需要的话,优点是在settings.py中保持相同的配置。但是不要忘记你可以“定制”你的蜘蛛的命令。如果您希望您的蜘蛛运行与默认设置稍有不同,您可以将其设置为scrapy crawl spider -s DOWNLOAD_DELAY=35,而不是您在settings.py中编写的25

功能程序设计

此外,这里我的例子很轻。实际上,您很少对原始数据感兴趣。你需要很多治疗,这代表了很多线。要提高代码的可读性,可以在模块中创建函数。但是对意大利面码要小心。

functions.py,定制模块

代码语言:javascript
复制
from re import search

def cloud_temp(response): #for WeatherSpider
    """returns a tuple containing temperature and percentage of clouds"""
    temperature = response.xpath('span[@class="temp"]/text()').extract() #returns a str as " 12°C"
    cloud = response.xpath('span[@class="cloud_perc"]/text()').extract() #returns a str as "30%"
    #treatments, you want to record it as integer
    temperature = int(re.search(r'[0-9]+',temperature).group()) #returns int as 12
    cloud = int(re.search(r'[0-9]+',cloud).group()) #returns int as 30
    return (cloud,temperature)

它在spiders.py中给出

代码语言:javascript
复制
import scrapy
from items.py import SwimItem, WeatherItem
from functions.py import *
...
class WeatherSpider(scrapy.Spider):
    name = "weather"
    start_urls = ['https://www.weather.com']
    def parse (self, response):
        cloud , temperature = cloud_temp(response) "this is shorter than the previous one
        ... # and so on
        item = WeatherItem() #needs to be called -> ()
        item['temperature'] = temperature
        item['cloud'] = cloud
        ... # and so on
        return item

此外,它在调试方面也有了很大的改进。假设我想做一个刮擦的shell会话。

代码语言:javascript
复制
>>> scrapy shell https://www.weather.com
...
#I check in the sys path if the directory where my `functions.py` module is present.
>>> import sys
>>> sys.path #returns a list of paths
>>> #if the directory is not present
>>> sys.path.insert(0, '/path/directory')
>>> #then I can now import my module in this session, and test in the shell, while I modify in the file functions.py itself
>>> from functions.py import *
>>> cloud_temp(response) #checking if it returns what I want.

这比复制和粘贴一段代码更舒服。因为Python是一种很好的函数式编程语言,所以您应该从中受益。这就是为什么我告诉您“更广泛地说,如果您限制行数、提高可读性、限制bug,则任何模式都是有效的。”它的可读性越强,您就越能限制这些bug。您编写的行数越少(例如避免复制和粘贴不同变量的相同处理),就越少限制bug。因为当你修正一个函数本身时,你修正了所有依赖于它的函数。

所以现在,如果您对函数式编程不太满意,我可以理解您为不同的项目模式做了几个项目。你用你目前的技能来改进它们,然后随着时间的推移来改进你的代码。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57861326

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档