Slide 1

Slide 1 text

Obtendo dados estruturados da Internet usando Scrapy

Slide 2

Slide 2 text

Renne Rocha Python Developer na Scrapinghub Laboratório Hacker de Campinas Grupy-Campinas @rennerocha (Twitter, Instagram, Github, Bitbucket) [email protected]

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Ge ng informa on off the Internet is like taking a drink from a fire hydrant. Mitchell Kapor

Slide 5

Slide 5 text

Web Scraping Extrair dados estruturados de fontes de dados não estruturadas (tipicamente páginas web)

Slide 6

Slide 6 text

Casos de Uso 1. Pesquisas com dados governamentais 2. Monitorar o que estão falando do meu produto 3. Monitorar os produtos dos concorrentes 4. Ofertas de emprego, imóveis, bens de consumo 5. Análise de redes sociais

Slide 7

Slide 7 text

Ferramentas

Slide 8

Slide 8 text

urllib.request + re + xml

Slide 9

Slide 9 text

urllib.request + re + xml

Slide 10

Slide 10 text

requests + beau fulsoup

Slide 11

Slide 11 text

Obtendo os nomes dos palestrantes do ABC-Dev 2018

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

import requests response = requests.get('https://2018.abcdevelopers.org/')

Slide 15

Slide 15 text

import requests from bs4 import BeautifulSoup response = requests.get('https://2018.abcdevelopers.org/') soup = BeautifulSoup(response.text, 'html.parser') speakers = soup.find_all('article', {'class': 'speaker'}) speaker_names = [ speaker.find('h4').get_text() for speaker in speakers ] print(speaker_names)

Slide 16

Slide 16 text

(.venv) renne@capivara:code$ python3 abcdev.py ['Larissa Abreu', 'Renne Rocha', 'Pamela Iupi Peixinho', 'William Grasel', 'Marina Limeira', 'Caique Mitsuoka', 'Carol Soares', 'Ivan Amaro', 'Carolina Pascale', 'Bruno Rocha', 'Thabata Marchi', 'Flavio Milani', 'Daniela Rocha', 'James William Pontes Miranda', 'Andreza Rocha', 'Mateus Malaquias', 'Alda Rocha', 'Lucas J Silva', 'Livia Gabos', 'Matheus Hernandes', 'Regina Helena Macedo', 'Maicon Peixinho', 'Patricia Morimoto', 'Fábio Serrão', 'Carla Vieira', 'Ciro Valente Filho']

Slide 17

Slide 17 text

E se es vessemos processando? Um e-commerce com milhares de produtos? Um site governamental com dados de diversos anos? Um site de notícias com dezenas de categorias? Precisássemos navegar por vários links para agrupar as informações desejadas?

Slide 18

Slide 18 text

Processamento Síncrono

Slide 19

Slide 19 text

https://scrapy.org/ Um framework em Python de código aberto e colaborativo para extrair os dados que você precisa de páginas na internet, de um modo fácil, simples e extensível.

Slide 20

Slide 20 text

Diário Oficial Serenata de Amor Extrair as informações dos diários o ciais dos 100 maiores municípios do país https://github.com/okfn-brasil/diario-o cial

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

# sp_jundiai.py import scrapy class SpJundiaiDiarioOficialSpider(scrapy.Spider): pass https://github.com/okfn-brasil/diario-o cial

Slide 23

Slide 23 text

# sp_jundiai.py import scrapy class SpJundiaiDiarioOficialSpider(scrapy.Spider): name = 'sp_jundiai' https://github.com/okfn-brasil/diario-o cial

Slide 24

Slide 24 text

# sp_jundiai.py import scrapy class SpJundiaiDiarioOficialSpider(scrapy.Spider): name = 'sp_jundiai' def start_requests(self): url = 'https://imprensaoficial.jundiai.sp.gov.br/' yield scrapy.Request( url=url, callback=self.parse, ) https://github.com/okfn-brasil/diario-o cial

Slide 25

Slide 25 text

# sp_jundiai.py import scrapy class SpJundiaiDiarioOficialSpider(scrapy.Spider): name = 'sp_jundiai' def start_requests(self): url = 'https://imprensaoficial.jundiai.sp.gov.br/' yield scrapy.Request( url=url, callback=self.parse, ) def parse(self, response): self.logger.info( 'Received content from {}'.format( response.url)) https://github.com/okfn-brasil/diario-o cial

Slide 26

Slide 26 text

Executando meu spider $ scrapy runspider sp_jundiai.py ...[scrapy.utils.log] INFO: Scrapy 1.5.0 started (bot: scrapybot) ...[scrapy.core.engine] INFO: Spider opened...[scrapy.core.engine ...[sp_jundiai] INFO: Received content from https://imprensaofici ...[scrapy.core.engine] INFO: Closing spider (finished) ...[scrapy.statscollectors] INFO: Dumping Scrapy stats: ...[scrapy.core.engine] INFO: Spider closed (finished)

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

$ scrapy shell https://imprensaoficial.jundiai.sp.gov.br/

Slide 29

Slide 29 text

$ scrapy shell https://imprensaoficial.jundiai.sp.gov.br/ In [1]: response.css('#lista-edicoes li.edicao-atual') Out[1]: [, , , ...]

Slide 30

Slide 30 text

$ scrapy shell https://imprensaoficial.jundiai.sp.gov.br/ In [1]: response.css('#lista-edicoes li.edicao-atual') Out[1]: [, , , ...] In [2]: diario = response.css( ...: '#lista-edicoes li.edicao-atual')[0] In [3]: diario.getall() Out[3]: '
  • \n\n\t\n\t'
  • Slide 31

    Slide 31 text

    # sp_jundiai.py def parse(self, response): diarios = response.css('#lista-edicoes li.edicao-atual') https://github.com/okfn-brasil/diario-o cial

    Slide 32

    Slide 32 text

    # sp_jundiai.py def parse(self, response): diarios = response.css('#lista-edicoes li.edicao-atual') for diario in diarios: pass https://github.com/okfn-brasil/diario-o cial

    Slide 33

    Slide 33 text

    # sp_jundiai.py def parse(self, response): diarios = response.css('#lista-edicoes li.edicao-atual') for diario in diarios: diario_url = diario.css('a::attr(href)').get() diario_data = diario.xpath('.//span[2]/text()').get() https://github.com/okfn-brasil/diario-o cial

    Slide 34

    Slide 34 text

    # sp_jundiai.py def parse(self, response): diarios = response.css('#lista-edicoes li.edicao-atual') for diario in diarios: diario_url = diario.css('a::attr(href)').get() diario_data = diario.xpath('.//span[2]/text()').get() yield { 'url': diario_url, 'data': diario_data } https://github.com/okfn-brasil/diario-o cial

    Slide 35

    Slide 35 text

    No content

    Slide 36

    Slide 36 text

    No content

    Slide 37

    Slide 37 text

    # sp_jundiai.py def parse(self, response): diarios = response.css('#lista-edicoes li.edicao-atual') for diario in diarios: (...) prox_paginas = response.css('div.paginacao a.inactive') for pagina in prox_paginas: url = pagina.css('*::attr(href)').get() yield Request( url=url, callback=self.parse ) https://github.com/okfn-brasil/diario-o cial

    Slide 38

    Slide 38 text

    https://github.com/okfn-brasil/diario-o cial

    Slide 39

    Slide 39 text

    Item Exporters Uma vez que você obteve seus items, você vai querer persistí-los ou exportá-los para utilizar esses dados em outra aplicação. O Scrapy fornece um conjunto de Item Exporters para diferentes formatos como CSV, JSON, JL ou XML.

    Slide 40

    Slide 40 text

    Item Exporters Uma vez que você obteve seus items, você vai querer persistí-los ou exportá-los para utilizar esses dados em outra aplicação. O Scrapy fornece um conjunto de Item Exporters para diferentes formatos como CSV, JSON, JL ou XML. $ scrapy runspider sp_jundiai.py -o sp_jundiai.csv $ scrapy runspider sp_jundiai.py -o sp_jundiai.json $ scrapy runspider sp_jundiai.py -o sp_jundiai.jl

    Slide 41

    Slide 41 text

    Item Exporters Uma vez que você obteve seus items, você vai querer persistí-los ou exportá-los para utilizar esses dados em outra aplicação. O Scrapy fornece um conjunto de Item Exporters para diferentes formatos como CSV, JSON, JL ou XML. Mas é muito simples desenvolver o seu próprio exportador para outros formatos.

    Slide 42

    Slide 42 text

    import scrapy class ABCDevSpider(scrapy.Spider): name = 'abcdev' def start_requests(self): urls = [ 'https://2018.abcdevelopers.org' ] for url in urls: yield scrapy.Request(url=url, callback=self.parse) def parse(self, response): speaker_names = response.css( 'article h4::text').getall() yield { 'speakers': speaker_names }

    Slide 43

    Slide 43 text

    Páginas com JavaScript Conteúdo dinâmico Single Page Applications casos simples: emular requests AJAX casos complexos: Splash (https://github.com/scrapinghub/splash) Headless Browser

    Slide 44

    Slide 44 text

    h p:/ /quotes.toscrape.com/scroll

    Slide 45

    Slide 45 text

    No content

    Slide 46

    Slide 46 text

    No content

    Slide 47

    Slide 47 text

    No content

    Slide 48

    Slide 48 text

    No content

    Slide 49

    Slide 49 text

    import json import scrapy class ToScrapeScroll(scrapy.Spider): name = 'toscrape' start_urls = [ 'http://quotes.toscrape.com/api/quotes?page=1' ] def parse(self, response): data = json.loads(response.body) for quote in data.get('quotes'): yield { 'text': quote.get('text'), 'author': quote.get('name') }

    Slide 50

    Slide 50 text

    import json import scrapy from w3lib.url import add_or_replace_parameter class ToScrapeScroll(scrapy.Spider): # (...) def parse(self, response): data = json.loads(response.body) for quote in data.get('quotes'): (...) if data.get('has_next'): current_page = data.get('page') next_page_url = add_or_replace_parameter( response.url, 'page', current_page + 1) yield Request(next_page_url)

    Slide 51

    Slide 51 text

    Tutorial Scrapy https://2018.pythonbrasil.org.br/

    Slide 52

    Slide 52 text

    Dúvidas? [email protected] @rennerocha (Twitter, Instagram, Github, Bitbucket)