Python中POST请求与JS动态内容爬取的登录问题
解决方案
方案一:直接模拟浏览器登录操作(推荐)
既然要用Selenium处理动态内容,直接模拟用户填写表单并提交的操作是最稳妥的——全程用同一个浏览器实例,自动维护会话、CSRF token和所有关联的请求上下文,完全避开手动POST的会话不一致问题。
修改后的代码
from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # Chrome Options chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--headless') chrome_options.add_argument('--no-sandbox') chrome_options.add_argument('--disable-dev-shm-usage') # 初始化普通Selenium驱动(不需要seleniumrequests) driver = webdriver.Chrome(service=Service('/usr/local/bin/chromedriver'), options=chrome_options) wait = WebDriverWait(driver, 10) login_url = 'https://example.com/login' target_url = 'https://example.com/profile' try: # 打开登录页 driver.get(login_url) # 等待并填写用户名、密码 username_input = wait.until(EC.presence_of_element_located((By.NAME, 'username'))) password_input = wait.until(EC.presence_of_element_located((By.NAME, 'password'))) username_input.send_keys('你的用户名') password_input.send_keys('你的密码') # 找到登录按钮并点击(或者直接提交表单) login_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'button[type="submit"]'))) login_button.click() # 等待登录完成(可通过URL变化或目标元素出现判断) wait.until(EC.url_contains(target_url.split('/')[-1])) # 访问目标页并获取动态内容 driver.get(target_url) wait.until(EC.presence_of_element_located((By.TAG_NAME, 'vue-app'))) # 等待Vue内容加载完成 page_html = driver.page_source print(page_html) finally: driver.quit()
方案二:在Selenium中用JS发送POST请求
如果必须手动发送POST请求,可以利用Selenium的execute_script方法,在浏览器当前会话中执行JavaScript发送AJAX请求——这样请求会自动携带当前浏览器的会话信息,解决token不一致问题。
示例代码
from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--headless') chrome_options.add_argument('--no-sandbox') chrome_options.add_argument('--disable-dev-shm-usage') driver = webdriver.Chrome(service=Service('/usr/local/bin/chromedriver'), options=chrome_options) wait = WebDriverWait(driver, 10) login_url = 'https://example.com/login' target_url = 'https://example.com/profile' try: driver.get(login_url) # 获取页面中的CSRF token csrf_token = wait.until(EC.presence_of_element_located((By.NAME, 'user[_csrf_token]'))).get_attribute('value') # 用JS发送POST请求,保持当前会话 login_script = f""" return fetch('{login_url}', {{ method: 'POST', credentials: 'include', // 携带当前会话的凭证 headers: {{ 'Content-Type': 'application/x-www-form-urlencoded' }}, body: new URLSearchParams({{ 'username': '你的用户名', 'password': '你的密码', 'user[_csrf_token]': '{csrf_token}' }}) }}).then(response => response.text()); """ driver.execute_script(login_script) # 等待登录生效,再访问目标页 driver.get(target_url) wait.until(EC.presence_of_element_located((By.TAG_NAME, 'vue-app'))) page_html = driver.page_source print(page_html) finally: driver.quit()
原代码无效的原因
seleniumrequests的request方法本质是调用requests库单独发送请求,并没有和Selenium的浏览器会话共享Cookie、Session等上下文——相当于两个完全独立的客户端,所以你获取的CSRF token属于Selenium的会话,而POST请求用的是requests的全新会话,自然会出现token不匹配的问题。
内容的提问来源于stack exchange,提问作者prosyanoy
相关产品推荐
相关产品推荐

