Пользовательский скрипт для перебора нескольких HTTP-запросов и объединения результатов?

Теперь я знаю, , как загружать столбцы таблицу с внешней веб-страницы.

Теперь я хочу расширить это и:

  • Получить табличные данные с нескольких страниц (рейтинги по позиции игрока).
  • Объедините его в одну главную таблицу.

http://football.fantasysports.yahoo.com/f1/326198/pointsagainst?pos=QB, который в данный момент извлекает скрипт. Столбцы - это название команды и рейтинг команды для различных должностей.

Я хочу, чтобы он повторялся по другим позициям (например, WR, RB, TE). Это делается путем простого изменения двух последних букв URL-адреса на соответствующее значение. Затем я хочу, чтобы все эти данные были в одном массиве, где первый столбец — это название команды, второй столбец — рейтинг # для позиции QB, третий столбец — рейтинг # для позиции WR и так далее.

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

Еще одна проблема, с которой я столкнулся, — область видимости переменных. В настоящее время newStatTable определено внутри функции parseResponse, но я не могу получить к нему доступ извне функции. Я попытался удалить var из var newStatTable, чтобы сделать его глобальным, но это не сработало.

Вот пример выходного массива, который я ищу (Примечание: я просто выбрал эти ранги случайным образом):

TeamName                QB   WR   TE    and so on...
---                     --   --   --
Jacksonville Jaguars    1    6    28
Sanfrancisco 49ers      4    2    32
Seattle Seahawks        31   5    10

Вот моя попытка.

// ==UserScript==
// @name        _Grab stuff of a *static*, third-party web site.
// @include  http://football.fantasysports.yahoo.com/*
// @include  https://football.fantasysports.yahoo.com/*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @grant       GM_xmlhttpRequest
// ==/UserScript==

pos = ["QB", "WR", "RB", "TE", "K", "DEF"];
for (x in pos) {
    GM_xmlhttpRequest ( {
        method:     "GET",
        url:        "http://football.fantasysports.yahoo.com/f1/326198/pointsagainst?pos=" + pos[x],
        onload:     parseResponse,
        onerror:    function (e) { console.error ('**** error ', e); },
        onabort:    function (e) { console.error ('**** abort ', e); },
        ontimeout:  function (e) { console.error ('**** timeout ', e); }
    } );
}
function parseResponse (response) {
    var parser  = new DOMParser ();
    var ajaxDoc         = parser.parseFromString (response.responseText, "text/html");
    var statRows        = ajaxDoc.querySelectorAll ("#statTable0 > tbody > tr");
    var newStatTable    = $(statRows).map ( function () {
        var tblRow      = $(this);
        var teamRank    = parseInt (tblRow.find (".rank-indicator").text().trim(), 10);
        var teamName    = tblRow.find ("td:eq(1)").text().trim().split(" vs")[0];

        return [ [teamName, teamRank] ]; //Return Teamname, Rank #
    } ).get ();

    console.log (newStatTable);
}

person Bijan    schedule 01.09.2015    source источник
comment
Итак, проблема в том, что они не по порядку, верно? -- Я предполагаю, что вы знаете, что httpRequests являются асинхронными, так почему вы ожидаете, что они будут в порядке?   -  person Soren    schedule 02.09.2015
comment
Когда я говорю не по порядку, я имею в виду, например, что массив WR отображается перед массивом QB. В цикле for разве это не идет по порядку? Кроме того, моя большая проблема заключается в том, что я пытаюсь найти способ объединить все таблицы, как в моем образце вывода массива.   -  person Bijan    schedule 02.09.2015
comment
Ваш цикл for идет по порядку, но GM_xmlhttpRequest создает асинхронный вызов AJAX, и его завершение происходит после завершения цикла for, и они завершаются в том порядке, в котором HTTP-сервер вы вызываете. решает завершить их -- т.е. в любом порядке.   -  person Soren    schedule 02.09.2015


Ответы (1)


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

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

Общий подход заключается в следующем:

  1. Создайте объект Associative Array. для хранения данных.
  2. Передайте контекст в каждом вызове AJAX.
  3. Используйте этот контекст для управления тем, как функция обратного вызова AJAX анализирует данные и объединяет их в общий массив.
  4. Отслеживайте состояние вызовов AJAX и выполняйте окончательную обработку только после их завершения.

Вот как это выглядит в пользовательском скрипте:

// ==UserScript==
// @name        _Mash up tables from several *static*, third-party, web pages.
// @match       *://football.fantasysports.yahoo.com/*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @grant       GM_xmlhttpRequest
// ==/UserScript==
const playerPositions   = ["QB", "WR", "RB", "TE", "K", "DEF"];
const numPositions      = playerPositions.length;
const baseURL           = "http://football.fantasysports.yahoo.com/f1/326198/pointsagainst?pos=";
var rankingsTable       = {};  //-- This will be filled by the AJAX parser.
var numPagesFetched     = 0;

for (var J in playerPositions) {
    GM_xmlhttpRequest ( {
        method:     "GET",
        url:        baseURL + playerPositions[J],
        context:    playerPositions[J],
        onload:     parseResponse,
        onerror:    function (e) { console.error ('**** error ', e); },
        onabort:    function (e) { console.error ('**** abort ', e); },
        ontimeout:  function (e) { console.error ('**** timeout ', e); }
    } );
}
function parseResponse (response) {
    var playerPosition  = response.context;
    var parser          = new DOMParser ();
    var ajaxDoc         = parser.parseFromString (response.responseText, "text/html");
    var statRows        = ajaxDoc.querySelectorAll ("#statTable0 > tbody > tr");
    var newStatTable    = $(statRows).map ( function () {
        var tblRow          = $(this);
        var teamRank        = parseInt (tblRow.find (".rank-indicator").text().trim(), 10);
        var teamName        = tblRow.find ("td:eq(1)").text().trim().split(" vs")[0];

        return [ [teamName, teamRank] ];
    } ).get ();

    numPagesFetched++;
    console.log ('Fetched page ' + numPagesFetched + ' of ' + numPositions + '.');

    /*--- Now loop over the fetched rows and collate them into the master table, depending
          on playerPosition.
    */
    var columnIdx       = playerPositions.indexOf (playerPosition);

    for (var K in newStatTable) {
        var teamName        = newStatTable[K][0];
        var teamRank        = newStatTable[K][1];
        var teamStats       = rankingsTable[teamName]  ||  new Array (numPositions);

        teamStats[columnIdx]    = teamRank;
        rankingsTable[teamName] = teamStats;
    }

    if (numPagesFetched === numPositions) {
        displayFinalResult ();
    }
}

function displayFinalResult () {
    var sortedTeamNames = Object.keys (rankingsTable).sort ( function (zA, zB) {
        return zA.localeCompare (zB);
    } );

    const namePadStr    = new Array (25).join (' ');
    console.log (
        'Team                       Ranks QB, WR, RB, TE, K, DEF\n' +
        '------------------------   ------------------------------'
    );
    for (var J in sortedTeamNames) {
        var teamName    = sortedTeamNames[J];
        if (rankingsTable.hasOwnProperty (teamName) ) {
            console.log (
                (teamName + namePadStr).slice (0, 24) + '  ', rankingsTable[teamName]
            );
        }
    }
}

Мы собираемся получить большую часть вашего выигрыша в FF, верно? ;)

person Brock Adams    schedule 02.09.2015
comment
Вы оказали огромную помощь этому проекту. У меня есть последний вопрос. И чтобы ответить на ваш вопрос, если я выиграю, я пришлю вам нарезку :) - person Bijan; 02.09.2015