Scraw CrawlSpider для контента AJAX

Я пытаюсь просканировать сайт для новостных статей. Мой start_url содержит:

(1) ссылки на каждую статью: http://example.com/symbol/TSLA

и

(2) кнопка "Дополнительно" , которая вызывает вызов AJAX, который динамически загружает больше статей в пределах одного и того же start_url: http://example.com/account/ajax_headlines_content?type=in_focus_articles&page=0&slugs=tsla&is_symbol_page=true

Параметр вызова AJAX - это "страница", который увеличивается каждый раз при нажатии кнопки "Дополнительно" . Например, нажав "Дополнительно" , один раз загрузите дополнительные n статей и обновите параметр страницы в "Больше" на кнопке onClick, чтобы в следующий раз щелкнуть "Больше", "страница" будет загружена две статьи (при условии, Сначала была загружена страница "0", а "первая страница" была загружена при первом нажатии).

Для каждой "страницы" я хотел бы очистить содержимое каждой статьи с помощью правил, но я не знаю, сколько "страниц" есть, и я не хочу выбирать какие-либо произвольные m (например, 10k). Я не могу понять, как это сделать.

Из этого вопроса Сканирование URL-адресов сканирования в порядке, я попытался создать список потенциальных URL-адресов, но не могу определить, как и куда отправлять новый URL-адрес из пула после разбора предыдущего URL-адреса и обеспечения наличия ссылок новостей для CrawlSpider. Мои правила отправляют ответы на обратный вызов parse_items, где анализируется содержимое статьи.

Есть ли способ наблюдать за содержанием страницы ссылок (аналогично примеру BaseSpider) перед применением правил и вызовом parse_items, чтобы я мог знать, когда прекратить сканирование?

Упрощенный код (я удалил несколько полей, которые я разбираю для ясности):

class ExampleSite(CrawlSpider):

    name = "so"
    download_delay = 2

    more_pages = True
    current_page = 0

    allowed_domains = ['example.com']

    start_urls = ['http://example.com/account/ajax_headlines_content?type=in_focus_articles&page=0'+
                      '&slugs=tsla&is_symbol_page=true']

    ##could also use
    ##start_urls = ['http://example.com/symbol/tsla']

    ajax_urls = []                                                                                                                                                                                                                                                                                                                                                                                                                          
    for i in range(1,1000):
        ajax_urls.append('http://example.com/account/ajax_headlines_content?type=in_focus_articles&page='+str(i)+
                      '&slugs=tsla&is_symbol_page=true')

    rules = (
             Rule(SgmlLinkExtractor(allow=('/symbol/tsla', ))),
             Rule(SgmlLinkExtractor(allow=('/news-article.*tesla.*', '/article.*tesla.*', )), callback='parse_item')
            )

        ##need something like this??
        ##override parse?
        ## if response.body == 'no results':
            ## self.more_pages = False
            ## ##stop crawler??   
        ## else: 
            ## self.current_page = self.current_page + 1
            ## yield Request(self.ajax_urls[self.current_page], callback=self.parse_start_url)


    def parse_item(self, response):

        self.log("Scraping: %s" % response.url, level=log.INFO)

        hxs = Selector(response)

        item = NewsItem()

        item['url'] = response.url
        item['source'] = 'example'
        item['title'] = hxs.xpath('//title/text()')
        item['date'] = hxs.xpath('//div[@class="article_info_pos"]/span/text()')

        yield item

Ответ 1

Паук-сканер может быть слишком ограничен для ваших целей здесь. Если вам нужно много логики, вам обычно лучше наследовать от Spider.

Scrapy предоставляет исключение CloseSpider, которое может быть поднято, когда вам нужно прекратить синтаксический анализ при определенных условиях. На странице, которую вы просматриваете, возвращается сообщение "Нет статей о фокусе на ваших запасах", когда вы превысите максимальную страницу, вы можете проверить это сообщение и прекратить итерацию при появлении этого сообщения.

В вашем случае вы можете пойти примерно так:

from scrapy.spider import Spider
from scrapy.http import Request
from scrapy.exceptions import CloseSpider

class ExampleSite(Spider):
    name = "so"
    download_delay = 0.1

    more_pages = True
    next_page = 1

    start_urls = ['http://example.com/account/ajax_headlines_content?type=in_focus_articles&page=0'+
                      '&slugs=tsla&is_symbol_page=true']

    allowed_domains = ['example.com']

    def create_ajax_request(self, page_number):
        """
        Helper function to create ajax request for next page.
        """
        ajax_template = 'http://example.com/account/ajax_headlines_content?type=in_focus_articles&page={pagenum}&slugs=tsla&is_symbol_page=true'

        url = ajax_template.format(pagenum=page_number)
        return Request(url, callback=self.parse)

    def parse(self, response):
        """
        Parsing of each page.
        """
        if "There are no Focus articles on your stocks." in response.body:
            self.log("About to close spider", log.WARNING)
            raise CloseSpider(reason="no more pages to parse")


        # there is some content extract links to articles
        sel = Selector(response)
        links_xpath = "//div[@class='symbol_article']/a/@href"
        links = sel.xpath(links_xpath).extract()
        for link in links:
            url = urljoin(response.url, link)
            # follow link to article
            # commented out to see how pagination works
            #yield Request(url, callback=self.parse_item)

        # generate request for next page
        self.next_page += 1
        yield self.create_ajax_request(self.next_page)

    def parse_item(self, response):
        """
        Parsing of each article page.
        """
        self.log("Scraping: %s" % response.url, level=log.INFO)

        hxs = Selector(response)

        item = NewsItem()

        item['url'] = response.url
        item['source'] = 'example'
        item['title'] = hxs.xpath('//title/text()')
        item['date'] = hxs.xpath('//div[@class="article_info_pos"]/span/text()')

        yield item