我在如何储存我所有的蜘蛛上遇到了一个两难的问题。这些蜘蛛将通过命令行调用和从stdin读取的项被发送到Apache NiFi中。我还计划让这些蜘蛛的子集在单独的web服务器上使用scrapyrt返回单个项的结果。我将需要创建蜘蛛跨越许多不同的项目与不同的项目模型。它们都有类似的设置(比如使用相同的代理)。
我的问题是,什么是构建我的项目的最佳方式?
这似乎是正确的答案是#2。与特定程序相关的蜘蛛应该在他们自己的项目中,就像你为项目A创建web服务时一样,你不会说哦,我只需要把项目B的所有服务端点都扔到同一个服务中,因为这是我所有服务都会存在的地方,尽管有些设置可能会被复制。可以说,某些共享代码/类可以通过单独的包共享。
你认为如何?为了最大限度地提高可重用性,你们是如何构建自己的项目的呢?你在什么地方划清了同一个项目和单独项目的界限?它是基于您的项目模型还是数据源?
发布于 2019-09-16 00:03:31
谷歌集团线程名为"针对不同来源的单个Scrapy项目与多个项目“的雅各布建议:
蜘蛛是否应该进入同一个项目,主要取决于它们抓取的数据类型,而不是数据的来源。 假设您正在从所有目标站点中刮取用户配置文件,那么您可能有一个项目管道来清理和验证用户化身,并将它们导出到您的" avatars“数据库中。把所有的蜘蛛都放在同一个项目中是有意义的。毕竟,它们都使用相同的管道,因为不管数据是从哪里刮来的,数据总是具有相同的形状。另一方面,如果您正在从Stack溢出、Wikipedia中的用户配置文件以及Github中的问题中抓取问题,并且您以不同的方式验证/处理/导出所有这些数据类型,那么将这些蜘蛛放到单独的项目中就更有意义了。 换句话说,如果您的蜘蛛具有共同的依赖关系(例如,它们共享项定义/管道/中间件),那么它们可能属于同一个项目;如果每个蜘蛛都有各自的特定依赖项,则它们可能属于单独的项目。
Pablo是Scrapy的开发人员之一,他在另一个帖子"刮痕蜘蛛与工程“中回答道:
...recommend将所有蜘蛛保存在同一个项目中,以提高代码的可重用性(公共代码、辅助函数等)。 我们有时会在蜘蛛名称上使用前缀,比如film_spider1、film_spider2 actor_spider1、actor_spider2等等。有时我们还会编写爬行器,因为当爬行的页面上有很大的重叠时,这样做更有意义。
发布于 2019-09-13 07:10:10
首先,当我编写像'/path'这样的路径时,这是因为我是一个Ubuntu用户。如果您是Windows用户,请调整它。那是文件管理系统的问题。
轻例
让我们想象一下,您想要刮2不同的网站或更多。第一个是泳装零售网站。第二个是关于天气的。您想要刮,因为您希望观察泳衣价格和天气之间的联系,以便预测较低的购买价格。
注意,在pipelines.py中,我将使用mongo集合,因为这是我所使用的,我暂时不需要SQL。如果您不知道mongo,请考虑集合相当于关系数据库中的表。
scrapy项目可能如下所示:
spiderswebsites.py,在这里你可以写你想要的蜘蛛的数量。
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 itemitems.py,您可以在这里编写您想要的项目模式的数量。
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
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,定制模块
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中给出
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会话。
>>> 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。因为当你修正一个函数本身时,你修正了所有依赖于它的函数。
所以现在,如果您对函数式编程不太满意,我可以理解您为不同的项目模式做了几个项目。你用你目前的技能来改进它们,然后随着时间的推移来改进你的代码。
https://stackoverflow.com/questions/57861326
复制相似问题