35

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

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

scrapy crawl spider -o items.json -t json

scrapy教程中所述。

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

我知道有关此主题的各种帖子,例如: 链接 1 链接 2 链接 3

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

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

我并不完全了解这两者的优缺点,但似乎区别只是使用 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 项目的路径?(如果这是问题)

4

2 回答 2

77

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

由于 django 中的大多数项目都是在 default 之外的路径中创建的,因此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

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

注意:路径黑客的更好方法是在两个项目中都有setuptools基于- 的setup.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
    

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

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

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

    $ scrapy crawl example
    
于 2013-09-29T00:54:40.593 回答
5

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

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

  1. 将以下内容添加到您的scrapysettings.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的股票文件的全部内容,但第四行指向你的scrapy设置文件。诚然,使用and似乎有点奇怪,但它适用于我需要的一个命令:.manage.pydjango-admin startproject mywebDJANGO_SETTINGS_MODULEsettings.configuremanage.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文件中那样命名所有项目字段,则可以**item在创建 MyModel 对象时使用解包所有字段。

而已!

于 2013-09-29T22:19:41.610 回答