No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Aprende todo un fin de semana sin pagar una suscripción 🔥

Aprende todo un fin de semana sin pagar una suscripción 🔥

Regístrate

Comienza en:

3D
15H
29M
39S
Curso de Scrapy

Curso de Scrapy

Facundo García Martoni

Facundo García Martoni

Finalizando la creación del spider

21/27
Recursos

Aportes 26

Preguntas 5

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

No todos los parrafos de los documentos tienen la misma estructura o están en el mismo order. Con la expresión del body dada en la clase algunos parrafos me salieron vacíos. Llegué a esta expresión para obtener la descripción completa de los documentos

body = ''.join(response.xpath('//div[contains(@class, "field-item")]/p[not(child::strong and child::i) and not(@class)]/text()').getall())

Nueva etiqueta para el titulo :

In [7]: response.xpath('//h3/a/text()').get()
Out[7]: 'Lunik on Loan: A Space Age Spy Story'```

02/08/2022
Si cuando al final de la clase quieres ver el archivo en Json y no te deja, prueba este codigo:

import scrapy

# XPATHS

# Links
XPATH_LINKS_DECLASSIFIED = '//a[starts-with(@href, "collection") and (parent::h3|parent::h2)]/@href'
# Titles:
XPATH_TITLES = '//h1[@class="documentFirstHeading"]/text()'
# Body
XPATH_PARAGRAPHS = '//div[@class="field-item even"]//p[not(@class)]/text()'


class SpiderCIA(scrapy.Spider):

    name = 'cia'
    start_urls = [
        'https://www.cia.gov/readingroom/historical-collections'
    ]

    custom_settings = {
        'FEEDS': {
            'cia.json': {
                'format': 'json',
                'encoding': 'utf-8',
                'indent': 4,
            }
        },
    }

    def parse(self, response):
        links_declassified = response.xpath(XPATH_LINKS_DECLASSIFIED).getall()
        for link in links_declassified:
            yield response.follow(link, callback=self.parse_link, cb_kwargs={'url': response.urljoin(link)})

    def parse_link(self, response, **kwargs):
        link = kwargs['url']
        title = response.xpath(XPATH_TITLES).get()
        #paragraphs = response.xpath(XPATH_PARAGRAPHS)[1::].getall()
        paragraphs = response.xpath(XPATH_PARAGRAPHS).getall()

        yield {
            'url': link,
            'title': title,
            'body': paragraphs
        }

Updated 08.01.2021:

import scrapy

# XPATHS

# Links
XPATH_LINKS_DECLASSIFIED = '//a[starts-with(@href, "collection") and (parent::h3|parent::h2)]/@href'
# Titles:
XPATH_TITLES = '//h1[@class="documentFirstHeading"]/text()'
# Body
XPATH_PARAGRAPHS = '//div[@class="field-item even"]//p[not(@class)]/text()'

class SpiderCIA(scrapy.Spider):

    name = 'cia'
    start_urls = [
        'https://www.cia.gov/readingroom/historical-collections'
    ]

    custom_settings={
        'FEEDS':{
            'cia.json':{
                'format': 'json',
                'encoding': 'utf-8',
                'indent': 4,
                }
                },
    }

    def parse(self, response):
        links_declassified = response.xpath(XPATH_LINKS_DECLASSIFIED).getall()
        for link in links_declassified:
            yield response.follow(link, callback=self.parse_link, cb_kwargs={'url': response.urljoin(link)})

    def parse_link(self, response, **kwargs):
        link = kwargs['url']
        title = response.xpath(XPATH_TITLES).get()
        #paragraphs = response.xpath(XPATH_PARAGRAPHS)[1::].getall()
        paragraphs = response.xpath(XPATH_PARAGRAPHS).getall()

        yield {
            'url': link,
            'title': title,
            'body': paragraphs
        }

Con esta versión en el body se extraen todos los “párrafos”. En algunos comentarios he visto versiones alternativas para extraer sólo el primer párrafo teniendo en cuenta que no todos los artículos tienen la misma estructura que Facundo explicó en el tutorial.

https://devhints.io/xpath
para que no olviden los predicados

Aporte: si bien algunas informes tienen mas de un parrafo podemos ejecutar la siguiente linea de codigo.

response.xpath('normalize-space(//div[@class="field-item even"]/p/text())').getall()

Si bien getall() genera una lista con varios parrafos normalize-space genera una lista con un solo elemento lo cual nos facilita manejar el parrafo.

AngelFA04. Siento la respuesta muy generica y no se ha puesto en duda lo poderoso que sea. Pero si he visto en varias documentaciones que solo recomiendan Xpath sobre CSS Selector cuando ya es muy dificil extraer data con este. Asi que me gustaria conocer puntualmente esas ventaja/desventaja por parte del profesor o alguien experto en el tema.

Hola @facundo y demas compañeros.

Alguien me puede explicar cual es mas recomendado de usar entre CSS Selectors o Xpath?
Cual seria el escenario ideal para cada uno?

He visto varias documentaciones donde recomiendan el uso de CSS Selectors. Agradecere sus comentarios.

Saludos

Por alguna razó cuando intento correrlo como Facundo me arroja el json sin estar dentro de corchetes y además sin comas.

Si lo corro con:

scrapy crawl cia -o cia.json```
eso no pasa

En mi opinion con CSS tienes una sintaxis mas limpia y fácil de leer, pero XPATH es mucho más poderoso.

Amigos aquí solucionado para traer todos los párrafos en uno solo y guardar en nuestro json.

title = response.xpath('//h1[@class="documentFirstHeading"]/text()').get()
      contents = response.xpath('//div[@class="content"]//div[@class="field-items"]/div[@class="field-item even"]/p[not(@class)]/text()').getall()
      contents = ' '.join(contents)

si al ejecutar el programa, genera un archivo vacio.

Me sirvió cambiar los custom settings de esta manera, y ya me generó el documento completo,

custom_settings = {
        'FEEDS': {
            'cia.json': {
                'format': 'json',
                'encoding': 'utf-8',
                'indent': 4,
            }
        },
    }

Hola a todos, comparto mi código mas reciente y funcional
NOTA: Mi interés es la ciencia de datos, por lo que lo extraje tipo csv y poder tener datos para aplicarle un NLP mas fácilmente.

import scrapy

## links = '//div[@class="content"]//li[@class="leaf"]/a/@href'
# title = //li[@class="leaf active-trail"]/a/text()
# Body = //div[@class="content clearfix"]//p[not(child::strong and child::i) and not(@class)]/text()

class Cia(scrapy.Spider):
    name = 'cia'
    start_urls = ['https://www.cia.gov/readingroom/historical-collections']
    custom_settings = {
        'FEED_URI' : 'cia.csv',
        'FEED_FORMAT':'csv',
        'FEED_EXPORT_ENCODING':'utf-8'  
    }


    def parse(self,response):
        links_desclassified = response.xpath('//div[@class="content"]//li[@class="leaf"]/a/@href').getall()
        # Recorrer todos los links para extraer la información.
        for link in links_desclassified:
            yield response.follow(link,callback=self.parse_link,cb_kwargs= {
                                                                            'url':response.urljoin(link)            
                                                                            })


    def parse_link(self,response,**kwargs):
        link = kwargs['url']
        title = response.xpath('//li[@class="leaf active-trail"]/a/text()').get()
        body = response.xpath('//div[@class="content clearfix"]//p[not(child::strong and child::i) and not(@class)]/text()').get()
        
        
        yield {
            'url':link,
            'title':title,
            'body':body
        }

bueno … ví que a muchos les daba vacío el json, por lo que decidí no poner el custom files y esas caracterísitcas ponerlas en la terminal

scrapy crawl cia -o quotess.json

entonces …
el código actualizado a la fecha de hoy es

import scrapy

# XPath

# traser los links => response.xpath('//blockquote/p/a/@href').getall() 
# primer pparafo firstp=response.xpath('//div[@class="field-item even"]//p/text()').getall()
# title tittle = response.xpath('//h1/text()').get()

class SpiderCIA(scrapy.Spider):
    name = 'cia'
    start_urls = [
        'https://www.cia.gov/readingroom/'
    ]
   
    def parse(self, response):
        links_declassified = response.xpath('//blockquote/p/a/@href').getall()
        for link in links_declassified:
            yield response.follow(link, callback=self.parse_link, cb_kwargs={'url': response.urljoin(link)})

    def parse_link(self, response, **kwargs):
        link = kwargs['url']
        title = response.xpath('//h1/text()').get()
        firstp =response.xpath('//div[@class="field-item even"]//p/text()').getall()

        yield {
            'url': link,
            'title': title,
            'body': firstp
        }

no es la mejor, queda limpiar algo pero les soluciona el problema de que no impirme nada, además como ven están cambiando la estructura de la web bastante seguido, suerte!

Mi expresión para obtener los párrafos:

//div[contains(@class, "field-item even")]/p[not(@class)]/descendant-or-self::*/text()

Para obtener el texto de los p junto a sus hijos de distinta etiqueta

'//div[@class="field-item even"]//p[not(@class)]/descendant-or-self::node()/text()'

Ayuda!!!
mi .codigo no imprime nada en el archivo .json, alguna recomendaciòn??

import scrapy

#XPATH
#links = //a[starts-with(@href, "collection") and(parent::h3|parent::h2)]/@href
#title = //h1 [@class = "documentFirstHeading"]/text()
#Paragraph = //div [@class = "field-item even"]/p/text()

class SpiderCIA (scrapy.Spider):
    name = 'cia'
    start_urls: ['https://www.cia.gov/library/readingroom/historical-collections']

    custom_settings = {
        'FEED_URI': 'cia.json',
        'FEED_FORMAT': 'json',
        'FEED_EXPORT_ENCODING': 'utf-8'
    }

    def parse (self, response):
        links_declassified = response.xpath('//a[starts-with(@href, "collection") and(parent::h3|parent::h2)]/@href').getall()
        for link in links_declassified:
            yield response.follow(link, callback = self.parse_link, cb_kwargs={'url':response.urljoin(link)})
    
    def parse_link (self, response, **kwargs):
        link = kwargs['url']
        title = response.xpath('//h1 [@class = "documentFirstHeading"]/text()').get()
        paragraph = response.xpath('//div [@class = "field-item even"]/p/text()').get()

        yield {
            'url': link,
            'title': title,
            'body': paragraph,
        }`

No me funcionó el xpath propuesto por Facundo para obtener el body en este articulo https://www.cia.gov/readingroom/collection/aquiline. En su lugar implementé este:

"".join(response.xpath('//div[@class="field-item even"]/p[3]/text()').getall()) 

Si se fijan, hay un join de una lista porque en mi caso, el body tenia un elemento <i> que hace que el xpath rompa el parrafo en 2 partes… por eso hago un getall() y luego junto los elementos de la lista.

Scrapy 2.4.0 - no active project

Unknown command: crawl

Use “scrapy” to see available commands mmm me puedes ayudar con este error por favor.

interesante

Intente almacenar los “queries” en un archivo .yaml para acceder a ellos como en el proyecto del curso con el profe david, pero obtengo un error de que el modulo yaml no existe, estuve investigando y segun lo que entiendo podria ser por un problema de versiones con la libreria yaml, intente en color el archivo yaml en todos los niveles de identecion del scrapper y seguia sin funcionar alguien ha intentado algo parecido?

excelente, que bien. Los trucos , lo mejor

Con fetch podemos ir a otra url, por ejemplo:

>>> fetch('https://www.cia.gov/library/readingroom/collection/lunik-loan-space-age-spy-story')

en mi caso la primera tenia un párrafo que no decía nada pero tenia otro nodo strong al no llamarlo si me traía el primer párrafo

response.xpath('//div[@class="field-item even"]//p[not(@class) and not(strong)]/text()').get()

Buen día,

aveces el primer item con etiqueta <p> puede ser un pie de imagen, con esta pequeña lógica se puede extraer el verdadero body:

  • Primero no usar .get() sino usar .getall() para que me extraiga la lista y después:
if len(paragraph[0]) > 30:
            paragraph = paragraph[0]
        else:
            paragraph = paragraph[1]