Permintaan Python dan sesi persisten

119

Saya menggunakan modul permintaan (versi 0.10.0 dengan Python 2.5). Saya telah menemukan cara mengirimkan data ke formulir login di situs web dan mengambil kunci sesi, tetapi saya tidak dapat melihat cara yang jelas untuk menggunakan kunci sesi ini dalam permintaan berikutnya. Dapatkah seseorang mengisi elipsis pada kode di bawah ini atau menyarankan pendekatan lain?

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

Jawaban:

209

Anda dapat dengan mudah membuat sesi persisten menggunakan:

s = requests.Session()

Setelah itu, lanjutkan dengan permintaan Anda seperti yang Anda lakukan:

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

Untuk lebih lanjut tentang sesi: https://requests.kennethreitz.org/en/master/user/advanced/#session-objects

Anuj Gupta
sumber
4
Adakah cara untuk menyimpan Sesi itu sendiri di antara skrip yang dijalankan?
Gtx
10
Dapat pickle.dump cookie sesi ke file seperti pickle.dump (session.cookies._cookies, file) dan pickle.load ke sesi seperti berikut cookie = pickle.load (file) cj = requests.cookies.RequestsCookieJar () cj._cookies = cookie dan session.cookies = cj
Cyril
bagaimana jika saya melibatkan proxy?
brainLoop
1
Untuk permintaan yang dikirim ke localhost, mungkin ada masalah dengan login dan cookie lain yang dikembalikan oleh server web, jika berisi nilai properti domain yang salah. Karena localhost, server web harus mengembalikan cookie dengan properti domain disetel ke localhost.local, jika tidak, cookie tidak akan diterapkan ke sesi. Dalam kasus penggunaan 127.0.0.1bukannyalocalhost
Sergey Nudnov
@SergeyNudnov Terima kasih banyak atas komentar Anda. Saya membuang banyak waktu mencoba mencari tahu mengapa sesi tidak menangani cookie dengan benar. Mengubah domain dari localhost ke localhost.local memecahkan masalah. Terima kasih lagi.
Pulkownik
25

jawaban yang lain membantu untuk memahami bagaimana mempertahankan sesi seperti itu. Selain itu, saya ingin menyediakan kelas yang menjaga sesi dipertahankan selama menjalankan skrip yang berbeda (dengan file cache). Ini berarti "login" yang benar hanya dilakukan jika diperlukan (waktu habis atau tidak ada sesi di cache). Juga mendukung pengaturan proxy pada panggilan berikutnya ke 'get' atau 'post'.

Ini diuji dengan Python3.

Gunakan itu sebagai dasar untuk kode Anda sendiri. Cuplikan berikut dirilis dengan 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

Cuplikan kode untuk menggunakan kelas di atas mungkin terlihat seperti ini:

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
sumber
6
Ini adalah jawaban yang bagus, anehnya sulit untuk mencari solusi ini juga.
dualitas
Tidak seharusnya diimplementasikan sebagai bagian dari modul permintaan?
pengguna1602
Itu menggunakan requestsmodul. Bagaimana Anda akan menerapkannya sebagai bagian dari modul? atau apa maksud Anda @ user1602?
DomTomCat
17

Lihat jawaban saya di pertanyaan serupa ini:

python: urllib2 cara mengirim cookie dengan permintaan 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()

EDIT:

Saya melihat saya mendapatkan beberapa suara negatif untuk jawaban saya, tetapi tidak ada komentar yang menjelaskan. Saya menduga itu karena saya mengacu pada urllibperpustakaan, bukan requests. Saya melakukan itu karena OP meminta bantuan requestsatau seseorang menyarankan pendekatan lain.

Morten Jensen
sumber
2
Saya bukan salah satu dari down-voters Anda, tetapi sebagai tebakan, banyak pembaca yang mungkin mengabaikan kalimat terakhir OP sebagai "Dapatkah seseorang mengisi elipsis dalam kode di bawah ini atau menyarankan pendekatan lain [dengan pustaka permintaan yang akan melibatkan lebih banyak mayor operasi kode saya daripada hanya mengisi elips dengan sesuatu yang lain]. ” - tapi itu hanya tebakan di pihakku.
Brandon Rhodes
7
Sebagai OP, saya dapat mengatakan bahwa jawaban Anda memberikan alternatif yang berguna. Jika hanya untuk menunjukkan yang requestsmenawarkan solusi sederhana dan tingkat tinggi untuk masalah yang seharusnya membutuhkan 3 perpustakaan untuk diimplementasikan.
ChrisGuest
7

Dokumentasi mengatakan bahwa getmengambil cookiesargumen opsional yang memungkinkan Anda menentukan cookie yang akan digunakan:

dari dokumen:

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

Setelah mencoba semua jawaban di atas, saya menemukan bahwa menggunakan "RequestsCookieJar" alih-alih CookieJar biasa untuk permintaan berikutnya memperbaiki masalah saya.

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
sumber
1
Terima kasih @ jim-chertkov untuk ini! Jawaban yang bagus bahkan bertahun-tahun kemudian! Saya bisa menggunakan dan memahami ini.
JayRizzo
2

potongan untuk mengambil data json, dilindungi kata sandi

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())
Nol
sumber
1

Simpan hanya cookie yang diperlukan dan gunakan kembali.

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)
pengguna1602
sumber
0

Ini akan berhasil untuk Anda dengan 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
sumber