Подтвердить что ты не робот

Создание родового спайдера

Мой вопрос в том, как сделать то же самое, что и предыдущий вопрос, но в Scrapy 0.14.

Использование одного патча Scrapy для нескольких веб-сайтов

В принципе, у меня есть GUI, который принимает такие параметры, как домен, ключевые слова, имена тегов и т.д., и я хочу создать общий паук для обхода этих доменов для этих ключевых слов в этих тегах. Я читал противоречивые вещи, используя старые версии scrapy, либо переопределяя класс менеджера пауков, либо динамически создавая паука. Какой метод является предпочтительным и как я реализую и вызывать правильное решение? Спасибо заранее.

Вот код, который я хочу сделать общим. Он также использует BeautifulSoup. Я спарил его так, надеюсь, не устранил ничего важного, чтобы понять его.

class MySpider(CrawlSpider):

name = 'MySpider'
allowed_domains = ['somedomain.com', 'sub.somedomain.com']
start_urls = ['http://www.somedomain.com']

rules = (
    Rule(SgmlLinkExtractor(allow=('/pages/', ), deny=('', ))),

    Rule(SgmlLinkExtractor(allow=('/2012/03/')), callback='parse_item'),
)

def parse_item(self, response):
    contentTags = []

    soup = BeautifulSoup(response.body)

    contentTags = soup.findAll('p', itemprop="myProp")

    for contentTag in contentTags:
        matchedResult = re.search('Keyword1|Keyword2', contentTag.text)
        if matchedResult:
            print('URL Found: ' + response.url)

    pass
4b9b3361

Ответ 1

Вы можете создать пауз во время выполнения, который оценивается интерпретатором. Этот фрагмент кода можно было оценить во время выполнения следующим образом:

a = open("test.py")
from compiler import compile
d = compile(a.read(), 'spider.py', 'exec')
eval(d)

MySpider
<class '__main__.MySpider'>
print MySpider.start_urls
['http://www.somedomain.com']

Ответ 2

Я использую Scrapy Extensions для расширения класса Spider для класса с именем Masterspider, который включает в себя общий синтаксический анализатор.

Ниже приведена очень "короткая" версия моего общего расширенного анализатора. Обратите внимание, что вам нужно будет реализовать средство визуализации с движком Javascript (например, Selenium или BeautifulSoup), как только вы начнете работать на страницах с помощью AJAX. И много дополнительного кода для управления различиями между сайтами (лома, основанного на заголовке столбца, дескрипторе относительного относительно длинного URL-адреса, управлении контейнерами разных типов и т.д.).

Что вызывает интерес с помощью подхода Scrapy Extension, так это то, что вы все равно можете переопределить общий метод парсера, если что-то не подходит, но мне никогда не приходилось это делать. Класс Masterspider проверяет, были ли созданы некоторые методы (например, parser_start, next_url_parser...) в соответствии с классом spider для конкретного сайта, чтобы разрешить управление спецификациями: отправить форму, построить запрос next_url от элементов на странице и т.д.

Как я соскабливаю очень разные сайты, всегда есть особенности для управления. Поэтому я предпочитаю хранить класс для каждого очищенного сайта, чтобы я мог написать некоторые конкретные методы для его обработки (предварительная/пост-обработка, кроме PipeLines, генераторов запросов...).

masterspider/SiteSpider/settings.py

EXTENSIONS = {
    'masterspider.masterspider.MasterSpider': 500
}

masterspider/masterspdier/masterspider.py

# -*- coding: utf8 -*-
from scrapy.spider import Spider
from scrapy.selector import Selector
from scrapy.http import Request
from sitespider.items import genspiderItem

class MasterSpider(Spider):

    def start_requests(self):
        if hasattr(self,'parse_start'): # First page requiring a specific parser
            fcallback = self.parse_start
        else:
            fcallback = self.parse
        return [ Request(self.spd['start_url'],
                     callback=fcallback,
                     meta={'itemfields': {}}) ]

    def parse(self, response):
        sel = Selector(response)
        lines = sel.xpath(self.spd['xlines'])
        # ...
        for line in lines:
            item = genspiderItem(response.meta['itemfields'])               
            # ...
            # Get request_url of detailed page and scrap basic item info
            # ... 
            yield  Request(request_url,
                   callback=self.parse_item,
                   meta={'item':item, 'itemfields':response.meta['itemfields']})

        for next_url in sel.xpath(self.spd['xnext_url']).extract():
            if hasattr(self,'next_url_parser'): # Need to process the next page URL before?
                yield self.next_url_parser(next_url, response)
            else:
                yield Request(
                    request_url,
                    callback=self.parse,
                    meta=response.meta)

    def parse_item(self, response):
        sel = Selector(response)
        item = response.meta['item']
        for itemname, xitemname in self.spd['x_ondetailpage'].iteritems():
            item[itemname] = "\n".join(sel.xpath(xitemname).extract())
        return item

masterspider/SiteSpider/пауки/somesite_spider.py

# -*- coding: utf8 -*-
from scrapy.spider import Spider
from scrapy.selector import Selector
from scrapy.http import Request
from sitespider.items import genspiderItem
from masterspider.masterspider import MasterSpider

class targetsiteSpider(MasterSpider):
    name = "targetsite"
    allowed_domains = ["www.targetsite.com"]
    spd = {
        'start_url' : "http://www.targetsite.com/startpage", # Start page
        'xlines' : "//td[something...]",
        'xnext_url' : "//a[contains(@href,'something?page=')]/@href", # Next pages
        'x_ondetailpage' : {
            "itemprop123" :      u"id('someid')//text()"
            }
    }

#     def next_url_parser(self, next_url, response): # OPTIONAL next_url regexp pre-processor
#          ...

Ответ 3

Вместо того, чтобы привязать к классу переменные name, allowed_domains, start_urls и rules, вы должны написать MySpider.__init__, вызвать CrawlSpider.__init__, передав необходимые аргументы, и установить name, allowed_domains и т.д. на объект. MyProp, а ключевые слова также должны быть установлены в вашем __init__. Поэтому в конце вы должны иметь что-то вроде ниже. Вам не нужно добавлять name к аргументам, так как name устанавливается самой BaseSpider из kwargs:

class MySpider(CrawlSpider):

    def __init__(self, allowed_domains=[], start_urls=[], 
            rules=[], findtag='', finditemprop='', keywords='', **kwargs):
        CrawlSpider.__init__(self, **kwargs)
        self.allowed_domains = allowed_domains
        self.start_urls = start_urls
        self.rules = rules
        self.findtag = findtag
        self.finditemprop = finditemprop
        self.keywords = keywords

    def parse_item(self, response):
        contentTags = []

        soup = BeautifulSoup(response.body)

        contentTags = soup.findAll(self.findtag, itemprop=self.finditemprop)

        for contentTag in contentTags:
            matchedResult = re.search(self.keywords, contentTag.text)
            if matchedResult:
                print('URL Found: ' + response.url)

Ответ 4

Я не уверен, какой путь предпочтительнее, но я расскажу вам, что я сделал в прошлом. Я никоим образом не уверен, что это лучший (или правильный) способ сделать это, и мне было бы интересно узнать, что думают другие люди.

Я обычно просто переопределяю родительский класс (CrawlSpider) и либо передаю аргументы, а затем инициализирую родительский класс через super(MySpider, self).__init__() из моей собственной функции инициализации или я извлекаю эти данные из базы данных, где я сохранил список ссылок, которые будут добавлены к start_urls ранее.

Ответ 5

Что касается обхода определенных доменов, переданных в качестве аргументов, я просто переопределяю Spider.__init__:

class MySpider(scrapy.Spider):
    """
    This spider will try to crawl whatever is passed in `start_urls` which
    should be a comma-separated string of fully qualified URIs.

    Example: start_urls=http://localhost,http://example.com
    """
    def __init__(self, name=None, **kwargs):
        if 'start_urls' in kwargs:
            self.start_urls = kwargs.pop('start_urls').split(',')
        super(Spider, self).__init__(name, **kwargs)