Python-Anfragen und dauerhafte Sitzungen

119

Ich verwende das Anforderungsmodul (Version 0.10.0 mit Python 2.5). Ich habe herausgefunden, wie Daten an ein Anmeldeformular auf einer Website gesendet und der Sitzungsschlüssel abgerufen werden können, sehe jedoch keine offensichtliche Möglichkeit, diesen Sitzungsschlüssel in nachfolgenden Anforderungen zu verwenden. Kann jemand die Auslassungspunkte im folgenden Code ausfüllen oder einen anderen Ansatz vorschlagen?

>>> 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
quelle

Antworten:

209

Sie können ganz einfach eine dauerhafte Sitzung erstellen, indem Sie:

s = requests.Session()

Fahren Sie danach mit Ihren Anfragen fort, wie Sie es tun würden:

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 :)

Weitere Informationen zu Sitzungen finden Sie unter https://requests.kennethreitz.org/en/master/user/advanced/#session-objects

Anuj Gupta
quelle
4
Gibt es Möglichkeiten, die Sitzung selbst zwischen Skriptläufen zu speichern?
Gtx
10
Kann Sitzungscookies für pickle.dump in eine Datei wie pickle.dump (session.cookies._cookies, file) und pickle.load in die Sitzung wie folgt kopieren. Cookies = pickle.load (file) cj = request.cookies.RequestsCookieJar () cj._cookies = Cookies und session.cookies = cj
Cyril
Was ist, wenn ich einen Proxy einbeziehe?
brainLoop
1
Bei den an gesendeten Anforderungen localhostkönnen Probleme mit der Anmeldung und anderen vom Webserver zurückgegebenen Cookies auftreten, wenn sie einen falschen Wert für die Domäneneigenschaft enthalten. Der localhostWebserver sollte Cookies mit der Domain-Eigenschaft "Zurückgeben" zurückgeben localhost.local, da sonst das Cookie nicht auf die Sitzung angewendet wird. In diesem Fall verwenden Sie 127.0.0.1anstelle vonlocalhost
Sergey Nudnov
@SergeyNudnov Vielen Dank für Ihren Kommentar. Ich habe viel Zeit damit verschwendet, herauszufinden, warum die Sitzung Cookies nicht richtig verarbeitet. Das Ändern der Domain von localhost zu localhost.local löste das Problem. Danke noch einmal.
Pulkownik
25

Die anderen Antworten helfen zu verstehen, wie eine solche Sitzung aufrechterhalten wird. Außerdem möchte ich eine Klasse bereitstellen, die die Sitzung über verschiedene Skriptläufe (mit einer Cache-Datei) verwaltet. Dies bedeutet, dass eine ordnungsgemäße "Anmeldung" nur bei Bedarf durchgeführt wird (Zeitüberschreitung oder keine Sitzung im Cache vorhanden). Außerdem werden Proxy-Einstellungen für nachfolgende Aufrufe von 'get' oder 'post' unterstützt.

Es wird mit Python3 getestet.

Verwenden Sie es als Grundlage für Ihren eigenen Code. Die folgenden Snippets werden mit GPL v3 veröffentlicht

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

Ein Code-Snippet zur Verwendung der obigen Klasse kann folgendermaßen aussehen:

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
quelle
6
Dies ist eine großartige Antwort. Es ist seltsamerweise auch schwierig, nach dieser Lösung zu suchen.
Dualität
Sollte nicht als Teil des Anforderungsmoduls implementiert werden?
user1602
Es verwendet das requestsModul. Wie würden Sie es als Teil des Moduls implementieren? oder wie meinst du @ user1602?
DomTomCat
17

Schauen Sie sich meine Antwort in dieser ähnlichen Frage an:

python: urllib2 wie man ein Cookie mit einer URLopen-Anfrage sendet

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()

BEARBEITEN:

Ich sehe, ich habe ein paar Abstimmungen für meine Antwort bekommen, aber keine erklärenden Kommentare. Ich vermute, es liegt daran, dass ich mich urllibstattdessen auf die Bibliotheken beziehe requests. Ich mache das, weil das OP um Hilfe bittet requestsoder jemanden bittet , einen anderen Ansatz vorzuschlagen.

Morten Jensen
quelle
2
Ich bin nicht einer Ihrer Abwärtswähler, aber als Vermutung beschönigen viele Leser wahrscheinlich den letzten Satz des OP als „Kann jemand die Auslassungspunkte im folgenden Code ausfüllen oder einen anderen Ansatz vorschlagen [mit der Anforderungsbibliothek, die mehr Bedeutung haben würde? Operation nach meinem Code, als nur die Ellipsen mit etwas anderem auszufüllen]. “ - aber das ist nur eine Vermutung von meiner Seite.
Brandon Rhodes
7
Als OP kann ich sagen, dass Ihre Antwort eine nützliche Alternative darstellt. Wenn nur zu demonstrieren, dass dies requestseine einfache und umfassende Lösung für ein Problem bietet, für dessen Implementierung ansonsten 3 Bibliotheken erforderlich wären.
ChrisGuest
7

Die Dokumentation besagt, dass dies getoptional istcookies Argument verwendet wird, mit dem Sie die zu verwendenden Cookies angeben können:

aus den Dokumenten:

>>> 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
quelle
6

Beim Ausprobieren aller oben genannten Antworten stellte ich fest, dass die Verwendung von "RequestsCookieJar" anstelle des regulären CookieJar für nachfolgende Anfragen mein Problem behoben hat.

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
quelle
1
Danke @ jim-chertkov dafür! Tolle Antwort auch Jahre später! Ich konnte das nutzen und verstehen.
JayRizzo
2

Snippet zum Abrufen von JSON-Daten, passwortgeschützt

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())
Null
quelle
1

Speichern Sie nur die erforderlichen Cookies und verwenden Sie sie erneut.

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)
user1602
quelle
0

Dies funktioniert für Sie in 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
quelle