$30 off During Our Annual Pro Sale. View Details »

Explorando Scrapy além do tutorial

Explorando Scrapy além do tutorial

Slides da minha palestra na Python Brasil 2014 (com anotações para ler online =)

Seguem alguns links úteis:

Tutorial do Scrapy no PythonHelp: http://pythonhelp.wordpress.com/2014/08/05/web-scraping-com-scrapy-primeiros-passos/
Versão em inglês disponível em: http://hopefulramble.blogspot.com.br/2014/08/web-scraping-with-scrapy-first-steps_30.html

Tutorial de XPath (em inglês): http://www.zvon.org/comp/r/tut-XPath_1.html

Dicas de XPath (em inglês): http://blog.scrapinghub.com/2014/07/17/xpath-tips-from-the-web-scraping-trenches/

Ferramenta para converter código JS em XML, para extrair dados do JavaScript com XPath: https://github.com/redapple/js2xml (instale com: pip install js2xml)

Documentação do Scrapy (em inglês): http://scrapy.readthedocs.org/en/latest/

Elias Dorneles

November 07, 2014
Tweet

More Decks by Elias Dorneles

Other Decks in Programming

Transcript

  1. Explorando
    Python Brasil - 2014
    além do tutorial

    View Slide

  2. Web Scraping
    WAT?

    View Slide

  3. Existe muito
    conteúdo
    na Web

    View Slide

  4. Mas a gente
    quer dados

    View Slide

  5. Web Scraping em
    uma casca de noz

    View Slide

  6. POR QUÊ?

    View Slide

  7. POR QUÊ?
    Descobrir o que as
    pessoas gostam
    Monitorar o que
    estão dizendo
    sobre seu produto
    Rastrear ofertas de
    emprego, preços, etc
    Acompanhar tendências
    de assuntos e produtos
    Backup de conteúdo
    histórico (notícias, blogs)

    View Slide

  8. Obter informações quentes
    é um superpoder

    View Slide

  9. OK, vamos falar
    de Python!

    View Slide

  10. stdlib: urllib2, re, xml
    Para os puristas
    de verdade
    Para os puristas
    de verdade

    View Slide

  11. requests
    BeautifulSoup
    lxml
    Para os
    pragmáticos
    de plantão
    Para os
    pragmáticos
    de plantão

    View Slide

  12. http://scrapy.org
    pip install scrapy
    Para os pragmáticos
    que querem
    baterias inclusas =)
    Para os pragmáticos
    que querem
    baterias inclusas =)

    View Slide

  13. Rápido,
    poderoso,
    extensível e
    customizável

    View Slide

  14. SpiDERS!
    SCRAPING &&
    CRAWLing
    Um Spider, abstração
    central do Scrapy,
    se dedica a 2 coisas:
    crawling (seguir os links
    para as páginas que se
    queira) & scraping
    (extrair os dados da
    página)

    View Slide

  15. Synchronous
    requests suck!
    Synchronous
    requests suck!
    No Scrapy você trata
    requisições de forma
    assíncrona: melhor
    desempenho para
    crawlings grandes.

    View Slide

  16. Twisted Matrix
    Event-driven
    networking engine
    https://twistedmatrix.com
    “molho secreto”
    do Scrapy
    “molho secreto”
    do Scrapy

    View Slide

  17. View Slide

  18. View Slide

  19. # arquivo.py
    from scrapy import Spider, Request
    class Aranha(Spider):
    name = 'aranha'
    def start_requests(self):
    yield Request(
    url='http://www.python.org',
    callback=self.parse)
    def parse(self, response):
    self.log('Oi: %s' % response.url)
    SPIDERS PRECISAM
    DE UM NOME
    Eis o código
    dum Spider
    mínimo

    View Slide

  20. Requisições são
    tratadas em callbacks

    View Slide

  21. # arquivo.py
    from scrapy import Spider, Request
    class Aranha(Spider):
    name = 'aranha'
    def start_requests(self):
    yield Request(
    url='http://www.python.org',
    callback=self.parse)
    def parse(self, response):
    self.log('Oi: %s' % response.url)
    ACIONA AS PRIMEIRAS
    REQUISIÇÕES

    View Slide

  22. # arquivo.py
    from scrapy import Spider, Request
    class Aranha(Spider):
    name = 'aranha'
    def start_requests(self):
    yield Request(
    url='http://www.python.org',
    callback=self.parse)
    def parse(self, response):
    self.log('Oi: %s' % response.url)
    DEFAULT CALLBACK PARA
    TRATAR RESPOSTAS

    View Slide

  23. $ scrapy runspider arquivo.py

    View Slide

  24. $ scrapy runspider arquivo.py
    ...
    2014-08-24 17:45:58-0300 [aranha] INFO: Spider opened
    2014-08-24 17:45:58-0300 [aranha] INFO: Crawled 0 pages (at 0
    pages/min), scraped 0 items (at 0 items/min)
    2014-08-24 17:45:59-0300 [aranha] DEBUG: Redirecting (301) to https://www.python.org/> from
    2014-08-24 17:46:00-0300 [aranha] DEBUG: Crawled (200) https://www.python.org/> (referer: None)
    2014-08-24 17:46:00-0300 [aranha] DEBUG: Oi: https://www.python.org/
    2014-08-24 17:46:00-0300 [aranha] INFO: Closing spider (finished)
    2014-08-24 17:46:00-0300 [aranha] INFO: Dumping Scrapy stats:
    ...

    View Slide

  25. Callbacks retornam um
    iterable de:
    itens com dados coletados (scrapy.Item)
    outras requisições (scrapy.Request) com
    possivelmente outros callbacks

    View Slide

  26. View Slide

  27. def parse(self, response):
    article_links = response.xpath(
    "//header//h1/a/@href"
    ).extract()
    for link in article_links:
    article_url = urlparse.urljoin(
    response.url, link)
    yield scrapy.Request(
    article_url,
    self.extract_article)
    EXTRAI
    LINKS
    Callback gerando requisições

    View Slide

  28. def parse(self, response):
    article_links = response.xpath(
    "//header//h1/a/@href"
    ).extract()
    for link in article_links:
    article_url = urlparse.urljoin(
    response.url, link)
    yield scrapy.Request(
    article_url,
    self.extract_article)
    MONTA URL
    ABSOLUTA
    Callback gerando requisições

    View Slide

  29. def parse(self, response):
    article_links = response.xpath(
    "//header//h1/a/@href"
    ).extract()
    for link in article_links:
    article_url = urlparse.urljoin(
    response.url, link)
    yield scrapy.Request(
    article_url,
    self.extract_article)
    GERA REQUESTS COM OUTRO
    CALLBACK PARA EXTRAIR DADOS
    Callback gerando requisições

    View Slide

  30. class Article(scrapy.Item):
    author = scrapy.Field()
    ...
    def extract_article(self, response):
    article = Article()
    article['author'] = response.css(
    'div#posted-by::text'
    ).re(r'Posted by: (.*)')
    ...
    yield article
    DEFINE DADO
    QUE VAI
    EXTRAIR
    Callback extraindo item

    View Slide

  31. class Article(scrapy.Item):
    author = scrapy.Field()
    ...
    def extract_article(self, response):
    article = Article()
    article['author'] = response.css(
    'div#posted-by::text'
    ).re(r'Posted by: (.*)')
    ...
    yield article
    EXTRAI TEXTO FILTRANDO
    COM REGEX
    Callback extraindo item

    View Slide

  32. class Article(scrapy.Item):
    author = scrapy.Field()
    ...
    def extract_article(self, response):
    article = Article()
    article['author'] = response.css(
    'div#posted-by::text'
    ).re(r'Posted by: (.*)')
    ...
    yield article COLETA ITEM
    Callback extraindo item

    View Slide

  33. $ scrapy runspider arquivo.py \
    -o saida.json
    $ scrapy runspider arquivo.py \
    -o saida.csv
    E você também pode escrever um pipeline
    para armazenar num banco de dados

    View Slide

  34. That's it!

    View Slide

  35. Abstrações úteis

    View Slide

  36. from scrapy.contrib.linkextractors \
    import LinkExtractor
    le = LinkExtractor(
    allow=["/artigos/.+"],
    deny=[
    "/artigos/galeria-fotos"
    ],
    )
    print le.extract_links(response)
    [
    Link(url='http://site.com/artigos/noticia-001', text=u'Lançado
    Py4k', fragment='', nofollow=False),
    Link(url='http://site.com/artigos/opiniao-002', text=u'Carta a
    Guido', fragment='', nofollow=False),
    ]
    LinkExtractor extraindo links baseado em padrão nas URLs
    Saída:

    View Slide

  37. from scrapy.contrib.linkextractors \
    import LinkExtractor
    le = LinkExtractor(
    restrict_xpaths=[
    "//ul[@id='navlist']"
    ]
    )
    print le.extract_links(response)
    [
    Link(url='http://example.com/link01.html', text='Link 01',
    fragment='', nofollow=False),
    Link(url='http://example.com/link02.html', text='Link 02',
    fragment='', nofollow=False),
    ]
    LinkExtractor extraindo links dentro de um elemento HTML
    Saída:

    View Slide

  38. CrawlSpider
    Estende scrapy.Spider para seguir
    links usando regras definidas na
    classe no callback default

    View Slide

  39. from scrapy.contrib.spiders.crawl \
    import CrawlSpider, Rule
    class MySpider(CrawlSpider):
    name = 'myspider'
    rules = [
    Rule(LinkExtractor(...),
    callback='extrai_artigo'),
    Rule(LinkExtractor(...),
    callback='extrai_video'),
    ]
    def extrai_artigo(self, response):
    ...
    def extrai_video(self, response):
    ...
    REGRAS DE
    CRAWLING
    Veja como o
    crawling fica
    como algo
    declarativo:
    você define
    as regras de
    extração de
    links e como
    tratar cada
    tipo de link

    View Slide

  40. from scrapy.contrib.spiders.crawl \
    import CrawlSpider, Rule
    class MySpider(CrawlSpider):
    name = 'myspider'
    rules = [
    Rule(LinkExtractor(...),
    callback='parse'),
    Rule(LinkExtractor(...),
    callback='extrai_video'),
    ]
    def extrai_video(self, response):
    ...
    ACIONA REGRAS
    NOVAMENTE
    Não sobrescreva o método parse()!

    View Slide

  41. Configurações
    e outros truques

    View Slide

  42. $ scrapy runspider arquivo.py \
    -s CONFIG=VALUE
    Num projeto Scrapy, podem ser definidas
    no módulo settings.py
    $ CONFIG=VALUE scrapy \
    runspider arquivo.py
    Scrapy Settings

    View Slide

  43. DOWNLOAD_DELAY=0.5
    Seja gentil com os sites
    = máximo
    de 2 reqs/s
    = máximo
    de 2 reqs/s

    View Slide

  44. Só porque você
    pode não significa
    que você deva!

    View Slide

  45. REDIRECT_MAX_TIMES=5
    Evite loops de redirecionamento

    View Slide

  46. TELNETCONSOLE_ENABLED=False
    Livre-se de bisbillhoteiros

    View Slide

  47. Mais configurações
    de crawling:
    DOWNLOAD_TIMEOUT
    COOKIES_ENABLED
    RETRY_ENABLED
    DEPTH_LIMIT
    DEFAULT_REQUEST_HEADERS
    CONCURRENT_REQUESTS_PER_{DOMAIN,IP}
    consulte a documentação para mais...
    http://scrapy.readthedocs.org/en/latest/topics/settings.html

    View Slide

  48. Aprenda XPath!
    O domínio de XPath diferencia
    os gurus dos gafanhotos! =D

    View Slide

  49. Expressões XPath são apenas
    condições encadeadas
    //li/a[contains(., 'Next page')]/@href
    //div[re:test(@id, 'head(er)?') and ./h1]
    //li[a[contains(., 'Next page')]]
    /preceding-sibling::li[1]

    View Slide

  50. Expressões XPath são apenas
    condições encadeadas
    //li/a[contains(., 'Next page')]/@href
    //div[re:test(@id, 'head(er)?') and ./h1]
    //li[a[contains(., 'Next page')]]
    /preceding-sibling::li[1]
    XPath Axis (eixo) – existem vários!

    View Slide

  51. Expressões XPath são apenas
    condições encadeadas
    //li/a[contains(., 'Next page')]/@href
    //div[re:test(@id, 'head(er)?') and ./h1]
    //li[a[contains(., 'Next page')]]
    /preceding-sibling::li[1]
    Extensões EXSLT (regex, conjuntos)

    View Slide

  52. Converter
    CSS para XPath?
    from scrapy.selector.csstranslator import
    ScrapyHTMLTranslator
    translator = ScrapyHTMLTranslator()
    def css2xpath(css):
    return translator.css_to_xpath(css)
    print css2xpath('ul.nav') NOT BAD!
    u"descendant-or-self::ul[@class and
    contains(concat(' ', normalize-
    space(@class), ' '), ' nav ')]"
    response.css() é só
    um atalho pra isso! ;)
    response.css() é só
    um atalho pra isso! ;)

    View Slide

  53. $ scrapy shell http://example.com
    >>> response.url
    'http://example.com'
    >>> response.xpath('//h1/text()')
    []
    >>> view(response) # abre no browser
    >>> fetch('http://www.google.com') # vai para outra URL
    Use o shell para explorar sites

    View Slide

  54. Código boilerplate:
    w3lib & scrapylib
    https://github.com/scrapy/w3lib
    pip install w3lib
    https://github.com/scrapinghub/scrapylib
    pip install scrapylib
    Use em seu
    próximo projeto =)
    Use em seu
    próximo projeto =)

    View Slide

  55. Cool tools, bro!

    View Slide

  56. E o deploy?

    View Slide

  57. Opções de deploy

    Shell-scripts no cron de um host

    Scrapyd - https://github.com/scrapy/scrapyd
    – permite agendar jobs Scrapy via API, interface Web mínima

    Heroku - https://github.com/dmclain/scrapy-heroku

    Scrapy Cloud - http://scrapinghub.com/scrapy-cloud
    – solução PaaS da ScrapingHub*, fornece API & interface
    Web para jobs, dados coletados, settings, stats e mais.
    * Full-disclosure: ScrapingHub é a empresa em que o palestrante trabalha!

    View Slide

  58. Scrapy Cloud - Dashboard

    View Slide

  59. Recapitulando...
    1) Obter dados quentes com
    Web Scraping é um superpoder
    1) Obter dados quentes com
    Web Scraping é um superpoder

    View Slide

  60. Recapitulando...
    1) Obter dados quentes com
    Web Scraping é um superpoder
    1) Obter dados quentes com
    Web Scraping é um superpoder
    2) Scrapy resolve scraping
    & crawling, é rápido, extensível
    e vem com baterias inclusas
    2) Scrapy resolve scraping
    & crawling, é rápido, extensível
    e vem com baterias inclusas

    View Slide

  61. Recapitulando...
    1) Obter dados quentes com
    Web Scraping é um superpoder
    1) Obter dados quentes com
    Web Scraping é um superpoder
    2) Scrapy resolve scraping
    & crawling, é rápido, extensível
    e vem com baterias inclusas
    2) Scrapy resolve scraping
    & crawling, é rápido, extensível
    e vem com baterias inclusas
    3) Para aproveitar o Scrapy ao máximo,
    vasculhe os docs & aprenda XPath
    3) Para aproveitar o Scrapy ao máximo,
    vasculhe os docs & aprenda XPath

    View Slide

  62. Recapitulando...
    1) Obter dados quentes com
    Web Scraping é um superpoder
    1) Obter dados quentes com
    Web Scraping é um superpoder
    2) Scrapy resolve scraping
    & crawling, é rápido, extensível
    e vem com baterias inclusas
    2) Scrapy resolve scraping
    & crawling, é rápido, extensível
    e vem com baterias inclusas
    3) Para aproveitar o Scrapy ao máximo,
    vasculhe os docs & aprenda XPath
    3) Para aproveitar o Scrapy ao máximo,
    vasculhe os docs & aprenda XPath
    4) Crie algum projeto com Scrapy, publique
    nas interwebs e depois me conte! =)
    4) Crie algum projeto com Scrapy, publique
    nas interwebs e depois me conte! =)

    View Slide

  63. Elias Dorneles
    @eliasdorneles
    @ScrapingHub
    Q&A

    View Slide