
一、技术核心:理解Session与Cookie
在HTTP这个无状态协议中,Cookie是服务端用来识别用户身份的关键技术。当您成功登录后,服务器会返回一个或多个Cookie(通常是Session ID),浏览器会在后续的请求中自动携带这些Cookie,从而向服务器证明"你是谁"。
在爬虫中模拟这一过程有两个关键步骤:
- 模拟登录(Simulated Login):通过向登录接口发送正确的凭证(如用户名、密码、验证码等),从服务器获取合法的Cookie。
- Cookie持久化(Cookie Persistence):将获取到的Cookie保存下来(如到文件或数据库),并在下次运行爬虫时重新加载。这样无需每次运行都重新登录,既避免了频繁登录可能触发的反爬机制,也大大提升了效率。
Python的**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Requests</font>**
库中的**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Session</font>**
对象完美地封装了这一切。它会自动管理和维护Cookies,在一次会话中保持所有请求的Cookie状态。
二、实战准备:分析目标网站登录流程
在编写代码之前,至关重要的第一步是使用浏览器的"开发者工具"(F12)手动分析登录过程。
- 打开登录页面:找到中国汽车网的登录入口。
- 捕捉登录请求 :
- 在开发者工具中切换到 Network(网络) 选项卡。
- 勾选 Preserve log(保留日志)。
- 输入错误的测试账号(如用户名:
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">test@example.com</font>**
,密码:**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">test123</font>**
)并点击登录。 - 在网络请求列表中,找到一个
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">POST</font>**
类型的请求,其名称往往是**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">login</font>**
,**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">userLogin</font>**
等,这就是我们要找的登录接口。
- 分析请求载荷(Request Payload) :
- 点击该登录请求,查看
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Headers</font>**
和**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Payload</font>**
(或**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Form Data</font>**
)。 - 通常,我们会发现提交的数据不仅仅是用户名和密码,还可能包含隐藏的
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">token</font>**
、**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">csrfmiddlewaretoken</font>**
、**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">lt</font>**
等用于防止CSRF攻击的验证参数。这些参数往往需要先从登录页面HTML中提取。
- 点击该登录请求,查看
假设我们分析出的登录接口信息如下:
- URL :
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">https://account.chinaautoweb.com/api/login</font>**
- Method :
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">POST</font>**
- Data :
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">username: your_username</font>**
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">password: your_password</font>**
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">token: (a dynamic value from the login page)</font>**
三、代码实现:四步走策略
我们将过程分解为四个步骤:获取登录令牌、执行登录、持久化Cookie、访问专属页面。
第1步:获取动态Token
许多网站的登录页面会嵌入一个动态的Token,我们需要先**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">GET</font>**
登录页面,用解析库(如**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">lxml</font>**
)将其提取出来。
plain
import requests
from lxml import html
def get_login_token(session):
login_page_url = 'https://account.chinaautoweb.com/login'
# 使用session发起请求,保持cookie连贯性
response = session.get(login_page_url)
response.raise_for_status() # 检查请求是否成功
# 解析HTML,寻找token
tree = html.fromstring(response.text)
# 使用XPath定位token元素。此XPath需根据实际网站结构修改!
token = tree.xpath('//input[@name="token"]/@value')[0]
return token
第2步:模拟登录并保存Session
获取Token后,将其与用户名、密码一起构造为表单数据,提交给登录接口。
plain
def login(session, username, password):
login_api_url = 'https://account.chinaautoweb.com/api/login'
# 1. 首先获取token
token = get_login_token(session)
# 2. 构造登录数据
login_data = {
'username': username,
'password': password,
'token': token
}
# 3. 添加请求头,模拟浏览器行为
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://account.chinaautoweb.com/login',
'X-Requested-With': 'XMLHttpRequest' # 表明是Ajax请求
}
# 4. 发送POST请求
response = session.post(login_api_url, data=login_data, headers=headers)
response.raise_for_status()
# 5. 检查登录是否成功(假设成功返回JSON中包含'code': 200)
result = response.json()
if result.get('code') == 200:
print("登录成功!")
return True
else:
print(f"登录失败: {result.get('message')}")
return False
第3步:实现Cookie持久化
我们将Session中的Cookies转换为字典,然后用**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pickle</font>**
模块序列化保存到本地文件。
plain
import pickle
import os
def save_cookies(session, cookie_file):
with open(cookie_file, 'wb') as f:
# 将Session的cookies对象转为字典再保存
pickle.dump(requests.utils.dict_from_cookiejar(session.cookies), f)
def load_cookies(session, cookie_file):
if not os.path.exists(cookie_file):
return False
try:
with open(cookie_file, 'rb') as f:
cookies = pickle.load(f)
# 将字典转换回cookiejar并更新到session中
session.cookies = requests.utils.cookiejar_from_dict(cookies)
print("Cookie加载成功!")
return True
except Exception as e:
print(f"加载Cookie失败: {e}")
return False
第4步:访问用户专属榜单并解析数据
现在,我们可以使用这个已经包含登录状态的Session去访问任何需要登录的页面了。
plain
def fetch_user_ranking(session):
ranking_url = 'https://www.chinaautoweb.com/user/favorite-ranking'
response = session.get(ranking_url)
response.raise_for_status()
# 假设返回的是HTML页面
tree = html.fromstring(response.text)
# 使用XPath解析榜单数据(此处为示例,需根据实际页面调整)
car_list = tree.xpath('//div[@class="rank-item"]')
rankings = []
for car in car_list:
name = car.xpath('.//h3/text()')[0].strip()
score = car.xpath('.//span[@class="score"]/text()')[0].strip()
rankings.append({'车型': name, '热度得分': score})
return rankings
四、主程序:串联整个流程
最后,我们编写一个主函数来串联所有步骤,实现智能登录:优先尝试加载旧Cookie,失败后再重新登录。
plain
import requests
from lxml import html
import pickle
import os
# 代理配置信息
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"
# 构造代理格式
proxies = {
'http': f'http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}',
'https': f'http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}'
}
def get_login_token(session):
login_page_url = 'https://account.chinaautoweb.com/login'
response = session.get(login_page_url)
response.raise_for_status()
tree = html.fromstring(response.text)
token = tree.xpath('//input[@name="token"]/@value')[0]
return token
def login(session, username, password):
login_api_url = 'https://account.chinaautoweb.com/api/login'
token = get_login_token(session)
login_data = {
'username': username,
'password': password,
'token': token
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://account.chinaautoweb.com/login',
'X-Requested-With': 'XMLHttpRequest'
}
response = session.post(login_api_url, data=login_data, headers=headers)
response.raise_for_status()
result = response.json()
if result.get('code') == 200:
print("登录成功!")
return True
else:
print(f"登录失败: {result.get('message')}")
return False
def save_cookies(session, cookie_file):
with open(cookie_file, 'wb') as f:
pickle.dump(requests.utils.dict_from_cookiejar(session.cookies), f)
def load_cookies(session, cookie_file):
if not os.path.exists(cookie_file):
return False
try:
with open(cookie_file, 'rb') as f:
cookies = pickle.load(f)
session.cookies = requests.utils.cookiejar_from_dict(cookies)
print("Cookie加载成功!")
return True
except Exception as e:
print(f"加载Cookie失败: {e}")
return False
def fetch_user_ranking(session):
ranking_url = 'https://www.chinaautoweb.com/user/favorite-ranking'
response = session.get(ranking_url)
response.raise_for_status()
tree = html.fromstring(response.text)
car_list = tree.xpath('//div[@class="rank-item"]')
rankings = []
for car in car_list:
name = car.xpath('.//h3/text()')[0].strip()
score = car.xpath('.//span[@class="score"]/text()')[0].strip()
rankings.append({'车型': name, '热度得分': score})
return rankings
def main():
username = "your_actual_username"
password = "your_actual_password"
cookie_file = 'chinaautoweb_cookies.pkl'
# 创建一个持久化的Session,并设置代理
with requests.Session() as s:
# 全局设置代理
s.proxies = proxies
# 尝试加载历史Cookie
if load_cookies(s, cookie_file):
print("检测到已保存的Session,尝试直接访问...")
test_url = 'https://www.chinaautoweb.com/user/profile'
test_resp = s.get(test_url)
if test_resp.status_code == 200 and "我的资料" in test_resp.text:
print("Session依然有效!")
else:
print("Session已失效,需要重新登录。")
if not login(s, username, password):
return
save_cookies(s, cookie_file)
else:
print("未找到保存的Session,开始登录...")
if not login(s, username, password):
return
save_cookies(s, cookie_file)
print("开始抓取用户专属榜单...")
user_ranking_data = fetch_user_ranking(s)
for idx, item in enumerate(user_ranking_data, 1):
print(f"{idx}. {item['车型']} - 热度: {item['热度得分']}")
if __name__ == '__main__':
main()
五、注意事项与进阶思考
- 动态变化:本文代码中的URL、XPath、表单字段名称均为示例,实际应用中必须根据目标网站的实际结构进行修改。
- 验证码(CAPTCHA):如果网站有复杂的验证码,需要引入图像识别(如TesseractOCR)或第三方打码平台服务。
- 加密参数 :愈来愈多的网站会对密码进行前端加密(如RSA, AES)或添加复杂的、由JavaScript生成的签名参数。这时仅靠
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Requests</font>**
无法解决,需要使用**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Selenium</font>**
、**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Playwright</font>**
等自动化浏览器工具来模拟登录,或者直接逆向JavaScript代码。 - 伦理与法律 :务必遵守网站的
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">robots.txt</font>**
协议,仅抓取被允许的公开或自有数据。避免对服务器造成过大压力,合理设置请求间隔。尊重用户隐私和数据版权。