requests - Scrapy: Sugerencia para múltiples devoluciones/artículos a la base de datos
scrapy requests (1)
Conceptualmente, los elementos de Scrapy generalmente se refieren a una sola "cosa" que se raspa (en su caso, una película) y tienen Campos que representan los datos que componen esta "cosa". Así que considere tener:
class MovieItem(scrapy.item.Item):
title = Field()
director = Field()
actors = Field()
Luego, cuando raspe los artículos:
item = MovieItem()
title = hxs.select(''//some/long/xpath'').extract()
item[''title''] = title
actors = hxs.select(''//some/long/xpath'').extract()
item[''actors''] = actors
return item
Los métodos de análisis de araña siempre deben devolver o ceder objetos scrapy.item.Item o scrapy.http.Request objects.
A partir de ahí, la forma en que procesa los MovieItems depende de usted. Puede tener una interconexión para cada propiedad de MovieItem, pero no es recomendable. Lo que recomendaría en su lugar es tener un solo objeto MySQLPersistancePipeline que tenga métodos para persistir en cada uno de los campos del MovieItem. Entonces algo así como:
class MySQLPersistancePipeline(object):
...
def persist_producer(self, item):
self.cursor.execute(''insert into producers ...'', item[''producer''])
def persist_actors(self, item):
for actor in item[''actors'']:
self.cursor.execute(''insert into actors ...'', actor)
def process_item(self, item, spider):
persist_producer(item)
persist_actors(item)
return item
Para profundizar en el encabezado de esta pregunta: estoy robando información del sitio web de la película. Actualmente tengo una MySQL
datos MySQL
poblada con movie titles
, movie urls
, etc. Ahora voy a tomar esas urls
de la base de datos y establecerlas como mis start_urls
dentro de una nueva spider
. Cada url
es un enlace a la página web de [insertar película de la revista], se transmite mucha más información. La información que me interesa es:
- distribuidor (es decir, Fox)
- clasificación (es decir, Pg-13)
- director
- género (es decir, comedia)
- actores
- productor / s
De estos, distribuidor, calificación, director y género tendrán una ''cosa'' asociada con ellos de cada página web de la película (una clasificación, un director, etc.). Habrá múltiples, por supuesto, múltiples actores y, dependiendo, múltiples productores (películas de nombre más grande / la mayoría de las películas). Aquí es donde tengo un problema. Quiero establecer un pipeline'' which puts each piece of info in an appropriate
tabla pipeline'' which puts each piece of info in an appropriate
within my
base de database. So, a table for director, a table for rating, etc. Each table will also have
MySQL database. So, a table for director, a table for rating, etc. Each table will also have
database. So, a table for director, a table for rating, etc. Each table will also have
título de la película ". Puedo decir el problema de esta manera:
Tengo problemas para conciliar cómo construir una pipeline
adecuada con una spider
adecuada. No estoy seguro de si puedo devolver varias cosas de una araña y enviarlas a diferentes pipelines
(crear diferentes elementos para tratar con atributos single
, y un elemento diferente para tratar con atributos ''múltiples'') o si usar la misma canalización y de alguna manera especifique qué va a dónde (no estoy seguro si solo puedo devolver una cosa después de raspar). Mostraré mi código y espero que el problema se aclare. * Nota: aún no está completo. Solo intento completar los espacios en blanco con cómo hacer esto.
Araña:
class ActorSpider(BaseSpider):
import sys; sys.path.append("/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages")
import MySQLdb
db = MySQLdb.connect(db = ''testdb'', user=''testuser'', passwd=''test'')
dbc = db.cursor()
name = ''ActorSpider''
allowed_domains = [''movie website'']
#start_urls = #HAVE NOT FILLED THIS IN YET- WILL BE A SELECT STATEMENT, GATHERING ALL URLS
def parse(self, response):
hxs = HtmlXPathSelector(response)
#Expect only singular items (ie. one title, one rating, etc.)
single_info = SingleItem()
title = hxs.select(''[title tags here]'').extract()
distributor = hxs.select(''[distributor tags here]'').extract()
rating = hxs.select(''[rating tags here]'').extract()
director = hxs.select(''[director tags here]'').extract()
genre = hxs.select(''[genre tags here]'').extract()
single_items = []
single_info[''title''] = title
single_info[''distributor''] = distributor
single_info[''rating''] = rating
single_info[''director''] = director
single_info[''genre''] = genre
single_items.append(single_info) #Note: not sure if I want to return this or the single info
#return single_items
#Multiple items in a field
multi_info = MultiItem()
actors = hxs.select(''[actor tags here]'').extract()
producers = hxs.select(''[producer tags here]'').extract()
actor_items= []
for i in range(len(actors)):
multi_info[''title''] = title
multi_info[''actor''] = actors[i]
actor_items.append(multi_info)
#return actor_items - can I have multiple returns in my code to specify which pipeline is used, or which table this should be inserted into
producer_items = []
for i in range(len(producers)):
multi_info[''title''] = title
multi_info[''producer''] = producers[i]
producer_items.append(multi_info)
#return producer_items - same issue - are multiple returns allowed? Should I try to put both the ''single items'' and ''multiple items'' in on big ''items'' list? Can scrapy figure that out or how would I go about specifying?
He comentado en una serie de preguntas que pueden ser poco claras: no estoy seguro de cómo dirigir todo para que termine en la tabla adecuada. Esto puede ser más claro cuando lee la tubería, que es:
class IndMoviePipeline(object):
def __init__(self):
''initiate the database connnection''
self.conn = MySQLdb.connect(user=''testuser'', passwd=''test'', db=''testdb'', host=''localhost'', charset=''utf8'', use_unicode=True)
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
try:
if ''producer'' in item:
self.cursor.execute("""INSERT INTO Producers (title, producer) VALUES (%s, %s)""", (item[''title''], item[''producer'']))
elif ''actor'' in item:
self.cursor.execute("""INSERT INTO Actors (title, actor) VALUES (%s, %s)""", (item[''title''], item[''actor'']))
else:
self.cursor.execute("""INSERT INTO Other_Info (title, distributor, rating, director, genre) VALUES (%s, %s, %s, %s, %s)""", (item[''title''], item[''distributor''], item[''rating''], item[''director''], item[''genre''])) #NOTE: I will likely change ''Other_Info'' table to just populating the original table from which the URLS will be pulled
self.conn.commit()
except MySQLdb.Error, e:
print "Error %d: %s" % (e.args[0], e.args[1])
return item
Creo que eso funcionará para dirigir el item
a la table
apropiada dentro de la base de datos. En base a esto, creo que funcionaría tener una gran lista de items
y anexarle todo, así que:
items = []
items.append(single_info)
for i in range(len(producers)):
multi_info[''title''] = title
multi_info[''producer''] = producers[i]
items.append(multi_info)
for i in range(len(actors)):
multi_info[''title''] = title
multi_info[''actor''] = actors[i]
items.append(multi_info)
Simplemente dejando que la pipeline
solucione todo esto con esas declaraciones if
. No estoy seguro, sin embargo, si esta es la mejor manera de hacer esto y realmente agradecería sugerencias.