En este artículo vamos a construir un raspador para un trabajo independiente real donde el cliente quiere un programa de Python para raspar datos de Stack Overflow para agarrar nuevas preguntas (título de la pregunta y URL). Los datos raspados deben almacenarse en MongoDB. Vale la pena señalar que Stack Overflow tiene una API, que se puede usar para acceder a los mismos datos exactos. Sin embargo, el cliente quería un raspador, así que un raspador es lo que obtuvo.
Bono gratis: Haga clic aquí para descargar un esqueleto de proyecto Python + MongoDB con código fuente completo que le muestra cómo acceder a MongoDB desde Python.
Actualizaciones:
- 01/03/2014 – Refactorizado la araña. Gracias, kissgyorgy.
- 18/02/2015 – Añadida la segunda parte.
- 09/06/2015 – Actualizado a la última versión de Scrapy and PyMongo – ¡vítores!
Como siempre, asegúrese de revisar los términos de uso / servicio del sitio y respetar el archivo robots.txt antes de comenzar cualquier trabajo de raspado. Asegúrese de seguir las prácticas éticas de raspado al no inundar el sitio con numerosas solicitudes en un corto período de tiempo. Trata cualquier sitio que raspes como si fuera el tuyo.
Contenido
Instalación
Necesitamos la biblioteca Scrapy (v1.0.3) junto con PyMongo (v3.0.3) para almacenar los datos en MongoDB. También necesita instalar MongoDB (no cubierto).
Scrapy
Si está ejecutando OSX o un sabor de Linux, instale Scrapy con pip (con su virtualenv activado):
$ pip install Scrapy==1.0.3
$ pip freeze > requirements.txt
Si está en un equipo con Windows, deberá instalar manualmente varias dependencias. Consulte la documentación oficial para obtener instrucciones detalladas, así como este video de YouTube que he creado.
Una vez que Scrapy esté configurado, verifique su instalación ejecutando este comando en el shell de Python:
>>> import scrapy
>>>
Si no obtienes un error, ¡estás listo para ir!
PyMongo
A continuación, instala PyMongo con pip:
$ pip install pymongo
$ pip freeze > requirements.txt
Ahora podemos empezar a construir el rastreador.
Proyecto Scrapy
Empecemos un nuevo proyecto de Scrapy:
$ scrapy startproject stack
2015-09-05 20:56:40 [scrapy] INFO: Scrapy 1.0.3 started (bot: scrapybot)
2015-09-05 20:56:40 [scrapy] INFO: Optional features available: ssl, http11
2015-09-05 20:56:40 [scrapy] INFO: Overridden settings: {}
New Scrapy project 'stack' created in:
/stack-spider/stack
You can start your first spider with:
cd stack
scrapy genspider example example.com
Esto crea una serie de archivos y carpetas que incluye un boilerplate básico para que pueda comenzar rápidamente:
├── scrapy.cfg
└── stack
├── __init__.py
├── items.py
├── pipelines.py
├── settings.py
└── spiders
└── __init__.py
Especifique los datos
El archivo items.py se utiliza para definir “contenedores” de almacenamiento para los datos que planeamos raspar.
LosStackItem()
La clase hereda deItem
(docs), que básicamente tiene una serie de objetos predefinidos que Scrapy ya ha construido para nosotros:
import scrapy
class StackItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
Vamos a añadir algunos elementos que realmente queremos recoger. Para cada pregunta, el cliente necesita el título y la URL. Por lo tanto, actualizar items.py como así:
from scrapy.item import Item, Field
class StackItem(Item):
title = Field()
url = Field()
Crea la araña
Cree un archivo llamado stackspider.py en el directorio “spiders”. Aquí es donde sucede la magia, por ejemplo, donde le diremos a Scrapy cómo encontrar los datos exactos que estamos buscando. Como se puede imaginar, esto es específico para cada página web individual que desea raspar.
Comienza definiendo una clase que herede de Scrapy’sSpider
y luego agregar atributos según sea necesario:
from scrapy import Spider
class StackSpider(Spider):
name = "stack"
allowed_domains = ["stackoverflow.com"]
start_urls = [
"http://stackoverflow.com/questions?pagesize=50&sort=newest",
]
Las primeras variables son autoexplicativas (docs):
name
Define el nombre de la araña.allowed_domains
contiene las URL de base para los dominios permitidos para que la araña rastree.start_urls
es una lista de URL para que la araña comience a rastrear. Todas las URL posteriores se iniciarán a partir de los datos que la araña descargue de las URL enstart_urls
.
Selectores XPath
A continuación, Scrapy utiliza selectores XPath para extraer datos de un sitio web. En otras palabras, podemos seleccionar ciertas partes de los datos HTML en función de un XPath dado. Como se indica en la documentación de Scrapy, “XPath es un lenguaje para seleccionar nodos en documentos XML, que también se puede usar con HTML”.
Puedes encontrar fácilmente un Xpath específico usando las Herramientas para desarrolladores de Chrome. Simplemente inspeccione un elemento HTML específico, copie el XPath y luego ajuste (según sea necesario):
Herramientas para desarrolladores también le da la posibilidad de probar los selectores XPath en la Consola de JavaScript mediante el uso de$x
– es decir,$x("//img")
:
Una vez más, básicamente le decimos a Scrapy por dónde empezar a buscar información basada en un XPath definido. Vayamos al sitio de Stack Overflow en Chrome y encontremos los selectores XPath.
Haga clic derecho en la primera pregunta y seleccione “Inspeccionar elemento”:
Ahora agarra el XPath para el
//*[@id="question-summary-27624141"]/div[2]
, y luego probarlo en la Consola de JavaScript:
Como puedes ver, solo selecciona esa pregunta. Así que tenemos que alterar el XPath para agarrar todas las preguntas. ¿Alguna idea? Es simple://div[@class="summary"]/h3
. ¿Qué significa esto? Esencialmente, este XPath dice: Coge todo
Elementos que son hijos de a
Tiene una clase desummary
. Prueba este XPath en la Consola de JavaScript.
Observe cómo no estamos utilizando la salida XPath real de Chrome Developer Tools. En la mayoría de los casos, la salida es solo un lado útil, que generalmente le indica la dirección correcta para encontrar el XPath que funciona.
Ahora actualicemos el script stackspider.py:
from scrapy import Spider
from scrapy.selector import Selector
class StackSpider(Spider):
name = "stack"
allowed_domains = ["stackoverflow.com"]
start_urls = [
"http://stackoverflow.com/questions?pagesize=50&sort=newest",
]
def parse(self, response):
questions = Selector(response).xpath('//div[@class="summary"]/h3')
Extraer los datos
Todavía tenemos que analizar y raspar los datos que queremos, que cae dentro de
. De nuevo, actualiza stackspider.py así:
from scrapy import Spider
from scrapy.selector import Selector
from stack.items import StackItem
class StackSpider(Spider):
name = "stack"
allowed_domains = ["stackoverflow.com"]
start_urls = [
"http://stackoverflow.com/questions?pagesize=50&sort=newest",
]
def parse(self, response):
questions = Selector(response).xpath('//div[@class="summary"]/h3')
for question in questions:
item = StackItem()
item['title'] = question.xpath(
'a[@class="question-hyperlink"]/text()').extract()[0]
item['url'] = question.xpath(
'a[@class="question-hyperlink"]/@href').extract()[0]
yield item
````
We are iterating through the `questions` and assigning the `title` and `url` values from the scraped data. Be sure to test out the XPath selectors in the JavaScript Console within Chrome Developer Tools - e.g., `$x('//div[@class="summary"]/h3/a[@class="question-hyperlink"]/text()')` and `$x('//div[@class="summary"]/h3/a[@class="question-hyperlink"]/@href')`.
## Test
Ready for the first test? Simply run the following command within the "stack" directory:
```console
$ scrapy crawl stack
Junto con el seguimiento de la pila de Scrapy, debería ver 50 títulos de preguntas y URLs emitidas. Puede renderizar la salida a un archivo JSON con este pequeño comando:
$ scrapy crawl stack -o items.json -t json
Ahora hemos implementado nuestro Spider basado en nuestros datos que estamos buscando. Ahora necesitamos almacenar los datos raspados dentro de MongoDB.
Almacenar los datos en MongoDB
Cada vez que se devuelve un artículo, queremos validar los datos y luego agregarlos a una colección de Mongo.
El paso inicial es crear la base de datos que planeamos usar para guardar todos nuestros datos rastreados. Abra settings.py y especifique la canalización y agregue la configuración de la base de datos:
ITEM_PIPELINES = ['stack.pipelines.MongoDBPipeline', ]
MONGODB_SERVER = "localhost"
MONGODB_PORT = 27017
MONGODB_DB = "stackoverflow"
MONGODB_COLLECTION = "questions"
Gestión de tuberías
Hemos configurado nuestra araña para rastrear y analizar el HTML, y hemos configurado la configuración de nuestra base de datos. Ahora tenemos que conectar los dos juntos a través de una tubería en pipelines.py.
Conectarse a la base de datos
Primero, definamos un método para conectarse realmente a la base de datos:
import pymongo
from scrapy.conf import settings
class MongoDBPipeline(object):
def __init__(self):
connection = pymongo.MongoClient(
settings['MONGODB_SERVER'],
settings['MONGODB_PORT']
)
db = connection[settings['MONGODB_DB']]
self.collection = db[settings['MONGODB_COLLECTION']]
Aquí, creamos una clase,MongoDBPipeline()
, y tenemos una función constructora para inicializar la clase definiendo la configuración de Mongo y luego conectándonos a la base de datos.
Procesar los datos
A continuación, necesitamos definir un método para procesar los datos analizados:
import pymongo
from scrapy.conf import settings
from scrapy.exceptions import DropItem
from scrapy import log
class MongoDBPipeline(object):
def __init__(self):
connection = pymongo.MongoClient(
settings['MONGODB_SERVER'],
settings['MONGODB_PORT']
)
db = connection[settings['MONGODB_DB']]
self.collection = db[settings['MONGODB_COLLECTION']]
def process_item(self, item, spider):
valid = True
for data in item:
if not data:
valid = False
raise DropItem("Missing {0}!".format(data))
if valid:
self.collection.insert(dict(item))
log.msg("Question added to MongoDB database!",
level=log.DEBUG, spider=spider)
return item
Establecemos una conexión con la base de datos, desempaquetamos los datos y luego los guardamos en la base de datos. ¡Ahora podemos probar de nuevo!
Ensayo
Una vez más, ejecute el siguiente comando dentro del directorio “stack”:
$ scrapy crawl stack
NOTA: Asegúrese de que tiene el demonio Mongo –mongod
– se ejecuta en una ventana de terminal diferente.
¡Hooray! Hemos almacenado con éxito nuestros datos rastreados en la base de datos:

Conclusión
Este es un ejemplo bastante simple de usar Scrapy para rastrear y raspar una página web. El proyecto freelance real requería que el script siguiera los enlaces de paginación y raspara cada página usando elCrawlSpider
(docs), que es muy fácil de implementar. Intente implementar esto por su cuenta y deje un comentario a continuación con el enlace al repositorio de Github para una revisión rápida del código.
¿Necesitas ayuda? Comience con este guión, que está casi completo. A continuación, vea la Parte 2 para la solución completa!
Bono gratis: Haga clic aquí para descargar un esqueleto de proyecto Python + MongoDB con código fuente completo que le muestra cómo acceder a MongoDB desde Python.
Puedes descargar todo el código fuente desde el repositorio de Github. Comenta a continuación con preguntas. ¡Gracias por leer!
Enlaces Externos
- http://doc.scrapy.org/en/1.0/topics/item-pipeline.html
- https://github.com/realpython/stack-spider/blob/part1/stack/stack/spiders/stack_crawl.py
- http://api.mongodb.org/python/3.0.3/
- http://doc.scrapy.org/en/1.0/
- http://stackoverflow.com/questions?pagesize=50&sort=newest
- https://www.youtube.com/watch?v=eEK2kmmvIdw
- http://doc.scrapy.org/en/1.0/topics/spiders.html#spider
- https://github.com/realpython/stack-spider/releases/tag/v1
- https://api.stackexchange.com/
- http://docs.mongodb.org/v3.0/reference/program/mongod/
- https://twitter.com/kissgyorgy
- http://www.mongodb.org/
- http://doc.scrapy.org/en/1.0/topics/spiders.html#crawlspider
- http://doc.scrapy.org/en/1.0/topics/selectors.html
- http://doc.scrapy.org/en/1.0/topics/items.html
- http://doc.scrapy.org/en/latest/intro/install.html
- http://docs.mongodb.org/manual/installation/
summary
. Prueba este XPath en la Consola de JavaScript.
Observe cómo no estamos utilizando la salida XPath real de Chrome Developer Tools. En la mayoría de los casos, la salida es solo un lado útil, que generalmente le indica la dirección correcta para encontrar el XPath que funciona.
Ahora actualicemos el script stackspider.py:
from scrapy import Spider
from scrapy.selector import Selector
class StackSpider(Spider):
name = "stack"
allowed_domains = ["stackoverflow.com"]
start_urls = [
"http://stackoverflow.com/questions?pagesize=50&sort=newest",
]
def parse(self, response):
questions = Selector(response).xpath('//div[@class="summary"]/h3')
Extraer los datos
Todavía tenemos que analizar y raspar los datos que queremos, que cae dentro de
. De nuevo, actualiza stackspider.py así:
from scrapy import Spider
from scrapy.selector import Selector
from stack.items import StackItem
class StackSpider(Spider):
name = "stack"
allowed_domains = ["stackoverflow.com"]
start_urls = [
"http://stackoverflow.com/questions?pagesize=50&sort=newest",
]
def parse(self, response):
questions = Selector(response).xpath('//div[@class="summary"]/h3')
for question in questions:
item = StackItem()
item['title'] = question.xpath(
'a[@class="question-hyperlink"]/text()').extract()[0]
item['url'] = question.xpath(
'a[@class="question-hyperlink"]/@href').extract()[0]
yield item
````
We are iterating through the `questions` and assigning the `title` and `url` values from the scraped data. Be sure to test out the XPath selectors in the JavaScript Console within Chrome Developer Tools - e.g., `$x('//div[@class="summary"]/h3/a[@class="question-hyperlink"]/text()')` and `$x('//div[@class="summary"]/h3/a[@class="question-hyperlink"]/@href')`.
## Test
Ready for the first test? Simply run the following command within the "stack" directory:
```console
$ scrapy crawl stack
from scrapy import Spider
from scrapy.selector import Selector
from stack.items import StackItem
class StackSpider(Spider):
name = "stack"
allowed_domains = ["stackoverflow.com"]
start_urls = [
"http://stackoverflow.com/questions?pagesize=50&sort=newest",
]
def parse(self, response):
questions = Selector(response).xpath('//div[@class="summary"]/h3')
for question in questions:
item = StackItem()
item['title'] = question.xpath(
'a[@class="question-hyperlink"]/text()').extract()[0]
item['url'] = question.xpath(
'a[@class="question-hyperlink"]/@href').extract()[0]
yield item
````
We are iterating through the `questions` and assigning the `title` and `url` values from the scraped data. Be sure to test out the XPath selectors in the JavaScript Console within Chrome Developer Tools - e.g., `$x('//div[@class="summary"]/h3/a[@class="question-hyperlink"]/text()')` and `$x('//div[@class="summary"]/h3/a[@class="question-hyperlink"]/@href')`.
## Test
Ready for the first test? Simply run the following command within the "stack" directory:
```console
$ scrapy crawl stack
Junto con el seguimiento de la pila de Scrapy, debería ver 50 títulos de preguntas y URLs emitidas. Puede renderizar la salida a un archivo JSON con este pequeño comando:
$ scrapy crawl stack -o items.json -t json
Ahora hemos implementado nuestro Spider basado en nuestros datos que estamos buscando. Ahora necesitamos almacenar los datos raspados dentro de MongoDB.
Almacenar los datos en MongoDB
Cada vez que se devuelve un artículo, queremos validar los datos y luego agregarlos a una colección de Mongo.
El paso inicial es crear la base de datos que planeamos usar para guardar todos nuestros datos rastreados. Abra settings.py y especifique la canalización y agregue la configuración de la base de datos:
ITEM_PIPELINES = ['stack.pipelines.MongoDBPipeline', ]
MONGODB_SERVER = "localhost"
MONGODB_PORT = 27017
MONGODB_DB = "stackoverflow"
MONGODB_COLLECTION = "questions"
Gestión de tuberías
Hemos configurado nuestra araña para rastrear y analizar el HTML, y hemos configurado la configuración de nuestra base de datos. Ahora tenemos que conectar los dos juntos a través de una tubería en pipelines.py.
Conectarse a la base de datos
Primero, definamos un método para conectarse realmente a la base de datos:
import pymongo
from scrapy.conf import settings
class MongoDBPipeline(object):
def __init__(self):
connection = pymongo.MongoClient(
settings['MONGODB_SERVER'],
settings['MONGODB_PORT']
)
db = connection[settings['MONGODB_DB']]
self.collection = db[settings['MONGODB_COLLECTION']]
Aquí, creamos una clase,MongoDBPipeline()
, y tenemos una función constructora para inicializar la clase definiendo la configuración de Mongo y luego conectándonos a la base de datos.
Procesar los datos
A continuación, necesitamos definir un método para procesar los datos analizados:
import pymongo
from scrapy.conf import settings
from scrapy.exceptions import DropItem
from scrapy import log
class MongoDBPipeline(object):
def __init__(self):
connection = pymongo.MongoClient(
settings['MONGODB_SERVER'],
settings['MONGODB_PORT']
)
db = connection[settings['MONGODB_DB']]
self.collection = db[settings['MONGODB_COLLECTION']]
def process_item(self, item, spider):
valid = True
for data in item:
if not data:
valid = False
raise DropItem("Missing {0}!".format(data))
if valid:
self.collection.insert(dict(item))
log.msg("Question added to MongoDB database!",
level=log.DEBUG, spider=spider)
return item
Establecemos una conexión con la base de datos, desempaquetamos los datos y luego los guardamos en la base de datos. ¡Ahora podemos probar de nuevo!
Ensayo
Una vez más, ejecute el siguiente comando dentro del directorio “stack”:
$ scrapy crawl stack
NOTA: Asegúrese de que tiene el demonio Mongo –mongod
– se ejecuta en una ventana de terminal diferente.
¡Hooray! Hemos almacenado con éxito nuestros datos rastreados en la base de datos:
Conclusión
Este es un ejemplo bastante simple de usar Scrapy para rastrear y raspar una página web. El proyecto freelance real requería que el script siguiera los enlaces de paginación y raspara cada página usando elCrawlSpider
(docs), que es muy fácil de implementar. Intente implementar esto por su cuenta y deje un comentario a continuación con el enlace al repositorio de Github para una revisión rápida del código.
¿Necesitas ayuda? Comience con este guión, que está casi completo. A continuación, vea la Parte 2 para la solución completa!
Bono gratis: Haga clic aquí para descargar un esqueleto de proyecto Python + MongoDB con código fuente completo que le muestra cómo acceder a MongoDB desde Python.
Puedes descargar todo el código fuente desde el repositorio de Github. Comenta a continuación con preguntas. ¡Gracias por leer!
Enlaces Externos
- http://doc.scrapy.org/en/1.0/topics/item-pipeline.html
- https://github.com/realpython/stack-spider/blob/part1/stack/stack/spiders/stack_crawl.py
- http://api.mongodb.org/python/3.0.3/
- http://doc.scrapy.org/en/1.0/
- http://stackoverflow.com/questions?pagesize=50&sort=newest
- https://www.youtube.com/watch?v=eEK2kmmvIdw
- http://doc.scrapy.org/en/1.0/topics/spiders.html#spider
- https://github.com/realpython/stack-spider/releases/tag/v1
- https://api.stackexchange.com/
- http://docs.mongodb.org/v3.0/reference/program/mongod/
- https://twitter.com/kissgyorgy
- http://www.mongodb.org/
- http://doc.scrapy.org/en/1.0/topics/spiders.html#crawlspider
- http://doc.scrapy.org/en/1.0/topics/selectors.html
- http://doc.scrapy.org/en/1.0/topics/items.html
- http://doc.scrapy.org/en/latest/intro/install.html
- http://docs.mongodb.org/manual/installation/