2016-04-21 24 views
0

我正在尝试向RSCB PDB Web服务发布查询,如here所述。请求库无法正确POST,但urllib成功[python]

我设置的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错误信息。这是当在浏览器中显示的错误消息:我知道一点点JSP

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. 

,但还没没有能够收集到来自此错误信息这篇文章的失败的原因,也不是很清楚,我为什么请求失败,但标准库的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> 

...和urllib的被发送此...

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'} 
) 

感谢Philipp运行我的原代码和验证,这在技术上是可行的。我怀疑他有与我不同的请求版本。

+1

'application/x-www-form-urlencoded'是在那里发送的错误内容类型。服务器接受它的事实是奇怪的。你*应该*发送一个内容类型的'application/xml'或'text/xml'。 –

+0

我刚刚尝试了这两种方法,但都失败了 - 它不返回错误消息,它只是重定向到Web服务的RSCB文档。你说的对,虽然有点令人沮丧,因为他们有一天可能会改变它并破坏一切。 –

+0

虽然请记住,我在技术上发送原始字节而不是xml文本,因为我先编码它。 –

0

在我的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的确切版本是?我在Ubuntu上使用Python 3.4.3 14.03

+0

这真让人生气!我目前在Windows上运行Python 3.5。我的请求版本是2.8.1。我目前正在安装WireShark,以便我可以查看实际上离开我的机器的HTTP请求,并查看其中的差别。 –

+0

我解决了!看到我的答案。 –