35

私はPythonとDjangoに非常に慣れていません。私は現在、Scrapy を使用してサイトをスクレイピングし、データを Django データベースに保存することを検討しています。私の目標は、ユーザーが指定したドメインに基づいてスパイダーを実行することです。

必要なデータを抽出し、呼び出し時にjsonファイルに正しく保存するスパイダーを作成しました

scrapy crawl spider -o items.json -t json

スクレイピー チュートリアルで説明されているとおりです。

私の目標は、スパイダーを正常に Django データベースにデータを保存してから、ユーザー入力に基づいてスパイダーを実行させることです。

この件に関しては、次のようなさまざまな投稿があることは承知しています: リンク 1 リンク 2 リンク 3

しかし、これを機能させるために 8 時間以上を費やしてきたので、まだ問題に直面しているのは私だけではないと思います。そのため、この投稿でこれまでに得たすべての知識を集めようとします。また、うまくいけば、後で実用的なソリューションを投稿することもできます。このため、この投稿はかなり長いです。

Scrapy から Django データベースにデータを保存するには、2 つの異なる解決策があるようです。1 つはDjangoItemを使用することで、もう 1 つはモデルを直接インポートすることです (ここで行うように)。

私はこれら 2 つの長所と短所を完全には認識していませんが、違いは単に 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 シェルで機能する必要があります)。

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_APPSmyweb/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. スクレイピー プロジェクトを作成します。

    $ 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

これは最も簡単な解決策の 1 つです。この行を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モデル(別名Django ORM)で作業する方法を共有したいと思いまし た。また、DjangoItem は使用しません。

以下は、Scrapy 0.18.2 および Django 1.5.2 で動作します。私のスクレイピープロジェクトは、以下でスクレイピングと呼ばれます。

  1. 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するストック ファイルとほとんど同じですが、4 行目はスクレイピー設定ファイルを指しています。確かに、 と を使用するのは少し奇妙に思えますが、必要な 1 つのコマンドで機能します。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 に答える