Использование InitSpider с всплеском: только анализ страницы входа?

Это своего рода следующий вопрос к тому, который я задал ранее.

Я пытаюсь очистить веб-страницу, которую я должен войти, чтобы достичь первой. Но после аутентификации веб-страницы, которая мне нужна, требуется немного Javascript для запуска, прежде чем вы сможете просматривать содержимое. То, что я сделал, соответствует инструкциям здесь, чтобы установить всплеск, чтобы попытаться отобразить Javascript. Однако...

Прежде чем переключиться на всплеск, проверка подлинности с помощью Scrapy InitSpider была прекрасной. Я прошел через страницу входа в систему и очистил целевую страницу ОК (кроме, конечно, без работы Javascript). Но как только я добавляю код для передачи запросов через всплеск, похоже, что я не разбираю целевую страницу.

Паук ниже. Единственная разница между версией блэша (здесь) и версией без всплеска - это функция def start_requests(). Все остальное одинаково между ними.

import scrapy
from scrapy.spiders.init import InitSpider
from scrapy.spiders import Rule
from scrapy.linkextractors import LinkExtractor

class BboSpider(InitSpider):
    name = "bbo"
    allowed_domains = ["bridgebase.com"]
    start_urls = [
            "http://www.bridgebase.com/myhands/index.php"
            ]
    login_page = "http://www.bridgebase.com/myhands/myhands_login.php?t=%2Fmyhands%2Findex.php%3F" 

    # authentication
    def init_request(self):
        return scrapy.http.Request(url=self.login_page, callback=self.login)

    def login(self, response):
        return scrapy.http.FormRequest.from_response(
            response,
            formdata={'username': 'USERNAME', 'password': 'PASSWORD'},
            callback=self.check_login_response)

    def check_login_response(self, response):
        if "recent tournaments" in response.body:
            self.log("Login successful")
            return self.initialized()
        else:
            self.log("Login failed")
            print(response.body)

    # pipe the requests through splash so the JS renders 
    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(url, self.parse, meta={
                'splash': {
                    'endpoint': 'render.html',
                    'args': {'wait': 0.5}
                }
            }) 

    # what to do when a link is encountered
    rules = (
            Rule(LinkExtractor(), callback='parse_item'),
            )

    # do nothing on new link for now
    def parse_item(self, response):
        pass

    def parse(self, response):
        filename = 'test.html' 
        with open(filename, 'wb') as f:
            f.write(response.body)

Теперь происходит то, что test.html, результат parse(), теперь просто сама страница входа в систему, а не страница, на которую я должен быть перенаправлен после входа в систему.

Это сообщение в журнале - обычно, я вижу строку "Успешно подключен" от check_login_response(), но, как вы можете видеть ниже, кажется, что я даже не дошел до этого шага. Это потому, что scrapy теперь ставит запросы на аутентификацию через всплеск тоже, и что она повесилась там? Если это так, есть ли способ обойти всплеск только для части аутентификации?

2016-01-24 14:54:56 [scrapy] INFO: Spider opened
2016-01-24 14:54:56 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2016-01-24 14:54:56 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2016-01-24 14:55:02 [scrapy] DEBUG: Crawled (200) <POST http://localhost:8050/render.html> (referer: None)
2016-01-24 14:55:02 [scrapy] INFO: Closing spider (finished)

Я уверен, что неправильно использую всплеск. Может ли кто-нибудь указать мне какую-нибудь документацию, где я могу выяснить, что происходит?

Ответ 1

Я не думаю, что Splash самостоятельно справится с этим конкретным случаем.

Вот рабочая идея:

Код:

import scrapy
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class BboSpider(scrapy.Spider):
    name = "bbo"
    allowed_domains = ["bridgebase.com"]
    login_page = "http://www.bridgebase.com/myhands/myhands_login.php?t=%2Fmyhands%2Findex.php%3F"

    def start_requests(self):
        driver = webdriver.PhantomJS()
        driver.get(self.login_page)

        driver.find_element_by_id("username").send_keys("user")
        driver.find_element_by_id("password").send_keys("password")

        driver.find_element_by_name("submit").click()

        driver.save_screenshot("test.png")
        WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.LINK_TEXT, "Click here for results of recent tournaments")))

        cookies = driver.get_cookies()
        driver.close()

        yield scrapy.Request("http://www.bridgebase.com/myhands/index.php", cookies=cookies)

    def parse(self, response):
        if "recent tournaments" in response.body:
            self.log("Login successful")
        else:
            self.log("Login failed")
        print(response.body)

Печать Login successful и HTML страницы "руки".

Ответ 2

Update

Итак, кажется, что start_requests срабатывает перед входом в систему.

Вот код от InitSpider, минус комментарии.

class InitSpider(Spider):
    def start_requests(self):
        self._postinit_reqs = super(InitSpider, self).start_requests()
        return iterate_spider_output(self.init_request())

    def initialized(self, response=None):
        return self.__dict__.pop('_postinit_reqs')

    def init_request(self):
        return self.initialized()

InitSpider вызывает основной start_requests с помощью initialized.

Ваш start_requests - это модифицированная версия метода базового класса. Возможно, что-то вроде этого будет работать.

from scrapy.utils.spider import iterate_spider_output

...

def start_requests(self):
    self._postinit_reqs = my_start_requests()
    return iterate_spider_output(self.init_request())

def my_start_requests(self):
    for url in self.start_urls:
        yield scrapy.Request(url, self.parse, meta={
            'splash': {
                'endpoint': 'render.html',
                'args': {'wait': 0.5}
            }
        }) 

Вам нужно return self.initialized()

Ответ 3

Вы можете получить все данные без необходимости использования js вообще, есть ссылки, доступные для браузеров, у которых нет javascript, а URL-адреса - это тот же самый бар ?offset=0. Вам просто нужно проанализировать запросы с интересующего вас турнирного URL и создать форму Formrequest.

import scrapy
from scrapy.spiders.init import InitSpider
from urlparse import parse_qs, urlparse


class BboSpider(InitSpider):
    name = "bbo"
    allowed_domains = ["bridgebase.com"]
    start_urls = [
        "http://www.bridgebase.com/myhands/index.php"
    ]

    login_page = "http://www.bridgebase.com/myhands/myhands_login.php?t=%2Fmyhands%2Findex.php%3F"

    def start_requests(self):
        return [scrapy.FormRequest(self.login_page,
                                   formdata={'username': 'foo', 'password': 'bar'}, callback=self.parse)]

    def parse(self, response):
        yield scrapy.Request("http://www.bridgebase.com/myhands/index.php?offset=0", callback=self.get_all_tournaments)

    def get_all_tournaments(self, r):
        url = r.xpath("//a/@href[contains(., 'tourneyhistory')]").extract_first()
        yield scrapy.Request(url, callback=self.chosen_tourney)

    def chosen_tourney(self, r):
        url = r.xpath("//a[contains(./text(),'Speedball')]/@href").extract_first()
        query = urlparse(url).query
        yield scrapy.FormRequest("http://webutil.bridgebase.com/v2/tarchive.php?offset=0", callback=self.get_tourney_data_links,
                                 formdata={k: v[0] for k, v in parse_qs(query).items()})

    def get_tourney_data_links(self, r):
        print r.xpath("//a/@href").extract()

В выводе имеется множество ссылок. Для рук, которые вы получаете tview.php?-t=...., вы можете запросить каждого, присоединяющегося к http://webutil.bridgebase.com/v2/, и он даст вам таблицу всех данных, которые легко разобрать, есть также ссылки на tourney=4796-1455303720-&username=..., связанные с каждой рукой в ​​таблицах, фрагмент вывода из ссылки tview:

class="bbo_tr_t">
    <table class="bbo_t_l">
    <tr><td class="bbo_tll" align="left">Title</td><td class="bbo_tlv">#4796 Ind.  ACBL Fri 2pm</td></tr>
    <tr><td class="bbo_tll" align="left">Host</td><td class="bbo_tlv">ACBL</td></tr>
    <tr><td class="bbo_tll" align="left">Tables</td><td class="bbo_tlv">9</td></tr>



    </table>

    </div><div class='sectionbreak'>Section 1 </div><div class='onesection'> <table class='sectiontable' ><tr><th>Name</th><th>Score (IMPs)</th><th class='rank'>Rank</th><th>Prize</th><th>Points</th></tr>
<tr class='odd'><td>colt22</td><td><a href="#" onclick="location.href='http://www.bridgebase.com/myhands/hands.php?tourney=4796-1455303720-&username=colt22'; return false;" target="_blank">42.88</a></td><td class='rank' >1</td><td></td><td>0.90</td></tr>
<tr class='even'><td>francha</td><td><a href="#" onclick="location.href='http://www.bridgebase.com/myhands/hands.php?tourney=4796-1455303720-&username=francha'; return false;" target="_blank">35.52</a></td><td class='rank' >2</td><td></td><td>0.63</td></tr>
<tr class='odd'><td>MSMK</td><td><a href="#" onclick="location.href='http://www.bridgebase.com/myhands/hands.php?tourney=4796-1455303720-&username=MSMK'; return false;" target="_blank">34.38</a></td><td class='rank' >3</td><td></td><td>0.45</td></tr>

Остальная часть синтаксиса я оставлю для себя.