切換語言為:簡體

如何將 urllib2 升級至 pycurl

  • 爱糖宝
  • 2024-11-13
  • 2025
  • 0
  • 0

在 Python 中,urllib2 是一個用於處理 HTTP 請求的模組,但它在 Python 3 中被拆分成 urllib.requesturllib.error。相比之下,pycurl 提供了更高效的 HTTP 傳輸方式,因為它直接基於 libcurl,可以支援更多協議和更好的效能。因此,有時需要將 urllib2 的程式碼遷移到 pycurl

1、問題背景

我有一段程式碼(如下所示),它使用 urllib2 庫。我想將其轉換為 pycurl 庫,以便受益於 pycurl 的代理支援。pycurl 的轉換程式碼在原始程式碼之後。我想知道如何將 urllib.urlopen(req).read() 更改為 pycurl 中類似的方法,也許可以使用 StringIO 之類的方法?

urllib2 程式碼:

URL = 'URL'
UN = 'UN'
PWD = 'PWD'
HEADERS = { 'Accept': 'application/json',
            'Connection': 'Keep-Alive',
            'Accept-Encoding' : 'gzip',
            'Authorization' : 'Basic %s' % base64.encodestring('%s:%s' % (UN, PWD))  }
req = urllib2.Request(URL, headers=HEADERS)
response = urllib2.urlopen(req, timeout=(KEEP_ALIVE))
# header -  print response.info()
decompressor = zlib.decompressobj(16+zlib.MAX_WBITS)
remainder = ''
while True:
    tmp = decompressor.decompress(response.read(CHUNKSIZE))

pycurl 轉換程式碼(帶有代理支援):

URL = 'URL'
UN = 'UN'
PWD = 'PWD'
HEADERS = [ 'Accept : application/json',
            'Connection : Keep-Alive',
            'Accept-Encoding : gzip',
            'Authorization : Basic %s' % base64.encodestring('%s:%s' % (UN, PWD))  ]
req = pycurl.Curl()
req.setopt(pycurl.CONNECTTIMEOUT,KEEP_ALIVE)
req.setopt(pycurl.HTTPHEADER, HEADERS)
req.setopt(pycurl.TIMEOUT, 1+KEEP_ALIVE)
req.setopt(pycurl.PROXY, 'http://my-proxy')
req.setopt(pycurl.PROXYPORT, 8080)
req.setopt(pycurl.PROXYUSERPWD, "proxy_access_user : proxy_access_password")
req.setopt(pycurl.URL , URL)
response = req.perform()
decompressor = zlib.decompressobj(16+zlib.MAX_WBITS)
remainder = ''
while True:
    tmp = decompressor.decompress(urllib2.urlopen(req).read(CHUNKSIZE))

2、解決方案

與 urllib2(它返回一個可用於獲取資料的物件)不同,curl 需要您傳遞一個它可以用來儲存資料的物件。 最簡單的方法(在大多數示例中使用)是將檔案物件作為 WRITEDATA 選項傳遞。您可能認為可以像這樣直接傳遞一個 StringIO:

s = StringIO.StringIO()
req.setopt(pycurl.WRITEDATA, s)
req.perform()
data = s.getvalue()

不幸的是,這不起作用,因為檔案物件必須是真實的檔案(或至少是具有 C 級檔案描述符的東西),而 StringIO 不符合要求。

當然,您可以使用 NamedTemporaryFile,但如果您希望將檔案儲存在記憶體中——或者更好地說是不用儲存在記憶體或磁碟中,而是直接處理它——那這幫不了您。

解決方法是改用 WRITEFUNCTION 選項:

s = StringIO.StringIO()
req.setopt(pycurl.WRITEFUNCTION, s.write)
req.perform()
data = s.getvalue()

如您所見,您可以根據需要使用 StringIO,實際上,這就是 pycurl 物件文件所做的,但這並不會比其他任何累積字串的方法簡單太多(例如,將它們放入列表然後執行 ''.join,甚至只是將它們連線到字串上)。

請注意,我連結的是 C 級 libcurl 文件,而不是 pycurl 文件,因為 pycurl 的文件基本上只是說“FOO 與 CURLOPT_FOO 做同樣的事情”(即使存在差異,例如您的 WRITEFUNCTION 不獲取大小,nmemb 和 userdata 引數)。

如果您想實時傳輸資料,該怎麼辦?只需使用一個累積並即時處理資料的 WRITEFUNCTION。您無需自己編寫迴圈,但 curl 將在內部迴圈並驅動程序。例如:

z = zlib.decompressobj()
s = []
def handle(chunk):
    s.append(z.decompress(chunk))
    return len(chunk)
req.setopt(pycurl.WRITEFUNCTION, handle)
req.perform()
s.append(z.flush())
data = ''.join(s)

curl 將在檢索到每塊資料時呼叫您的函式一次,因此整個迴圈都發生在 req.perform() 呼叫中。(它也可能會在最後再次呼叫它,但您得確保您的回撥函式能夠處理這種情況。我認為 z.decompress 可以,但您可能需要驗證一下。)

有一些方法可以限制每次寫入的大小,在中間中止下載,將標頭作為寫入的一部分而不是單獨獲取,等等,但通常您不需要接觸這些方法。

劃重點

  • pycurl 使用 Curl 物件來管理 HTTP 請求設定,setopt 方法來配置不同的引數。

  • pycurl 提供了更靈活的選項,比如自定義請求頭、超時設定、自動處理重定向等功能。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.