Requêtes Python et sessions persistantes

120

J'utilise le module de requêtes (version 0.10.0 avec Python 2.5). J'ai trouvé comment soumettre des données à un formulaire de connexion sur un site Web et récupérer la clé de session, mais je ne vois pas de moyen évident d'utiliser cette clé de session dans les demandes ultérieures. Quelqu'un peut-il remplir les points de suspension dans le code ci-dessous ou suggérer une autre approche?

>>> import requests
>>> login_data =  {'formPosted':'1', 'login_email':'[email protected]', 'password':'pw'}
>>> r = requests.post('https://localhost/login.py', login_data)
>>> 
>>> r.text
u'You are being redirected <a href="profilePage?_ck=1349394964">here</a>'
>>> r.cookies
{'session_id_myapp': '127-0-0-1-825ff22a-6ed1-453b-aebc-5d3cf2987065'}
>>> 
>>> r2 = requests.get('https://localhost/profile_data.json', ...)
ChrisGuest
la source

Réponses:

210

Vous pouvez facilement créer une session persistante en utilisant:

s = requests.Session()

Après cela, continuez avec vos demandes comme vous le feriez:

s.post('https://localhost/login.py', login_data)
#logged in! cookies saved for future requests.
r2 = s.get('https://localhost/profile_data.json', ...)
#cookies sent automatically!
#do whatever, s will keep your cookies intact :)

Pour en savoir plus sur les sessions: https://requests.kennethreitz.org/en/master/user/advanced/#session-objects

Anuj Gupta
la source
4
Existe-t-il des moyens de sauvegarder la session elle-même entre les exécutions de script?
Gtx
10
Peut pickle.dump les cookies de session dans un fichier tel que pickle.dump (session.cookies._cookies, file) et pickle.load en session comme suit cookies = pickle.load (fichier) cj = requests.cookies.RequestsCookieJar () cj._cookies = cookies et session.cookies = cj
Cyril
et si j'implique un proxy?
brainLoop
1
Pour les demandes envoyées à localhost, il peut y avoir des problèmes de connexion et d'autres cookies renvoyés par le serveur Web, s'ils contiennent une valeur de propriété de domaine incorrecte. Pour localhost, le serveur Web doit renvoyer les cookies avec la propriété de domaine définie sur localhost.local, sinon le cookie ne sera pas appliqué à la session. Dans ce cas, utilisez à la 127.0.0.1place delocalhost
Sergey Nudnov
@SergeyNudnov Merci beaucoup pour votre commentaire J'ai perdu beaucoup de temps à essayer de comprendre pourquoi session ne gère pas correctement les cookies. Le changement de domaine de localhost en localhost.local a résolu le problème. Merci encore.
Pulkownik le
25

les autres réponses aident à comprendre comment maintenir une telle session. De plus, je souhaite fournir une classe qui maintient la session sur différentes exécutions d'un script (avec un fichier cache). Cela signifie qu'une "connexion" correcte n'est effectuée que lorsque cela est nécessaire (délai d'expiration ou aucune session n'existe dans le cache). Il prend également en charge les paramètres de proxy lors des appels suivants pour «obtenir» ou «publier».

Il est testé avec Python3.

Utilisez-le comme base pour votre propre code. Les extraits de code suivants sont publiés avec la GPL v3

import pickle
import datetime
import os
from urllib.parse import urlparse
import requests    

class MyLoginSession:
    """
    a class which handles and saves login sessions. It also keeps track of proxy settings.
    It does also maintine a cache-file for restoring session data from earlier
    script executions.
    """
    def __init__(self,
                 loginUrl,
                 loginData,
                 loginTestUrl,
                 loginTestString,
                 sessionFileAppendix = '_session.dat',
                 maxSessionTimeSeconds = 30 * 60,
                 proxies = None,
                 userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1',
                 debug = True,
                 forceLogin = False,
                 **kwargs):
        """
        save some information needed to login the session

        you'll have to provide 'loginTestString' which will be looked for in the
        responses html to make sure, you've properly been logged in

        'proxies' is of format { 'https' : 'https://user:pass@server:port', 'http' : ...
        'loginData' will be sent as post data (dictionary of id : value).
        'maxSessionTimeSeconds' will be used to determine when to re-login.
        """
        urlData = urlparse(loginUrl)

        self.proxies = proxies
        self.loginData = loginData
        self.loginUrl = loginUrl
        self.loginTestUrl = loginTestUrl
        self.maxSessionTime = maxSessionTimeSeconds
        self.sessionFile = urlData.netloc + sessionFileAppendix
        self.userAgent = userAgent
        self.loginTestString = loginTestString
        self.debug = debug

        self.login(forceLogin, **kwargs)

    def modification_date(self, filename):
        """
        return last file modification date as datetime object
        """
        t = os.path.getmtime(filename)
        return datetime.datetime.fromtimestamp(t)

    def login(self, forceLogin = False, **kwargs):
        """
        login to a session. Try to read last saved session from cache file. If this fails
        do proper login. If the last cache access was too old, also perform a proper login.
        Always updates session cache file.
        """
        wasReadFromCache = False
        if self.debug:
            print('loading or generating session...')
        if os.path.exists(self.sessionFile) and not forceLogin:
            time = self.modification_date(self.sessionFile)         

            # only load if file less than 30 minutes old
            lastModification = (datetime.datetime.now() - time).seconds
            if lastModification < self.maxSessionTime:
                with open(self.sessionFile, "rb") as f:
                    self.session = pickle.load(f)
                    wasReadFromCache = True
                    if self.debug:
                        print("loaded session from cache (last access %ds ago) "
                              % lastModification)
        if not wasReadFromCache:
            self.session = requests.Session()
            self.session.headers.update({'user-agent' : self.userAgent})
            res = self.session.post(self.loginUrl, data = self.loginData, 
                                    proxies = self.proxies, **kwargs)

            if self.debug:
                print('created new session with login' )
            self.saveSessionToCache()

        # test login
        res = self.session.get(self.loginTestUrl)
        if res.text.lower().find(self.loginTestString.lower()) < 0:
            raise Exception("could not log into provided site '%s'"
                            " (did not find successful login string)"
                            % self.loginUrl)

    def saveSessionToCache(self):
        """
        save session to a cache file
        """
        # always save (to update timeout)
        with open(self.sessionFile, "wb") as f:
            pickle.dump(self.session, f)
            if self.debug:
                print('updated session cache-file %s' % self.sessionFile)

    def retrieveContent(self, url, method = "get", postData = None, **kwargs):
        """
        return the content of the url with respect to the session.

        If 'method' is not 'get', the url will be called with 'postData'
        as a post request.
        """
        if method == 'get':
            res = self.session.get(url , proxies = self.proxies, **kwargs)
        else:
            res = self.session.post(url , data = postData, proxies = self.proxies, **kwargs)

        # the session has been updated on the server, so also update in cache
        self.saveSessionToCache()            

        return res

Un extrait de code pour utiliser la classe ci-dessus peut ressembler à ceci:

if __name__ == "__main__":
    # proxies = {'https' : 'https://user:pass@server:port',
    #           'http' : 'http://user:pass@server:port'}

    loginData = {'user' : 'usr',
                 'password' :  'pwd'}

    loginUrl = 'https://...'
    loginTestUrl = 'https://...'
    successStr = 'Hello Tom'
    s = MyLoginSession(loginUrl, loginData, loginTestUrl, successStr, 
                       #proxies = proxies
                       )

    res = s.retrieveContent('https://....')
    print(res.text)

    # if, for instance, login via JSON values required try this:
    s = MyLoginSession(loginUrl, None, loginTestUrl, successStr, 
                       #proxies = proxies,
                       json = loginData)
DomTomCat
la source
6
C'est une excellente réponse, il est étrangement difficile de rechercher cette solution également.
dualité
Ne devrait pas être implémenté dans le cadre du module de requête?
user1602
Il utilise le requestsmodule. Comment procéderiez-vous pour l'implémenter dans le cadre du module? ou comment voulez-vous dire @ user1602?
DomTomCat
17

Découvrez ma réponse dans cette question similaire:

python: urllib2 comment envoyer un cookie avec une requête urlopen

import urllib2
import urllib
from cookielib import CookieJar

cj = CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
# input-type values from the html form
formdata = { "username" : username, "password": password, "form-id" : "1234" }
data_encoded = urllib.urlencode(formdata)
response = opener.open("https://page.com/login.php", data_encoded)
content = response.read()

ÉDITER:

Je vois que j'ai reçu quelques votes négatifs pour ma réponse, mais aucun commentaire explicatif. Je suppose que c'est parce que je parle des urllibbibliothèques plutôt que de requests. Je fais cela parce que le PO demande de l'aide requestsou que quelqu'un suggère une autre approche.

Morten Jensen
la source
2
Je ne suis pas l'un de vos votants négatifs, mais comme une supposition, de nombreux lecteurs glissent probablement la dernière phrase du PO comme suit: «Quelqu'un peut-il remplir les points de suspension dans le code ci-dessous ou suggérer une autre approche [avec la bibliothèque de requêtes qui impliquerait plus chirurgie à mon code plutôt que de simplement remplir les ellipses avec autre chose]. » - mais ce n'est qu'une supposition de ma part.
Brandon Rhodes
7
En tant qu'OP, je peux dire que votre réponse offre une alternative utile. Ne requestsserait-ce que pour démontrer que cela offre une solution simple et de haut niveau à un problème qui prendrait autrement 3 bibliothèques à implémenter.
ChrisGuest
7

La documentation indique que cela getprend uncookies argument vous permettant de spécifier les cookies à utiliser:

à partir de la documentation:

>>> url = 'http://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')

>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{"cookies": {"cookies_are": "working"}}'

http://docs.python-requests.org/en/latest/user/quickstart/#cookies

dm03514
la source
6

En essayant toutes les réponses ci-dessus, j'ai trouvé que l'utilisation de "RequestsCookieJar" au lieu du CookieJar régulier pour les demandes ultérieures a résolu mon problème.

import requests
import json

# The Login URL
authUrl = 'https://whatever.com/login'

# The subsequent URL
testUrl = 'https://whatever.com/someEndpoint'

# Logout URL
testlogoutUrl = 'https://whatever.com/logout'

# Whatever you are posting
login_data =  {'formPosted':'1', 
               'login_email':'[email protected]', 
               'password':'pw'
               }

# The Authentication token or any other data that we will receive from the Authentication Request. 
token = ''

# Post the login Request
loginRequest = requests.post(authUrl, login_data)
print("{}".format(loginRequest.text))

# Save the request content to your variable. In this case I needed a field called token. 
token = str(json.loads(loginRequest.content)['token'])  # or ['access_token']
print("{}".format(token))

# Verify Successful login
print("{}".format(loginRequest.status_code))

# Create your Requests Cookie Jar for your subsequent requests and add the cookie
jar = requests.cookies.RequestsCookieJar()
jar.set('LWSSO_COOKIE_KEY', token)

# Execute your next request(s) with the Request Cookie Jar set
r = requests.get(testUrl, cookies=jar)
print("R.TEXT: {}".format(r.text))
print("R.STCD: {}".format(r.status_code))

# Execute your logout request(s) with the Request Cookie Jar set
r = requests.delete(testlogoutUrl, cookies=jar)
print("R.TEXT: {}".format(r.text))  # should show "Request Not Authorized"
print("R.STCD: {}".format(r.status_code))  # should show 401
Jim Chertkov
la source
1
Merci @ jim-chertkov pour cela! Excellente réponse même des années plus tard! J'ai pu utiliser et comprendre cela.
JayRizzo
2

extrait de code pour récupérer les données json, protégé par mot de passe

import requests

username = "my_user_name"
password = "my_super_secret"
url = "https://www.my_base_url.com"
the_page_i_want = "/my_json_data_page"

session = requests.Session()
# retrieve cookie value
resp = session.get(url+'/login')
csrf_token = resp.cookies['csrftoken']
# login, add referer
resp = session.post(url+"/login",
                  data={
                      'username': username,
                      'password': password,
                      'csrfmiddlewaretoken': csrf_token,
                      'next': the_page_i_want,
                  },
                  headers=dict(Referer=url+"/login"))
print(resp.json())
Néant
la source
1

Enregistrez uniquement les cookies nécessaires et réutilisez-les.

import os
import pickle
from urllib.parse import urljoin, urlparse

login = '[email protected]'
password = 'secret'
# Assuming two cookies are used for persistent login.
# (Find it by tracing the login process)
persistentCookieNames = ['sessionId', 'profileId']
URL = 'http://example.com'
urlData = urlparse(URL)
cookieFile = urlData.netloc + '.cookie'
signinUrl = urljoin(URL, "/signin")
with requests.Session() as session:
    try:
        with open(cookieFile, 'rb') as f:
            print("Loading cookies...")
            session.cookies.update(pickle.load(f))
    except Exception:
        # If could not load cookies from file, get the new ones by login in
        print("Login in...")
        post = session.post(
            signinUrl,
            data={
                'email': login,
                'password': password,
            }
        )
        try:
            with open(cookieFile, 'wb') as f:
                jar = requests.cookies.RequestsCookieJar()
                for cookie in session.cookies:
                    if cookie.name in persistentCookieNames:
                        jar.set_cookie(cookie)
                pickle.dump(jar, f)
        except Exception as e:
            os.remove(cookieFile)
            raise(e)
    MyPage = urljoin(URL, "/mypage")
    page = session.get(MyPage)
utilisateur1602
la source
0

Cela fonctionnera pour vous en Python;

# Call JIRA API with HTTPBasicAuth
import json
import requests
from requests.auth import HTTPBasicAuth

JIRA_EMAIL = "****"
JIRA_TOKEN = "****"
BASE_URL = "https://****.atlassian.net"
API_URL = "/rest/api/3/serverInfo"

API_URL = BASE_URL+API_URL

BASIC_AUTH = HTTPBasicAuth(JIRA_EMAIL, JIRA_TOKEN)
HEADERS = {'Content-Type' : 'application/json;charset=iso-8859-1'}

response = requests.get(
    API_URL,
    headers=HEADERS,
    auth=BASIC_AUTH
)

print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))
Dasitha Abeysinghe
la source