2013-09-28 14 views
30

Sono molto nuovo a Python e Django. Attualmente sto esplorando usando Scrapy per raschiare i siti e salvare i dati nel database Django. Il mio obiettivo è quello di eseguire uno spider basato sul dominio fornito da un utente.Accesso ai modelli di Django con scrapy: definizione del percorso per il progetto Django

Ho scritto un ragno che estrae i dati che ho bisogno e conservarla correttamente in un file JSON al momento della chiamata

scrapy crawl spider -o items.json -t json 

Come descritto nel scrapy tutorial.

Il mio obiettivo è ora ottenere lo spider con successo per salvare i dati nel database Django, e quindi lavorare su come far funzionare lo spider in base all'input dell'utente.

Sono consapevole del fatto che i vari messaggi esiste su questo argomento, come queste: i link 1 link 2 link 3

Ma avere spendere più di 8 ore per cercare di arrivare a questo lavoro, sto assumendo non sono l'unico che sta ancora affrontando problemi con questo. Per questo cercherò di raccogliere tutta la conoscenza che ho ottenuto finora in questo post, nonché di postulare sperabilmente una soluzione funzionante in un secondo momento. Per questo motivo, questo post è piuttosto lungo.

Mi sembra che ci siano due diverse soluzioni per salvare i dati nel database Django da Scrapy. Uno è quello di utilizzare DjangoItem, un altro è quello di importare direttamente i modelli (come fatto here).

Non sono completamente consapevole dei vantaggi e degli svantaggi di questi due, ma sembra che la differenza sia semplicemente l'utilizzo di DjangoItem che è solo più conveniente e più breve.

quello che ho fatto:

ho aggiunto:

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

errore che sto ricevendo è:

ImportError: No module named settings 

Sto pensando i' Sto definendo il percorso del mio progetto Django in modo sbagliato?

Ho anche provato la seguente:

setup_django_env('../../') 

Come faccio a definire il percorso per il mio progetto Django correttamente? (se questo è il problema)

risposta

61

Penso che il malinteso principale sia il percorso del pacchetto rispetto al percorso del modulo delle impostazioni. Per utilizzare i modelli di django da uno script esterno è necessario impostare lo DJANGO_SETTINGS_MODULE. Quindi, questo modulo deve essere importabile (ad esempio se il percorso delle impostazioni è myproject.settings, la dichiarazione from myproject import settings dovrebbe funzionare in una shell python).

Poiché la maggior parte dei progetti in django vengono creati in un percorso esterno al valore predefinito PYTHONPATH, è necessario aggiungere il percorso del progetto alla variabile di ambiente PYTHONPATH.

Ecco una guida passo-passo per creare un completamente funzionante (e minimale) Django integrazione modelli in un progetto Scrapy:

Nota: Queste istruzioni funzionano alla data dell'ultima modifica. Se non funziona per te, aggiungi un commento e descrivi il tuo problema e le versioni scrapy/django.

  1. I progetti saranno create all'interno /home/rolando/projects directory.

  2. Avviare il progetto django.

    $ cd ~/projects 
    $ django-admin startproject myweb 
    $ cd myweb 
    $ ./manage.py startapp myapp 
    
  3. creare un modello in myapp/models.py.

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

    # at the end of settings.py 
    INSTALLED_APPS += ('myapp',) 
    
  5. impostato il mio impostazioni db in myweb/settings.py.

    # at the end of settings.py 
    DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3' 
    DATABASES['default']['NAME'] = '/tmp/myweb.db' 
    
  6. Creare il database.

    $ ./manage.py syncdb --noinput 
    Creating tables ... 
    Installing custom SQL ... 
    Installing indexes ... 
    Installed 0 object(s) from 0 fixture(s) 
    
  7. Creare il progetto Scrapy.

    $ cd ~/projects 
    $ scrapy startproject mybot 
    $ cd mybot 
    
  8. creare un elemento in mybot/items.py.

Nota: Nelle versioni più recenti di Scrapy, è necessario installare e utilizzare scrapy_djangoitemfrom 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 

La struttura di directory finale è questo:

/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 

Da qui, in fondo abbiamo finito con il codice necessario per utilizzare i modelli di Django in un progetto Scrapy. Siamo in grado di testare subito usando scrapy shell comando, ma essere consapevoli delle variabili di ambiente richieste:

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

Quindi, si sta lavorando come previsto.

Infine, potrebbe non essere necessario impostare le variabili di ambiente ogni volta che si esegue il bot. Esistono molte alternative per risolvere questo problema, anche se il migliore è che i pacchetti dei progetti siano effettivamente installati in un percorso impostato in PYTHONPATH.

Questa è una delle soluzioni più semplici: aggiungi queste righe al tuo file mybot/settings.py per impostare le variabili di ambiente.

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

Nota: Un approccio migliore alla pirateria percorso è quello di avere setuptools basata su setup.py file in entrambi i progetti ed eseguire python setup.py develop che collegherà il vostro percorso del progetto nel percorso del pitone (sto supponendo di utilizzare virtualenv).

Questo è sufficiente. Per completezza, ecco uno spider e una pipeline di base per un progetto pienamente funzionante:

  1. Creare il ragno.

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

    Il codice di ragno:

    # 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. crea una pipeline in mybot/pipelines.py per salvare l'elemento.

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

    Qui è possibile utilizzare item.save() se si utilizza la classe DjangoItem o importare direttamente il modello di Django e creare l'oggetto manualmente. In entrambi i casi il problema principale è definire le variabili di ambiente in modo da poter utilizzare i modelli di django.

  3. Aggiungere l'impostazione della pipeline al file mybot/settings.py.

    ITEM_PIPELINES = { 
        'mybot.pipelines.MybotPipeline': 1000, 
    } 
    
  4. Eseguire il ragno.

    $ scrapy crawl example 
    
+6

Descrizione incredibilmente dettagliata, grazie. Ha funzionato come un fascino. Ho incontrato solo un problema, ho dovuto cambiare os.environ ['DJANGO_SETTINGS_MODULE'] = 'myweb.settings' in os.environ ['DJANGO_SETTINGS_MODULE'] = 'impostazioni'. In modo che il prefisso myweb non è stato aggiunto, altrimenti non riconoscerebbe il modulo. – Splurk

+2

Giusto. Il valore di 'DJANGO_SETTINGS_MODULE' dipende molto da come hai impostato la tua variabile path python. Ciò potrebbe essere piuttosto confuso poiché django e scrapy usano di default lo stesso nome per la directory del progetto e il pacchetto del progetto. Il percorso aggiunto a 'sys.path' dovrebbe essere il genitore della directory contenente il file' settings.py'. Ad ogni modo, sono felice che questo abbia risolto il tuo problema. – Rolando

+6

Questa è una risposta incredibilmente completa. L'unico problema che ho avuto è stato in fase di shell scrapy, ho dovuto aggiungere 'import django django.setup()' –

5

Anche se la risposta di Rho sembra molto buono ho pensato di condividere come ho ottenuto Scrapy lavorare con Django modelle (aka Django ORM) senza un progetto in piena regola Django dal momento che la domanda si afferma solo il uso di un "database Django". Inoltre non uso DjangoItem.

Quanto segue funziona con Scrapy 0.18.2 e Django 1.5.2. Il mio progetto scrapy è chiamato demolizione nel seguito.

  1. Aggiungere il seguente al Scrapy settings.py file di

    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. Creare un file manage.py nella stessa cartella del scrapy.cfg: Questo file non è necessaria quando si esegue il ragno in sé, ma è super conveniente per la creazione del database. Quindi qui si va:

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

    Questo è l'intero contenuto del manage.py ed è più o meno esattamente file stock manage.py si ottiene dopo l'esecuzione django-admin startproject myweb ma i punti di linea 4 al tuo file di impostazioni Scrapy. Certo, usare DJANGO_SETTINGS_MODULE e settings.configure sembra un po 'strano ma funziona con i comandi manage.py necessari: $ python ./manage.py syncdb.

  3. tuo models.py tuo models.py deve essere posizionato nella cartella del progetto Scrapy (cioè scrapping.models´). After creating that file you should be able to run you $ python ./manage.py syncdb` Può apparire come segue:..

    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. tuo items.py e pipeline.py: Ho usato DjangoItem come descritto nella risposta di Rho, ma mi sono imbattuto in problemi con esso durante l'esecuzione di molte scansioni in parallelo con scrapyd e utilizzando Postgresql. L'eccezione max_locks_per_transaction è stata lanciata ad un certo punto rompendo tutte le scansioni in esecuzione. non ha capito come propi Ripristinare un errore item.save() nella pipeline. Per farla breve, ho finito col non usare DjangoItem per risolvere tutti i miei problemi. Ecco come: items.py:

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

    Nota che i campi devono avere lo stesso nome del modello, se si desidera decomprimere loro comodamente come nel passaggio successivo! 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 
    

    Come accennato in precedenza, se avete chiamato tutti i campi degli elementi come avete fatto nel vostro file models.py è possibile utilizzare **item per decomprimere tutti i campi quando si crea l'oggetto MyModel.

Questo è tutto!

+0

Bella risposta. Mostra quanto può essere flessibile l'integrazione di Django. A proposito, penso che dovresti aprire un ticket riguardante il problema "max_locks_per_transaction' in quanto rende la classe' DjangoItem' praticamente inutile per i progetti di dimensioni medio-grandi dove l'archiviazione in un RDBMS è un requisito. – Rolando

+0

Mi sono finalmente liberato di farlo: https: // github.it/scrapy/scrapy/issues/415 – Chris

+0

A partire da Django 1.8 e successivi usare 'atomic()' invece di 'commit_on_success()'. – Nir

Problemi correlati