Python爬虫第6课:Selenium自动化浏览器与动态内容抓取

目录

    • 课程目标
    • [1. Selenium简介](#1. Selenium简介)
      • [1.1 为什么需要Selenium?](#1.1 为什么需要Selenium?)
      • [1.2 安装Selenium](#1.2 安装Selenium)
      • [1.3 基本使用](#1.3 基本使用)
    • [2. 浏览器驱动配置](#2. 浏览器驱动配置)
      • [2.1 Chrome驱动配置](#2.1 Chrome驱动配置)
      • [2.2 Firefox驱动配置](#2.2 Firefox驱动配置)
    • [3. 元素定位方法](#3. 元素定位方法)
      • [3.1 基本定位方法](#3.1 基本定位方法)
      • [3.2 查找多个元素](#3.2 查找多个元素)
      • [3.3 等待元素出现](#3.3 等待元素出现)
    • [4. 用户行为模拟](#4. 用户行为模拟)
      • [4.1 基本操作](#4.1 基本操作)
      • [4.2 高级操作](#4.2 高级操作)
      • [4.3 处理弹窗和框架](#4.3 处理弹窗和框架)
    • [5. 实战案例:爬取动态加载的商品列表](#5. 实战案例:爬取动态加载的商品列表)
    • [6. 处理常见问题](#6. 处理常见问题)
      • [6.1 反爬虫检测](#6.1 反爬虫检测)
      • [6.2 处理验证码](#6.2 处理验证码)
      • [6.3 性能优化](#6.3 性能优化)
    • [7. 实践练习](#7. 实践练习)
    • [8. 课程小结](#8. 课程小结)
    • [9. 下节预告](#9. 下节预告)
    • [10. 作业](#10. 作业)

专栏导读

🌸 欢迎来到Python办公自动化专栏---Python处理办公问题,解放您的双手
🏳️‍🌈 个人博客主页:请点击------> 个人的博客主页 求收藏
🏳️‍🌈 Github主页:请点击------> Github主页 求Star⭐
🏳️‍🌈 知乎主页:请点击------> 知乎主页 求关注
🏳️‍🌈 CSDN博客主页:请点击------> CSDN的博客主页 求关注
👍 该系列文章专栏:请点击------>Python办公自动化专栏 求订阅
🕷 此外还有爬虫专栏:请点击------>Python爬虫基础专栏 求订阅
📕 此外还有python基础专栏:请点击------>Python基础学习专栏 求订阅
文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
❤️ 欢迎各位佬关注! ❤️

课程目标

  • 掌握Selenium的安装和基本使用
  • 学会处理JavaScript动态生成的内容
  • 理解浏览器自动化的原理和应用
  • 掌握模拟用户行为的技巧

1. Selenium简介

Selenium是一个用于自动化浏览器操作的工具,它可以模拟真实用户的行为,处理JavaScript动态生成的内容。

1.1 为什么需要Selenium?

  • 处理JavaScript动态内容
  • 模拟用户交互(点击、输入、滚动等)
  • 处理AJAX请求
  • 绕过一些反爬虫机制
  • 截图和页面监控

1.2 安装Selenium

bash 复制代码
# 安装Selenium
pip install selenium

# 下载浏览器驱动
# Chrome: https://chromedriver.chromium.org/
# Firefox: https://github.com/mozilla/geckodriver/releases
# Edge: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/

1.3 基本使用

python 复制代码
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在PATH中

try:
    # 访问网页
    driver.get("https://example.com")
    
    # 查找元素
    element = driver.find_element(By.ID, "element-id")
    
    # 获取文本
    text = element.text
    print(text)
    
finally:
    # 关闭浏览器
    driver.quit()

2. 浏览器驱动配置

2.1 Chrome驱动配置

python 复制代码
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service

def create_chrome_driver():
    """创建Chrome驱动"""
    chrome_options = Options()
    
    # 无头模式(不显示浏览器窗口)
    chrome_options.add_argument('--headless')
    
    # 禁用图片加载(提高速度)
    chrome_options.add_argument('--blink-settings=imagesEnabled=false')
    
    # 禁用JavaScript(如果不需要)
    # chrome_options.add_argument('--disable-javascript')
    
    # 设置窗口大小
    chrome_options.add_argument('--window-size=1920,1080')
    
    # 禁用GPU加速
    chrome_options.add_argument('--disable-gpu')
    
    # 禁用开发者工具
    chrome_options.add_argument('--disable-dev-shm-usage')
    
    # 设置用户代理
    chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36')
    
    # 指定驱动路径(如果不在PATH中)
    service = Service('path/to/chromedriver.exe')
    
    driver = webdriver.Chrome(service=service, options=chrome_options)
    
    # 设置隐式等待
    driver.implicitly_wait(10)
    
    return driver

# 使用示例
driver = create_chrome_driver()

2.2 Firefox驱动配置

python 复制代码
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service

def create_firefox_driver():
    """创建Firefox驱动"""
    firefox_options = Options()
    
    # 无头模式
    firefox_options.add_argument('--headless')
    
    # 禁用图片
    firefox_options.set_preference('permissions.default.image', 2)
    
    # 禁用CSS
    firefox_options.set_preference('permissions.default.stylesheet', 2)
    
    # 设置用户代理
    firefox_options.set_preference('general.useragent.override', 
                                 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0')
    
    service = Service('path/to/geckodriver.exe')
    
    driver = webdriver.Firefox(service=service, options=firefox_options)
    driver.implicitly_wait(10)
    
    return driver

3. 元素定位方法

3.1 基本定位方法

python 复制代码
from selenium.webdriver.common.by import By

# 通过ID定位
element = driver.find_element(By.ID, "element-id")

# 通过类名定位
element = driver.find_element(By.CLASS_NAME, "class-name")

# 通过标签名定位
element = driver.find_element(By.TAG_NAME, "div")

# 通过名称定位
element = driver.find_element(By.NAME, "element-name")

# 通过链接文本定位
element = driver.find_element(By.LINK_TEXT, "链接文本")

# 通过部分链接文本定位
element = driver.find_element(By.PARTIAL_LINK_TEXT, "部分文本")

# 通过CSS选择器定位
element = driver.find_element(By.CSS_SELECTOR, ".class-name")

# 通过XPath定位
element = driver.find_element(By.XPATH, "//div[@class='class-name']")

3.2 查找多个元素

python 复制代码
# 查找所有匹配的元素
elements = driver.find_elements(By.CLASS_NAME, "item")

for element in elements:
    print(element.text)

# 在特定元素内查找子元素
parent = driver.find_element(By.ID, "parent-id")
child = parent.find_element(By.CLASS_NAME, "child-class")

3.3 等待元素出现

python 复制代码
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

def wait_for_element(driver, locator, timeout=10):
    """等待元素出现"""
    try:
        element = WebDriverWait(driver, timeout).until(
            EC.presence_of_element_located(locator)
        )
        return element
    except TimeoutException:
        print(f"元素 {locator} 在 {timeout} 秒内未出现")
        return None

# 使用示例
element = wait_for_element(driver, (By.ID, "dynamic-content"))

# 等待元素可点击
clickable_element = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "button-id"))
)

# 等待元素可见
visible_element = WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.CLASS_NAME, "popup"))
)

4. 用户行为模拟

4.1 基本操作

python 复制代码
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

# 点击元素
element = driver.find_element(By.ID, "button")
element.click()

# 输入文本
input_field = driver.find_element(By.NAME, "username")
input_field.clear()  # 清空输入框
input_field.send_keys("用户名")

# 提交表单
form = driver.find_element(By.ID, "login-form")
form.submit()

# 按键操作
input_field.send_keys(Keys.ENTER)
input_field.send_keys(Keys.TAB)
input_field.send_keys(Keys.ESCAPE)

# 组合键
input_field.send_keys(Keys.CONTROL + "a")  # Ctrl+A
input_field.send_keys(Keys.CONTROL + "c")  # Ctrl+C

4.2 高级操作

python 复制代码
# 鼠标操作
actions = ActionChains(driver)

# 悬停
element = driver.find_element(By.ID, "hover-element")
actions.move_to_element(element).perform()

# 右键点击
actions.context_click(element).perform()

# 双击
actions.double_click(element).perform()

# 拖拽
source = driver.find_element(By.ID, "source")
target = driver.find_element(By.ID, "target")
actions.drag_and_drop(source, target).perform()

# 滚动到元素
driver.execute_script("arguments[0].scrollIntoView();", element)

# 滚动页面
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

4.3 处理弹窗和框架

python 复制代码
# 处理JavaScript弹窗
try:
    alert = driver.switch_to.alert
    alert_text = alert.text
    print(f"弹窗内容:{alert_text}")
    alert.accept()  # 点击确定
    # alert.dismiss()  # 点击取消
except:
    print("没有弹窗")

# 处理iframe
iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe)

# 在iframe中操作
element_in_iframe = driver.find_element(By.ID, "element-in-iframe")

# 切换回主页面
driver.switch_to.default_content()

# 处理新窗口
original_window = driver.current_window_handle

# 点击打开新窗口的链接
driver.find_element(By.LINK_TEXT, "新窗口").click()

# 等待新窗口出现
WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) > 1)

# 切换到新窗口
for window_handle in driver.window_handles:
    if window_handle != original_window:
        driver.switch_to.window(window_handle)
        break

# 在新窗口中操作
print(driver.title)

# 关闭新窗口并切换回原窗口
driver.close()
driver.switch_to.window(original_window)

5. 实战案例:爬取动态加载的商品列表

python 复制代码
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
from selenium.webdriver.chrome.options import Options
import time
import json
import random

class DynamicProductSpider:
    def __init__(self, headless=True):
        self.driver = self.create_driver(headless)
        self.products = []
    
    def create_driver(self, headless):
        """创建浏览器驱动"""
        chrome_options = Options()
        
        if headless:
            chrome_options.add_argument('--headless')
        
        chrome_options.add_argument('--no-sandbox')
        chrome_options.add_argument('--disable-dev-shm-usage')
        chrome_options.add_argument('--disable-blink-features=AutomationControlled')
        chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
        chrome_options.add_experimental_option('useAutomationExtension', False)
        
        driver = webdriver.Chrome(options=chrome_options)
        driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
        
        return driver
    
    def scroll_and_load(self, max_scrolls=10):
        """滚动页面加载更多内容"""
        last_height = self.driver.execute_script("return document.body.scrollHeight")
        scrolls = 0
        
        while scrolls < max_scrolls:
            # 滚动到页面底部
            self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            
            # 等待新内容加载
            time.sleep(random.uniform(2, 4))
            
            # 检查是否有新内容
            new_height = self.driver.execute_script("return document.body.scrollHeight")
            
            if new_height == last_height:
                # 尝试点击"加载更多"按钮
                try:
                    load_more_btn = self.driver.find_element(By.CLASS_NAME, "load-more")
                    if load_more_btn.is_displayed():
                        load_more_btn.click()
                        time.sleep(3)
                        continue
                except:
                    break
            
            last_height = new_height
            scrolls += 1
            print(f"已滚动 {scrolls} 次,页面高度:{new_height}")
    
    def extract_products(self):
        """提取商品信息"""
        try:
            # 等待商品列表加载
            WebDriverWait(self.driver, 10).until(
                EC.presence_of_element_located((By.CLASS_NAME, "product-item"))
            )
            
            # 获取所有商品元素
            product_elements = self.driver.find_elements(By.CLASS_NAME, "product-item")
            print(f"找到 {len(product_elements)} 个商品")
            
            for element in product_elements:
                product_data = self.extract_single_product(element)
                if product_data:
                    self.products.append(product_data)
            
        except Exception as e:
            print(f"提取商品失败:{e}")
    
    def extract_single_product(self, element):
        """提取单个商品信息"""
        try:
            # 商品名称
            name_elem = element.find_element(By.CLASS_NAME, "product-name")
            name = name_elem.text.strip()
            
            # 商品价格
            price_elem = element.find_element(By.CLASS_NAME, "product-price")
            price = price_elem.text.strip()
            
            # 商品链接
            link_elem = element.find_element(By.TAG_NAME, "a")
            link = link_elem.get_attribute("href")
            
            # 商品图片
            img_elem = element.find_element(By.TAG_NAME, "img")
            image_url = img_elem.get_attribute("src")
            
            # 评分(可能不存在)
            try:
                rating_elem = element.find_element(By.CLASS_NAME, "rating")
                rating = rating_elem.get_attribute("data-rating")
            except:
                rating = ""
            
            # 销量(可能不存在)
            try:
                sales_elem = element.find_element(By.CLASS_NAME, "sales")
                sales = sales_elem.text.strip()
            except:
                sales = ""
            
            return {
                'name': name,
                'price': price,
                'link': link,
                'image_url': image_url,
                'rating': rating,
                'sales': sales
            }
            
        except Exception as e:
            print(f"提取单个商品失败:{e}")
            return None
    
    def search_products(self, keyword, max_pages=3):
        """搜索商品"""
        try:
            # 访问搜索页面
            search_url = f"https://example-shop.com/search?q={keyword}"
            self.driver.get(search_url)
            
            # 等待页面加载
            time.sleep(3)
            
            for page in range(max_pages):
                print(f"正在处理第 {page + 1} 页...")
                
                # 滚动加载更多内容
                self.scroll_and_load()
                
                # 提取当前页面的商品
                self.extract_products()
                
                # 尝试翻页
                try:
                    next_btn = self.driver.find_element(By.CLASS_NAME, "next-page")
                    if next_btn.is_enabled():
                        next_btn.click()
                        time.sleep(3)
                    else:
                        break
                except:
                    break
            
        except Exception as e:
            print(f"搜索商品失败:{e}")
    
    def handle_login(self, username, password):
        """处理登录"""
        try:
            # 点击登录按钮
            login_btn = self.driver.find_element(By.CLASS_NAME, "login-btn")
            login_btn.click()
            
            # 等待登录表单出现
            WebDriverWait(self.driver, 10).until(
                EC.presence_of_element_located((By.NAME, "username"))
            )
            
            # 输入用户名和密码
            username_field = self.driver.find_element(By.NAME, "username")
            password_field = self.driver.find_element(By.NAME, "password")
            
            username_field.send_keys(username)
            time.sleep(1)
            password_field.send_keys(password)
            time.sleep(1)
            
            # 提交表单
            submit_btn = self.driver.find_element(By.CLASS_NAME, "submit-btn")
            submit_btn.click()
            
            # 等待登录完成
            WebDriverWait(self.driver, 10).until(
                EC.presence_of_element_located((By.CLASS_NAME, "user-info"))
            )
            
            print("登录成功")
            return True
            
        except Exception as e:
            print(f"登录失败:{e}")
            return False
    
    def save_products(self, filename='products.json'):
        """保存商品数据"""
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(self.products, f, ensure_ascii=False, indent=2)
        
        print(f"已保存 {len(self.products)} 个商品到 {filename}")
    
    def close(self):
        """关闭浏览器"""
        self.driver.quit()

# 使用示例
if __name__ == "__main__":
    spider = DynamicProductSpider(headless=False)  # 显示浏览器窗口用于调试
    
    try:
        # 搜索商品
        spider.search_products("手机", max_pages=3)
        
        # 保存结果
        spider.save_products()
        
    finally:
        spider.close()

6. 处理常见问题

6.1 反爬虫检测

python 复制代码
def setup_stealth_driver():
    """设置隐蔽的浏览器驱动"""
    chrome_options = Options()
    
    # 禁用自动化标识
    chrome_options.add_argument('--disable-blink-features=AutomationControlled')
    chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
    chrome_options.add_experimental_option('useAutomationExtension', False)
    
    # 随机用户代理
    user_agents = [
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
    ]
    chrome_options.add_argument(f'--user-agent={random.choice(user_agents)}')
    
    driver = webdriver.Chrome(options=chrome_options)
    
    # 移除webdriver属性
    driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
    
    return driver

6.2 处理验证码

python 复制代码
def handle_captcha(driver):
    """处理验证码"""
    try:
        # 检查是否有验证码
        captcha_img = driver.find_element(By.ID, "captcha-image")
        
        if captcha_img.is_displayed():
            print("检测到验证码,需要人工处理")
            
            # 截图保存验证码
            captcha_img.screenshot("captcha.png")
            
            # 等待用户手动输入验证码
            input("请手动输入验证码后按回车继续...")
            
    except:
        pass  # 没有验证码

6.3 性能优化

python 复制代码
def optimize_driver_performance():
    """优化驱动性能"""
    chrome_options = Options()
    
    # 禁用图片加载
    prefs = {
        "profile.managed_default_content_settings.images": 2,
        "profile.default_content_setting_values.notifications": 2,
        "profile.managed_default_content_settings.media_stream": 2,
    }
    chrome_options.add_experimental_option("prefs", prefs)
    
    # 禁用扩展
    chrome_options.add_argument('--disable-extensions')
    
    # 禁用插件
    chrome_options.add_argument('--disable-plugins')
    
    # 禁用GPU
    chrome_options.add_argument('--disable-gpu')
    
    # 设置页面加载策略
    chrome_options.page_load_strategy = 'eager'  # 不等待所有资源加载完成
    
    return webdriver.Chrome(options=chrome_options)

7. 实践练习

练习1:模拟登录

编写程序模拟登录某个网站,并爬取登录后的内容。

练习2:处理无限滚动

处理无限滚动的页面,自动加载所有内容。

练习3:表单自动填写

编写程序自动填写复杂的表单并提交。

8. 课程小结

本课程我们学习了:

  1. Selenium的安装和基本配置
  2. 浏览器驱动的设置和优化
  3. 元素定位和等待机制
  4. 用户行为模拟技术
  5. 处理动态内容和JavaScript
  6. 反爬虫检测的应对方法

9. 下节预告

下一课我们将学习:

  • 多线程和异步爬虫技术
  • 提高爬虫效率的方法
  • 并发控制和资源管理
  • 分布式爬虫基础

10. 作业

  1. 使用Selenium爬取一个需要JavaScript的网站
  2. 实现自动翻页功能
  3. 处理包含验证码的登录流程
  4. 优化Selenium的性能和稳定性

提示:Selenium虽然功能强大,但相对较慢,应该在必要时才使用,能用requests解决的问题优先使用requests。

结尾

希望对初学者有帮助;致力于办公自动化的小小程序员一枚
希望能得到大家的【❤️一个免费关注❤️】感谢!
求个 🤞 关注 🤞 +❤️ 喜欢 ❤️ +👍 收藏 👍
此外还有办公自动化专栏,欢迎大家订阅:Python办公自动化专栏
此外还有爬虫专栏,欢迎大家订阅:Python爬虫基础专栏
此外还有Python基础专栏,欢迎大家订阅:Python基础学习专栏
相关推荐
番石榴AI3 小时前
自己动手做一款ChatExcel数据分析系统,智能分析 Excel 数据
人工智能·python·数据挖掘·excel
星期天要睡觉4 小时前
深度学习——循环神经网络(RNN)
人工智能·python·rnn·深度学习·神经网络
Blossom.1184 小时前
把AI“撒”进农田:基于极值量化与状态机的1KB边缘灌溉决策树
人工智能·python·深度学习·算法·目标检测·决策树·机器学习
Red Car4 小时前
如何向文件夹内所有PDF增加水印
python·pdf
Q_Q5110082854 小时前
python+uniapp基于微信小程序团购系统
spring boot·python·微信小程序·django·uni-app·node.js·php
java1234_小锋4 小时前
TensorFlow2 Python深度学习 - 循环神经网络(LSTM)示例
python·rnn·深度学习·tensorflow2
测试老哥5 小时前
Postman环境变量设置全攻略
自动化测试·软件测试·python·测试工具·职场和发展·接口测试·postman
惜月_treasure6 小时前
Text2SQL与工作流实现:让数据库查询变得轻松又高效
数据库·人工智能·python
码猩6 小时前
获取dm音视频文案
python