DrissionPage实战:高效稳定爬取拉勾网招聘数据

发布时间:2026/7/14 19:38:10

DrissionPage实战:高效稳定爬取拉勾网招聘数据 1. 项目概述与核心思路最近在帮一个做招聘分析的朋友处理数据他需要定期获取拉勾网上特定技术岗位的招聘信息来做趋势分析。传统的爬虫方案比如requestsBeautifulSoup或者Selenium要么容易被反爬机制拦截要么运行效率低下、资源消耗大。在尝试了多种方案后我最终选择了DrissionPage这个库它完美地解决了“既要模拟浏览器行为绕过复杂验证又要保持高效率和低资源占用”的矛盾。今天我就把这个从环境搭建、核心代码编写到数据清洗存储的完整实战过程以及我踩过的几个“坑”和优化技巧详细地分享出来。简单来说DrissionPage是一个将浏览器自动化如Selenium和网络请求如requests能力融合在一起的Python库。它最大的魅力在于你可以用类似requests的简洁语法直接发送HTTP请求获取数据但当遇到需要执行JavaScript、处理动态加载内容或绕过一些前端检测时又能无缝切换到“浏览器模式”用真实的浏览器环境来渲染页面。对于拉勾网这种前端技术栈复杂、反爬措施较多的网站这种“双模式”策略非常有效。我们的目标不仅仅是“能爬下来”更是要“稳定、高效、可维护地爬下来”。2. DrissionPage核心优势与Selenium/Requests对比在深入代码之前我们有必要搞清楚为什么选择DrissionPage而不是更广为人知的Selenium或纯requests方案。这决定了我们后续编码的底层逻辑和应对策略。2.1 传统方案的痛点我最初尝试用requests直接模拟请求。拉勾网的数据接口通常是XHRAjax请求通过浏览器开发者工具的“网络Network”面板可以找到。但问题很快出现了首先请求头Headers需要模拟得非常精确特别是User-Agent、Referer和一些自定义的签名参数如x-anti-request-token这些参数往往由前端JavaScript动态生成静态分析困难。其次拉勾网对频繁的、规律性的请求非常敏感很容易触发IP限制或要求验证码单纯靠设置请求间隔和代理IP维护成本很高。于是转向Selenium。它能完美地渲染页面执行所有JS拿到最终呈现的HTML。但它的缺点同样明显速度慢。每个页面加载都需要启动浏览器内核、下载资源、渲染DOM爬取几十页数据就可能耗时几分钟。资源占用高一个浏览器实例就会占用几百MB内存。稳定性依赖浏览器驱动Chrome版本一升级对应的chromedriver可能就失效了需要手动更新匹配。2.2 DrissionPage的融合之道DrissionPage的设计哲学是“用正确的方式做正确的事”。它内部维护了两个核心对象Session对象类似requests.Session和Page对象类似Selenium的WebDriver。Session模式混合模式这是默认且推荐的模式。在此模式下DrissionPage会先用Session对象发送HTTP请求获取初始HTML。如果发现页面内容是通过JS动态加载的比如拉勾网的职位列表它会自动分析网络请求找到真正的数据接口通常是JSON格式然后继续用Session去请求这个接口直接拿到结构化数据。这个过程绕过了不必要的页面渲染速度极快。Page模式浏览器模式当遇到Session模式无法解决的问题时比如需要处理复杂的登录状态、滑动验证码或者页面结构过于复杂难以解析接口可以一键切换到Page模式。此时它会启动一个后台浏览器默认基于Chromium完全模拟用户操作。你可以在两种模式间无缝切换共享cookies和会话状态。对于拉勾网我们的策略是主要使用Session模式直接请求数据接口仅在首次可能需要处理登录或验证时短暂使用Page模式。这样既保证了速度又具备了应对复杂情况的能力。注意直接爬取公开数据需遵守网站的robots.txt协议并合理设置请求间隔如每次请求后time.sleep(random.uniform(2, 5))避免对目标服务器造成压力。本教程仅用于技术学习交流。3. 环境准备与DrissionPage安装配置工欲善其事必先利其器。稳定的环境是成功的一半。3.1 创建独立的Python虚拟环境我强烈建议为每个爬虫项目创建独立的虚拟环境。这可以避免不同项目间的库版本冲突。使用venvPython 3.3内置非常简单# 在项目目录下创建名为 venv 的虚拟环境 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate激活后命令行提示符前会出现(venv)字样。3.2 安装DrissionPage及相关库在激活的虚拟环境中使用pip安装pip install DrissionPage pandasDrissionPage 核心库。pandas 用于将爬取的数据规整地保存为CSV或Excel文件进行初步清洗和分析非常方便。安装过程会自动处理DrissionPage的依赖包括它所需的浏览器内核默认会下载一个便携版的Chromium无需单独安装Chrome和驱动。第一次运行代码时如果本地没有它会自动下载你也可以通过配置指定已安装的Chrome路径。3.3 初始化DrissionPage并配置基础参数新建一个Python文件比如lagou_spider.py开始编写代码。首先进行基础配置from DrissionPage import SessionPage, ChromiumPage import pandas as pd import time import random import logging # 配置日志方便调试和记录运行状态 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) # 初始化一个SessionPage对象这是我们工作的主力 session_page SessionPage() # 设置请求头模拟真实浏览器 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.lagou.com/, Accept-Language: zh-CN,zh;q0.9, } session_page.headers.update(headers) # 设置全局请求超时和重试 session_page.set.timeout(10) # 请求超时10秒 session_page.set.retry(2) # 失败重试2次 session_page.set.interval(2, 5) # 设置随机请求间隔为2-5秒这是礼貌爬虫的关键配置详解与心得SessionPagevsChromiumPage 我们主要用SessionPage因为它轻量。ChromiumPage是备用的“重型武器”。请求头HeadersUser-Agent是必须的。Referer来源页对于拉勾这类网站很重要很多接口会校验它。直接复制你浏览器中正常访问时的Headers是最稳妥的。set.interval() 这个功能太实用了。它会在每次网络请求后自动休眠一个随机时间这里2-5秒模拟人类操作的不规律性极大降低被封IP的风险。比自己在代码里写time.sleep()更优雅、更统一。日志Logging 爬虫运行时你肯定不想只盯着控制台看。配置好日志所有信息INFO、警告WARNING、错误ERROR都会记录到文件出问题时回溯时间线非常方便。4. 拉勾网页面分析与核心爬取策略接下来是核心环节分析拉勾网找到数据入口和规律。4.1 手动分析数据流打开拉勾网职位搜索页 在浏览器中访问https://www.lagou.com/搜索“Python”进入列表页。打开开发者工具 按F12切换到“网络Network”面板。刷新页面在筛选器中选择“XHR”或“Fetch”。寻找数据接口 滚动页面加载更多职位时你会看到名为positionAjax.json或类似名称的请求。点击它查看“标头Headers”和“响应Response”。请求URL 通常是https://www.lagou.com/jobs/positionAjax.json?needAddtionalResultfalse。注意它可能是一个POST请求而不是GET。请求参数 在“负载Payload”或“查询参数Query String Parameters”中你会看到像first是否首页、pn页码、kd关键词等参数。响应数据 在“响应Response”标签页里是结构清晰的JSON数据包含了职位列表content-positionResult-result和总页数等信息。我们的目标就是直接请求这个接口解析这个JSON。4.2 构造请求参数与处理反爬拉勾网的接口通常需要携带一些特定的参数和请求头。以下是我分析出的一个较稳定的参数构造方式def construct_request_params(keywordPython, city全国, page1): 构造请求拉勾网Ajax接口的参数 params { city: city, needAddtionalResult: false, # 关键参数 first: true if page 1 else false, # 第一页为true后续为false pn: str(page), # 页码 kd: keyword, # 搜索关键词 } # 对于POST请求这些参数通常放在data里。但观察发现拉勾网当前接口是GET带查询参数。 # 我们需要根据实际抓包情况调整。这里以GET为例。 return params # 此外接口可能还需要一个特定的Referer格式为fhttps://www.lagou.com/jobs/list_{keyword}?city{city} def get_referer(keyword, city): return fhttps://www.lagou.com/jobs/list_{keyword}?city{city}关键点与避坑指南first参数 这是最容易出错的地方。爬取第一页时first必须为true服务器会返回一些初始化的上下文信息。从第二页开始必须为false否则可能返回空数据或错误。很多爬虫卡在只能爬第一页问题就出在这里。动态Token 拉勾网的请求头里可能包含一个x-antispam-token或类似的令牌由前端JS生成每次请求都变化。如果遇到403错误可能就是它在作祟。应对策略有两种用Page模式先获取一次 用ChromiumPage打开一次页面让浏览器环境执行JS生成Token然后从页面里提取出来再交给SessionPage使用。DrissionPage可以方便地在两种模式间共享cookies和部分上下文。逆向JS 难度较大但一劳永逸。需要分析前端JS代码找到生成Token的算法并用Python复现。对于学习而言第一种方案更可行。Cookie处理DrissionPage的SessionPage对象会自动管理Cookie。但有时初始Cookie是必要的。你可以先用浏览器正常访问一次拉勾网然后把Cookie字符串复制出来通过session_page.cookies.add()方法手动设置。5. 核心爬取函数实现与数据解析掌握了接口规律我们就可以编写核心的爬取函数了。5.1 单页数据爬取函数def fetch_one_page(session_page, keyword, city, page_num): 爬取单页职位数据 url https://www.lagou.com/jobs/positionAjax.json # 1. 构造请求参数和头信息 params construct_request_params(keyword, city, page_num) referer get_referer(keyword, city) session_page.headers[Referer] referer # 动态更新Referer logger.info(f正在爬取: 关键词『{keyword}』, 城市『{city}』, 第{page_num}页) try: # 2. 发送GET请求根据实际情况可能是POST # 如果是POST使用session_page.post(url, dataparams) resp_page session_page.get(url, paramsparams) # 3. 检查请求是否成功 if resp_page.status_code ! 200: logger.error(f请求失败状态码: {resp_page.status_code}) return None # 4. 解析JSON响应 # DrissionPage的响应对象可以直接.json()解析 json_data resp_page.json() # 5. 检查拉勾网返回的业务状态码 # 拉勾网的成功状态码通常是0或1000具体看接口返回 if json_data.get(code) not in [0, 1000]: logger.warning(f接口返回错误: {json_data.get(msg)}) # 可能是反爬触发这里可以加入重试或切换模式的逻辑 return None # 6. 提取职位列表 position_list json_data.get(content, {}).get(positionResult, {}).get(result, []) total_count json_data.get(content, {}).get(positionResult, {}).get(totalCount, 0) logger.info(f第{page_num}页爬取成功获取到{len(position_list)}条职位总计{total_count}条) return position_list, total_count except Exception as e: logger.exception(f爬取第{page_num}页时发生异常: {e}) return None代码解析与技巧resp_page.json()DrissionPage的响应对象和requests库的响应对象API高度相似直接调用.json()方法即可将响应内容解析为Python字典非常方便。异常处理 网络请求充满不确定性必须用try...except包裹。日志记录异常详情logger.exception会打印堆栈跟踪便于调试。状态码判断 除了HTTP状态码200还要判断拉勾网自定义的业务码如code。非成功码通常意味着参数错误、频率过高或触发了反爬。5.2 多页爬取与循环控制单页爬取完成后我们需要一个循环来爬取所有页。def fetch_all_pages(session_page, keywordPython, city全国, max_pages10): 爬取多页数据直到达到最大页数或没有更多数据 all_positions [] current_page 1 total_count 0 while current_page max_pages: # 调用单页爬取函数 result fetch_one_page(session_page, keyword, city, current_page) if result is None: # 如果爬取失败等待稍长时间后重试一次 logger.warning(f第{current_page}页爬取失败等待10秒后重试...) time.sleep(10) result fetch_one_page(session_page, keyword, city, current_page) if result is None: logger.error(f第{current_page}页重试后仍失败可能触发反爬停止爬取。) break page_positions, page_total result # 如果是第一页更新总职位数并计算实际最大页数每页通常15条 if current_page 1 and page_total 0: total_count page_total # 拉勾网每页固定显示15条数据 actual_max_pages min(max_pages, (page_total 14) // 15) logger.info(f预计总页数: {actual_max_pages} (基于总数{page_total})) max_pages actual_max_pages # 用实际计算出的页数覆盖输入值 # 将当前页数据添加到总列表 if page_positions: all_positions.extend(page_positions) # 如果当前页返回的数据条数不足15非第一页通常意味着已经是最后一页 if current_page 1 and len(page_positions) 15: logger.info(f第{current_page}页数据不足15条({len(page_positions)}条)视为最后一页停止爬取。) break # 翻页并等待随机时间间隔已在session_page.set.interval中设置这里可额外加一点 current_page 1 time.sleep(random.uniform(1, 3)) # 页间额外等待 logger.info(f爬取结束。共爬取{len(all_positions)}条职位信息。) return all_positions循环控制逻辑精讲页数计算 从第一页的响应中获取totalCount总职位数拉勾网每页固定15条由此计算出真实的总页数避免无意义的空请求。终止条件 两个关键终止条件a) 达到设定的最大页数max_pagesb)当前页非第一页返回的职位数少于15条。这是一个非常可靠的、判断是否到达末页的启发式规则。失败重试 单页失败后等待更长时间10秒再重试一次。如果还失败则可能IP被临时封禁应停止爬取避免进一步恶化。节奏控制 虽然session_page.set.interval控制了请求间隔但在翻页这个更宏观的层面再增加一个time.sleep(random.uniform(1, 3))让爬虫行为更“人性化”。6. 数据清洗、解析与存储爬取到的原始JSON数据包含很多字段我们需要提取关键信息并保存。6.1 定义数据解析函数def parse_position_info(position_dict): 从单条职位JSON数据中解析出我们需要的信息 # 使用字典的get方法安全获取值避免KeyError info { 职位ID: position_dict.get(positionId), 职位名称: position_dict.get(positionName), 公司名称: position_dict.get(companyFullName), 公司规模: position_dict.get(companySize), 融资阶段: position_dict.get(financeStage), 所在城市: position_dict.get(city), 工作区域: position_dict.get(district), 薪资范围: position_dict.get(salary), 工作经验: position_dict.get(workYear), 学历要求: position_dict.get(education), 职位标签: , .join(position_dict.get(positionLables, [])) if position_dict.get(positionLables) else , 技能标签: , .join(position_dict.get(skillLables, [])) if position_dict.get(skillLables) else , 职位优势: position_dict.get(positionAdvantage, ).replace(\n, ), # 替换换行符 发布时间: position_dict.get(createTime), 最后更新时间: position_dict.get(updateTime), 经度: position_dict.get(longitude), 纬度: position_dict.get(latitude), 业务类型: position_dict.get(industryField), 第一类别: position_dict.get(firstType), 第二类别: position_dict.get(secondType), 职位详情页URL: fhttps://www.lagou.com/jobs/{position_dict.get(positionId)}.html if position_dict.get(positionId) else , } return info字段处理技巧安全访问 一律使用.get(‘key’, default_value)方法。原始数据中某些字段可能缺失直接使用[‘key’]会引发程序崩溃。列表转字符串 像positionLables职位标签和skillLables技能标签是列表我们用‘ ‘.join(…)将其合并为一个用逗号分隔的字符串方便后续存入CSV。构造URL 职位详情页的URL有固定格式我们可以用职位ID拼出来无需从JSON里找。清洗文本 对于positionAdvantage职位优势这类可能包含换行符\n的文本字段将其替换为空格避免破坏CSV文件的结构。6.2 使用Pandas进行数据整合与存储def save_to_csv(data_list, filenamelagou_positions.csv): 将解析后的数据列表保存为CSV文件 if not data_list: logger.warning(没有数据可保存) return # 1. 将字典列表转换为Pandas DataFrame df pd.DataFrame(data_list) # 2. 数据清洗示例处理空值、去重 # 删除所有字段都为空的记录理论上不会有 df.dropna(howall, inplaceTrue) # 根据职位ID去重保留最新的一条按发布时间排序 df[发布时间] pd.to_datetime(df[发布时间], errorscoerce) df.sort_values(发布时间, ascendingFalse, inplaceTrue) df.drop_duplicates(subset[职位ID], keepfirst, inplaceTrue) # 3. 调整列的顺序可选让CSV更易读 columns_order [职位名称, 公司名称, 薪资范围, 所在城市, 工作经验, 学历要求, 发布时间, 职位标签, 技能标签, 职位详情页URL] # 只保留我们调整顺序的列其他列按原顺序附在后面 existing_columns [col for col in columns_order if col in df.columns] other_columns [col for col in df.columns if col not in existing_columns] final_columns existing_columns other_columns df df[final_columns] # 4. 保存到CSV文件 df.to_csv(filename, indexFalse, encodingutf-8-sig) # 使用utf-8-sig编码解决Excel打开中文乱码问题 logger.info(f数据已保存至 {filename} 共 {len(df)} 条记录。) print(df.head()) # 打印前几行预览 return dfPandas操作心得utf-8-sig编码 这是解决用Excel打开CSV文件时中文乱码的“银弹”。utf-8-sig会在文件开头添加一个BOM头Excel就能正确识别编码。数据去重 爬虫运行多次或翻页时可能有重复数据。根据职位ID去重是最可靠的。先按发布时间排序ascendingFalse降序最新的在前再drop_duplicates并keep’first’就能确保保留的是最新的一条记录。列顺序调整 把最重要的信息职位、公司、薪资放在CSV文件的前几列方便直接查看。这是一个提升数据可用性的小细节。7. 主函数与完整流程串联最后我们将所有模块组合起来形成一个完整的、可执行的脚本。def main(): 主函数串联整个爬取流程 # 初始化Session session SessionPage() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, }) session.set.timeout(10) session.set.retry(2) session.set.interval(2, 5) # 核心设置自动随机延迟 # 配置爬取参数 search_keyword Python search_city 上海 max_pages_to_crawl 5 # 首次测试可先爬5页 logger.info(f开始爬取拉勾网职位信息关键词: {search_keyword}, 城市: {search_city}) # 执行多页爬取 raw_position_list fetch_all_pages(session, search_keyword, search_city, max_pages_to_crawl) if not raw_position_list: logger.error(未能爬取到任何数据程序退出。) return # 解析每一条职位数据 parsed_data_list [] for pos in raw_position_list: parsed_info parse_position_info(pos) parsed_data_list.append(parsed_info) logger.info(f共解析了 {len(parsed_data_list)} 条职位信息。) # 保存数据 filename flagou_{search_keyword}_{search_city}_{time.strftime(%Y%m%d_%H%M%S)}.csv save_to_csv(parsed_data_list, filename) # 可选简单的数据摘要 if parsed_data_list: df_summary pd.DataFrame(parsed_data_list) print(\n 数据摘要 ) print(f城市分布:\n{df_summary[所在城市].value_counts().head()}) print(f\n薪资范围分布:\n{df_summary[薪资范围].value_counts().head()}) print(f\n热门技能标签前10:) # 将所有技能标签字符串合并再分割统计词频 all_skills .join(df_summary[技能标签].dropna().astype(str)).split(, ) from collections import Counter skill_counter Counter([s.strip() for s in all_skills if s.strip()]) for skill, count in skill_counter.most_common(10): print(f {skill}: {count}次) if __name__ __main__: main()完整流程要点模块化 每个函数职责单一如获取、解析、保存主函数main()清晰地将它们串联起来。这样的代码易于调试、维护和复用。文件命名 保存的CSV文件名包含了关键词、城市和时间戳如lagou_Python_上海_20231027_143022.csv这样多次运行也不会覆盖旧文件便于数据管理和归档。即时反馈 在控制台打印简单的数据摘要城市分布、薪资分布、热门技能让你在爬取完成后立刻对数据有个直观的了解而无需马上打开CSV文件。8. 高级技巧、常见问题与排查实录在实际操作中你几乎一定会遇到下面这些问题。这里是我总结的应对方案。8.1 应对反爬策略升级拉勾网的反爬机制不是一成不变的。如果你的爬虫突然失效可以按以下步骤排查和升级请求返回403或400错误数据为空检查点1请求头。用浏览器无痕模式访问拉勾网复制所有请求头特别是Cookie,User-Agent,Referer,X-Requested-With,X-Anti-Request-Token等。在代码中完全模拟。DrissionPage的session.headers.update()可以批量更新。检查点2动态参数。如果发现x-antispam-token这类动态变化的参数采用“Page模式先行”策略from DrissionPage import ChromiumOptions # 配置浏览器选项无头模式不显示图形界面 co ChromiumOptions() co.headless(True) # 设置为True则在后台运行 # 创建Page对象 page ChromiumPage(co) page.get(https://www.lagou.com/jobs/list_Python?city上海) # 等待页面加载完成特别是包含Token的JS执行完毕 page.wait.load_start() time.sleep(3) # 适当等待 # 关键从Page对象中提取cookies和headers应用到Session对象 # DrissionPage 可以方便地传递cookies # 假设Token在某个全局变量或Meta标签里这里需要你根据实际情况定位元素 # 例如token page.ele(tag:meta[nameanti-token]).attr(content) # session_page.headers[X-Anti-Request-Token] token # 更简单的方式直接用这个Page对象去请求接口效率低但可靠 # json_data page.get(接口URL).json() # 拿到数据后可以关闭浏览器 page.quit()IP被限制返回验证码页面根本解决方法使用代理IP池。DrissionPage的Session模式可以方便地设置代理。proxy { http: http://your-proxy-ip:port, https: https://your-proxy-ip:port, } session_page.set.proxies(proxy) # 为本次会话设置代理 # 或者对单个请求设置session_page.get(url, proxiesproxy)行为模拟 确保set.interval()设置的随机延迟足够大例如3-8秒。不要在短时间内发起大量请求。模拟人的浏览节奏比如每爬取3-5页随机休眠30-60秒。8.2 数据解析中的细节处理薪资字段解析 拉勾网的薪资是“8k-16k”这样的字符串。如果你想进行数值分析计算平均薪资需要解析它def parse_salary(salary_str): 将‘8k-16k’解析为最低和最高薪资单位千 if not salary_str or k not in salary_str: return None, None try: # 去除‘k’按‘-’分割 parts salary_str.lower().replace(k, ).split(-) if len(parts) 2: low float(parts[0]) high float(parts[1]) return low, high elif len(parts) 1: # 可能为‘15k以上’ val float(parts[0]) return val, None except: pass return None, None # 在parse_position_info函数中调用并新增‘最低薪资’‘最高薪资’两列公司福利标签positionLables字段包含“五险一金”、“年终奖”、“带薪年假”等标签。你可以统计哪些福利最常出现作为分析公司吸引力的一个维度。8.3 稳定性与可维护性优化实现断点续爬 爬取大量数据时网络中断或程序崩溃很常见。可以设计一个检查点机制将已爬取的页码或职位ID记录到一个文件如checkpoint.json中。程序启动时读取这个文件跳过已爬取的内容。将配置参数外置 不要将关键词、城市、最大页数等硬编码在脚本里。可以创建一个config.py文件或使用argparse库接收命令行参数这样不用修改代码就能改变爬取目标。# 使用argparse示例 import argparse parser argparse.ArgumentParser(description拉勾网职位爬虫) parser.add_argument(--keyword, typestr, defaultPython, help搜索关键词) parser.add_argument(--city, typestr, default上海, help城市) parser.add_argument(--pages, typeint, default10, help最大爬取页数) args parser.parse_args() # 然后在main()中使用 args.keyword, args.city, args.pages数据存储多样化 除了保存为CSV你还可以直接存入数据库如SQLite、MySQL。Pandas的DataFrame有to_sql方法可以很方便地实现。import sqlite3 # 建立连接 conn sqlite3.connect(lagou_jobs.db) # 将DataFrame写入数据库表如果表存在则替换 df.to_sql(positions, conn, if_existsreplace, indexFalse) conn.close()8.4 常见错误速查表错误现象可能原因解决方案返回{success:false, msg:您操作太频繁,请稍后再访问}请求频率过高IP被临时限制。1. 大幅增加set.interval()的等待时间范围如5-15秒。2. 每爬几页后用time.sleep(random.randint(30, 60))长休眠一次。3. 使用代理IP。只能爬到第一页数据后续页为空first参数设置错误或翻页时Referer等头信息未更新。1. 确保第一页firsttrue后续页firstfalse。2. 翻页时更新Referer为新的列表页URL。3. 检查页码pn参数是否正确递增。status_code为403请求头不完整或被识别为爬虫动态Token缺失。1. 使用Page模式先获取一次页面捕获完整的Headers和Cookies。2. 检查并补齐所有必要的请求头特别是从浏览器复制来的那些。解析JSON时出错服务器返回的不是JSON可能是HTML错误页面如验证码。1. 在解析前打印resp_page.text[:500]看看返回了什么。2. 如果是HTML说明触发了反爬需要按上述方法处理。3. 增加异常处理跳过错误页面。程序运行一段时间后卡死或无响应可能内存泄漏或网络连接未正常关闭。1. 确保在try...except...finally块中或使用with语句管理资源。2. 对于ChromiumPage对象爬取完成后务必调用.quit()关闭浏览器。3. 为爬虫设置总超时时间。这个项目从环境搭建到数据落地的完整流程就是这样。DrissionPage这个库确实极大地简化了在“简单请求”和“浏览器模拟”两种模式间切换的复杂度让爬虫的编写更聚焦于业务逻辑本身。最深的体会是面对现代网站的反爬尊重规则、模拟真人、留有冗余是长期稳定运行的关键。不要试图用极限速度去挑战对方的防御底线一个每天能稳定跑8小时的中速爬虫远比一个全速跑10分钟就被封的爬虫有价值得多。

相关新闻