Запити Python та постійні сеанси


119

Я використовую модуль запитів (версія 0.10.0 з Python 2.5). Я зрозумів, як подати дані до форми для входу на веб-сайт та отримати ключ сеансу, але я не бачу очевидного способу використання цього ключа сеансу в наступних запитах. Чи може хтось заповнити еліпсис у наведеному нижче коді чи запропонувати інший підхід?

>>> import requests
>>> login_data =  {'formPosted':'1', 'login_email':'me@example.com', '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', ...)

Відповіді:


209

Ви можете легко створити стійкий сеанс, використовуючи:

s = requests.Session()

Після цього продовжуйте свої запити так, як хочете:

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

Докладніше про сеанси: https://requests.kennethreitz.org/en/master/user/advanced/#session-objects


4
Будь-які способи зберегти сам сеанс між запуском сценарію?
Gtx

10
Можна pickle.dump сесійні файли cookie у такий файл, як pickle.dump (session.cookies._cookies, файл) та pickle.load для сеансу, як слід cookie = pickle.load (файл) cj = questions.cookies.RequestsCookieJar () cj._cookies = cookies і session.cookies = cj
Кирило

що робити, якщо я залучаю проксі?
brainLoop

1
Для запитів, надісланих до localhost, можуть виникнути проблеми з логіном та іншими файлами cookie, поверненими веб-сервером, якщо вони містять неправильне значення властивості домену. Наприклад localhost, веб-сервер повинен повернути файли cookie з властивістю домену, встановлені в localhost.localіншому випадку файли cookie не будуть застосовані до сеансу. У такому випадку використовуйте 127.0.0.1замістьlocalhost
Нуднов Сергій

@SergeyNudnov Велике спасибі за ваш коментар, я витратив багато часу, намагаючись з’ясувати, чому сесія не обробляє файли cookie правильно. Зміна домену з localhost на localhost.local вирішила проблему. Знову дякую.
Пулковник

25

інші відповіді допомагають зрозуміти, як підтримувати такий сеанс. Крім того, я хочу надати клас, який підтримує сеанс у різних сценаріях (із файлом кешу). Це означає, що належне "вхід" виконується лише за необхідності (тайм-аут або відсутність сеансу в кеші). Крім того, він підтримує налаштування проксі для наступних дзвінків на "отримати" або "опублікувати".

Це тестується з Python3.

Використовуйте його як основу для власного коду. Наступні фрагменти випущені з 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

Фрагмент коду для використання вищевказаного класу може виглядати так:

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)

6
Це чудова відповідь, і дивно, і для цього рішення важко знайти.
подвійність

Не слід реалізовуватись як частина модуля запиту?
user1602

Він дійсно використовує requestsмодуль. Як би ви вирішили реалізувати його як частину модуля? або як ти маєш на увазі @ user1602?
DomTomCat

17

Ознайомтеся з моєю відповіддю в цьому подібному питанні:

python: urllib2 як надсилати cookie із запитом 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()

Редагувати:

Я бачу, що я отримав кілька відгуків для своєї відповіді, але жодних пояснюючих коментарів немає. Я здогадуюсь це тому, що я маю на увазі urllibбібліотеки, а не requests. Я роблю це тому, що ОП просить допомогти з requestsчи хтось запропонує інший підхід.


2
Я не один з ваших виборців, але, як здогадуєтесь, багато читачів, мабуть, промальовують останнє речення ОП як "Чи може хтось заповнити еліпсис у наведеному нижче коді чи запропонувати інший підхід [з бібліотекою запитів, яка передбачає більш великі операція моєму коду, ніж просто заповнення еліпсів чимось іншим]. " - але це лише здогадка з мого боку.
Брендон Родос,

7
Як ОП, я можу сказати, що ваша відповідь дає корисну альтернативу. Якби лише продемонструвати, що requestsпропонує просте і високорівневе рішення проблеми, яка в іншому випадку потребує 3 бібліотеки для реалізації.
ChrisGuest

7

Документація говорить про те, що getмістить необов'язковий cookiesаргумент, що дозволяє вам вказати файли cookie для використання:

з документів:

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


6

Спробувавши всі відповіді вище, я виявив, що використання "RequestsCookieJar" замість звичайного CookieJar для подальших запитів вирішило мою проблему.

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':'me@example.com', 
               '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

1
Дякую @ Jim-chertkov за це! Чудова відповідь навіть через роки! Я зміг це використати і зрозуміти.
JayRizzo

2

фрагмент для отримання даних json, захищених паролем

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

1

Збережіть лише необхідні файли cookie та повторно їх використовуйте.

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

login = 'my@email.com'
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)

0

Це працюватиме для вас у 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=(",", ": ")))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.