Yêu cầu Python và các phiên liên tục


119

Tôi đang sử dụng mô-đun yêu cầu (phiên bản 0.10.0 với Python 2.5). Tôi đã tìm ra cách gửi dữ liệu tới biểu mẫu đăng nhập trên trang web và truy xuất khóa phiên, nhưng tôi không thể thấy cách rõ ràng để sử dụng khóa phiên này trong các yêu cầu tiếp theo. Ai đó có thể điền vào dấu chấm lửng trong đoạn mã dưới đây hoặc đề xuất cách tiếp cận khác không?

>>> 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', ...)

Câu trả lời:


209

Bạn có thể dễ dàng tạo một phiên liên tục bằng cách sử dụng:

s = requests.Session()

Sau đó, tiếp tục với các yêu cầu của bạn như bạn sẽ làm:

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

Để biết thêm về các phiên: https://requests.kennethreitz.org/en/master/user/advanced/#session-objects


4
Có cách nào để tự lưu Session giữa các lần chạy script không?
Gtx 10/1215

10
Có thể pickle.dump cookie phiên vào một tệp như pickle.dump (session.cookies._cookies, tệp) và pickle.load tới phiên như sau cookie = pickle.load (tệp) cj = request.cookies.RequestsCookieJar () cj._cookies = cookies and session.cookies = cj
Cyril

nếu tôi liên quan đến proxy thì sao?
brainLoop

1
Đối với các yêu cầu được gửi đến localhost, có thể có sự cố với đăng nhập và các cookie khác do máy chủ web trả về, nếu chúng chứa giá trị thuộc tính miền không chính xác. Đối với localhost, máy chủ web phải trả về cookie với thuộc tính miền được đặt thành localhost.local, nếu không cookie sẽ không được áp dụng cho phiên. Trong trường hợp đó, hãy sử dụng 127.0.0.1thay vìlocalhost
Sergey Nudnov

@SergeyNudnov Rất cảm ơn nhận xét của bạn Tôi đã lãng phí rất nhiều thời gian để tìm hiểu lý do tại sao phiên không xử lý cookie một cách chính xác. Thay đổi tên miền từ localhost thành localhost.local đã giải quyết được vấn đề. Cảm ơn một lần nữa.
Pulkownik

25

các câu trả lời khác giúp hiểu cách duy trì một phiên như vậy. Ngoài ra, tôi muốn cung cấp một lớp giúp duy trì phiên này qua các lần chạy khác nhau của một tập lệnh (với tệp bộ đệm). Điều này có nghĩa là "đăng nhập" thích hợp chỉ được thực hiện khi được yêu cầu (timout hoặc không có phiên nào tồn tại trong bộ nhớ cache). Ngoài ra, nó hỗ trợ cài đặt proxy qua các cuộc gọi tiếp theo để 'lấy' hoặc 'đăng'.

Nó được thử nghiệm với Python3.

Sử dụng nó làm cơ sở cho mã của riêng bạn. Các đoạn mã sau được phát hành với 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

Đoạn mã để sử dụng lớp trên có thể giống như sau:

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
Đây là một câu trả lời tuyệt vời, thật khó để tìm kiếm giải pháp này.
kép

Không nên được triển khai như một phần của mô-đun yêu cầu?
user1602

Nó sử dụng requestsmô-đun. Bạn sẽ thực hiện nó như thế nào như một phần của mô-đun? hay ý bạn là @ user1602 như thế nào?
DomTomCat

17

Kiểm tra câu trả lời của tôi trong câu hỏi tương tự này:

python: urllib2 cách gửi cookie với yêu cầu 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()

BIÊN TẬP:

Tôi thấy rằng tôi đã nhận được một số phản đối cho câu trả lời của mình, nhưng không có nhận xét giải thích. Tôi đoán đó là vì tôi đang đề cập đến các urllibthư viện thay vì requests. Tôi làm điều đó vì OP yêu cầu giúp đỡ requestshoặc nhờ ai đó đề xuất cách tiếp cận khác.


2
Tôi không phải là một trong số những người bỏ phiếu từ chối của bạn, nhưng theo dự đoán, nhiều độc giả có lẽ đang chú thích câu cuối cùng của OP là “Ai đó có thể điền vào dấu chấm lửng trong đoạn mã bên dưới hoặc đề xuất một cách tiếp cận khác [với thư viện yêu cầu sẽ liên quan đến nhiều giải phẫu mã của tôi thay vì chỉ điền vào các dấu chấm lửng với một cái gì đó khác]. ” - nhưng đó chỉ là phỏng đoán của tôi.
Brandon Rhodes

7
Là OP, tôi có thể nói rằng câu trả lời của bạn cung cấp một giải pháp thay thế hữu ích. Nếu chỉ để chứng minh điều đó requestsđưa ra một giải pháp đơn giản và cấp cao cho một vấn đề mà nếu không thì cần 3 thư viện để thực hiện.
ChrisGuest

7

Tài liệu nói rằng getcó một cookiesđối số tùy chọn cho phép bạn chỉ định cookie để sử dụng:

từ các tài liệu:

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

Khi thử tất cả các câu trả lời ở trên, tôi nhận thấy rằng việc sử dụng "RequestCookieJar" thay vì CookieJar thông thường cho các yêu cầu tiếp theo đã khắc phục được sự cố của tôi.

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
Cảm ơn bạn @ jim-chertkov về điều này! Câu trả lời tuyệt vời thậm chí nhiều năm sau! Tôi đã có thể sử dụng và hiểu điều này.
JayRizzo

2

đoạn mã để truy xuất dữ liệu json, được bảo vệ bằng mật khẩu

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

Chỉ lưu các cookie được yêu cầu và sử dụng lại chúng.

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

Điều này sẽ làm việc cho bạn bằng 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=(",", ": ")))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.