一、技术核心:理解Session与Cookie
在HTTP这个无状态协议中,Cookie是服务端用来识别用户身份的关键技术。当您成功登录后,服务器会返回一个或多个Cookie(通常是Session ID),浏览器会在后续的请求中自动携带这些Cookie,从而向服务器证明“你是谁”。
在爬虫中模拟这一过程有两个关键步骤:
- 模拟登录(Simulated Login):通过向登录接口发送正确的凭证(如用户名、密码、验证码等),从服务器获取合法的Cookie。
- Cookie持久化(Cookie Persistence):将获取到的Cookie保存下来(如到文件或数据库),并在下次运行爬虫时重新加载。这样无需每次运行都重新登录,既避免了频繁登录可能触发的反爬机制,也大大提升了效率。
Python的Requests库中的Session对象完美地封装了这一切。它会自动管理和维护Cookies,在一次会话中保持所有请求的Cookie状态。
二、实战准备:分析目标网站登录流程
在编写代码之前,至关重要的第一步是使用浏览器的“开发者工具”(F12)手动分析登录过程。 - 打开登录页面:找到中国汽车网的登录入口。
- 捕捉登录请求:
○ 在开发者工具中切换到 Network(网络) 选项卡。
○ 勾选 Preserve log(保留日志)。
○ 输入错误的测试账号(如用户名:test@example.com,密码:test123)并点击登录。
○ 在网络请求列表中,找到一个POST类型的请求,其名称往往是login, userLogin等,这就是我们要找的登录接口。 - 分析请求载荷(Request Payload):
○ 点击该登录请求,查看Headers和Payload(或Form Data)。
○ 通常,我们会发现提交的数据不仅仅是用户名和密码,还可能包含隐藏的token、csrfmiddlewaretoken、lt等用于防止CSRF攻击的验证参数。这些参数往往需要先从登录页面HTML中提取。
假设我们分析出的登录接口信息如下:
● URL: https://account.chinaautoweb.com/api/login
● Method: POST
● Data:
○ username: your_username
○ password: your_password
○ token: (a dynamic value from the login page)
三、代码实现:四步走策略
我们将过程分解为四个步骤:获取登录令牌、执行登录、持久化Cookie、访问专属页面。
第1步:获取动态Token
许多网站的登录页面会嵌入一个动态的Token,我们需要先GET登录页面,用解析库(如lxml)将其提取出来。
```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后,将其与用户名、密码一起构造为表单数据,提交给登录接口。
```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转换为字典,然后用pickle模块序列化保存到本地文件。
```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去访问任何需要登录的页面了。
```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,失败后再重新登录。
```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生成的签名参数。这时仅靠Requests无法解决,需要使用Selenium、Playwright等自动化浏览器工具来模拟登录,或者直接逆向JavaScript代码。
伦理与法律:务必遵守网站的robots.txt协议,仅抓取被允许的公开或自有数据。避免对服务器造成过大压力,合理设置请求间隔。尊重用户隐私和数据版权。