【问题标题】:Access Django models with scrapy: defining path to Django project使用 scrapy 访问 Django 模型:定义 Django 项目的路径
【发布时间】:2013-10-04 18:52:59
【问题描述】:

我对 Python 和 Django 非常陌生。我目前正在探索使用 Scrapy 来抓取网站并将数据保存到 Django 数据库。我的目标是根据用户提供的域运行蜘蛛。

我写了一个蜘蛛,它可以提取我需要的数据,并在调用时将其正确存储在一个 json 文件中

scrapy crawl spider -o items.json -t json

scrapy tutorial 中所述。

我现在的目标是让蜘蛛成功地将数据保存到 Django 数据库,然后根据用户输入让蜘蛛运行。

我知道有很多关于这个主题的帖子,例如: link 1 link 2 link 3

但是我花了超过 8 个小时试图让它工作,我假设我不是唯一一个仍然面临这个问题的人。因此,我将尝试收集到目前为止我在这篇文章中获得的所有知识,并希望稍后发布一个可行的解决方案。正因为如此,这篇文章比较长。

在我看来,将数据从 Scrapy 保存到 Django 数据库有两种不同的解决方案。一种是使用DjangoItem,另一种是直接导入模型(和here一样)。

我不完全了解这两者的优缺点,但似乎区别只是使用 DjangoItem 更方便更短。

我做了什么:

我已添加:

def setup_django_env(path):
    import imp, os
    from django.core.management import setup_environ

    f, filename, desc = imp.find_module('settings', [path])
    project = imp.load_module('settings', f, filename, desc)       

    setup_environ(project)

setup_django_env('/Users/Anders/DjangoTraining/wsgi/')

我得到的错误是:

ImportError: No module named settings

我认为我以错误的方式定义了我的 Django 项目的路径?

我还尝试了以下方法:

setup_django_env('../../') 

如何正确定义 Django 项目的路径? (如果这是问题)

【问题讨论】:

    标签: python django django-models scrapy


    【解决方案1】:

    我认为主要的误解是包路径与设置模块路径。为了从外部脚本使用 django 的模型,您需要设置 DJANGO_SETTINGS_MODULE。然后,这个模块必须是importable(即如果设置路径是myproject.settings,那么from myproject import settings 语句应该在python shell 中工作)。

    由于 django 中的大多数项目都是在默认 PYTHONPATH 之外的路径中创建的,因此您必须将项目的路径添加到 PYTHONPATH 环境变量中。

    这里是创建一个完整工作(和最小)的 Django 模型集成到 Scrapy 项目的分步指南:

    注意:此说明在最后一次编辑日期有效。如果它不适合您,请添加评论并描述您的问题和 scrapy/django 版本。

    1. 项目将在/home/rolando/projects 目录中创建。

    2. 启动 django 项目

      $ cd ~/projects
      $ django-admin startproject myweb
      $ cd myweb
      $ ./manage.py startapp myapp
      
    3. myapp/models.py 中创建模型。

      from django.db import models
      
      
      class Person(models.Model):
          name = models.CharField(max_length=32)
      
    4. myweb/settings.py 中将myapp 添加到INSTALLED_APPS

      # at the end of settings.py
      INSTALLED_APPS += ('myapp',)
      
    5. myweb/settings.py中设置我的数据库设置。

      # at the end of settings.py
      DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'
      DATABASES['default']['NAME'] = '/tmp/myweb.db'
      
    6. 创建数据库。

      $ ./manage.py syncdb --noinput
      Creating tables ...
      Installing custom SQL ...
      Installing indexes ...
      Installed 0 object(s) from 0 fixture(s)
      
    7. 创建 scrapy 项目

      $ cd ~/projects
      $ scrapy startproject mybot
      $ cd mybot
      
    8. mybot/items.py中创建一个项目。

    注意:在较新版本的 Scrapy 中,您需要安装 scrapy_djangoitem 并使用 from scrapy_djangoitem import DjangoItem

        from scrapy.contrib.djangoitem import DjangoItem
        from scrapy.item import Field
    
        from myapp.models import Person
    
    
        class PersonItem(DjangoItem):
            # fields for this item are automatically created from the django model
            django_model = Person
    

    最终的目录结构是这样的:

    /home/rolando/projects
    ├── mybot
    │   ├── mybot
    │   │   ├── __init__.py
    │   │   ├── items.py
    │   │   ├── pipelines.py
    │   │   ├── settings.py
    │   │   └── spiders
    │   │       └── __init__.py
    │   └── scrapy.cfg
    └── myweb
        ├── manage.py
        ├── myapp
        │   ├── __init__.py
        │   ├── models.py
        │   ├── tests.py
        │   └── views.py
        └── myweb
            ├── __init__.py
            ├── settings.py
            ├── urls.py
            └── wsgi.py
    

    从这里开始,基本上我们已经完成了在 scrapy 项目中使用 django 模型所需的代码。我们可以立即使用scrapy shell 命令对其进行测试,但请注意所需的环境变量:

    $ cd ~/projects/mybot
    $ PYTHONPATH=~/projects/myweb DJANGO_SETTINGS_MODULE=myweb.settings scrapy shell
    
    # ... scrapy banner, debug messages, python banner, etc.
    
    In [1]: from mybot.items import PersonItem
    
    In [2]: i = PersonItem(name='rolando')
    
    In [3]: i.save()
    Out[3]: <Person: Person object>
    
    In [4]: PersonItem.django_model.objects.get(name='rolando')
    Out[4]: <Person: Person object>
    

    所以,它按预期工作。

    最后,您可能不想在每次运行机器人时都设置环境变量。有很多替代方法可以解决这个问题,但最好的方法是项目的包实际上安装在 PYTHONPATH 中设置的路径中。

    这是最简单的解决方案之一:将此行添加到您的 mybot/settings.py 文件以设置环境变量。

    # Setting up django's project full path.
    import sys
    sys.path.insert(0, '/home/rolando/projects/myweb')
    
    # Setting up django's settings module name.
    # This module is located at /home/rolando/projects/myweb/myweb/settings.py.
    import os
    os.environ['DJANGO_SETTINGS_MODULE'] = 'myweb.settings'
    
    # Since Django 1.7, setup() call is required to populate the apps registry.
    import django; django.setup()
    

    注意:更好的路径黑客方法是在两个项目中都有基于setuptoolssetup.py文件并运行python setup.py develop,这会将您的项目路径链接到python的路径(我假设你使用virtualenv)。

    这就够了。为了完整起见,这里是一个完整工作项目的基本蜘蛛和管道:

    1. 创建蜘蛛。

      $ cd ~/projects/mybot
      $ scrapy genspider -t basic example example.com
      

      蜘蛛代码:

      # file: mybot/spiders/example.py
      from scrapy.spider import BaseSpider
      from mybot.items import PersonItem
      
      
      class ExampleSpider(BaseSpider):
          name = "example"
          allowed_domains = ["example.com"]
          start_urls = ['http://www.example.com/']
      
          def parse(self, response):
              # do stuff
              return PersonItem(name='rolando')
      
    2. mybot/pipelines.py 中创建管道以保存项目。

      class MybotPipeline(object):
          def process_item(self, item, spider):
              item.save()
              return item
      

      如果您使用DjangoItem 类,您可以在这里使用item.save(),或者直接导入django 模型并手动创建对象。这两种方式的主要问题是定义环境变量,以便您可以使用 django 模型。

    3. 将管道设置添加到您的 mybot/settings.py 文件中。

      ITEM_PIPELINES = {
          'mybot.pipelines.MybotPipeline': 1000,
      }
      
    4. 运行蜘蛛。

      $ scrapy crawl example
      

    【讨论】:

    • 非常详细的描述,谢谢。像魅力一样工作。我只遇到了一个问题,我不得不将 os.environ['DJANGO_SETTINGS_MODULE'] = 'myweb.settings' 更改为 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'。这样就不要加上myweb前缀了,不然就无法识别模块了。
    • 对。 DJANGO_SETTINGS_MODULE 的值很大程度上取决于您如何设置 Python 路径变量。这可能会让人很困惑,因为 django 和 scrapy 默认使用相同的项目目录和项目包名称。添加到sys.path 的路径应该是包含settings.py 文件的目录的父级。无论如何,我很高兴这解决了您的问题。
    • 这是一个非常完整的答案。我遇到的唯一问题是在scrapy shell 步骤中我必须添加import django django.setup()
    • 我需要 scrapy_djangoitem。 pip install scrapy_djangoitemmybot/items.py: from scrapy_djangoitem import DjangoItem
    • @DanSandland 感谢您的评论,我已经添加了关于此的注释。
    【解决方案2】:

    尽管 Rho 的回答看起来非常好,但我想我还是会分享一下我是如何在没有完整的 Django 项目的情况下使用 Django Models(又名 Django ORM),因为问题只说明了使用一个“Django 数据库”。我也不使用 DjangoItem。

    以下适用于 Scrapy 0.18.2 和 Django 1.5.2。我的scrapy项目在下面叫做scraping。

    1. 将以下内容添加到您的scrapy settings.py 文件中

      from django.conf import settings as d_settings
      d_settings.configure(
          DATABASES={
              'default': {
                  'ENGINE': 'django.db.backends.postgresql_psycopg2',
                  'NAME': 'db_name',
                  'USER': 'db_user',
                  'PASSWORD': 'my_password',
                  'HOST': 'localhost',  
                  'PORT': '',
              }},
          INSTALLED_APPS=(
              'scrapping',
          )
      )
      
    2. 在与您的scrapy.cfg 相同的文件夹中创建一个manage.py 文件: 运行蜘蛛本身时不需要此文件,但对于设置数据库非常方便。所以我们开始:

      #!/usr/bin/env python
      import os
      import sys
      
      if __name__ == "__main__":
          os.environ.setdefault("DJANGO_SETTINGS_MODULE", "scrapping.settings")
      
          from django.core.management import execute_from_command_line
      
          execute_from_command_line(sys.argv)
      

      这就是manage.py 的全部内容,几乎就是你在运行django-admin startproject myweb 后得到的库存manage.py 文件,但第4 行指向你的scrapy 设置文件。 诚然,使用DJANGO_SETTINGS_MODULEsettings.configure 似乎有点奇怪,但它适用于我需要的manage.py 命令:$ python ./manage.py syncdb

    3. 您的models.py 你的 models.py 应该放在你的 scrapy 项目文件夹中(即scrapping.models´). After creating that file you should be able to run you$ python ./manage.py syncdb`。它可能看起来像这样:

      from django.db import models
      
      class MyModel(models.Model):
          title = models.CharField(max_length=255)
          description = models.TextField()
          url = models.URLField(max_length=255, unique=True)
      
    4. 您的items.pypipeline.py: 我曾经使用 Rho 的答案中描述的 DjangoItem,但是在与 scrapyd 并行运行许多爬网并使用 Postgresql 时遇到了麻烦。异常max_locks_per_transaction 在某个时候抛出,打破了所有正在运行的爬网。此外,我没有弄清楚如何正确回滚管道中失败的item.save()。长话短说,我最终没有使用 DjangoItem 解决了我所有的问题。方法如下: items.py:

      from scrapy.item import Item, Field
      
      class MyItem(Item):
          title = Field()
          description = Field()
          url = Field()
      

      请注意,如果您想像下一步一样方便地解压它们,则字段需要与模型中的名称相同! pipelines.py:

      from django.db import transaction
      from models import MyModel
      class Django_pipeline(object):
          def process_item(self, item, spider):
              with transaction.commit_on_success():
                  scraps = MyModel(**item)
                  scraps.save()
              return item
      

      如上所述,如果您像在 models.py 文件中那样命名所有项目字段,则可以在创建 MyModel 对象时使用 **item 解压缩所有字段。

    就是这样!

    【讨论】:

    • 不错的答案。它显示了 django 集成的灵活性。顺便说一句,我认为您应该就max_locks_per_transaction 问题开一张票,因为它使DjangoItem 类对于需要存储到RDBMS 中的中型项目几乎毫无用处。
    • 从 Django 1.8 开始,使用atomic() 而不是commit_on_success()
    猜你喜欢
    • 2011-05-15
    • 2022-11-30
    • 1970-01-01
    • 2017-06-13
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-15
    相关资源
    最近更新 更多