Программная очистка заголовка ответа в R

Я пытаюсь получить доступ к выделенному тексту response header: location на снимке экрана ниже, используя только R и его библиотеки веб-скрейпинга на основе curl. к этому можно легко перейти в любом веб-браузере, посетив http://www.worldvaluessurvey.org/WVSDocumentationWVL.jsp, нажав на загрузку любого из файлов данных и заполнив форму соглашения. Загрузка начинается автоматически в веб-браузере.

введите здесь описание изображения

Я считаю, что единственный способ получить действительный файл cookie — это library(curlconverter) (см. Как загрузить файл за полусломанной функцией javascript asp с помощью R), но этого ответа недостаточно для программного определения URL-адреса http файла, только чтобы загрузить сжатый файл, как только он уже известен.

Я вставил некоторый код ниже с другим кодом httr и curlconverter, с которым я играл, но здесь я что-то упустил. Опять же, единственная цель — программно определить выделенный текст полностью в R (кросс-платформенный).

library(curlconverter)
library(httr)

browserPOST <-
    "curl 'http://www.worldvaluessurvey.org/AJDownload.jsp'
    -H 'Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
    -H 'Accept-Encoding:gzip, deflate'
    -H 'Accept-Language:en-US,en;q=0.8'
    -H 'Cache-Control:max-age=0'
    --compressed -H 'Connection:keep-alive'
    -H 'Content-Length:188'
    -H 'Content-Type:application/x-www-form-urlencoded'
    -H 'Cookie:ASPSESSIONIDCASQAACD=IBLGBFOAEHFILMMJJCFEOEMI; JSESSIONID=50DABDEDD0B2FC370C415B4BD1855260; __atuvc=13%7C45; __atuvs=58224f37d312c42400c'
    -H 'Host:www.worldvaluessurvey.org'
    -H 'Origin:http://www.worldvaluessurvey.org'
    -H 'Referer:http://www.worldvaluessurvey.org/AJDownloadLicense.jsp'
    -H 'Upgrade-Insecure-Requests:1'
    -H 'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36'"

form_data <-
    list( 
        ulthost = "WVS" ,
        CMSID = "" ,
        LITITLE = "" ,
        LINOMBRE = "fas" ,
        LIEMPRESA = "asf" ,
        LIEMAIL = "asdf" ,
        LIPROJECT = "asfd" ,
        LIUSE = "1" ,
        LIPURPOSE = "asdf" ,
        LIAGREE = "1" ,
        DOID = "3996" ,
        CndWAVE = "-1" ,
        SAID = "-1" ,
        AJArchive = "WVS Data Archive" ,
        EdFunction = "" ,
        DOP = "" 
    )   



getDATA <- (straighten(browserPOST) %>% make_req)[[1]]()

a <- VERB(verb = "POST", url = "http://www.worldvaluessurvey.org/AJDownload.jsp", 
    httr::add_headers(Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 
        `Accept-Encoding` = "gzip, deflate", `Accept-Language` = "en-US,en;q=0.8", 
        `Cache-Control` = "max-age=0", Connection = "keep-alive", 
        `Content-Length` = "188", Host = "www.worldvaluessurvey.org", 
        Origin = "http://www.worldvaluessurvey.org", Referer = "http://www.worldvaluessurvey.org/AJDownloadLicense.jsp", 
        `Upgrade-Insecure-Requests` = "1", `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36"), 
    httr::set_cookies(`Cookie:ASPSESSIONIDCASQAACD` = "IBLGBFOAEHFILMMJJCFEOEMI", 
        JSESSIONID = "50DABDEDD0B2FC370C415B4BD1855260", `__atuvc` = "13%7C45", 
        `__atuvs` = "58224f37d312c42400c"), encode = "form",body=form_data)

person Anthony Damico    schedule 08.11.2016    source источник
comment
Я добавил к вашему вопросу заглавные буквы и знаки препинания. Пожалуйста, подумайте о том, чтобы сделать это самостоятельно в будущем, поскольку мы стараемся поддерживать высокие стандарты качества для десятков и тысяч людей, которые могут читать это с течением времени.   -  person Hack-R    schedule 09.11.2016
comment
Одна проблема здесь заключается в том, что ссылки встроены в iframe, который встроен в другой iframe. Собрать их, мягко говоря, непросто.   -  person yeedle    schedule 23.11.2016
comment
Голосование как неясное согласно stackoverflow.com/questions/40498277/   -  person ivan_pozdeev    schedule 25.11.2016
comment
Попробуйте перефразировать вопрос, если вы действительно хотите получить желаемый ответ. Невозможно ответить на вопрос, задав черт знает что, а телепатов здесь нет.   -  person ivan_pozdeev    schedule 26.11.2016
comment
@Энтони - Просто убедись, что я понимаю, чего ты хочешь. Вы хотите создать сценарий R для загрузки файлов, чтобы пользователю не приходилось вводить регистрационные данные вручную? Верный? Если это ваша цель, вы можете сделать это с помощью пакета RSelenium. (Безголовый браузер)   -  person Technophobe01    schedule 01.12.2016
comment
@ Technophobe01 нет, я просто хочу полностью захватить выделенный URL-адрес с помощью R (кросс-платформенный и без внешних установок). связанный вопрос SO уже загружается правильно, если URL-адрес известен. спасибо   -  person Anthony Damico    schedule 01.12.2016


Ответы (2)


Это был хороший вызов!

Проблема не связана с языком R. У нас будет тот же результат на любом языке, если мы просто попытаемся опубликовать некоторые данные в скрипте загрузки. Здесь мы должны иметь дело с неким «шаблоном» безопасности. Сайт запрещает пользователям извлекать URL-адреса файлов и просит их заполнять формы данными для предоставления этих ссылок. Если браузер может получить эти ссылки, то и мы сможем, написав соответствующие HTTP-вызовы. Дело в том, что нам нужно точно знать, какие звонки мы должны сделать. Чтобы найти это, нам нужно увидеть отдельные звонки, которые сайт делает каждый раз, когда кто-то нажимает, чтобы загрузить. Вот что я обнаружил за несколько вызовов до успешного 302 AJDownload.jsp POST вызова:

HTTP-запросы

Мы можем это ясно увидеть, если посмотрим на исходный код AJDocumentation.jsp, он делает эти вызовы, используя jQuery $.get:

$.get("http://ipinfo.io?token=xxxxxxxxxxxxxx", function (response) {
    var geodatos=encodeURIComponent(response.ip+"\t"+response.country+"\t"+response.postal+"\t"+
    response.loc+"\t"+response.region+"\t"+response.city+"\t"+
    response.org);

    $.get("jdsStatJD.jsp?ID="+geodatos+
        "&url=http%3A%2F%2Fwww.worldvaluessurvey.org%2FAJDocumentation.jsp&referer=null&cms=Documentation",
        function (resp2) {
    });
}, "jsonp");

Затем, несколькими вызовами ниже, мы можем увидеть успешный POST /AJDownload.jsp со статусом 302 Moved Temporarily и разыскиваемым Location в заголовках ответов:

HTTP-запросы

HTTP/1.1 302 Moved Temporarily
Content-Length: 0
Content-Type: text/html
Location: http://www.worldvaluessurvey.org/wvsdc/CO00001/F00003724-WVS_Longitudinal_1981-2014_stata_dta_v_2015_04_18.zip
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Thu, 01 Dec 2016 16:24:37 GMT

Итак, это механизм безопасности этого сайта. Он использует ipinfo.io для хранения информации о посетителях об их IP-адресе, местонахождении и даже организации интернет-провайдера непосредственно перед тем, как пользователь собирается чтобы начать загрузку, нажав на ссылку. Скрипт, который получает эти данные, называется /jdsStatJD.jsp. Я не использовал ни ipinfo.io, ни их API-ключ для этой службы (он скрыт на моих скриншотах), а вместо этого создал фиктивную валидную последовательность данных только для проверки запроса. Данные почтовой формы для «защищенных» файлов вообще не требуются. Файлы можно скачивать без публикации этих данных.

Также не требуется библиотека curlconverter. Все, что нам нужно сделать, это простые запросы GET и POST с использованием библиотеки httr. Я хочу отметить одну важную часть: чтобы функция httr POST не следовала за заголовком Location, полученным со статусом 302 при нашем последнем вызове, нам нужно использовать параметр конфигурации config(followlocation = FALSE), который, конечно же, не позволит ей следовать за Location. и давайте извлечем Location из заголовков.

ВЫВОД

Мой сценарий R можно запустить из командной строки, и он может принимать DOID числовых значений параметров для получения необходимого файла. Например, если мы хотим получить ссылку на файл WVS_Longitudinal_1981-2014_stata_dta_v_2015_04_18, то мы должны добавить его DOID (что равно 3724) в конец нашего скрипта при вызове его с помощью команды Rscript:

Rscript wvs_fetch_downloads.r 3724
[1] "http://www.worldvaluessurvey.org/wvsdc/CO00001/F00003724-WVS_Longitudinal_1981-2014_stata_dta_v_2015_04_18.zip"

Я создал функцию R, чтобы получить каждое местоположение файла, которое вы хотите, просто передав DOID:

getFileById <- function(fileId)

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

#args <- commandArgs(TRUE)
#if(length(args) == 0) {
#   print("No file id specified. Use './script.r ####'.")
#   quit("no")
#}

#fileId <- args[1]
fileId <- "3724"

# DOID=3843 : WVS_EVS_Integrated_Dictionary_Codebook v_2014_09_22 (Excel)
# DOID=3844 : WVS_Values Surveys Integrated Dictionary_TimeSeries_v_2014-04-25 (Excel)
# DOID=3725 : WVS_Longitudinal_1981-2014_rdata_v_2015_04_18
# DOID=3996 : WVS_Longitudinal_1981-2014_sas_v_2015_04_18
# DOID=3723 : WVS_Longitudinal_1981-2014_spss_v_2015_04_18
# DOID=3724 : WVS_Longitudinal_1981-2014_stata_dta_v_2015_04_18

getFileById(fileId)

Окончательный вариант рабочего сценария R

library(httr)

getFileById <- function(fileId) {
    response <- GET(
        url = "http://www.worldvaluessurvey.org/AJDocumentation.jsp?CndWAVE=-1", 
        add_headers(
            `Accept` = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 
            `Accept-Encoding` = "gzip, deflate",
            `Accept-Language` = "en-US,en;q=0.8", 
            `Cache-Control` = "max-age=0",
            `Connection` = "keep-alive", 
            `Host` = "www.worldvaluessurvey.org", 
            `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0",
            `Content-type` = "application/x-www-form-urlencoded",
            `Referer` = "http://www.worldvaluessurvey.org/AJDownloadLicense.jsp", 
            `Upgrade-Insecure-Requests` = "1"))

    set_cookie <- headers(response)$`set-cookie`
    cookies <- strsplit(set_cookie, ';')
    cookie <- cookies[[1]][1]

    response <- GET(
        url = "http://www.worldvaluessurvey.org/jdsStatJD.jsp?ID=2.72.48.149%09IT%09undefined%0941.8902%2C12.4923%09Lazio%09Roma%09Orange%20SA%20Telecommunications%20Corporation&url=http%3A%2F%2Fwww.worldvaluessurvey.org%2FAJDocumentation.jsp&referer=null&cms=Documentation", 
        add_headers(
            `Accept` = "*/*", 
            `Accept-Encoding` = "gzip, deflate",
            `Accept-Language` = "en-US,en;q=0.8", 
            `Cache-Control` = "max-age=0",
            `Connection` = "keep-alive", 
            `X-Requested-With` = "XMLHttpRequest",
            `Host` = "www.worldvaluessurvey.org", 
            `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0",
            `Content-type` = "application/x-www-form-urlencoded",
            `Referer` = "http://www.worldvaluessurvey.org/AJDocumentation.jsp?CndWAVE=-1",
            `Cookie` = cookie))

    post_data <- list( 
        ulthost = "WVS",
        CMSID = "",
        CndWAVE = "-1",
        SAID = "-1",
        DOID = fileId,
        AJArchive = "WVS Data Archive",
        EdFunction = "",
        DOP = "",
        PUB = "")  

    response <- POST(
        url = "http://www.worldvaluessurvey.org/AJDownload.jsp", 
        config(followlocation = FALSE),
        add_headers(
            `Accept` = "*/*", 
            `Accept-Encoding` = "gzip, deflate",
            `Accept-Language` = "en-US,en;q=0.8", 
            `Cache-Control` = "max-age=0",
            `Connection` = "keep-alive",
            `Host` = "www.worldvaluessurvey.org",
            `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0",
            `Content-type` = "application/x-www-form-urlencoded",
            `Referer` = "http://www.worldvaluessurvey.org/AJDocumentation.jsp?CndWAVE=-1",
            `Cookie` = cookie),
        body = post_data,
        encode = "form")

    location <- headers(response)$location
    location
}

args <- commandArgs(TRUE)
if(length(args) == 0) {
    print("No file id specified. Use './script.r ####'.")
    quit("no")
}

fileId <- args[1]

# DOID=3843 : WVS_EVS_Integrated_Dictionary_Codebook v_2014_09_22 (Excel)
# DOID=3844 : WVS_Values Surveys Integrated Dictionary_TimeSeries_v_2014-04-25 (Excel)
# DOID=3725 : WVS_Longitudinal_1981-2014_rdata_v_2015_04_18
# DOID=3996 : WVS_Longitudinal_1981-2014_sas_v_2015_04_18
# DOID=3723 : WVS_Longitudinal_1981-2014_spss_v_2015_04_18
# DOID=3724 : WVS_Longitudinal_1981-2014_stata_dta_v_2015_04_18

getFileById(fileId)
person Christos Lytras    schedule 02.12.2016
comment
Что ж, вы сделали это - на этот раз. В любом другом случае — и каждый раз, когда что-то меняется на сайте — это нужно будет делать снова и снова, и это будет такой же вызов — каждый раз возвращаться к чертежной доске. Это действительно задача для безголового браузера. P.S. Теперь я иронически вижу это на боковой панели: security.stackexchange.com/questions/144155/ - person ivan_pozdeev; 02.12.2016
comment
@ivan_pozdeev Вы здесь открываете большую тему про безопасность. Вопрос ОП касается конкретного веб-приложения и некоторых конкретных файлов. Конечно, когда приложение будет обновлено с помощью самого надежного шаблона безопасности, этот код выборки сломается. Разработчики могли бы использовать даже сервис капчи, чтобы защитить свои формы от парсинга ботами, как здесь. Я просто перевернул HTTP-запросы, чтобы узнать, что нужно отправить на сервер, чтобы вернуть желаемый результат, то есть ответ 302 с запрошенным файлом. Это то, что ищет ОП. - person Christos Lytras; 02.12.2016
comment
@ivan_pozdeev Интересно, как безголовый браузер может успешно продолжать получать такой запрос, если он изменит свой шаблон и даже если разработчики добавят защиту, такую ​​​​как капча. Я не думаю, что это возможно, но не могли бы вы предоставить некоторые ресурсы с такими примерами и тем, как безголовый браузер может продолжать работу и успешно возвращать желаемый результат после полного или частичного изменения метода безопасности? Я мало что знаю об безголовом браузере и хотел бы углубиться в эту тему. - person Christos Lytras; 02.12.2016
comment
Я не говорю, что безголовый браузер автоматически адаптируется к изменениям. Я говорю, что к ним будет намного проще адаптироваться, потому что вам не нужно эмулировать логику Javascript и вы можете скопировать все необходимые xpaths прямо из обычного браузера. Конечно, ваше решение прекрасно подходит для разовой задачи — или если предполагается, что изменения происходят редко и/или усилия по их обнаружению и отслеживанию допустимы. - person ivan_pozdeev; 02.12.2016
comment
Итак, безголовый браузер делает что-то вроде синтаксического анализа ответов, манипулирования javascript dom и вызовов XMLHttpRequest для каждого тела ответа HTTP, а затем у него есть результат документа со всеми изменениями, вызванными javascript, и дополнительными вызовами, я прав? - person Christos Lytras; 02.12.2016
comment
@ivan_pozdeev Я вижу, ты прав. При использовании безголового браузера наиболее вероятно, что скрипт продолжит работать после некоторых изменений/дополнений. Например, разработчики добавляют вызовы в дополнительные запросы проверки. Этот код здесь не работает. Но с безголовым браузером он выполнит все согласно исходникам и скрипт продолжит работать! Я не очень увлекался безголовыми браузерами, даже если бы знал о них кое-что. Я постараюсь найти решение этой проблемы для безголового браузера здесь и вернусь с обновлением! - person Christos Lytras; 02.12.2016
comment
Это также о легкости. Мне удалось быстро собрать селеновый скрипт для управления браузером, чтобы получить этот URL. Но мне никак не удавалось получить его с помощью curl. Был iframe, потом после перехода по ссылке вложенный iframe, для которого был добавлен исходник с js. Затем было действие формы, также добавленное с помощью js. Затем промежуточная страница jsp, которая использовала файл ipinfo.io. Я должен похвалить твое достижение, @ChristosLytras. +1 - person Ivan Chaer; 02.12.2016
comment
привет, спасибо, что сделали это возможным github.com/ajdamico/asdfree/issues/130 извините, что не рассмотрел вашу работу вовремя, чтобы присудить первую награду, я дам вам 300+. большое спасибо - person Anthony Damico; 04.12.2016
comment
@AnthonyDamico, пожалуйста. Спасибо за дополнительную награду, которую вы предлагаете. Мне очень нравится реверсировать и решать такие проблемы. - person Christos Lytras; 05.12.2016

Согласно основному httr::request_perform, объект, который вы получаете от VERB(), выглядит так:

res <- response(
  url = resp$url,
  status_code = resp$status_code,
  headers = headers,
  all_headers = all_headers,
  cookies = curl::handle_cookies(handle),
  content = resp$content,
  date = date,
  times = resp$times,
  request = req,
  handle = handle
)

Итак, вас интересует его headers или all_headers (response равно а структура). Если было задействовано перенаправление, all_headers будет иметь несколько наборов заголовков, возвращенных curl::parse_headers(), headers всегда являются последним набором.

person ivan_pozdeev    schedule 24.11.2016
comment
привет, извините, я голосую против, потому что объекты a$headers и a$all_headers из моего примера в вопросе не решают заявленную проблему - person Anthony Damico; 25.11.2016
comment
@AnthonyDamico заявленная проблема заключается в том, чтобы программно очистить заголовок ответа в R, и он это делает. Если реальная проблема состоит в том, чтобы очистить для вас конкретную страницу (т. е. определить, какой набор запросов необходим для программного доступа к файлу на этой конкретной странице), это совершенно другая проблема, чем заявленная. - person ivan_pozdeev; 25.11.2016
comment
@AnthonyDamico, лично я считаю, что в SO очень часто используются голоса против. Вы задали вопрос, человек постарался помочь. Если вы просто объясните в комментарии, почему этот ответ не решил проблему, в большинстве случаев вы просто получите дополнительную помощь. Вы видите, как это может быть воспринято как неуважение к усилиям, которые были вложены в попытку помочь. - person Ivan Chaer; 01.12.2016
comment
@IvanChaer Лично я считаю, что, наоборот, система слишком сильно препятствует их использованию там, где они должны (в магнитах с плохими ответами), поэтому я не держу здесь никаких обид. Текст при наведении на отрицательный голос гласит: этот ответ бесполезен. Если мой ответ оказался бесполезен для представителя целевой аудитории, это не хорошо и не плохо, это факт жизни. Если он действительно так хорош, как я написал, в конечном итоге он получит одобрение — заголовок вопроса удобен для Google, а ответ точно соответствует тому, что он говорит. - person ivan_pozdeev; 01.12.2016
comment
@IvanChaer Что касается вашего предположения о неуважении ... вам не кажется, что если бы разработчики систем чувствовали то же самое, они бы вообще не вводили отрицательные голоса? - person ivan_pozdeev; 01.12.2016
comment
Когда есть более одного вопроса, конечно, чтобы различать. Разумно, я думаю, так как это может иногда привлекать негатив. Видимо было не так. ;) - person Ivan Chaer; 01.12.2016