我是Python和Django的新手。我目前正在探索使用Scrapy来抓取网站并将数据保存到Django数据库中。我的目标是根据用户提供的域运行蜘蛛。
我写了一个蜘蛛,它提取我需要的数据并在调用
时将其正确存储在json文件中scrapy crawl spider -o items.json -t json
如scrapy tutorial中所述。
我的目标是让蜘蛛成功地将数据保存到Django数据库,然后根据用户输入让蜘蛛运行。
我知道这个主题上存在各种帖子,例如: link 1 link 2 link 3
但是我花了超过8个小时试图让它发挥作用,我假设我不是唯一仍然面临这个问题的人。我将尝试收集到目前为止我在这篇文章中获得的所有知识,以及希望稍后发布一个有效的解决方案。因此,这篇文章相当长。
在我看来,有两种不同的解决方案可以将数据从Scrapy保存到Django数据库。一种是使用DjangoItem,另一种是直接导入模型(完成here)。
我并不完全了解这两者的优点和缺点,但似乎区别在于使用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项目的路径? (如果那是问题)
答案 0 :(得分:67)
我认为主要的误解是包路径与设置模块路径。要从外部脚本使用django的模型,您需要设置DJANGO_SETTINGS_MODULE
。然后,此模块必须可导入(即,如果设置路径为myproject.settings
,则语句from myproject import settings
应在python shell中工作。)
由于django中的大多数项目是在默认PYTHONPATH
之外的路径中创建的,因此必须将项目的路径添加到PYTHONPATH
环境变量。
以下是创建一个完全工作(和最小)Django模型集成到Scrapy项目的分步指南:
注意:此说明适用于上次编辑的日期。如果它不适合您,请添加评论并描述您的问题和scrapy / django版本。
项目将在/home/rolando/projects
目录中创建。
启动 django项目。
$ cd ~/projects
$ django-admin startproject myweb
$ cd myweb
$ ./manage.py startapp myapp
在myapp/models.py
中创建模型。
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=32)
将myapp
添加到INSTALLED_APPS
中的myweb/settings.py
。
# at the end of settings.py
INSTALLED_APPS += ('myapp',)
在myweb/settings.py
中设置我的数据库设置。
# at the end of settings.py
DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'
DATABASES['default']['NAME'] = '/tmp/myweb.db'
创建数据库。
$ ./manage.py syncdb --noinput
Creating tables ...
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
创建 scrapy项目。
$ cd ~/projects
$ scrapy startproject mybot
$ cd mybot
在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>
所以,它按预期工作。
最后,您可能不希望每次运行bot时都必须设置环境变量。有很多替代方法可以解决这个问题,尽管最好的是项目的包实际安装在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
的{{1}}文件并运行setup.py
,这将链接您的项目进入python路径的路径(我假设您使用python setup.py develop
)。
这就够了。为了完整起见,这是一个基本的蜘蛛和管道,用于完全工作的项目:
制作蜘蛛。
virtualenv
蜘蛛代码:
$ 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')
中创建一个管道以保存该项目。
mybot/pipelines.py
如果您使用的是class MybotPipeline(object):
def process_item(self, item, spider):
item.save()
return item
类,或者直接导入django模型并手动创建对象,可以使用item.save()
。在两种方式中,主要问题是定义环境变量,以便您可以使用django模型。
将管道设置添加到DjangoItem
文件中。
mybot/settings.py
运行蜘蛛。
ITEM_PIPELINES = {
'mybot.pipelines.MybotPipeline': 1000,
}
答案 1 :(得分:5)
尽管Rho的回答看起来非常好,但我想我会分享我如何使用Django模型(也就是Django ORM)而没有一个完整的Django项目,因为这个问题只说明了使用一个“Django数据库”。我也不使用DjangoItem。
以下适用于Scrapy 0.18.2和Django 1.5.2。我的scrapy项目在下面称为报废。
将以下内容添加到您的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',
)
)
在与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
的全部内容,几乎就是您在运行manage.py
后获得的库存django-admin startproject myweb
文件,但第4行指向您的scrapy设置文件。
不可否认,使用DJANGO_SETTINGS_MODULE
和settings.configure
似乎有点奇怪,但它适用于我需要的manage.py
个命令:$ python ./manage.py syncdb
。
您的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)
您的items.py
和pipeline.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
文件中那样命名了所有项目字段,则可以在创建MyModel对象时使用**item
解压缩所有字段。
就是这样!