Sto cercando un codice di esempio di una pipeline SQLite in Scrapy. So che non c'è un supporto per questo, ma sono sicuro che sia stato fatto. Solo il codice attuale può aiutarmi, poiché conosco abbastanza Python e Scrapy per completare il mio compito molto limitato e ho bisogno del codice come punto di partenza.Qualcuno ha codice di esempio per una pipeline sqlite in Scrapy?
risposta
Se ti trovi bene con adbapi attorcigliata, si può prendere come punto di partenza questo mysql oleodotto: http://github.com/darkrho/scrapy-googledir-mysql/blob/master/googledir/pipelines.py
E utilizzare questa linea alla __init__
:
self.dbpool = adbapi.ConnectionPool("sqlite3", database="/path/sqlite.db")
Se si utilizza sqlite3, prestare attenzione ai requisiti di threading che richiedono l'uso di oggetti sqlite nello stesso thread della creazione – Lionel
ho fatto qualcosa di simile:
#
# 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)
Per chiunque cerchi di risolvere un problema simile, mi sono imbattuto in un simpatico elemento Sqlite Exproter per SQLite: https://github.com/RockyZ/Scrapy-sqlite-item-exporter.
Dopo aver incluso per le impostazioni del progetto, è possibile utilizzarlo con:
scrapy crawl <spider name> -o sqlite.db -t sqlite
Potrebbe anche essere adattato per essere utilizzato come Pipeline elemento anziché Exporter Item.
Ecco una pipeline sqlite con sqlalchemy. Con sqlalchemy puoi facilmente modificare il tuo database se necessario.
In configurazione del database aggiuntivo settings.py
# settings.py
# ...
DATABASE = {
'drivername': 'sqlite',
# 'host': 'localhost',
# 'port': '5432',
# 'username': 'YOUR_USERNAME',
# 'password': 'YOUR_PASSWORD',
'database': 'books.sqlite'
}
Poi nel pipelines.py
aggiungere il seguente
# 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
e items.py
dovrebbe assomigliare a questo
#items.py
import scrapy
class BookItem(scrapy.Item):
title = scrapy.Field()
author = scrapy.Field()
publisher = scrapy.Field()
scrape_date = scrapy.Field()
Si può anche considerare di spostare class Book
in items.py
Non Scrapy, che viene eseguito in modo asincrono, richiede un datastore non bloccante? In tal caso, SQLite non funzionerebbe? – zelusp
Sembra che sqlite3 sia abbastanza veloce e intelligente da gestire la concorrenza (fino a un certo punto). [Vedi qui] (http://stackoverflow.com/questions/4060772/sqlite3-concurrent-access) – zelusp