使用scrapy访问Django模型:定义Django项目的路径

时间:2013-09-28 15:02:34

标签: python django django-models scrapy

我是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项目的路径? (如果那是问题)

2 个答案:

答案 0 :(得分:67)

我认为主要的误解是包路径与设置模块路径。要从外部脚本使用django的模型,您需要设置DJANGO_SETTINGS_MODULE。然后,此模块必须可导入(即,如果设置路径为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. myapp添加到INSTALLED_APPS中的myweb/settings.py

    # 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

  9. 中创建一个项目

    注意:在较新版本的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>
    

    所以,它按预期工作。

    最后,您可能不希望每次运行bot时都必须设置环境变量。有很多替代方法可以解决这个问题,尽管最好的是项目的包实际安装在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()
    

    注意:更好的路径攻击方法是在两个项目中都有基于setuptools的{​​{1}}文件并运行setup.py,这将链接您的项目进入python路径的路径(我假设您使用python setup.py develop)。

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

    1. 制作蜘蛛。

      virtualenv

      蜘蛛代码:

      $ cd ~/projects/mybot
      $ scrapy genspider -t basic example example.com
      
    2. # 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') 中创建一个管道以保存该项目。

      mybot/pipelines.py

      如果您使用的是class MybotPipeline(object): def process_item(self, item, spider): item.save() return item 类,或者直接导入django模型并手动创建对象,可以使用item.save()。在两种方式中,主要问题是定义环境变量,以便您可以使用django模型。

    3. 将管道设置添加到DjangoItem文件中。

      mybot/settings.py
    4. 运行蜘蛛。

      ITEM_PIPELINES = {
          'mybot.pipelines.MybotPipeline': 1000,
      }
      

答案 1 :(得分:5)

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

以下适用于Scrapy 0.18.2和Django 1.5.2。我的scrapy项目在下面称为报废。

  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. 在与manage.py相同的文件夹中创建scrapy.cfg个文件: 运行蜘蛛本身时不需要此文件,但对于设置数据库非常方便。所以我们走了:

    #!/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的全部内容,几乎就是您在运行manage.py后获得的库存django-admin startproject myweb文件,但第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解压缩所有字段。

  5. 就是这样!