基于Selenium的通用分页数据爬虫实战(处理反爬机制)

先看成功实现的效果!!


先大概说一下我在爬取这个网站时的经历,一开始我是打算直接通过接口爬取这个网站的数据,但是发现里面的接口和cookie都做了反爬机制。

然后我配置了本地代理,想从多种方法绕过反爬机制,但是都没有用,最后是通过selenium模拟人为操作分页爬取,但是一开始有一个问题就是:始终爬的都是第一页的数据,后面通过校对每一页的数据进行验证,修改换页爬取的逻辑,但是又有一个问题,就是不知道是电脑问题还是驱动浏览器的问题,爬到20页的时候,报错提示浏览器崩溃了,然后最麻烦的一部分,就是定时关闭并重启浏览器,重启后再对当前关闭的断点进行继续爬取,通过这些慢慢优化才最终成功完成,代码结尾部分还有一些页面判断的逻辑没有完善,因为接口说的是200多页,但是实际只有二十多页,但是数据是完整的,应该是前端和后端的处理问题,所以后续完善的话会对当前页面没有数据的页面进行判断后关闭当前程序。

 

一、应用场景

本文介绍一种基于Selenium的通用型分页数据采集方案,适用于需要处理以下场景的数据采集任务:

  • 基于Ajax动态加载的网页
  • 需要登录或cookie验证的网站
  • 分页结构复杂的接口调用
  • 需要人机行为模拟的采集场景

二、核心功能

本爬虫框架实现以下关键功能:

  1. 智能分页控制:自动处理分页逻辑
  2. 断点续爬:异常中断后可从断点恢复
  3. 反检测机制:模拟人类操作特征
  4. 数据持久化:实时保存采集结果
  5. 异常重试:网络波动自动恢复

三、技术栈

  • Selenium 4.0+
  • ChromeDriver
  • Pandas 数据存储
  • WebDriver Manager 自动驱动管理

四、实现原理

五、代码解析(核心部分)

1. 浏览器初始化配置

def init_driver():
    options = Options()
    options.add_argument("--disable-blink-features=AutomationControlled")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    
    driver = webdriver.Chrome(
        service=Service(ChromeDriverManager().install()),
        options=options
    )
    
    # 隐藏自动化特征
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": """
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        })
        """
    })
    return driver

2. 分页采集逻辑
 

def pagination_crawl(start_page=1):
    current_page = start_page
    while current_page <= MAX_PAGES:
        try:
            data = get_page_data(current_page)
            save_temp_data(data, current_page)
            current_page += 1
            random_sleep()
        except Exception as e:
            handle_error(e, current_page)

3. 智能等待机制

def random_sleep(base=3, variance=5):
    """随机等待函数"""
    sleep_time = base + random.random() * variance
    time.sleep(sleep_time)
    
    # 每10页增加等待
    if current_page % 10 == 0:
        time.sleep(max(15, sleep_time*2))

六、完整代码(网站数据不方便透露,已隐藏,仅供参考学习)
 

import pandas as pd
import time
import random
import json
import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager


def get_player_stats_with_selenium(page=1, max_pages=100, max_retries=3, output_file="player_stats.csv"):
    """通用分页数据采集函数"""
    all_players = []
    current_page = page
    retry_count = 0
    driver = None

    # 检查已有数据文件
    if os.path.exists(output_file):
        try:
            existing_df = pd.read_csv(output_file, encoding='utf-8-sig')
            all_players = existing_df.to_dict('records')
            print(f"已读取现有数据: {len(existing_df)} 条")
        except Exception as e:
            print(f"读取文件出错: {str(e)}")

    while current_page <= max_pages:
        try:
            if driver is None:
                # 浏览器配置
                chrome_options = Options()
                chrome_options.add_argument("--disable-gpu")
                chrome_options.add_argument("--no-sandbox")
                chrome_options.add_argument("--window-size=1920,1080")
                chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])

                # 初始化驱动
                driver = webdriver.Chrome(
                    service=Service(ChromeDriverManager().install()),
                    options=chrome_options
                )

                # 隐藏自动化特征
                driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
                    "source": """
                        Object.defineProperty(navigator, 'webdriver', {
                            get: () => undefined
                        })
                    """
                })

                # 示例页面访问(需替换实际地址)
                driver.get("https://example.com")
                time.sleep(random.uniform(2, 4))

            print(f"正在处理第 {current_page} 页...")

            # 示例API请求(需替换实际地址)
            api_url = f"https://api.example.com/data?page={current_page}"
            
            # 获取数据
            driver.get("https://example.com/api_entry")
            time.sleep(1)
            
            # 执行数据请求脚本
            script = f"""
            return new Promise((resolve, reject) => {{
                const xhr = new XMLHttpRequest();
                xhr.open('GET', '{api_url}', true);
                xhr.onload = function() {{
                    if (xhr.status === 200) {{
                        resolve(xhr.responseText);
                    }} else {{
                        reject(new Error('请求失败'));
                    }}
                }};
                xhr.send(null);
            }});
            """
            
            response_text = driver.execute_script(script)
            data = json.loads(response_text)
            players = data.get('items', [])

            if not players:
                print(f"第 {current_page} 页无数据")
                if retry_count < max_retries:
                    retry_count += 1
                    continue
                else:
                    break

            # 处理数据
            retry_count = 0
            all_players.extend(players)
            print(f"已获取 {len(players)} 条数据")

            # 实时保存
            all_df = pd.DataFrame(all_players)
            all_df = all_df.drop_duplicates(subset=['id'])
            all_df = translate_fields(all_df)
            all_df.to_csv(output_file, index=False, encoding='utf-8-sig')

            # 临时备份
            pd.DataFrame(players).to_csv(f"temp_page_{current_page}.csv", index=False)

            current_page += 1
            time.sleep(random.uniform(3, 6))

            # 定期维护
            if current_page % 10 == 0:
                time.sleep(random.uniform(10, 15))
            if current_page % 20 == 0:
                driver.quit()
                driver = None

        except Exception as e:
            print(f"页面处理异常: {str(e)}")
            if driver:
                driver.quit()
                driver = None
            time.sleep(random.uniform(10, 20))

    if driver:
        driver.quit()
    return all_players


def translate_fields(df):
    """字段翻译示例"""
    translation = {
        'id': '唯一标识',
        'name': '姓名',
        'team': '所属团队',
        'score': '评分',
        'matches': '出场次数',
        'goals': '进球数'
    }
    return df.rename(columns=translation)


def merge_temp_files(output_file="combined_data.csv"):
    """合并临时文件"""
    temp_files = [f for f in os.listdir() if f.startswith("temp_page_")]
    
    if not temp_files:
        return False

    dfs = []
    for file in sorted(temp_files, key=lambda x: int(x.split('_')[-1].split('.')[0])):
        try:
            dfs.append(pd.read_csv(file, encoding='utf-8-sig'))
        except Exception as e:
            print(f"文件读取失败: {file}")

    combined_df = pd.concat(dfs).drop_duplicates('id')
    combined_df.to_csv(output_file, index=False)
    return True


def main():
    """主控制流程"""
    output = "final_data.csv"
    
    if any(f.startswith("temp_page_") for f in os.listdir()):
        choice = input("检测到临时文件,请选择操作 (1合并/2继续/3重置): ")
        # 处理用户选择逻辑...
    else:
        get_player_stats_with_selenium(output_file=output)


if __name__ == "__main__":
    main()

七、注意事项

  1. 遵守目标网站的robots.txt协议
  2. 控制请求频率(建议≥3秒/请求)
  3. 避免对目标网站造成负载压力
  4. 仅用于技术研究,禁止商业用途
     
为了在Python中使用Selenium进行自动化测试和爬虫时绕过机制,可以通过执行stealth.min.js文件来模拟真实用户的浏览器行为。在Webdriver初始化之后,我们需要使用`execute_cdp_cmd`函数来执行stealth.min.js,从而修改浏览器的行为特征,隐藏Selenium的痕迹。以下是一个具体的实施步骤: 参考资源链接:[Selenium与stealth.min.js:模拟浏览器防实践](https://wenku.youkuaiyun.com/doc/3jii0nf4r1?spm=1055.2569.3001.10343) 首先,确保你的Selenium库和对应的WebDriver都已经安装并正确配置。然后,导入必要的模块,并初始化WebDriver: ```python from selenium import webdriver ``` 接着,创建一个WebDriver实例: ```python driver = webdriver.Chrome() ``` 在这里,我们使用的是Chrome WebDriver,但同样的方法也适用于其他浏览器的WebDriver。然后,执行stealth.min.js来修改浏览器的初始脚本: ```python driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', { 'source': open('stealth.min.js', 'r').read() }) ``` 执行完毕后,就可以继续你的自动化测试或者爬虫脚本的编写,例如访问目标网页: ```python driver.get('***') ``` 除了使用stealth.min.js,还应该注意其他策略的配合使用,例如设置合适的请求头、使用无头浏览器模式、管理好IP代理池等。这样才能更有效地隐藏自动化脚本的行为特征,提高爬虫的成功率。 在进行此类操作时,特别提醒要遵守目标网站的爬虫协议和相关法律法规,确保不会违数据隐私和爬虫道德。合理规划抓取频率和数据量,做好异常处理,这些都将有助于提高爬虫的稳定性和持久性。 通过这种方法,你可以有效地模拟真实用户的浏览器行为,从而提高自动化测试和爬虫项目的成功率。关于更多的技术细节和最佳实践,建议深入阅读《Selenium与stealth.min.js:模拟浏览器防实践》,这将为你的项目提供更多的技术指导和实用技巧。 参考资源链接:[Selenium与stealth.min.js:模拟浏览器防实践](https://wenku.youkuaiyun.com/doc/3jii0nf4r1?spm=1055.2569.3001.10343)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值