คำขอ Python และเซสชันถาวร


120

ฉันใช้โมดูลคำขอ (เวอร์ชัน 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', ...)

คำตอบ:


210

คุณสามารถสร้างเซสชันถาวรได้อย่างง่ายดายโดยใช้:

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 คุกกี้เซสชันไปยังไฟล์เช่น pickle.dump (session.cookies._cookies, file) และ pickle.load ไปยังเซสชันดังต่อไปนี้ cookies = pickle.load (file) cj = request.cookies.RequestsCookieJar () cj._cookies = cookies and session.cookies = cj
Cyril

จะเกิดอะไรขึ้นถ้าฉันเกี่ยวข้องกับพร็อกซี?
brainLoop

1
สำหรับคำขอที่ส่งไปlocalhostอาจมีปัญหาในการเข้าสู่ระบบและคุกกี้อื่น ๆ ที่ส่งคืนโดยเว็บเซิร์ฟเวอร์หากมีค่าคุณสมบัติโดเมนที่ไม่ถูกต้อง สำหรับlocalhostเว็บเซิร์ฟเวอร์ควรส่งคืนคุกกี้พร้อมคุณสมบัติโดเมนที่ตั้งค่าเป็นlocalhost.localมิฉะนั้นคุกกี้จะไม่ถูกนำไปใช้กับเซสชัน ในกรณีนั้นให้ใช้127.0.0.1แทนlocalhost
Sergey Nudnov

@SergeyNudnov ขอบคุณมากสำหรับความคิดเห็นของคุณฉันเสียเวลาไปมากในการพยายามหาสาเหตุว่าทำไมเซสชันไม่จัดการคุกกี้อย่างถูกต้อง การเปลี่ยนโดเมนจาก localhost เป็น localhost.local ช่วยแก้ปัญหาได้ ขอบคุณอีกครั้ง.
Pulkownik

26

คำตอบอื่น ๆ จะช่วยให้เข้าใจวิธีการรักษาเซสชันดังกล่าว นอกจากนี้ฉันต้องการจัดเตรียมคลาสที่เก็บรักษาเซสชันไว้เหนือการรันสคริปต์ที่แตกต่างกัน (พร้อมไฟล์แคช) ซึ่งหมายความว่า "ล็อกอิน" ที่ถูกต้องจะดำเนินการเมื่อจำเป็นเท่านั้น (หมดเวลาหรือไม่มีเซสชันอยู่ในแคช) นอกจากนี้ยังรองรับการตั้งค่าพร็อกซีในการโทรเพื่อ 'รับ' หรือ 'โพสต์' ในภายหลัง

ทดสอบด้วย 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)

7
นี่เป็นคำตอบที่ยอดเยี่ยมมันยากที่จะค้นหาโซลูชันนี้เช่นกัน
ความเป็นคู่

ไม่ควรนำไปใช้เป็นส่วนหนึ่งของโมดูลคำขอ?
1602

มันใช้requestsโมดูล คุณจะนำมันไปใช้เป็นส่วนหนึ่งของโมดูลได้อย่างไร? หรือคุณหมายถึง @ user1602 ได้อย่างไร?
DomTomCat

17

ตรวจสอบคำตอบของฉันในคำถามที่คล้ายกันนี้:

python: urllib2 วิธีส่งคุกกี้พร้อมคำขอ 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ฉันทำเช่นนั้นเพราะ OP ขอความช่วยเหลือrequestsหรือให้ใครแนะนำแนวทางอื่น


2
ฉันไม่ได้เป็นหนึ่งในผู้มีสิทธิเลือกตั้งของคุณ แต่อย่างที่เดาได้ว่าผู้อ่านหลายคนอาจจะพูดประโยคสุดท้ายของ OP ว่า“ มีใครสามารถเติมจุดไข่ปลาในโค้ดด้านล่างนี้หรือแนะนำวิธีการอื่นได้ [ด้วยไลบรารีคำขอที่เกี่ยวข้องกับหลักมากกว่า การผ่าตัดโค้ดของฉันไม่ใช่แค่การเติมจุดไข่ปลาด้วยอย่างอื่น]” - แต่นั่นเป็นเพียงการคาดเดาในส่วนของฉัน
Brandon Rhodes

7
ในฐานะ OP ฉันสามารถพูดได้ว่าคำตอบของคุณเป็นทางเลือกที่มีประโยชน์ หากเพียงเพื่อแสดงให้เห็นว่าrequestsมีวิธีแก้ปัญหาที่เรียบง่ายและระดับสูงสำหรับปัญหาที่อาจต้องใช้ห้องสมุด 3 แห่งในการดำเนินการ
ChrisGuest

7

เอกสารระบุว่าgetใช้ในcookiesอาร์กิวเมนต์ที่เป็นทางเลือกซึ่งอนุญาตให้คุณระบุคุกกี้ที่จะใช้:

จากเอกสาร:

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

เมื่อลองทำตามคำตอบทั้งหมดข้างต้นฉันพบว่าการใช้ "RequestCookieJar" แทน 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

บันทึกเฉพาะคุกกี้ที่จำเป็นและใช้ซ้ำ

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=(",", ": ")))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.