1. 从零到一:为什么Cookie是爬虫登录的“通行证”
最近在折腾一个自动化项目,需要从某个内容平台定时抓取数据,第一步就卡在了登录上。这个网站没有提供公开API,登录过程还带上了图形验证码和动态令牌,直接用requests发个POST请求显然行不通。相信很多刚开始写爬虫的朋友都遇到过类似的门槛:页面看起来能正常访问,但一涉及到个人中心、订单列表这些需要登录才能查看的数据,脚本就哑火了,返回的永远是“请先登录”的提示页。
问题的核心就在于,大多数现代网站都采用了基于会话(Session)的认证机制。简单来说,当你用浏览器成功输入账号密码登录后,服务器会为你创建一个唯一的会话,并把这个会话的“钥匙”——通常是一个叫sessionid或类似名称的Cookie——发回给你的浏览器。之后,浏览器在访问该网站下的每一个页面时,都会自动带上这把“钥匙”。服务器收到请求,一看钥匙对得上,就知道“哦,是老朋友来了”,于是允许你访问受保护的资源。
爬虫脚本默认是没有这个“自动带钥匙”功能的。它就像一个新来的访客,每次敲门都空着手,服务器自然不认识它。因此,实现Cookie登录的本质,就是让我们的爬虫程序学会“保管”和“使用”这把钥匙,模拟出浏览器登录后的状态。这不仅仅是复制一段Cookie字符串那么简单,它涉及到对HTTP协议无状态特性的理解、对会话管理机制的把握,以及如何稳定地维持这个登录状态。掌握了它,你就打开了爬取大量需要认证数据的大门,从电商价格监控到社交媒体分析,应用场景非常广泛。
2. 核心原理拆解:Cookie、Session与Token的三角关系
在动手写代码之前,我们必须把几个容易混淆的概念理清楚:Cookie、Session和Token。很多教程一上来就教你怎么复制Cookie,但如果不明白背后的逻辑,一旦遇到更复杂的认证流程(比如OAuth、JWT),你就会束手无策。
Cookie:客户端的“储物柜”Cookie是服务器发送到用户浏览器并保存在本地的一小块数据。它就像是服务器给你的一张“会员卡”,上面写了一些信息(比如session_id=abc123)。浏览器会按照一定的规则(域名、路径、有效期)来存储这张卡,并且在后续向同一服务器发起请求时,自动将这张卡附在HTTP请求头(Cookie:)里发送过去。Cookie是存储在浏览器(客户端)的,因此可以被用户查看、修改甚至禁用。我们爬虫要做的,就是把这个“储物柜”里的内容,原封不动地搬运到我们的程序里。
Session:服务器端的“账本”与Cookie相对,Session数据是存储在服务器端的。当用户登录成功,服务器会在内存或数据库中创建一个Session对象,里面记录了用户的登录状态、个人信息等。同时,服务器会生成一个唯一的Session ID,并通过Set-Cookie响应头,将这个ID作为Cookie值发送给浏览器。之后,浏览器带着这个Session ID来访问,服务器就能通过ID找到对应的Session数据,从而识别用户身份。Session是存储在服务器端的,它本身的安全性更高,但会给服务器带来存储压力。
Token:自包含的“令牌”Token(如JWT)是另一种流行的认证方式。它将用户信息、过期时间等数据,经过加密签名后,组合成一个字符串。这个字符串本身包含了验证所需的所有信息,服务器收到后只需验证签名即可,无需在服务端存储会话状态。Token通常被放在HTTP请求的Authorization头部中发送。虽然本次主题是Cookie登录,但了解Token有助于你区分不同网站的认证方式。有些网站登录后,虽然也使用Cookie,但里面存储的可能就是一个JWT Token。
它们如何协同工作?对于最常见的基于Session的网站,流程是这样的:
- 用户提交登录表单(用户名、密码)。
- 服务器验证凭证,创建Session记录,生成Session ID。
- 服务器通过
Set-Cookie响应头,将sessionid=xxxx发回浏览器。 - 浏览器保存此Cookie。
- 浏览器访问其他页面时,自动在请求头中带上
Cookie: sessionid=xxxx。 - 服务器读取Cookie中的Session ID,查找对应Session,确认用户已登录。
我们的爬虫任务,核心就是模拟第1步到第5步的过程。要么,我们模拟整个登录流程,获取服务器下发的Cookie;要么,我们手动从浏览器“借”来已经登录有效的Cookie,直接用于爬虫请求。前者更通用、更持久,后者更快捷、但容易过期。
注意:直接使用他人或从非法渠道获取的Cookie涉及隐私和安全问题,务必只用于测试自己拥有权限的账户或完全公开、允许爬取的数据,严格遵守网站的
robots.txt协议和法律法规。
3. 实战准备:手动获取并解析登录Cookie
对于不那么复杂或者仅需一次性抓取的登录需求,最快速的方法就是从浏览器里把已经登录好的Cookie“借”过来。这里以Chrome浏览器为例,演示完整过程。
3.1 使用开发者工具捕获Cookie
首先,用你的浏览器正常登录目标网站。登录成功后,停留在需要登录才能访问的页面(例如个人主页)。
- 打开开发者工具:在页面任意位置右键,选择“检查”(Inspect),或直接按
F12键。 - 切换到“网络”(Network)标签页。为了清晰,可以先点击红色的录制按钮旁边的清除按钮,清空当前记录。
- 刷新当前页面(按
F5)。此时网络面板会记录页面刷新时加载的所有资源(HTML、CSS、JS、图片、XHR请求等)。 - 在资源列表中找到第一个或主要的文档请求(通常是
www.target-site.com,类型为document)。点击它。 - 在右侧展开的详情面板中,找到“请求头”(Request Headers)部分。
- 在其中找到名为
Cookie:的一行。后面那一长串字符串,就是我们需要的全部Cookie信息。
它可能看起来像这样:
Cookie: sessionid=abcdefg123456; csrftoken=hijklmn789012; Hm_lvt_xxx=1; Hm_lpvt_xxx=1这一整串就是我们需要复制的内容。
3.2 将Cookie转化为爬虫可用的字典
复制下来的Cookie字符串不能直接扔给requests,需要将其解析成Python字典格式。每个键值对由分号和空格分隔。
我们可以写一个简单的函数来处理:
def parse_cookie_string(cookie_str): """ 将浏览器复制来的Cookie字符串解析为字典。 例如: "name1=value1; name2=value2" -> {'name1': 'value1', 'name2': 'value2'} """ cookie_dict = {} # 按分号分割成多个键值对 items = cookie_str.split(';') for item in items: # 去除首尾空格 item = item.strip() if not item: continue # 有些Cookie值可能包含等号,所以只分割第一个等号 if '=' in item: key, value = item.split('=', 1) cookie_dict[key.strip()] = value.strip() return cookie_dict # 用法示例 cookie_str_from_browser = "sessionid=abcdefg123456; csrftoken=hijklmn789012" cookies = parse_cookie_string(cookie_str_from_browser) print(cookies) # 输出: {'sessionid': 'abcdefg123456', 'csrftoken': 'hijklmn789012'}现在,我们就得到了一个标准的Python字典cookies,它可以直接用于requests库的请求中。
3.3 直接使用Cookie发起请求
有了Cookie字典,发起一个携带登录状态的请求就非常简单了。我们使用requests.get()或requests.post()方法,并通过cookies参数传入这个字典。
import requests # 目标网址(需要登录才能访问的页面) url = 'https://www.target-site.com/user/profile' # 从浏览器复制的Cookie字符串(此处为示例,请替换为你自己的) raw_cookie = "sessionid=abcdefg123456; csrftoken=hijklmn789012" # 解析为字典 cookies = parse_cookie_string(raw_cookie) # 还可以添加一个User-Agent头,让自己看起来更像浏览器 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } try: response = requests.get(url, cookies=cookies, headers=headers) # 检查请求是否成功(状态码200通常表示成功) if response.status_code == 200: # 打印页面标题或部分内容,验证是否登录成功 print("请求成功!") # 可以进一步解析response.text或response.json() # 例如,检查页面是否包含你的用户名 if "我的账户" in response.text: print("确认已处于登录状态。") else: print("页面内容异常,可能Cookie已失效。") else: print(f"请求失败,状态码:{response.status_code}") print(response.text[:500]) # 打印前500字符看错误信息 except requests.exceptions.RequestException as e: print(f"网络请求出错:{e}")实操心得一:Cookie的时效性。通过浏览器复制来的Cookie是有生命周期的。它可能是会话Cookie(关闭浏览器即失效),也可能是持久化Cookie(有过期时间
Expires或Max-Age)。如果脚本运行一段时间后突然失效,大概率是Cookie过期了。此时需要重新登录并获取新的Cookie。对于需要长期运行的任务,手动复制Cookie不是长久之计,这就需要我们模拟自动登录流程。
4. 进阶:模拟完整登录流程自动获取Cookie
手动复制Cookie适合临时任务,但对于需要定期、长期运行的爬虫,我们必须让程序自己完成登录,自动获取并管理Cookie。这需要分析网站的登录接口和表单数据。
4.1 分析登录请求
我们再次打开浏览器的开发者工具,这次在“网络”(Network)标签页下,勾选“保留日志”(Preserve log)。
- 打开网站的登录页面。
- 在用户名、密码框中输入测试账号(或错误的账号),点击登录按钮。
- 在网络面板中,你会看到登录动作触发了一个新的请求。通常是一个
POST请求,名称可能是login,signin,authenticate等。点击这个请求进行查看。
我们需要关注这个请求的以下几个关键信息:
- 请求URL (Request URL):登录请求发送到的具体地址。
- 请求方法 (Request Method):通常是
POST。 - 请求头 (Request Headers):特别是
Content-Type(通常是application/x-www-form-urlencoded或application/json)和可能存在的特殊头如X-CSRFToken。 - 请求负载 (Request Payload / Form Data):这是最重要的部分,包含了提交的用户名、密码以及其他隐藏字段。
4.2 处理常见的安全机制
现代网站登录通常不会只有用户名和密码那么简单。
1. CSRF Token(跨站请求伪造令牌)很多使用表单的网站(尤其是Django、Flask等框架)会使用CSRF Token来防止恶意提交。你会在登录页面的HTML表单中找到一个类似<input type="hidden" name="csrfmiddlewaretoken" value="很长的一串字符">的隐藏输入框。服务器会验证提交的Token是否与它发给浏览器的Token匹配。
应对策略:我们需要先发起一个GET请求到登录页面,从返回的HTML中解析出这个CSRF Token,然后在POST登录请求时一并提交。
import requests from bs4 import BeautifulSoup login_url = 'https://www.target-site.com/login/' post_url = 'https://www.target-site.com/login/' # 有时和登录页面相同 # 1. 创建会话对象,它会自动管理Cookie session = requests.Session() # 2. 获取登录页面,提取CSRF Token login_page = session.get(login_url) soup = BeautifulSoup(login_page.text, 'html.parser') # 查找csrf token的input标签,name可能是'csrf_token', 'csrfmiddlewaretoken', '_token'等 csrf_token = soup.find('input', {'name': 'csrfmiddlewaretoken'}).get('value') # 3. 准备登录数据 login_data = { 'username': 'your_username', 'password': 'your_password', 'csrfmiddlewaretoken': csrf_token, # 加入csrf token # 可能还有其他隐藏字段,需要一并抓取 } # 4. 提交登录请求 headers = { 'User-Agent': 'Mozilla/5.0 ...', 'Referer': login_url # 有时需要Referer头 } response = session.post(post_url, data=login_data, headers=headers) # 5. 检查是否登录成功 if response.status_code == 200 and '登录成功' in response.text: # 根据实际成功跳转或提示判断 print("模拟登录成功!") # 此时session对象已经自动保存了服务器返回的Cookie # 可以直接用session去访问其他需要登录的页面 profile_response = session.get('https://www.target-site.com/user/profile') print(profile_response.text[:200]) else: print("登录失败。") print(response.status_code, response.text[:500])2. 验证码(CAPTCHA)如果登录页面有验证码(图形、滑块、点选等),这就是一个巨大的障碍。全自动破解验证码通常涉及图像识别、机器学习,复杂度很高,且可能违反网站服务条款。
应对策略:
- 寻找替代接口:有些网站的移动端API或旧版登录页面可能没有验证码。
- 半自动化处理:对于低频、重要的任务,可以采用“人工介入”的方式。例如,程序在遇到验证码时,将验证码图片保存下来,暂停并提示用户手动输入,然后再继续。
- 使用商业打码平台:付费调用第三方API来识别验证码(需考虑成本和稳定性)。
- 评估必要性:如果目标网站验证码机制非常严格,可能需要重新评估爬取该网站数据的可行性和合法性。
4.3 使用Session对象维持状态
上面的代码中,我们使用了requests.Session()。这是一个非常重要的对象。Session对象会在多次请求间自动保持某些参数和Cookie,就像浏览器一样。你用它发起的第一次GET请求获取的Cookie,会自动被保存,并在后续的POST请求中携带。登录成功后,这个session对象就处于登录状态了,直接用session.get()访问其他页面即可,无需再手动处理Cookie字典。
这是与直接使用requests.get(cookies=...)最大的区别,也是更规范、更接近浏览器行为的方式。
5. 工程化实践:构建一个健壮的Cookie登录爬虫
一个用于生产环境的爬虫,不能只是几行脚本。我们需要考虑错误处理、状态维持、日志记录和遵守规则。
5.1 封装登录逻辑与状态检查
我们可以将登录功能封装成一个类,使其更易于管理和复用。
import requests import time import logging from bs4 import BeautifulSoup from urllib.parse import urljoin logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) class LoginCrawler: def __init__(self, base_url): self.base_url = base_url self.session = requests.Session() # 设置一个通用的浏览器UA self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', }) self.is_logged_in = False def _get_csrf_token(self, url): """从指定页面提取CSRF Token。""" try: resp = self.session.get(url, timeout=10) resp.raise_for_status() soup = BeautifulSoup(resp.text, 'html.parser') # 尝试多种可能的csrf token名称 token_selectors = [ {'name': 'csrfmiddlewaretoken'}, {'name': 'csrf_token'}, {'name': '_token'}, {'attrs': {'name': lambda x: x and 'csrf' in x.lower()}} ] for selector in token_selectors: token_elem = soup.find('input', selector) if token_elem and token_elem.get('value'): return token_elem.get('value') # 也可能在meta标签里 meta_token = soup.find('meta', {'name': 'csrf-token'}) if meta_token: return meta_token.get('content') logger.warning(f"未能在 {url} 找到CSRF Token。") return None except Exception as e: logger.error(f"获取CSRF Token失败: {e}") return None def login(self, username, password, login_path='/login/'): """执行登录操作。""" login_url = urljoin(self.base_url, login_path) logger.info(f"尝试登录: {login_url}") # 1. 获取登录页和CSRF Token csrf_token = self._get_csrf_token(login_url) if not csrf_token: logger.error("无法获取CSRF Token,登录终止。") return False # 2. 准备登录数据(根据实际表单调整字段名) login_payload = { 'username': username, 'password': password, 'csrfmiddlewaretoken': csrf_token, # 'remember_me': 'on', # 如果有记住我选项 } # 3. 提交登录 try: # 注意:有些网站登录后是重定向,allow_redirects=True(默认)让session自动跟随 resp = self.session.post(login_url, data=login_payload, timeout=15) # 判断登录成功的条件需要根据目标网站调整 # 条件1:状态码是200或302(重定向到首页/用户页) # 条件2:响应内容中包含登录成功的标识(如用户名、跳转链接) # 条件3:检查session里的cookie是否包含了关键的session id if resp.status_code in [200, 302]: # 示例:检查是否跳转到了非登录页,或页面包含用户名 if username in resp.text or resp.url != login_url: self.is_logged_in = True logger.info(f"登录成功!当前会话状态: {self.is_logged_in}") # 可以在这里保存关键的cookie信息,用于后续持久化 self._save_cookies() return True logger.warning(f"登录可能失败。状态码: {resp.status_code}, 最终URL: {resp.url}") # 打印部分响应内容辅助调试 logger.debug(resp.text[:300]) return False except requests.exceptions.RequestException as e: logger.error(f"登录请求发生错误: {e}") return False def _save_cookies(self): """将当前会话的cookies保存下来(例如到文件),用于下次启动时恢复。""" # 这是一个简单示例,实际可以保存为json文件 cookies_dict = requests.utils.dict_from_cookiejar(self.session.cookies) # 这里可以添加将cookies_dict保存到文件的逻辑 # with open('cookies.json', 'w') as f: # json.dump(cookies_dict, f) logger.info("Cookie已保存(示例,实际未写入文件)。") def _load_cookies(self, cookie_file='cookies.json'): """从文件加载cookies到会话。""" # 加载逻辑,如果文件存在且未过期,则加载 # self.session.cookies.update(loaded_dict) pass def fetch_protected_page(self, url): """获取需要登录才能访问的页面。""" if not self.is_logged_in: logger.error("未登录,请先调用login方法。") return None try: full_url = urljoin(self.base_url, url) resp = self.session.get(full_url, timeout=10) resp.raise_for_status() logger.info(f"成功获取受保护页面: {full_url}") return resp.text except requests.exceptions.RequestException as e: logger.error(f"获取页面失败 {url}: {e}") return None # 使用示例 if __name__ == '__main__': crawler = LoginCrawler('https://www.example.com') if crawler.login('your_username', 'your_password'): # 登录成功后,抓取数据 data = crawler.fetch_protected_page('/user/orders') if data: # 处理data... print("数据抓取成功!") else: print("登录失败,请检查账号密码或网站结构是否变化。")5.2 异常处理与重试机制
网络请求充满不确定性,必须添加健壮的异常处理和重试逻辑。可以使用tenacity库或自己实现简单的重试。
import requests from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type class RobustCrawler(LoginCrawler): @retry( stop=stop_after_attempt(3), # 最多重试3次 wait=wait_exponential(multiplier=1, min=2, max=10), # 指数退避等待 retry=retry_if_exception_type((requests.exceptions.ConnectionError, requests.exceptions.Timeout)) ) def safe_fetch(self, url, method='GET', **kwargs): """带重试机制的请求方法。""" try: resp = self.session.request(method, url, timeout=15, **kwargs) resp.raise_for_status() # 如果状态码不是200-399,抛出HTTPError异常 return resp except requests.exceptions.HTTPError as e: # 对于403/404/500等错误,重试可能无济于事,直接记录并抛出 logger.error(f"HTTP错误 {e.response.status_code} for {url}: {e}") # 如果是401未授权,可能是登录失效 if e.response.status_code == 401: self.is_logged_in = False logger.warning("会话可能已过期,登录状态被重置。") raise # 重新抛出异常,让上层处理 # ConnectionError, Timeout 等会被tenacity捕获并重试5.3 遵守robots.txt与设置请求间隔
一个负责任的爬虫必须尊重网站的robots.txt规则,并设置合理的请求间隔(delay),避免对目标服务器造成过大压力。
import time from urllib.robotparser import RobotFileParser class PoliteCrawler(RobustCrawler): def __init__(self, base_url, crawl_delay=2): super().__init__(base_url) self.crawl_delay = crawl_delay # 两次请求间的最小间隔(秒) self.last_request_time = 0 self.robot_parser = RobotFileParser() self.robot_parser.set_url(urljoin(base_url, '/robots.txt')) try: self.robot_parser.read() except Exception as e: logger.warning(f"无法读取或解析robots.txt: {e}") def can_fetch(self, url): """检查是否允许抓取。""" user_agent = self.session.headers.get('User-Agent', '*') return self.robot_parser.can_fetch(user_agent, url) def polite_get(self, url, **kwargs): """遵守延迟规则的GET请求。""" # 检查robots.txt if not self.can_fetch(url): logger.error(f"根据robots.txt,不允许抓取: {url}") return None # 控制请求频率 elapsed = time.time() - self.last_request_time if elapsed < self.crawl_delay: time.sleep(self.crawl_delay - elapsed) self.last_request_time = time.time() return self.safe_fetch(url, 'GET', **kwargs)实操心得二:动态内容与反爬。我们目前处理的是传统的同步HTML表单登录。越来越多的网站采用前端框架(如React, Vue),登录可能是一个异步的XHR(Ajax)请求,提交的数据格式是JSON,并且返回的也是JSON。此时,你需要分析的是XHR请求,而不是
document请求。使用开发者工具时,筛选XHR或Fetch类型的请求来找登录接口。处理方式也从data参数变为json参数:session.post(login_api_url, json={'user': name, 'pass': pwd})。
6. 高级话题与疑难排错
即使按照上述步骤操作,你仍然可能会遇到各种问题。这里总结几个常见的坑和解决思路。
6.1 Cookie失效与会话管理
问题:脚本运行一段时间后,突然无法获取数据,返回登录页面。原因:
- 会话过期:服务器设置的Session有效期到了。
- Cookie被刷新:某些网站会在一定操作后更新Cookie。
- IP变动或用户端信息改变:有些风控策略会绑定会话与IP、User-Agent等。
排查与解决:
- 定期重新登录:对于长时间运行的爬虫,实现一个定时任务或检查机制,当
fetch请求返回登录页时,触发重新登录流程。 - 持久化与加载Cookie:如前面代码所示,将登录成功后的Cookie(通过
session.cookies获取)保存到文件或数据库。下次启动脚本时,先尝试加载旧Cookie并发一个测试请求。如果失效,再重新登录。这可以避免频繁输入账号密码。 - 保持环境一致:尽量使用固定的User-Agent,如果可能,使用代理池保持出口IP相对稳定。
6.2 处理登录后的重定向
问题:登录请求返回302/301状态码,但后续请求依然未登录。原因:requests的Session默认会自动处理重定向(allow_redirects=True)。但有时重定向链复杂,或者重定向后的页面需要执行JavaScript才能最终设置Cookie,这超出了requests的能力范围。
排查:
- 在登录请求时,设置
allow_redirects=False,然后手动检查响应头中的Location字段和Set-Cookie字段,确保关键的Cookie被正确接收。 - 打印并检查登录后
session.cookies的内容,确认是否包含了关键的会话标识(如sessionid,JSESSIONID等)。
resp = self.session.post(login_url, data=payload, allow_redirects=False) print(f"登录响应状态码: {resp.status_code}") print(f"登录响应头: {resp.headers}") print(f"登录后CookieJar: {self.session.cookies}") # 如果重定向是预期的,可以手动让session去get重定向的URL if resp.status_code in [301, 302]: redirect_url = resp.headers['Location'] self.session.get(redirect_url)6.3 应对JavaScript渲染的登录页面
问题:登录按钮是通过JS绑定的,表单可能是动态生成的,直接用requests获取的HTML里没有表单信息。原因:网站采用前后端分离,登录逻辑由前端JavaScript控制。
解决方案:
- 寻找隐藏的API接口:使用开发者工具监控网络活动,直接寻找点击登录按钮时触发的XHR/Fetch API调用。然后模拟这个API请求。这通常是最有效的方法。
- 使用Selenium或Playwright:当登录流程极度复杂,涉及大量JS交互、滑块验证等时,可以动用浏览器自动化工具。它们能真实地驱动浏览器,执行JS,等待元素加载,完全模拟人的操作。缺点是速度慢,资源消耗大。
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Chrome() # 需要安装ChromeDriver driver.get(login_url) driver.find_element(By.NAME, 'username').send_keys('your_user') driver.find_element(By.NAME, 'password').send_keys('your_pass') driver.find_element(By.TAG_NAME, 'button').click() # 等待登录成功,例如等待某个只有登录后才出现的元素 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "user-profile")) ) # 从Selenium的driver中获取Cookie,并转化为requests可用的格式 cookies_selenium = driver.get_cookies() cookies_dict = {c['name']: c['value'] for c in cookies_selenium} # 现在可以将cookies_dict用于requests.Session session = requests.Session() session.cookies.update(cookies_dict) driver.quit()6.4 调试技巧:对比浏览器与爬虫的请求
当你的爬虫登录失败,而浏览器可以成功时,最有效的调试方法是对比。
- 在浏览器开发者工具中,找到成功的登录
POST请求。 - 右键点击该请求,选择“Copy as cURL”。
- 将cURL命令粘贴到终端或在线转换工具(如https://curlconverter.com/python/)中,可以将其转换为Python
requests代码。 - 将生成的代码与你自己的代码逐字段对比,检查差异:
- URL是否完全一致?
- 请求头(Headers)是否缺少了
Origin,Referer,X-Requested-With等关键头? - 请求体(Data)的字段名和格式是否正确?是
application/x-www-form-urlencoded还是application/json? - Cookie:在发送登录请求前,浏览器是否已经携带了一些初始Cookie(例如访问登录页时服务器下发的)?你的爬虫
Session是否也获取了这些前置Cookie?
通过这种细致的对比,你能发现绝大多数参数遗漏或格式错误的问题。编写爬虫,尤其是处理登录,很大程度上就是一个“模仿浏览器”的过程,模仿得越像,成功率就越高。这个过程需要耐心和细致的观察,但一旦打通,后续的数据抓取就会畅通无阻。