Библиотека запросов не может POST правильно, но urllib успешно [python]

Я пытаюсь отправить запрос к веб-сервисам RSCB PDB, как описано здесь.

Я установил URL-адрес и запрос в формате XML:

import urllib.request as urllib
import requests

url = "http://www.rcsb.org/pdb/rest/search"

queryText = """
<?xml version="1.0" encoding="UTF-8"?>
<orgPdbQuery>
<version>B0907</version>
<queryType>org.pdb.query.simple.ExpTypeQuery</queryType>
<description>Experimental Method Search: Experimental Method=SOLID-STATE NMR</description>
<mvStructure.expMethod.value>SOLID-STATE NMR</mvStructure.expMethod.value>
</orgPdbQuery>
"""

Затем я определяю два возможных способа размещения этих данных:

def query_old_fashioned(url, query_xml):
    req = urllib.Request(url, data=query_xml.encode())
    f = urllib.urlopen(req)
    result = f.read()
    return result.decode()


def query_with_requests(url, query_xml):
    response = requests.post(url, data=query_xml.encode())
    return response.text

# result = query_old_fashioned(url, queryText)
# result = query_with_requests(url, queryText)

С первой функцией, используя старый добрый urllib.request, я получаю правильный результат - список из 4-х символьных строк.

Со второй функцией, которая, насколько я могу судить, делает то же самое, я получаю сообщение об ошибке JSP, возвращаемое HTML. Это сообщение об ошибке при отображении в браузере:

type Exception report

message

description The server encountered an internal error that prevented it from fulfilling this request.

exception

java.lang.NullPointerException
    java.util.StringTokenizer.<init>(StringTokenizer.java:199)
    java.util.StringTokenizer.<init>(StringTokenizer.java:221)
    org.rcsb.servlet.RestfulServiceServlet.doPost(RestfulServiceServlet.java:1371)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:86)
    org.pdb.util.web.OutOfServiceFilter.doFilter(OutOfServiceFilter.java:91)
    org.pdb.util.web.DOSFilter.doFilter(DOSFilter.java:158)
    org.pdb.util.web.AntiRobotFilter.doFilter(AntiRobotFilter.java:29)
    org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:176)
    org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145)
    org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92)
    org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:394)
note The full stack trace of the root cause is available in the Apache Tomcat/7.0.61 logs.

Я немного знаю JSP, но я не смог найти причину сбоя POST в этом сообщении об ошибке, и мне не ясно, почему запросы не выполняются, а urllib стандартной библиотеки успешно выполняется. Я даже пытался просмотреть исходный код библиотеки запросов на github, пытаясь выяснить, как именно запросы создают свои запросы, но мне это не удалось.

Это использует Python 3. Я впервые столкнулся с этой проблемой, используя Ubuntu, и с тех пор воспроизвел ее на Windows 10.

Любая помощь могла бы быть полезна.

2 ответа

Решение

Мне удалось это исправить.

Я проверил отправляемые HTTP-запросы и увидел, что запросы отправляют это:

POST /pdb/rest/search HTTP/1.1
Host: www.rcsb.org
User-Agent: python-requests/2.8.1
Connection: keep-alive
Accept: */*
Content-Length: 316
Accept-Encoding: gzip, deflate


<?xml version="1.0" encoding="UTF-8"?>
<orgPdbQuery>
<version>B0907</version>
<queryType>org.pdb.query.simple.ExpTypeQuery</queryType>
<description>Experimental Method Search: Experimental Method=SOLID-STATE NMR</de
scription>
<mvStructure.expMethod.value>SOLID-STATE NMR</mvStructure.expMethod.value>
</orgPdbQuery>

... и Урлиб отправлял это...

POST /pdb/rest/search HTTP/1.1
Accept-Encoding: identity
Content-Type: application/x-www-form-urlencoded
Content-Length: 316
User-Agent: Python-urllib/3.4
Connection: close
Host: www.rcsb.org


<?xml version="1.0" encoding="UTF-8"?>
<orgPdbQuery>
<version>B0907</version>
<queryType>org.pdb.query.simple.ExpTypeQuery</queryType>
<description>Experimental Method Search: Experimental Method=SOLID-STATE NMR</de
scription>
<mvStructure.expMethod.value>SOLID-STATE NMR</mvStructure.expMethod.value>
</orgPdbQuery>

Существует несколько разных заголовков, и, поиграв с ними, я обнаружил, что именно заголовок Content-Type необходим в запросе запросов.

Теперь работает следующее:

response = requests.post(
  url,
  data=query_xml.encode(),
  headers={'Content-Type': 'application/x-www-form-urlencoded'}
)

Спасибо Филиппу за запуск моего оригинального кода и проверку того, что это технически возможно. Я подозреваю, что у него другая версия запросов, чем у меня.

На моей машине с Ubuntu все работает нормально.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import requests
import urllib.request as urllib


def query_old_fashioned(url, query_xml):
    req = urllib.Request(url, data=query_xml.encode())
    f = urllib.urlopen(req)
    result = f.read()
    return result.decode()


def query_with_requests(url, query_xml):
    response = requests.post(url, data=query_xml.encode())
    return response.text


def test():
    url = "http://www.rcsb.org/pdb/rest/search"

    query = """
<?xml version="1.0" encoding="UTF-8"?>
<orgPdbQuery>
<version>B0907</version>
<queryType>org.pdb.query.simple.ExpTypeQuery</queryType>
<description>Experimental Method Search: Experimental Method=SOLID-STATE NMR</description>
<mvStructure.expMethod.value>SOLID-STATE NMR</mvStructure.expMethod.value>
</orgPdbQuery>"""

    print(query_old_fashioned(url, query))
    print(query_with_requests(url, query))

if __name__ == '__main__':
    test()

    print("done")

Оба распечатывают одинаково. Какую точную версию Python вы используете? Я использую Python 3.4.3 на Ubuntu 14.03

Другие вопросы по тегам