Je cherche un exemple de code d'un pipeline SQLite dans Scrapy. Je sais qu'il n'y a pas de soutien intégré, mais je suis sûr que cela a été fait. Seul le code actuel peut m'aider, car je ne connais que Python et Scrapy pour accomplir ma tâche très limitée et j'ai besoin du code comme point de départ.Quelqu'un a-t-il un exemple de code pour un pipeline sqlite dans Scrapy?
Répondre
Si vous vous sentez à l'aise avec le adbapi de tordu, vous pouvez prendre comme point de départ ce pipeline mysql: http://github.com/darkrho/scrapy-googledir-mysql/blob/master/googledir/pipelines.py
Et utiliser cette ligne à __init__
:
self.dbpool = adbapi.ConnectionPool("sqlite3", database="/path/sqlite.db")
Si vous utilisez sqlite3, tenez compte des exigences de threading qui requièrent que des objets sqlite soient utilisés dans le même thread que la création – Lionel
Je l'ai fait quelque chose comme ceci:
#
# Author: Jay Vaughan
#
# Pipelines for processing items returned from a scrape.
# Dont forget to add pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/topics/item-pipeline.html
#
from scrapy import log
from pysqlite2 import dbapi2 as sqlite
# This pipeline takes the Item and stuffs it into scrapedata.db
class scrapeDatasqLitePipeline(object):
def __init__(self):
# Possible we should be doing this in spider_open instead, but okay
self.connection = sqlite.connect('./scrapedata.db')
self.cursor = self.connection.cursor()
self.cursor.execute('CREATE TABLE IF NOT EXISTS myscrapedata ' \
'(id INTEGER PRIMARY KEY, url VARCHAR(80), desc VARCHAR(80))')
# Take the item and put it in database - do not allow duplicates
def process_item(self, item, spider):
self.cursor.execute("select * from myscrapedata where url=?", item['url'])
result = self.cursor.fetchone()
if result:
log.msg("Item already in database: %s" % item, level=log.DEBUG)
else:
self.cursor.execute(
"insert into myscrapedata (url, desc) values (?, ?)",
(item['url'][0], item['desc'][0])
self.connection.commit()
log.msg("Item stored : " % item, level=log.DEBUG)
return item
def handle_error(self, e):
log.err(e)
Pour ceux qui tentent de résoudre un problème similaire, je suis tombé sur un bon Exproter d'objet Sqlite pour SQLite: https://github.com/RockyZ/Scrapy-sqlite-item-exporter.
Après y compris à vos paramètres du projet, vous pouvez l'utiliser avec:
scrapy crawl <spider name> -o sqlite.db -t sqlite
Il pourrait également être adapté pour être utilisé comme un pipeline article au lieu de l'exportateur d'article.
Voici un pipeline sqlite avec sqlalchemy. Avec sqlalchemy, vous pouvez facilement modifier votre base de données si nécessaire.
Dans la configuration de base de données settings.py
add
# settings.py
# ...
DATABASE = {
'drivername': 'sqlite',
# 'host': 'localhost',
# 'port': '5432',
# 'username': 'YOUR_USERNAME',
# 'password': 'YOUR_PASSWORD',
'database': 'books.sqlite'
}
Puis, en pipelines.py
ajouter ce qui suit
# pipelines.py
import logging
from scrapy import signals
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy import create_engine
from sqlalchemy.engine.url import URL
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool
logger = logging.getLogger(__name__)
DeclarativeBase = declarative_base()
class Book(DeclarativeBase):
__tablename__ = "books"
id = Column(Integer, primary_key=True)
title = Column('title', String)
author = Column('author', String)
publisher = Column('publisher', String)
url = Column('url', String)
scrape_date = Column('scrape_date', DateTime)
def __repr__(self):
return "<Book({})>".format(self.url)
class SqlitePipeline(object):
def __init__(self, settings):
self.database = settings.get('DATABASE')
self.sessions = {}
@classmethod
def from_crawler(cls, crawler):
pipeline = cls(crawler.settings)
crawler.signals.connect(pipeline.spider_opened, signals.spider_opened)
crawler.signals.connect(pipeline.spider_closed, signals.spider_closed)
return pipeline
def create_engine(self):
engine = create_engine(URL(**self.database), poolclass=NullPool, connect_args = {'charset':'utf8'})
return engine
def create_tables(self, engine):
DeclarativeBase.metadata.create_all(engine, checkfirst=True)
def create_session(self, engine):
session = sessionmaker(bind=engine)()
return session
def spider_opened(self, spider):
engine = self.create_engine()
self.create_tables(engine)
session = self.create_session(engine)
self.sessions[spider] = session
def spider_closed(self, spider):
session = self.sessions.pop(spider)
session.close()
def process_item(self, item, spider):
session = self.sessions[spider]
book = Book(**item)
link_exists = session.query(Book).filter_by(url=item['url']).first() is not None
if link_exists:
logger.info('Item {} is in db'.format(book))
return item
try:
session.add(book)
session.commit()
logger.info('Item {} stored in db'.format(book))
except:
logger.info('Failed to add {} to db'.format(book))
session.rollback()
raise
return item
et items.py
devrait ressembler à ceci
#items.py
import scrapy
class BookItem(scrapy.Item):
title = scrapy.Field()
author = scrapy.Field()
publisher = scrapy.Field()
scrape_date = scrapy.Field()
Vous pouvez également envisager de se déplacer class Book
dans items.py
Est-ce que Scrapy, qui s'exécute de manière asynchrone, nécessite un magasin de données non bloquant? Dans quel cas SQLite ne fonctionnerait pas? – zelusp
Il semble que sqlite3 soit assez rapide et intelligent pour gérer la concurrence (jusqu'à un certain point). [Voir ici] (http://stackoverflow.com/questions/4060772/sqlite3-concurrent-access) – zelusp