1. 爬虫工具选型requests与selenium的核心差异第一次用requests爬电影网站时我被它的速度惊艳到了——不到3秒就抓完10页数据。但当我遇到需要登录的页面时立刻傻眼了。这就是requests和selenium最根本的区别一个像闪电侠一个像瑞士军刀。速度与效率的较量requests直接发送HTTP请求获取HTML省去了渲染环节。实测爬取ssr1.scrape.center的10页电影数据requests平均耗时2.8秒而selenium需要42秒。但当你需要抓取豆瓣电影评分这种需要执行JavaScript的页面时requests就无能为力了。静态与动态的抉择最近帮朋友抓取一个电影排期网站页面数据是通过AJAX加载的。用requests只能拿到空壳HTML这时候selenium的浏览器渲染能力就成了救命稻草。记得加上options.add_argument(--headless)启用无头模式能节省不少资源。# requests极简示例 import requests response requests.get(https://ssr1.scrape.center/page/1) print(response.text[:500]) # 查看前500字符 # selenium基础配置 from selenium import webdriver options webdriver.ChromeOptions() options.add_argument(--headless) driver webdriver.Chrome(optionsoptions) driver.get(https://ssr1.scrape.center) print(driver.page_source[:500])2. 数据处理实战XPath的妙用与陷阱新手最常踩的坑就是XPath定位失败。上周我还遇到个案例有个开发者用//div[classname]/text()死活抓不到数据原来页面里有多个同名class。教他改用//div[contains(class,primary)]/h2/text()后问题立刻解决。文本提取的艺术在电影类型抓取时遇到标签嵌套不规则的情况div classcategories span动作/span span科幻/span /div用//text()会包含多余空白我推荐这样处理types [t.strip() for t in html.xpath(//div[classcategories]//text()) if t.strip()]空值处理的黄金法则永远不要直接取索引0这是我用5次爬虫崩溃换来的教训。正确的做法# 危险写法 # score html.xpath(//p[classscore]/text())[0] # 安全写法 score_nodes html.xpath(//p[classscore]/text()) score score_nodes[0] if score_nodes else None3. 翻页策略对比两种技术的分水岭requests的翻页像坐电梯——直接跳转到目标楼层。而selenium则像爬楼梯——必须一步步点击下一页。去年优化一个项目时我发现requests的URL参数翻页比selenium的点击操作快20倍。requests的优雅分页base_url https://ssr1.scrape.center/page/{} for page in range(1, 11): url base_url.format(page) response requests.get(url) # 处理数据...selenium的智能终止很多人不知道可以用expected_conditions判断元素是否存在from selenium.webdriver.support import expected_conditions as EC while True: # ...处理当前页数据 try: next_btn WebDriverWait(driver, 5).until( EC.presence_of_element_located((By.XPATH, //a[contains(class,next)])) ) next_btn.click() except: break4. 异常处理实战手册上个月有个学员的爬虫半夜崩溃原因是网站改版导致XPath失效。这提醒我们健壮的异常处理不是可选项而是必选项。requests的异常矩阵try: response requests.get(url, timeout10) response.raise_for_status() # 检查HTTP状态码 except requests.exceptions.Timeout: print(f请求超时{url}) except requests.exceptions.TooManyRedirects: print(重定向次数过多) except requests.exceptions.RequestException as e: print(f致命错误{str(e)})selenium的防御编程推荐使用POMPage Object Model模式把定位器集中管理class MoviePage: TITLE_XPATH //h2[classmovie-title] def __init__(self, driver): self.driver driver def get_title(self): try: return self.driver.find_element(By.XPATH, self.TITLE_XPATH).text except NoSuchElementException: return None5. 性能优化从入门到专业最近给某影视公司做爬虫优化把他们的selenium脚本从每小时500条提升到3500条。关键技巧是混合使用两种技术混合架构设计用requests快速获取列表页URL仅对需要JS渲染的详情页使用selenium使用concurrent.futures实现并行处理from concurrent.futures import ThreadPoolExecutor def crawl_movie(url): if detail in url: return selenium_crawl(url) return requests_crawl(url) with ThreadPoolExecutor(max_workers5) as executor: results list(executor.map(crawl_movie, url_list))浏览器资源管理selenium最耗内存记得总是用try-finally保证退出driver webdriver.Chrome() try: # 你的代码... finally: driver.quit() # 重要否则会残留进程6. 反爬应对策略虽然ssr1.scrape.center没有反爬但真实项目中必须考虑这些requests的伪装技巧headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept-Language: zh-CN,zh;q0.9, Referer: https://www.google.com/ } cookies {session_id: fake_session_123} proxies {http: http://10.10.1.10:3128}selenium的隐身模式options webdriver.ChromeOptions() options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) driver webdriver.Chrome(optionsoptions)7. 数据存储与后续处理爬下来的电影数据如果乱存一通后续处理会很痛苦。我推荐这样的结构化存储SQLite存储示例import sqlite3 conn sqlite3.connect(movies.db) cursor conn.cursor() cursor.execute(CREATE TABLE IF NOT EXISTS movies (name TEXT, type TEXT, score REAL, area TEXT)) # 插入数据 cursor.execute(INSERT INTO movies VALUES (?,?,?,?), (movie[name], movie[type], movie[score], movie[area])) conn.commit()Pandas清洗技巧处理抓取到的脏数据时import pandas as pd df pd.DataFrame(data_list) # 去除空值 df df.dropna(subset[name]) # 类型转换 df[score] pd.to_numeric(df[score], errorscoerce) # 保存 df.to_csv(cleaned_movies.csv, indexFalse)8. 项目实战电影数据抓取完整流程去年用混合方案给某影视分析平台抓取了Top 5000部电影数据核心逻辑如下阶段式抓取策略第一轮用requests快速扫描所有列表页收集电影ID第二轮用selenium并行处理详情页第三轮异常重试机制def get_movie_ids(): # requests快速获取所有ID ids [] for page in range(1, 51): html requests.get(fhttps://ssr1.scrape.center/page/{page}).text ids.extend(re.findall(rhref/detail/(\d), html)) return ids def get_detail(movie_id): # selenium处理详情页 driver.get(fhttps://ssr1.scrape.center/detail/{movie_id}) # ...解析数据... return movie_data性能对比表指标requests方案selenium方案混合方案完成时间(5000条)无法完成6.8小时2.1小时内存占用峰值120MB1.2GB450MB代码复杂度★★☆☆☆★★★☆☆★★★★☆9. 调试技巧与开发工具新手最常问为什么我的XPath不工作推荐这些调试方法requests调试技巧# 保存原始HTML用于分析 with open(debug.html, w, encodingutf-8) as f: f.write(response.text) # 使用BeautifulSoup辅助调试 from bs4 import BeautifulSoup soup BeautifulSoup(response.text, lxml) print(soup.find_all(div, class_movie-item))selenium的调试利器# 截图功能 driver.save_screenshot(debug.png) # 控制台执行JS console_log driver.execute_script(return console.log(调试信息)) # 元素高亮脚本 def highlight(element): driver.execute_script(arguments[0].style.border3px solid red, element)10. 最佳实践与经验总结五年爬虫开发让我总结出这些铁律先静态后动态永远先尝试requests方案必要时才用selenium异常处理三原则记录原始错误、保留现场证据、实现自动恢复数据验证机制对关键字段设置验证规则比如电影评分应在0-10之间速率控制即使没有反爬也建议添加time.sleep(random.uniform(1,3))最后分享一个真实案例有次用selenium抓取时页面加载总是不完整。后来发现是响应式布局的问题通过设置浏览器窗口大小解决了driver.set_window_size(1920, 1080) # 确保PC端布局加载爬虫开发就像侦探工作需要耐心和技巧。当你遇到问题时记住所有动态渲染的页面最终都会生成静态DOM关键是要找到正确的观察角度。