Поиск по блогу

воскресенье, 11 января 2015 г.

Подборка примеров Scrapy LinkExtractor, Rules для последующих практикумов

Здесь скопированы десятка полтора фрагментов кода с примерами Scrapy LinkExtractor, Rules Больше двух правил в одном примере мне найти не удалось. Однако, надеюсь, что примеры регулярных выражений здесь на все случай паучьей жизни.

Здесь устаревший SgmlLinkExtractor, но есть два правила Rule они работают по тем же принципам, что и сейчас

In []:
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.contrib.loader.processor import TakeFirst
from scrapy.contrib.loader import XPathItemLoader
from scrapy.selector import HtmlXPathSelector
from orphanage.items import OrphanageItem
 
 
class OrphanSpider(CrawlSpider):
    name = "detskiedomiki"
    allowed_domains = ["www.detskiedomiki.ru"]
    start_urls = ["http://www.detskiedomiki.ru/guide/child/"]
 
    rules = (
        Rule(SgmlLinkExtractor(allow=('act=home_reg', 'act=home_zone')), follow=True),
        Rule(SgmlLinkExtractor(allow=('act=home_more')), callback='parse_item'),
    )
 
    def parse_item(self, response):
        ...

rules - список правил обхода ресурса. В данном случае данный список содержит 2 правила - первое будет срабатывать при попадании паука на страницы, URL которых содержит act=home_reg или act=home_zone.
Под срабатыванием в данном случае подразумевается переход по ссылкам, извлеченным из этих страниц (за что отвечает аргумент follow=True).
Второе правило будет срабатывать при попадании паука на страницы, URL которых содержит act=home_more (именно на этих страницах содержится информация, которую мы хотим извлечь), например http://detskiedomiki.ru/?act=home_more&id=6278&z_id=3&part_id=65. В данном случае у правила не указан аргумент follow, что означает, что при попадании паука на данную страницу ссылки из неё не извлекаются, вместо этого содержимое страницы передаётся на вход функции, указанной в аргументе callback - назовём её в нашем случае parse_item.

Этих страниц на сайте уже нет, но сайт есть... здесь меня заинтересовали регулярные вражения (DATE_REGEX) и необычная структура модуля

In []:
# -*- coding: utf-8 -*-

from scrapy.xpath import HtmlXPathSelector
from scrapy.link.extractors import RegexLinkExtractor
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib_exp import adaptors
from eventscraper.items import EventscraperItem
from scrapy import log
from eventscraper import util
from string import join as j
from datetime import datetime, timedelta
import re


DATE_REGEX = re.compile(r'(\d+)\.(%s)\.(\d){,2}.*?([0-1]\d|2[0-4]):([0-5]\d)hs' % j([m[:3] for m in util.MONTHS], '|'), re.IGNORECASE)

def _fecha_adaptor(value):
    m = DATE_REGEX.search(value)
    if m is None: 
        raise Exception("Couldn't parse: %s" % value)
    g = m.groups()
    d = datetime(2000 + int(g[2]), 
                 [m[:3] for m in util.MONTHS].index(g[1].lower()) + 1, 
                 int(g[0]))
    if int(g[3]) == 24: # 24 no es valido como hora, sumamos 1 dia a la fecha y seteamos hora a 0
        d = d + timedelta(1)
        d = datetime(d.year, d.month, d.day, 0, int(g[4]))
    else:
        d = datetime(d.year, d.month, d.day, int(g[3]), int(g[4]))

    return d



class ProyectoUnderSpider(CrawlSpider):
    domain_name = 'proyectounder.com'
    start_urls = ['http://www.proyectounder.com/agenda.php?agenda=1']

    rules = (
        Rule(RegexLinkExtractor(allow=(r'/evento/\d+',)), 'parse_event', follow=True),
        Rule(RegexLinkExtractor(allow=(r'/agenda.php\?action=display',)))
    )

    adaptor_pipe = [adaptors.extract,  adaptors.delist(''), adaptors.strip]

    adaptor_map = {
        'summary': adaptor_pipe,
        'description': adaptor_pipe,
        'start_date': [adaptors.extract, adaptors.delist(''), _fecha_adaptor]
    }


    def parse_event(self, response):
        i = EventscraperItem()
        i.set_adaptors(self.adaptor_map)

        xs = HtmlXPathSelector(response)

        i.attribute('guid', response.url)
        i.attribute('url', response.url)
        i.attribute('summary', xs.x('//div[contains(@class, "eventoItem")]/h1/text()'))
        i.attribute('description', xs.x('//div[contains(@class, "eventoItem")]/p/text()'))
        i.attribute('start_date', xs.x('//div[contains(@class, "eventoItem")]/span[contains(@class, "fecha")][1]/text()'))

        venue = {
            'name': xs.x('//div[contains(@class, "agendaVermas")]//a[contains(@href, "agenda.php?lugar=")][1]/text()').extract()[0].strip(),
            'city': xs.x('//div[contains(@class, "agendaVermas")]//a[contains(@href, "agenda.php?ciudad=")][1]/text()').extract()[0].strip()
        }

        log.msg(venue)

        i.attribute('meta', {'source_site': 'proyectounder.com', 
                             'scraped_on': datetime.now(),
                             'source_html': response.body })

        return [i]

SPIDER = ProyectoUnderSpider()

Страница сайта рабочая http://sfbay.craigslist.org/npo/ В ссылке выше не только код, а подробный мануал, есть код на Гитхабе

In the first tutorial, I showed you how to write a crawler with Scrapy to scrape Craiglist Nonprofit jobs in San Francisco and store the data to a CSV file. This tutorial continues from where we left off, adding to the existing code, in order to build a recursive crawler to scrape multiple pages. Make sure you read the first tutorial first

In []:
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.selector import HtmlXPathSelector
from craigslist_sample.items import CraigslistSampleItem

class MySpider(CrawlSpider):
    name = "craigs"
    allowed_domains = ["sfbay.craigslist.org"]
    start_urls = ["http://sfbay.craigslist.org/npo/"]

    rules = (Rule (SgmlLinkExtractor(allow=("index\d00\.html", ),restrict_xpaths=('//p[@class="nextpage"]',))
    , callback="parse_items", follow= True),
    )

    def parse_items(self, response):
        hxs = HtmlXPathSelector(response)
        titles = hxs.select('//span[@class="pl"]')
        items = []
        for titles in titles:
            item = CraigslistSampleItem()
            item ["title"] = titles.select("a/text()").extract()
            item ["link"] = titles.select("a/@href").extract()
            items.append(item)
        return(items)

Приме интересен тем, что в одном Rule и обратный вызов и следование по ссылке - По-видимому, это автор и называет рекурсией. Вот, как он описывает работу паука:

In essence, this spider started crawling at http://sfbay.craigslist.org/npo/ and then followed the “next 100 postings” link at the bottom, scraping the next page, until there where no more links to crawl.
Again, this can be used to create some powerful crawlers, so use with caution and set delays to throttle the crawling speed if necessary.

You have to create either two rules or one telling scrapy to allow the url of those types. Basically you want the rules list will be something like this

In []:
rules = (
        Rule(SgmlLinkExtractor(allow=('http://www.cseblog.com/{d+}/{d+}/{*}.html', ), deny=( )),call_back ='parse_save' ),
        Rule(SgmlLinkExtractor(allow=('http://www.cseblog.com/search/{*}', ), deny=( )),,call_back = 'parse_only' ))

А вот первоначальный код, сайт рабочий. Используется BeautifulSoup для парсинга.

In []:
from scrapy.spider import BaseSpider
from bs4 import BeautifulSoup ## This is BeautifulSoup4
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor

from blogscraper.items import BlogArticle ## This is for saving data. Probably insignificant.

class BlogArticleSpider(BaseSpider):
    name = "blogscraper"
    allowed_domains = ["cseblog.com"]
    start_urls = [
        "http://www.cseblog.com/",
    ]

    rules = (
        Rule(SgmlLinkExtractor(allow=('\d+/\d+/*"', ), deny=( ))),
    )

    def parse(self, response):
        site = BeautifulSoup(response.body_as_unicode())
        items = []
        item = BlogArticle()
        item['title'] = site.find("h3" , {"class": "post-title" } ).text.strip()
        item['link'] = site.find("h3" , {"class": "post-title" } ).a.attrs['href']
        item['text'] = site.find("div" , {"class": "post-body" } )
        items.append(item)
        return items

Из этого блога я скопирова код из двух постов... Что это за scrapylib не знал

In []:
scrapy crawl scrapy_test -o file.csv -t csv
In []:
#код scrapy_test:

#! coding: utf-8
__author__ = 'acman'
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.item import Item, Field
#
#
class ScrapyTestItem(Item):
    title = Field()
    category = Field()
    url = Field()
    text = Field()
#
#
class ScrapyTestSpider(CrawlSpider):
    name = "scrapy_test"
    allowed_domains = ["www.acman.ru"]
    start_urls = ["http://www.acman.ru/blog/"]
#
    rules = (
        Rule(LinkExtractor(allow=(r'/blog/\d+')), 
            callback='parse_item', follow=True),
    )
#
    def parse_item(self, response):
        self.log('%s' % response.url)
        item = ScrapyTestItem()
        item['title'] = response.xpath('//h2[@class]/a/text()').extract()
        item['category'] = response.xpath('//h3/a/text()').extract()
        item['text'] = response.xpath(
            '//div[@class="entry"]//p | //pre/code').extract()
        item['url'] = response.url
        return item
In []:
#Одни и те же материалы нет смысла каждый раз записывать, будем добавлять в таблицу только новые.

#Все остается по старому как в предыдущей статье Scrapy: парсер блога , 
#запускаем паука тоже так же, добавим только middleware DeltaFetch из пакета scrapylib

#Установим:

pip install scrapylib

#Добавим в settings.py
SPIDER_MIDDLEWARES = {
    'scrapylib.deltafetch.DeltaFetch': 100,
}
DELTAFETCH_ENABLED = True
#Все готово!

Обратил внимание на regex \/.+.

In []:
rules = [
    Rule(LinkExtractor(allow='page=\d+')),
    Rule(LinkExtractor(allow=['article\/.+\.html']), callback='parse_article')
]
In []:
*Пример со start_requests*
In []:
from scrapy.http.request import Request

def start_requests(self):
    for i in xrange(1, 100):
        url = 'www.examples.com/sports/companies?searchTerm=news+sports&pg=' + i
        yield Request(url=url, callback=parse_torrent)

Следует разобрать пример с re.compile(r'^http://example.com/category/\?.?(?=page=\d+)')*

In []:
Rule(LinkExtractor(allow=('^http://example.com/category/\?.*?(?=page=\d+)', )), callback='parse_item'),
In []:
#Demo (using your example urls):
>>> import re
>>> pattern = re.compile(r'^http://example.com/category/\?.*?(?=page=\d+)')
>>> should_match = [
...     'http://example.com/category/?sort=a-z&page=1',
...     'http://example.com/category/?page=1&sort=a-z&cache=1',
...     'http://example.com/category/?page=1&sort=a-z#'
... ]
>>> for url in should_match:
...     print "Matches" if pattern.search(url) else "Doesn't match"


Посты чуть ниже также могут вас заинтересовать

3 комментария: