2016-04-21 23 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

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