Измените ввод и отправьте форму для отмены задачи - PullRequest
0 голосов
/ 10 марта 2020

В настоящее время я работаю над заданием по сбору данных, чтобы удалить экономические c данные календаря из https://tradingeconomics.com/calendar, но я хотел бы изменить ввод startDate и endDate, чтобы он извлекал данные из разных дат .

Это то, что я написал до сих пор:

import scrapy
from datetime import datetime
from example.items import CalendarItem
import pytz

class calendarSpider(scrapy.Spider):
    name = "calendar"

    def start_requests(self):
        url  = 'https://tradingeconomics.com/calendar'
        yield scrapy.Request(url=url, callback=self.parse)


    def parse(self, response):
        item = CalendarItem()
        filename = 'calendar.html'
        with open(filename, 'wb') as f:
            f.write(response.body)
        self.log('Saved file %s' % filename)

        table = response.xpath('//body//div[@class="container"]//div[@class="row"]//div[@class="col-lg-8 col-md-9"and@role="main"]//div[@class="table-responsive panel panel-default"]')
        # table has length 2, table[1] is the actual table I would like to scrape

       # China & US table records + date info
        rows = table[1].xpath('.//tr[@data-country="china" or @data-country="united states"] | .//thead[@class="table-header"]//tr//th[@colspan="3"]')

        date = rows[0].xpath('.//text()').get().strip()

        for this_row in rows:

            # date
            new_date = this_row.xpath('.//text()').get().strip()
            if len(new_date) and (new_date != date):
                date = new_date

            if not len(new_date):
                # when new_date = ''
                time = this_row.xpath('.//td/span[starts-with(@class,"calendar-date")]/text()').get()
                item['date'] = date
                item['time'] = [time.strip() if time else 'All Day'][0]

                # country
                item['country'] = this_row.xpath('.//td[@class="calendar-item"]//table//tr//td[@class="calendar-iso"]/text()').extract()

                #event
                event1 = this_row.xpath('.//td/a[@class="calendar-event"]/text()').get() # usually the main title
                event2 = this_row.xpath('.//td/span[not(@id) and not(@class)]/text()').get() # some has additional information (e.g.:FEB,Q4)
                events = [event1, event2]
                item['event'] = ' '.join(filter(None, events))

                yield item

И если мы запустим скрипт один раз, он извлечет данные за ~ 10 дней. (скажем, сегодня 10 марта, тогда отработанные данные будут 10 марта - 20 марта.

Однако я хотел бы извлечь данные за 20 дней, поэтому мне нужны данные с 20 марта - 10 апреля.

Я заметил, что мы можем добавить данные на сайт, мы можем изменить фильтр даты: изменить фильтр даты

, и соответствующий код HTML является startDate и endDate здесь:

<div id="datesDiv" class="collapse">
            <br />
            <div class="input-group">
                <span class="input-group-addon hidden-sm hidden-xs">From</span>
                <input type="text" id="startDate" class="form-control" value="2020-03-09 17:58" />
                <span class="input-group-addon hidden-sm hidden-xs">Until</span>
                <input type="text" id="endDate" class="form-control" value="2020-03-15 01:58" />
                <span class="input-group-btn">
                    <button class="btn btn-success" onclick="setCustomDates(event);">Submit</button>
                </span>
            </div>
        </div>

У меня такое ощущение, что я должен изменить ввод и "отправить форму", чтобы на сломанном веб-сайте были данные на нужные даты, но я не мог понять, как это сделать успешно.

Я сослался на здесь и попробовал это в scrapy shell

fetch(scrapy.FormRequest.from_response(response,formdata={'startDate':'2020-03-20', 'endDate':'2020-04-10'}))

Видимо, это не работает ... Я проверил сеть под Developer Tools для веб-сайта, но я не видел раздел POST каждый раз, когда я меняю дату. И FormData содержит только предопределенные фильтры, например:

FormData

соответствует

setCalendarRange

Поэтому я не уверен, что мне делать, если я хочу добавить вход для фильтра даты на этапе очистки.

Может ли кто-нибудь помочь? Большое спасибо!

- Обновление

Попробовал это

def start_request(selfself):
    yield Request(url=url,
                  cookies={'te-custom-range-importance' :'2020-03-09|2020-03-26',
                           'ASP.NET_SessionId': 'bkvsst1k4ocgs2v4d0x1cjfa',
                           'TEServer': 'TEIIS3',
                           'TECalendarOffset':'-240',
                           'te_sso_sync_/calendar':'Tue Mar 10 2020 01:23:00 GMT-0400 (Eastern Daylight Time)'},
                  callback = self.parse)

Но, похоже, ничего не скинуло с сайта и без календаря . html сгенерировано ..

1 Ответ

1 голос
/ 10 марта 2020

ASP сайты делают некоторые забавные вещи. Обычно он отправляет строку в кодировке base64 в переменную __VIEWSTATE. В этом случае он фактически передает данные через куки.

Глядя на кнопку отправки, он фактически не отправляет форму, которую он выполняет JS setCustomDates, что вызывает refre sh, вызывающий $ (document) .ready to будет вызван снова.

location.reload обновляет браузер так же, как и кнопка refre sh.

Соответствующий код для попытки обратного инжиниринга. Поиск div id = "aspnetForm" содержит скрытые атрибуты формы. Вы можете декодировать __VIEWSTATE, используя base64, и посмотреть, не сможете ли вы перепроектировать его. Обычно в этот момент мне надоело смотреть на источник, и если нет необходимости перепроектировать его, он просто будет использовать Selenium для своевременного завершения проекта.

Если вы посмотрите вниз в функции JS в document.ready есть код для анализа файлов cookie. Я думаю, что если вы сможете выполнить обратный инжиниринг этого создания ie, создать и настроить файлы cookie и переменную __VIEWSTATE, то у вас все получится.

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

Также всегда есть Selenium ...

   $(document).ready(function () {
        console.info("/js/calendar.ascx");

        if (!isPostBack) {
            var offset = new Date().getTimezoneOffset() * -1;  // Difference in minutes between UTC and local time.

            var offsetCookie = GetCalendarCookie("TECalendarOffset");
            if (offsetCookie) {
                offset = offsetCookie;
                console.info("CALENDAR TIMEZONE SET FROM COOKIE TECalendarOffset");
            }

            // Avoid refresh if offset is default (From London)
            if (offset == 0) {
                console.info("CALENDAR USER TIME ZONE IS UTC");
            }
            else if (offsetCookie) {
                // Need to implement a server way to get the cookie
                console.info("CALENDAR SERVED USING COOKIE TECalendarOffset");
            }
            else if ($("#DropDownListTimezone option[value=" + offset + "]").length > 0) {
                setOffset(offset)

            }
        }

        function GetCalendarCookie(name) {
            console.log('READING CALENDAR COOKIE', name);
            var nameEQ = encodeURIComponent(name) + "=";
            var ca = document.cookie.split(';');
            for (var i = 0; i < ca.length; i++) {
                var c = ca[i];
                while (c.charAt(0) === ' ') c = c.substring(1, c.length);
                if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
            }
            return null;

    function setCustomDates(e) {
        e.stopPropagation();
        e.preventDefault();
        var startDate = $("#startDate").val();
        var endDate = $("#endDate").val();
        //console.log('startDate', startDate, 'endDate', endDate);
        var lifetime = .5 * 24 / 60 / 60; //five minutes in days
        //console.log('lifetime', lifetime);
        // This variable is found above in the JS var CUSTOM_RANGE_COOKIE_NAME = "te-custom-range-importance"; 
        window.TEcreateCookie(CUSTOM_RANGE_COOKIE_NAME, startDate + "|" + endDate, lifetime);
        window.TEeraseCookie(RANGE_COOKIE_NAME);
        location.reload();
    }



Cookie: te-custom-range-importance=2020-03-09|2020-03-26; ASP.NET_SessionId=bkvsst1k4ocgs2v4d0x1cjfa; TEServer=TEIIS3; TECalendarOffset=-240; te_sso_sync_/calendar=Tue Mar 10 2020 01:23:00 GMT-0400 (Eastern Daylight Time)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...