Fazendo uma solicitação para uma API RESTful usando python

221

Eu tenho uma API RESTful que eu expus usando uma implementação do Elasticsearch em uma instância do EC2 para indexar um corpus de conteúdo. Eu posso consultar a pesquisa executando o seguinte no meu terminal (MacOSX):

curl -XGET 'http://ES_search_demo.com/document/record/_search?pretty=true' -d '{
  "query": {
    "bool": {
      "must": [
        {
          "text": {
            "record.document": "SOME_JOURNAL"
          }
        },
        {
          "text": {
            "record.articleTitle": "farmers"
          }
        }
      ],
      "must_not": [],
      "should": []
    }
  },
  "from": 0,
  "size": 50,
  "sort": [],
  "facets": {}
}'

Como transformar acima em uma solicitação de API usando python/requestsou python/urllib2(não tenho certeza qual deles escolher - está usando urllib2, mas ouço que as solicitações são melhores ...)? Eu passo como cabeçalho ou não?

user7289
fonte

Respostas:

340

Usando pedidos :

import requests
url = 'http://ES_search_demo.com/document/record/_search?pretty=true'
data = '''{
  "query": {
    "bool": {
      "must": [
        {
          "text": {
            "record.document": "SOME_JOURNAL"
          }
        },
        {
          "text": {
            "record.articleTitle": "farmers"
          }
        }
      ],
      "must_not": [],
      "should": []
    }
  },
  "from": 0,
  "size": 50,
  "sort": [],
  "facets": {}
}'''
response = requests.post(url, data=data)

Dependendo do tipo de resposta que sua API retorna, você provavelmente desejará examinar response.textou response.json()(ou possivelmente inspecionar response.status_codeprimeiro). Veja os documentos de início rápido aqui , especialmente esta seção .

andersschuller
fonte
3
eu acho, deve ser: Resposta = requests.post (url, data = Dados)
CK.Nguyen
8
"orders.get" não aceita o parâmetro "data". Pode levar o parâmetro "params" opcional, que geralmente é um dict que carrega uma string de consulta. Se for necessária uma carga útil para buscar dados (como o exemplo postado em questão), será necessário usar "orders.post". Além disso, o uso da biblioteca "json" facilita a análise da resposta do json.
HVS
4
@ParveenShukhala "Os pedidos suportam oficialmente o Python 2.6-2.7 e 3.3-3.5 e funcionam muito bem no PyPy." # pypi.python.org/pypi/requests
danio
2
Como é JSON que você está enviando, você pode usar o parâmetro JSON em vez de dados assim: resposta = requests.post (url, json = dados)
Mark Chorley
101

Usando pedidos e JSON torna simples.

  1. Ligue para a API
  2. Supondo que a API retorne um JSON, analise o objeto JSON em um ditado Python usando a json.loadsfunção
  3. Faça um loop pelo dict para extrair informações.

O módulo Solicitações fornece uma função útil para repetir para obter sucesso e fracasso.

if(Response.ok): ajudará a determinar se sua chamada à API foi bem-sucedida (Código de resposta - 200)

Response.raise_for_status() ajudará você a buscar o código http retornado da API.

Abaixo está um código de exemplo para fazer essas chamadas de API. Também pode ser encontrado no github . O código pressupõe que a API faz uso da autenticação Digest. Você pode pular isso ou usar outros módulos de autenticação apropriados para autenticar o cliente que está chamando a API.

#Python 2.7.6
#RestfulClient.py

import requests
from requests.auth import HTTPDigestAuth
import json

# Replace with the correct URL
url = "http://api_url"

# It is a good practice not to hardcode the credentials. So ask the user to enter credentials at runtime
myResponse = requests.get(url,auth=HTTPDigestAuth(raw_input("username: "), raw_input("Password: ")), verify=True)
#print (myResponse.status_code)

# For successful API call, response code will be 200 (OK)
if(myResponse.ok):

    # Loading the response data into a dict variable
    # json.loads takes in only binary or string variables so using content to fetch binary content
    # Loads (Load String) takes a Json file and converts into python data structure (dict or list, depending on JSON)
    jData = json.loads(myResponse.content)

    print("The response contains {0} properties".format(len(jData)))
    print("\n")
    for key in jData:
        print key + " : " + jData[key]
else:
  # If response code is not ok (200), print the resulting http error code with description
    myResponse.raise_for_status()
HVS
fonte
2
A última parte com iteração sobre chaves nem sempre funciona porque o documento JSON pode ter matriz como um elemento de nível superior. Então, seria um erro para tentar obterjData[key]
Denis The Menace
@DenisTheMenace se for um array, como eu daria um loop nele?
qasimalbaqali
@qasimalbaqali da mesma maneira que você faz um loop no dicionário. Mas os elementos de matriz será simplesmente jData, nãojData[key]
Denis The Menace
Nota: Se a sua API retornar uma resposta JSON grande, você poderá imprimi-la da seguinte maneira: print(json.dumps(jData, indent=4, sort_keys=True))
Marco
2
Sob python3, foi citado o seguinte 'JSON deve ser str, não bytes'. Isso é corrigido decodificando a saída, ou seja, json.loads (myResponse.content.decode ('utf-8')). Além disso, você deve envolver a chave e a chave jData com str () para que, quando a API RESTful retorne números inteiros, ela não se queixe.
Mirkules 8/18
11

Então, se você deseja transmitir dados no corpo de uma solicitação GET, seria melhor fazê-lo na chamada POST. Você pode conseguir isso usando as duas solicitações.

Solicitação bruta

GET http://ES_search_demo.com/document/record/_search?pretty=true HTTP/1.1
Host: ES_search_demo.com
Content-Length: 183
User-Agent: python-requests/2.9.0
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip, deflate

{
  "query": {
    "bool": {
      "must": [
        {
          "text": {
            "record.document": "SOME_JOURNAL"
          }
        },
        {
          "text": {
            "record.articleTitle": "farmers"
          }
        }
      ],
      "must_not": [],
      "should": []
    }
  },
  "from": 0,
  "size": 50,
  "sort": [],
  "facets": {}
}

Amostra de chamada com solicitações

import requests

def consumeGETRequestSync():
data = '{
  "query": {
    "bool": {
      "must": [
        {
          "text": {
            "record.document": "SOME_JOURNAL"
          }
        },
        {
          "text": {
            "record.articleTitle": "farmers"
          }
        }
      ],
      "must_not": [],
      "should": []
    }
  },
  "from": 0,
  "size": 50,
  "sort": [],
  "facets": {}
}'
url = 'http://ES_search_demo.com/document/record/_search?pretty=true'
headers = {"Accept": "application/json"}
# call get service with headers and params
response = requests.get(url,data = data)
print "code:"+ str(response.status_code)
print "******************"
print "headers:"+ str(response.headers)
print "******************"
print "content:"+ str(response.text)

consumeGETRequestSync()
gvir
fonte
Tenho um link morto lá
user3157940
4
cabeçalhos variável deve ser usado: requests.get (... headers = cabeçalhos, ....)
Markus Meyer
9

Abaixo está o programa para executar a restante API em python-

import requests
url = 'https://url'
data = '{  "platform": {    "login": {      "userName": "name",      "password": "pwd"    }  } }'
response = requests.post(url, data=data,headers={"Content-Type": "application/json"})
print(response)
sid=response.json()['platform']['login']['sessionId']   //to extract the detail from response
print(response.text)
print(sid)
Shashank G
fonte