Scrapy进阶封装(第六阶段:Selenium模板封装)

本阶段在模板的基础上增加自动化封装方法,包括获取网页源码,获取网页cookie,点击,控制浏览器,等等方法。

1.selelnium模板解释

复制代码
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 selenium.common.exceptions import TimeoutException
import time
import os
class SeleniumCrawler:
    """
    Selenium爬虫类,用于网页自动化操作和数据抓取
    """
    def __init__(self, driver_path=None, headless=False,
                 disable_images=False, proxy=None,
                 disable_automation_control=True, implicit_wait=10):
        """
        初始化Selenium爬虫
        参数:
            browser_type (str): 浏览器类型,支持'chrome',
            driver_path (str): 浏览器驱动路径,如果为None则使用系统PATH中的驱动
            headless (bool): 是否使用无头模式(不显示浏览器界面)
            disable_images (bool): 是否禁用图片加载以提高性能
            user_agent (str): 自定义User-Agent
            proxy (str): 代理服务器地址,格式为'ip:port'
            disable_automation_control (bool): 是否禁用自动化控制特征(反爬虫检测)
            implicit_wait (int): 隐式等待时间(秒)
        """
        self.browser_type = 'chrome'
        self.driver_path = driver_path
        self.driver = None
        self.headless = headless
        self.disable_images = disable_images
        self.user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
        self.proxy = proxy
        self.disable_automation_control = disable_automation_control
        self.implicit_wait = implicit_wait
        # 初始化浏览器
        self._init_browser()

    def _init_browser(self):
        """
        根据配置初始化浏览器
        """
        if self.browser_type == 'chrome':
            self._init_chrome()
        else:
            raise ValueError(f"不支持的浏览器类型: {self.browser_type},请使用'chrome'")

        # 设置隐式等待时间
        if self.driver:
            self.driver.implicitly_wait(self.implicit_wait)

    def _init_chrome(self):
        """
        初始化Chrome浏览器
        """
        options = Options()

        # 无头模式配置
        if self.headless:
            options.add_argument('--headless')
            options.add_argument('--disable-gpu')

        # # 禁用图片加载
        # if self.disable_images:
        #     options.add_argument('--blink-settings=imagesEnabled=false')

        # 设置User-Agent
        if self.user_agent:
            options.add_argument(f'--user-agent={self.user_agent}')

        # 设置代理
        if self.proxy:
            options.add_argument(f'--proxy-server={self.proxy}')

        # 禁用自动化控制特征(反爬虫检测)
        if self.disable_automation_control:
            options.add_argument("--disable-blink-features=AutomationControlled")
            options.add_experimental_option("excludeSwitches", ["enable-automation"])
            options.add_experimental_option('useAutomationExtension', False)

        # 初始化Chrome浏览器
        if self.driver_path:
            service = Service(executable_path=self.driver_path)
            self.driver = webdriver.Chrome(service=service, options=options)
        else:
            self.driver = webdriver.Chrome(options=options)

        # 进一步防止被检测为自动化工具
        if self.disable_automation_control:
            self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
                "source": """
                Object.defineProperty(navigator, 'webdriver', {get: () => undefined})
                """
            })
    #获取url
    def open_url(self, url):
        """
        打开指定URL
        参数:
            url (str): 要访问的网页URL
        """
        self.driver.get(url)
    def close(self):
        """
        关闭当前浏览器窗口
        """
        if self.driver:
            self.driver.close()

    def quit(self):
        """
        退出浏览器,释放所有资源
        """
        if self.driver:
            self.driver.quit()
            self.driver = None
    def get_page_source(self,url=None) -> str:
        if url:
            self.driver.get(url)
        """
        获取指定URL的网页HTML源代码
        参数:
            url (str): 要访问的网页URL
        返回:
            str: 网页的HTML源代码,如果获取失败则返回None
        """
        try:
            # 访问URL
            # 等待页面加载完成,可以根据特定元素的存在来判断
            # 这里简单地等待页面完全加载
            time.sleep(1)
            # 获取页面源代码
            page_source = self.driver.page_source
            return page_source
        except TimeoutException:
            print(f"页面加载超时: {self.driver.current_url}")
            return None
        except Exception as e:
            print(f"获取页面源代码时发生错误: {e}")
            return None

    def get_cookies(self,url = None) -> dict:

        """
        获取当前页面的cookies,并以cookies[name] = cookies[value]的形式返回
        返回:
            dict: cookies字典,如果获取失败则返回None
            对于cookies反爬可以用此手段
        """
        time.sleep(1)
        try:
            if url:
                self.driver.get(url)
            # 获取所有cookies
            cookies = self.driver.get_cookies()
            # 将cookies转换为字典格式
            cookies_dict = {}
            for cookie in cookies:
                cookies_dict[cookie['name']] = cookie['value']

            return cookies_dict
        except Exception as e:
            print(f"获取cookies时发生错误: {e}")
            return None

    def send_keys(self, by, value, text, timeout=10):
        """
        向指定元素发送文本
        参数:
            by (str): 元素定位方式(如ID、NAME、CLASS_NAME等)
            value (str): 元素定位值
            text (str): 要输入的文本
            timeout (int): 等待元素出现的超时时间(秒)
        返回:
            bool: 如果操作成功返回True,否则返回False
        """
        try:
            by = 'By.'+by
            # 设置显式等待
            wait = WebDriverWait(self.driver, timeout)
            #等待出现时点击
            element = wait.until(EC.presence_of_element_located((eval(by), value)))
            # 清空输入框并输入文本
            element.clear()
            element.send_keys(text)
            return True
        except TimeoutException:
            print(f"元素未找到: {by} = {value}")
            return False
        except Exception as e:
            print(f"输入文本时发生错误: {e}")
            return False
    def click_element(self, by, value, timeout=10):
        """
        点击指定元素
        参数:
            by (str): 元素定位方式(如ID、NAME、CLASS_NAME等)
            value (str): 元素定位值
            timeout (int): 等待元素出现的超时时间(秒)
        返回:
            bool: 如果操作成功返回True,否则返回False
        """
        try:
            by = 'By.' + by
            # 设置显式等待
            wait = WebDriverWait(self.driver, timeout)
            element = wait.until(EC.element_to_be_clickable((eval(by), value)))

            # 点击元素
            element.click()

            return True
        except TimeoutException:
            print(f"元素未找到或不可点击: {by} = {value}")
            return False
        except Exception as e:
            print(f"点击元素时发生错误: {e}")
            return False

    def execute_console_command(self, command):
        """
        在浏览器控制台执行JavaScript命令
        参数:
            command (str): 要执行的JavaScript命令
        返回:
            执行命令的结果
        """
        try:
            # 执行JavaScript命令
            result = self.driver.execute_script(command)
            return result
        except Exception as e:
            print(f"执行JavaScript命令时发生错误: {e}")
            return None

以上是初步封装的Selenium模板,教程来自于我另一篇文章

Selenium使用教程-爬虫版(超详细)-CSDN博客

这里我作一下简述,selenium模板初始化解释

初始化Selenium 参数:

browser_type (str): 浏览器类型,支持'chrome', driver_path (str):

浏览器驱动路径,如果为None则使用系统PATH中的驱动

headless (bool): 是否使用无头模式(不显示浏览器界面)

disable_images (bool): 是否禁用图片加载以提高性能

user_agent (str): 自定义User-Agent

proxy (str): 代理服务器地址,格式为'ip:port'

disable_automation_control (bool): 是否禁用自动化控制特征(反爬虫检测) implicit_wait (int): 隐式等待时间(秒)

def _init_chrome(self):

此方法初始化浏览器,包括无头模式开启,禁用图片,设置UA,设置代理,伪装 禁用自动化控制特征(反爬虫检测),初始化Chrome浏览器,进一步防止被检测为自动化工具。

以下是封装的方法

def open_url(self, url): 开启浏览器

def close(self):关闭浏览器

def quit(self):退出浏览器

def get_page_source: 获取网页源码

def get_cookies 获取网页cookies 绕过安全产品反爬

def send_keys 填充,一般用于登录

def click_element点击, 用于翻页和登录等

def execute_console_command 浏览器控制台,一般用于下滑加载

2.封装scrapy自动化方法

第一步,

导入封装好的selenium模板

第二步,设置好是否开启自动化

第三步,封装selenium方法

复制代码
    def get_html(self, url=None):
        if self.open_selenium:
            return self.seleniumCrawler.get_page_source(url)
        else:
            raise '自动化未开启'
    def scrapy_get_cookies(self, url):
        if self.open_selenium:
            return self.seleniumCrawler.get_cookies(url)
        else:
            raise '自动化未开启'''
    def scrapy_click_element(self, by, value, timeout=10):
        if self.open_selenium:
            self.seleniumCrawler.click_element(by, value, timeout)
        else:
            raise '自动化未开启'''

    def scrapy_execute_console_command(self,command):
        if self.open_selenium:
            self.seleniumCrawler.execute_console_command(command)
        else:
            raise '自动化未开启'''
    def scrapy_send_keys(self, key, value):
        if self.open_selenium:
            self.seleniumCrawler.send_keys(key, value)
        else:
            raise '自动化未开启'''
    def scrapy_oepn_url(self,url):
        if self.open_selenium:
            self.seleniumCrawler.open_url(url)
        else:
            raise '自动化未开启'''
    def scrapy_close_url(self,url):
        if self.open_selenium:
            self.seleniumCrawler.close()
        else:
            raise '自动化未开启'''

selenium对于爬虫基本比较重要的方法就这些。

框架源代码

python 复制代码
import json
from scrapy import Request
import scrapy
from scrapy import FormRequest
from scrapy.http import JsonRequest

import scrapy
from myspider.selenium_tem.selenium_tem import SeleniumCrawler


class superspider(scrapy.Spider):
    def_headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
    }  # 默认请求头
    custom_settings = {}  # 初始设置
    start_page = 1  # 起始翻页数
    cookie_dict = None  # 设置cookies
    allowed_domains = []  # 允许域名
    max_page = 1  # 最大翻页数
    start_urls = []  # 单个爬虫脚本请求列表
    open_selenium = False  # 是否开启自动化
    SeleniumCrawler = None
    def __init__(self, **kwargs):
        super(superspider, self).__init__(**kwargs)
        super().__init__(**kwargs)
        if self.open_selenium:
            self.seleniumCrawler = SeleniumCrawler()

    def start_requests(self):
        # 爬虫开始前准备
        self.gen_source_necessary_info()
        for params in self.start_urls:
            params['page'] = self.start_page
            yield from self.getListData(params)

    def getListData(self, params):
        if params.get('method') == 'POST':
            if params.get('payload'):
                for i in range(self.start_page, self.max_page + 1):
                    params['payload'][str(params['f_page'])] = str(i)
                    print(params['payload'])
                    yield self.post(url=params['url'], callback=self.parse, cookies=self.cookie_dict,
                                    headers=self.def_headers,
                                    meta={'params': params}, payload=eval(self.url_rule(params)) or params['payload'])
            else:
                for i in range(self.start_page, self.max_page + 1):
                    params['data'][str(params['f_page'])] = str(i)
                    print(params['data'])
                    yield self.post(url=params['url'], callback=self.parse, cookies=self.cookie_dict,
                                    headers=self.def_headers,
                                    meta={'params': params}, data=eval(self.url_rule(params)) or params['data'])
        else:
            for i in range(self.start_page, self.max_page + 1):
                yield self.get(url=eval(self.url_rule(params)) or params['url'].format(i), callback=self.parse,
                               cookies=self.cookie_dict, meta={'params': params},
                               headers=self.def_headers)

    def get(self, url, headers=None, cookies=None, callback=None, meta=None, dont_filter=True, cb_kwargs=None):
        return Request(url=url, headers=self.def_headers, cookies=self.cookie_dict, callback=callback, meta=meta,
                       dont_filter=dont_filter,
                       cb_kwargs=cb_kwargs or {})

    def post(self, url, headers=None, cookies=None, callback=None, meta=None, data=None, dont_filter=True, payload=None,
             cb_kwargs=None):
        if payload:
            return JsonRequest(url=url, headers=headers, cookies=cookies,
                               callback=callback, meta=meta, method='POST', data=payload, dont_filter=dont_filter,
                               cb_kwargs=cb_kwargs or {}
                               )
        if data:
            return scrapy.FormRequest(url=url, headers=headers, cookies=cookies,
                                      callback=callback, meta=meta, method='POST', formdata=data,
                                      dont_filter=dont_filter,
                                      cb_kwargs=cb_kwargs or {}
                                      )

    def gen_source_necessary_info(self):
        pass

    def parse(self, response, **kwargs):
        pass

    def url_rule(self, params):
        return "None"

    def get_html(self, url=None):
        if self.open_selenium:
            return self.seleniumCrawler.get_page_source(url)
        else:
            raise '自动化未开启'
    def scrapy_get_cookies(self, url):
        if self.open_selenium:
            return self.seleniumCrawler.get_cookies(url)
        else:
            raise '自动化未开启'''
    def scrapy_click_element(self, by, value, timeout=10):
        if self.open_selenium:
            self.seleniumCrawler.click_element(by, value, timeout)
        else:
            raise '自动化未开启'''

    def scrapy_execute_console_command(self,command):
        if self.open_selenium:
            self.seleniumCrawler.execute_console_command(command)
        else:
            raise '自动化未开启'''
    def scrapy_send_keys(self, key, value):
        if self.open_selenium:
            self.seleniumCrawler.send_keys(key, value)
        else:
            raise '自动化未开启'''
    def scrapy_oepn_url(self,url):
        if self.open_selenium:
            self.seleniumCrawler.open_url(url)
        else:
            raise '自动化未开启'''
    def scrapy_close_url(self):
        if self.open_selenium:
            self.seleniumCrawler.close()
        else:
            raise '自动化未开启'''

3.案例

还是以豆瓣为例,豆瓣比较简单,一般不用自动化。

首先看豆瓣的翻页规律,

所以我们可以用xpath定位

self.scrapy_click_element('XPATH', f'.//a[@href = "?start={(i) * 25}&filter="]')

selenium基本的流程

结果如下:

自动化非常的好用。降低逆向的技术栈,但是,对于每日更新大量数据,或者会弹出大量验证码,或者有检测自动化工具的网站。自动化没用,说实话,这篇文章重点讲爬虫框架构成,不是selenium。我就业时,遇到的网站,基于服务器上,selenium根本没法解决,很不稳定,基本爬不了多少数据,甚至不能运行(因为线上有并发多个爬虫脚本,如果都是自动化,相当于后台开启几百个浏览器,CPU爆满)所以以前觉得有selenium等自动化工具存在,有必要学逆向吗?直到真正就业时,真正遇到大站点网站时,我才觉得selenium局限性大,逆向才是真王道。