Acceda a los modelos de Django con scrapy: definiendo la ruta al proyecto Django

Soy muy nuevo en Python y Django. Actualmente estoy explorando el uso de Scrapy para raspar sitios y guardar datos en la base de datos de Django. Mi objective es ejecutar una araña basada en el dominio dado por un usuario.

He escrito una araña que extrae los datos que necesito y los almaceno correctamente en un archivo json al llamar

scrapy crawl spider -o items.json -t json 

Como se describe en el tutorial de scrapy .

Mi objective ahora es lograr que la araña guarde con éxito los datos en la base de datos de Django y luego trabajar para que la araña se ejecute en función de la información del usuario.

Soy consciente de que existen varias publicaciones sobre este tema, como las siguientes: enlace 1 enlace 2 enlace 3

Pero después de haber pasado más de 8 horas tratando de hacer que esto funcione, asumo que no soy el único que aún enfrenta problemas con esto. Por lo tanto, trataré de reunir todos los conocimientos que he recibido hasta ahora en este post, así como, con suerte, publicar una solución de trabajo en un momento posterior. Debido a esto, este post es bastante largo.

Me parece que hay dos soluciones diferentes para guardar datos en la base de datos Django de Scrapy. Una es usar DjangoItem , otra es importar los modelos directamente (como se hace aquí ).

No estoy completamente consciente de las ventajas y desventajas de estos dos, pero parece que la diferencia es que el uso de DjangoItem es más conveniente y corto.

Qué he hecho:

He añadido:

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

El error que estoy consiguiendo es:

 ImportError: No module named settings 

¿Estoy pensando que estoy definiendo la ruta a mi proyecto Django de una manera incorrecta?

También he intentado lo siguiente:

 setup_django_env('../../') 

¿Cómo defino la ruta a mi proyecto Django correctamente? (si ese es el problema)

Creo que el error principal es la ruta del paquete frente a la ruta del módulo de configuración. Para usar los modelos de django desde un script externo, debe configurar el DJANGO_SETTINGS_MODULE . Entonces, este módulo debe ser importable (es decir, si la ruta de configuración es myproject.settings , entonces la statement from myproject import settings debería funcionar en un shell de python).

Como la mayoría de los proyectos en django se crean en una ruta fuera de la PYTHONPATH predeterminada, debe agregar la ruta del proyecto a la variable de entorno PYTHONPATH .

Aquí hay una guía paso a paso para crear una integración de modelos Django totalmente funcional (y mínima) en un proyecto Scrapy:

Nota: estas instrucciones funcionan en la fecha de la última edición. Si no funciona para usted, agregue un comentario y describa su problema y las versiones de scrapy / django.

  1. Los proyectos se crearán dentro del directorio /home/rolando/projects .

  2. Inicia el proyecto django .

     $ cd ~/projects $ django-admin startproject myweb $ cd myweb $ ./manage.py startapp myapp 
  3. Crea un modelo en myapp/models.py .

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

     # at the end of settings.py INSTALLED_APPS += ('myapp',) 
  5. Establecer mi configuración de db en myweb/settings.py .

     # at the end of settings.py DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3' DATABASES['default']['NAME'] = '/tmp/myweb.db' 
  6. Crear la base de datos.

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

     $ cd ~/projects $ scrapy startproject mybot $ cd mybot 
  8. Crea un artículo en mybot/items.py .

Nota: en las versiones más recientes de Scrapy, debe instalar scrapy_djangoitem y usar 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 

La estructura final del directorio es esta:

 /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 

A partir de aquí, básicamente hemos terminado con el código requerido para utilizar los modelos de django en un proyecto desechable. Podemos probarlo de inmediato con el comando scrapy shell pero tenga en cuenta las variables de entorno necesarias:

 $ 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]:  In [4]: PersonItem.django_model.objects.get(name='rolando') Out[4]:  

Por lo tanto, está funcionando según lo previsto.

Finalmente, es posible que no desee tener que configurar las variables de entorno cada vez que ejecute su bot. Existen muchas alternativas para abordar este problema, aunque lo mejor es que los paquetes de los proyectos realmente se instalan en una ruta establecida en PYTHONPATH .

Esta es una de las soluciones más simples: agregue estas líneas a su archivo mybot/settings.py para configurar las variables de entorno.

 # 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 mejor enfoque para la ruta de la piratería es tener setuptools basado en archivos setup.py en ambos proyectos y ejecutar python setup.py develop que vinculará la ruta de su proyecto con la ruta de python (supongo que usted usa virtualenv ).

Es suficiente. Para completar, aquí hay una araña y un tramo básicos para un proyecto en pleno funcionamiento:

  1. Crea la araña.

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

    El código de la araña:

     # 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. Cree una canalización en mybot/pipelines.py para guardar el elemento.

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

    Aquí puede usar item.save() si está usando la clase DjangoItem o importar el modelo de django directamente y crear el objeto manualmente. En ambos sentidos, el problema principal es definir las variables de entorno para que pueda utilizar los modelos de django.

  3. Agregue la configuración de canalización a su archivo mybot/settings.py .

     ITEM_PIPELINES = { 'mybot.pipelines.MybotPipeline': 1000, } 
  4. Ejecutar la araña.

     $ scrapy crawl example 

A pesar de que la respuesta de Rho parece muy buena, pensé que compartiría la forma en que obtuve el trabajo con Django Models (también conocido como Django ORM) sin un proyecto Django completo, ya que la pregunta solo establece el uso de una “base de datos Django”. Tampoco uso DjangoItem.

Los siguientes trabajos con Scrapy 0.18.2 y Django 1.5.2. Mi proyecto de chatarra se llama desguace en lo siguiente.

  1. Agrega lo siguiente a tu archivo 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. Cree un archivo manage.py en la misma carpeta que su scrapy.cfg : este archivo no es necesario cuando ejecuta la araña en sí, pero es muy conveniente para configurar la base de datos. Así que, aquí vamos:

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

    Ese es el contenido completo de manage.py y es casi exactamente el archivo stock manage.py que se obtiene después de ejecutar django-admin startproject myweb pero la cuarta línea apunta a su archivo de configuración de scrapy. Es cierto que usar DJANGO_SETTINGS_MODULE y settings.configure parece un poco extraño pero funciona para los comandos de manage.py que necesito: $ python ./manage.py syncdb .

  3. Your models.py Your models.py debe colocarse en la carpeta del proyecto de scrapy (es decir, scrapping.models´). After creating that file you should be able to run you scrapping.models´). After creating that file you should be able to run you $ python ./manage.py syncdb`. Puede verse así:

     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. Sus items.py y pipeline.py : solía usar DjangoItem como se describe en la respuesta de Rho, pero tuve problemas cuando ejecuté muchos rastreos en paralelo con scrapyd y usé Postgresql. La excepción max_locks_per_transaction se lanzó en algún momento, rompiendo todos los rastreos en ejecución. Además, no descubrí cómo revertir correctamente un item.save() fallido en la tubería. En pocas palabras, terminé sin usar DjangoItem, lo que resolvió todos mis problemas. Aquí es cómo: items.py :

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

    ¡Tenga en cuenta que los campos deben tener el mismo nombre que en el modelo si desea descomprimirlos convenientemente como en el siguiente paso! 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 

    Como se mencionó anteriormente, si nombró todos los campos de elementos como lo hizo en su archivo models.py , puede usar el **item para desempaquetar todos los campos al crear su objeto MyModel.

¡Eso es!