2013-09-28 13 views
30

Jestem bardzo nowy w Pythonie i Django. Obecnie badam wykorzystanie Scrapy do skrobania witryn i zapisywania danych w bazie danych Django. Moim celem jest uruchomienie pająka opartego na domenie podanej przez użytkownika.Dostęp do modeli Django ze scrapy: definiowanie ścieżki do projektu Django

Pisałem pająka, która pobiera dane mi potrzebne i przechowywać go prawidłowo w pliku json Dzwoniąc

scrapy crawl spider -o items.json -t json 

Jak opisano w scrapy tutorial.

Moim celem jest teraz sprawić, że pająk z powodzeniem będzie zapisywać dane w bazie danych Django, a następnie pracować nad uruchomieniem pająka w oparciu o dane wprowadzone przez użytkownika.

jestem świadomy, że istnieje wiele postów na ten temat, takie jak te: link 1 link 2 link 3

Ale mając wydać więcej niż 8 godzin na próby uzyskania to do pracy, jestem przy założeniu, że nie jestem jedynym, który wciąż boryka się z tym problemem. W tym celu postaram się zebrać całą wiedzę, którą zdobyłem w tym poście, a także mam nadzieję, że w późniejszym czasie opublikuję działające rozwiązanie. Z tego powodu ten post jest dość długi.

Wydaje mi się, że istnieją dwa różne sposoby zapisywania danych w bazie danych Django ze Scrapy. Jednym z nich jest użycie DjangoItem, innym jest zaimportowanie modeli bezpośrednio (jak zrobiono here).

Nie jestem w pełni świadomy zalet i wad tych dwóch, ale wydaje się, że różnica polega na tym, że używanie DjangoItem jest po prostu wygodniejsze i krótsze.

co zrobiłem:

Dodałem:

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/') 

Błąd Dostaję to:

ImportError: No module named settings 

myślę i” m definiuję ścieżkę do mojego projektu Django w niewłaściwy sposób?

Ja również próbowałem następujące:

setup_django_env('../../') 

Jak mogę określić ścieżkę do mojego projektu Django poprawnie? (jeśli to jest problem)

Odpowiedz

61

Myślę, że głównym nieporozumieniem jest ścieżka pakietu vs ścieżka modułu ustawień. Aby użyć modeli django z zewnętrznego skryptu, musisz ustawić DJANGO_SETTINGS_MODULE. Następnie moduł ten musi być importowalny (tj. Jeśli ścieżka ustawień to myproject.settings, wówczas instrukcja from myproject import settings powinna działać w powłoce Pythona).

Ponieważ większość projektów w django jest tworzona w ścieżce poza domyślnym PYTHONPATH, należy dodać ścieżkę projektu do zmiennej środowiskowej PYTHONPATH.

Oto przewodnik krok po kroku stworzyć w pełni funkcjonalny (a minimalny) Django modeli integracji do projektu Scrapy:

Uwaga: Te instrukcje pracy w dacie ostatniej edycji. Jeśli to nie działa, dodaj komentarz i opisz swój problem oraz wersje scrapy/django.

  1. Projekty zostaną utworzone w katalogu /home/rolando/projects.

  2. Rozpocznij projekt django.

    $ cd ~/projects 
    $ django-admin startproject myweb 
    $ cd myweb 
    $ ./manage.py startapp myapp 
    
  3. Utwórz model w myapp/models.py.

    from django.db import models 
    
    
    class Person(models.Model): 
        name = models.CharField(max_length=32) 
    
  4. Dodaj myapp do INSTALLED_APPS w myweb/settings.py.

    # at the end of settings.py 
    INSTALLED_APPS += ('myapp',) 
    
  5. Ustaw swoje ustawienia dB myweb/settings.py.

    # at the end of settings.py 
    DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3' 
    DATABASES['default']['NAME'] = '/tmp/myweb.db' 
    
  6. Utwórz bazę danych.

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

    $ cd ~/projects 
    $ scrapy startproject mybot 
    $ cd mybot 
    
  8. Załóż element mybot/items.py.

Uwaga: W nowszych wersjach Scrapy, trzeba zainstalować i używać from scrapy_djangoitem import DjangoItemscrapy_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 

Ostateczna struktura katalogów jest następująca:

/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 

Stąd, w zasadzie mamy zrobić z kodem wymaganym do korzystania z modeli django w projekcie scrapy. Możemy przetestować to od razu za pomocą polecenia scrapy shell ale należy pamiętać o wymaganych zmiennych środowiskowych:

$ 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> 

Tak, to działa zgodnie z przeznaczeniem.

Wreszcie możesz nie chcieć ustawiać zmiennych środowiskowych przy każdym uruchomieniu bota. Istnieje wiele alternatyw pozwalających rozwiązać ten problem, chociaż najlepiej, że pakiety projektów są rzeczywiście zainstalowane w ścieżce ustawionej w PYTHONPATH.

Jest to jedno z najprostszych rozwiązań: dodaj te linie do pliku mybot/settings.py, aby skonfigurować zmienne środowiskowe.

# 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() 

Uwaga: Lepszym podejściem do hakerstwa ścieżka ma mieć setuptools -na setup.py plików w obu projektach i uruchomić python setup.py develop która połączy swoją ścieżkę projektu na ścieżce Pythona (Jestem przy założeniu korzystania virtualenv).

To wystarczy. Dla kompletności, tutaj jest prosty pająk i rurociąg dla w pełni działającego projektu:

  1. Stwórz pająka.

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

    Kod pająk:

    # 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. Tworzenie rurociągu w mybot/pipelines.py aby zapisać pozycję.

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

    Tutaj można użyć item.save() jeśli używasz klasę DjangoItem lub importować modelu django bezpośrednio i utworzenie obiektu ręcznie. W obu przypadkach głównym problemem jest zdefiniowanie zmiennych środowiskowych, aby można było używać modeli django.

  3. Dodaj ustawienie potoku do pliku mybot/settings.py.

    ITEM_PIPELINES = { 
        'mybot.pipelines.MybotPipeline': 1000, 
    } 
    
  4. Uruchom pająka.

    $ scrapy crawl example 
    
+6

Niewiarygodnie szczegółowy opis, dziękuję. Pracował jak urok. Wystąpił tylko jeden problem, musiałem zmienić os.environ ['DJANGO_SETTINGS_MODULE'] = 'myweb.settings' na os.environ ['DJANGO_SETTINGS_MODULE'] = 'settings'. Tak więc przedrostek myweb nie został dodany, inaczej nie rozpoznałby modułu. – Splurk

+2

W prawo. Wartość 'DJANGO_SETTINGS_MODULE' bardzo zależy od tego, jak skonfigurowałeś zmienną ścieżki Pythona. To może być dość mylące, ponieważ django i scrapy używa domyślnie tej samej nazwy dla katalogu projektu i pakietu projektu. Ścieżka dodana do 'sys.path' powinna być nadrzędną katalogu zawierającego plik' settings.py'. W każdym razie, cieszę się, że to rozwiązało twój problem. – Rolando

+6

To jest niesamowicie kompletna odpowiedź. Jedyny problem, jaki miałem, polegał na tym, że musiałem dodać 'import django django.setup()' –

5

Chociaż odpowiedź Rho wydaje się bardzo dobry myślałem, że dzielić jak mam scrapy pracy z Django Models (aka Django ORM) bez pełnowartościowy projekt Django ponieważ kwestia stanowi jedynie korzystanie z "bazy danych Django". Również nie używam DjangoItem.

Poniższe działa z Scrapy 0.18.2 i Django 1.5.2. Mój projekt scrapy nazywa się złomowaniem w następujący sposób.

  1. Dodaj następujące do scrapy settings.py plik

    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. Utwórz plik w tym samym folderze manage.py jako scrapy.cfg: Ten plik nie jest konieczna podczas uruchamiania pająk sobie lecz jest super wygodne do konfiguracji bazy danych. Więc zaczynamy:

    #!/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) 
    

    To cała zawartość manage.py i jest prawie dokładnie taki plik stock manage.py masz po uruchomieniu django-admin startproject myweb ale 4. Punkty linię do pliku ustawień scrapy. Prawdą jest, że używanie DJANGO_SETTINGS_MODULE i settings.configure wydaje się nieco dziwne, ale działa na potrzeby jednego z tych manage.py potrzebnych mi poleceń: $ python ./manage.py syncdb.

  3. Twój models.py Twój models.py powinny być umieszczone w folderze projektu scrapy (tj scrapping.models´). After creating that file you should be able to run you $ python ./manage.py syncdb` To może wyglądać następująco:..

    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. Twój items.py i pipeline.py: Kiedyś użyłem DjangoItem jako opisanego w odpowiedzi Rho, ale wpadłem na kłopoty z nim, gdy uruchomiłem wiele przeszukiwań równolegle ze zgrywaniem i użyciem PostgreSQL, Wyjątek max_locks_per_transaction został rzucony w pewnym momencie zerwania wszystkich uruchomionych indeksowań. nie wymyśliłem, jak prope rly wycofuje nieudany item.save() w przygotowaniu. Krótko mówiąc, w końcu nie korzystałem z DjangoItem, który rozwiązał wszystkie moje problemy. Oto jak: items.py:

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

    Uwaga że pola muszą mieć taką samą nazwę jak w modelu, jeśli chcesz rozpakować je wygodnie jak w następnym kroku! 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 
    

    Jak wspomniano powyżej, jeśli wszystkie pola o nazwie artykuł jak to zrobiłeś w pliku models.py można użyć **item rozpakować wszystkie pola podczas tworzenia obiektu MyModel.

To wszystko!

+0

Dobra odpowiedź. Pokazuje, jak elastyczna może być integracja django. Nawiasem mówiąc, myślę, że powinieneś otworzyć zgłoszenie dotyczące problemu 'max_locks_per_transaction', ponieważ klasa' DjangoItem' jest praktycznie bezużyteczna w przypadku średnich i dużych projektów, gdzie wymagane jest przechowywanie w RDBMS. – Rolando

+0

W końcu udało mi się to zrobić: https: // github.com/scrapy/scrapy/issues/415 – Chris

+0

Od wersji Django 1.8 i nowszej użyj 'atomic()' zamiast 'commit_on_success()'. – Nir