0

0

Python动态网页库存监控与Discord通知:Selenium实战指南

聖光之護

聖光之護

发布时间:2025-11-24 13:55:31

|

444人浏览过

|

来源于php中文网

原创

Python动态网页库存监控与Discord通知:Selenium实战指南

本教程详细探讨了使用python监控动态加载网页商品库存并发送discord通知的方法。针对传统网络爬虫(如beautifulsoup)在处理javascript渲染内容时的局限性,文章重点介绍了如何利用selenium这一无头浏览器工具来模拟用户行为,有效获取实时库存信息。教程涵盖了环境配置、代码实现、异步集成及最佳实践,旨在帮助读者构建健壮的库存监控系统。

1. 理解动态网页与传统爬虫的局限性

在构建库存监控系统时,我们通常会想到使用Python的requests库获取网页内容,再结合BeautifulSoup进行解析。然而,这种方法对于现代网站而言存在一个显著的局限性:许多网站的内容,尤其是商品库存状态,是通过JavaScript在浏览器端动态加载的。

问题分析: 当我们尝试使用requests.get(url)获取网页内容时,BeautifulSoup只能解析服务器返回的原始HTML源代码。如果商品的库存信息(例如特定尺码是否可选)是在页面加载后由JavaScript异步请求并渲染到DOM中的,那么这些信息将不会出现在requests获取的初始HTML中。

例如,在示例网站(courir.com)上,尺码选项的可用性(如尺码40)并不是直接嵌入在初始HTML中的。通过浏览器开发者工具检查,你会发现:

  • 当某个尺码缺货时,其对应的
  • 元素可能带有unselectable类或aria-disabled="true"属性。
  • 当尺码有货时,其
  • 元素可能带有selectable类,并且对应的链接(标签)是可点击的。

然而,这些状态的切换和元素的出现,都是在JavaScript执行之后才发生的。因此,单纯依赖soup.find('li', {'class': 'unselectable'})这样的代码,可能无法准确判断尺码40的实时库存状态,因为它无法“看到”JavaScript渲染后的页面。

2. 解决方案:使用Selenium处理动态内容

为了解决BeautifulSoup无法处理动态加载内容的问题,我们需要一个能够模拟真实浏览器行为的工具,即无头浏览器(Headless Browser)。Selenium就是这样一个强大的工具,它允许我们通过编程方式控制浏览器,执行JavaScript,等待元素加载,甚至模拟点击等用户交互。

立即学习Python免费学习笔记(深入)”;

LALALAND
LALALAND

AI驱动的时尚服装设计平台

下载

Selenium工作原理: Selenium启动一个真实的浏览器实例(可以是可见的,也可以是无头的),然后通过WebDriver与该浏览器进行通信。这意味着Selenium能够“看到”并操作JavaScript渲染后的完整DOM。

2.1 环境准备

在使用Selenium之前,需要安装相应的库和浏览器驱动:

  1. 安装Selenium库:
    pip install selenium
  2. 安装浏览器驱动: Selenium需要一个与你本地浏览器版本匹配的WebDriver。常用的有:
    • ChromeDriver (for Chrome):ChromeDriver Downloads 下载。
    • GeckoDriver (for Firefox):GeckoDriver Releases 下载。 将下载的驱动文件(例如chromedriver.exe)放到系统的PATH环境变量中,或者在代码中指定其路径。

2.2 构建Selenium库存检查函数

我们将重构check_stock函数,使其使用Selenium来加载页面并检查尺码40的库存状态。为了与原有的asyncio框架兼容,我们将Selenium的同步操作封装在一个异步函数中,并使用loop.run_in_executor在单独的线程中执行。

import asyncio
import aiohttp
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.common.exceptions import TimeoutException, NoSuchElementException
import time # For sleep in the main loop

# Discord Webhook URL (替换为你的实际URL)
webhook_url = 'YOUR_DISCORD_WEBHOOK_URL'

async def send_webhook_message(content):
    """
    异步发送Discord Webhook消息。
    """
    async with aiohttp.ClientSession() as session:
        try:
            async with session.post(webhook_url, json={"content": content}) as response:
                if response.status == 204:
                    print("Discord message sent successfully.")
                else:
                    print(f"Error sending Discord message. Status code: {response.status}, Response: {await response.text()}")
        except aiohttp.ClientError as e:
            print(f"Error sending Discord message: {e}")

def _check_stock_selenium_sync(url, size_to_check):
    """
    同步的Selenium函数,用于检查特定尺码的库存。
    此函数将在单独的线程中运行。
    """
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')  # 无头模式,浏览器不在UI中显示
    options.add_argument('--no-sandbox')
    options.add_argument('--disable-dev-shm-usage')
    options.add_argument('--disable-gpu') # 禁用GPU硬件加速,在某些环境下可能需要
    options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124124 Safari/537.36') # 设置User-Agent

    # 确保 ChromeDriver 路径正确,如果不在PATH中,请指定 executable_path
    driver = webdriver.Chrome(options=options) 

    is_in_stock = False
    try:
        driver.get(url)
        print(f"Navigated to {url}")

        # 等待尺码选择器元素加载。根据网站结构,这些通常是带有特定类名的
  • 元素 # 这里我们等待所有包含 'swatchanchor' 类的链接元素出现 WebDriverWait(driver, 15).until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, "a.swatchanchor")) ) print("Size swatches loaded.") # 查找特定尺码(例如“40”)并且是可选择的元素 # 网站结构通常是:
  • ...
  • # 我们可以通过CSS选择器或XPath来定位 # CSS Selector: 查找父级为 'selectable' 的 'li' 元素,其子级 'a.swatchanchor' 的 title 属性包含 '40' # 或者更直接地查找所有 'swatchanchor',然后过滤 # 尝试查找所有尺码选项 size_elements = driver.find_elements(By.CSS_SELECTOR, "a.swatchanchor") for element in size_elements: title = element.get_attribute('title') parent_li_class = element.find_element(By.XPATH, "./..").get_attribute('class') # 获取父级
  • 的class if size_to_check in title: print(f"Found size element for {size_to_check} with title: '{title}' and parent class: '{parent_li_class}'") if 'selectable' in parent_li_class: is_in_stock = True break else: print(f"Size {size_to_check} found but is not selectable (class: {parent_li_class}).") if is_in_stock: print(f"尺码 {size_to_check} 有货。") else: print(f"尺码 {size_to_check} 缺货或未找到。") except TimeoutException: print(f"Timeout waiting for page elements on {url}. Page might not have loaded correctly or elements not found within time.") except NoSuchElementException: print(f"Specific elements (like parent li) not found for {size_to_check}.") except Exception as e: print(f"An unexpected error occurred during Selenium operation: {e}") finally: driver.quit() # 确保关闭浏览器实例 return is_in_stock async def check_stock(url, size): """ 异步包装器,在单独的线程中运行同步的Selenium库存检查。 """ loop = asyncio.get_event_loop() return await loop.run_in_executor(None, _check_stock_selenium_sync, url, size)
  • 2.3 整合Discord通知与异步循环

    现在,我们将Selenium的库存检查功能集成到原有的异步主循环中,以便定期检查并发送Discord通知。

    product_data = [
        {'url': 'https://www.courir.com/fr/p/ugg-tasman-1499533.html', 'size': '40'}, # 目标尺码改为 '40'
    ]
    
    async def main():
        previous_stock_status = {}  # 存储每个产品的上一次库存状态
    
        while True:
            result_message = ""
    
            for product_info in product_data:
                url = product_info['url']
                size = product_info['size']
    
                print(f"\nChecking stock for {url} (size: {size})...")
                current_stock_status = await check_stock(url, size) # 调用异步的Selenium检查函数
    
                # 获取上一次的库存状态,如果不存在则默认为False(缺货)
                last_status = previous_stock_status.get(url, {}).get(size, False)
    
                # 检查:如果之前缺货(last_status为False)且现在有货(current_stock_status为True)
                if not last_status and current_stock_status:
                    message = f"? {url} - 尺码 {size} 现在有货啦!"
                    result_message += message + "\n"
                    print(message)
                elif last_status and not current_stock_status:
                    # 如果之前有货,现在缺货,也可以选择发送通知
                    message = f"⚠️ {url} - 尺码 {size} 已经售罄。"
                    # result_message += message + "\n" # 根据需求决定是否通知缺货
                    print(message)
                else:
                    print(f"尺码 {size} 状态未改变 ({'有货' if current_stock_status else '缺货'})。")
    
                # 更新当前产品的库存状态
                if url not in previous_stock_status:
                    previous_stock_status[url] = {}
                previous_stock_status[url][size] = current_stock_status
    
            # 如果有任何库存更新,发送Discord消息
            if result_message.strip(): # 确保消息不为空白
                print("\nSending Discord message...")
                await send_webhook_message(result_message)
                print("Discord message sent.")
            else:
                print("\nNo stock updates to send.")
    
            # 设置检查间隔
            check_interval_seconds = 600 # 10分钟
            print(f"Waiting {check_interval_seconds} seconds before next check...")
            await asyncio.sleep(check_interval_seconds)
    
    # 运行主函数
    if __name__ == "__main__":
        asyncio.run(main())

    3. 注意事项与最佳实践

    • WebDriver路径: 确保chromedriver或geckodriver可执行文件位于系统的PATH中,或者在webdriver.Chrome()或webdriver.Firefox()中通过executable_path参数明确指定其路径。
    • 无头模式: 在生产环境中,强烈建议使用options.add_argument('--headless')来运行Selenium,这样浏览器就不会在后台打开可见窗口,节省资源。
    • 等待策略: 动态加载内容需要时间。使用WebDriverWait和expected_conditions(如EC.presence_of_element_located或EC.visibility_of_element_located)是确保元素加载后再尝试查找的最佳实践,而不是使用硬编码的time.sleep()。
    • 元素定位: 选择器(CSS Selector或XPath)应尽可能健壮,避免使用过于依赖页面结构变化的定位方式。通过浏览器开发者工具仔细检查目标元素的属性(class, id, title, data-属性等)来构建可靠的选择器。
    • User-Agent: 设置User-Agent可以模拟真实浏览器,减少被网站识别为爬虫的风险。
    • 错误处理: 增加try-except块来捕获TimeoutException、NoSuchElementException以及其他可能的网络或Selenium相关的异常,提高程序的健壮性。
    • 爬虫道德与频率:
      • robots.txt: 在爬取任何网站之前,请检查其robots.txt文件(例如:https://www.courir.com/robots.txt),了解网站的爬取规则。
      • 服务条款: 遵守网站的服务条款。
      • 爬取频率: 设置合理的检查间隔(例如10分钟或更长),避免对网站服务器造成过大负担,防止IP被封禁。
    • 资源消耗: Selenium会启动一个完整的浏览器实例,相比requests+BeautifulSoup,资源消耗(CPU、内存)更大。在服务器上部署时,请考虑服务器性能。
    • 异步与同步: Selenium本身是同步的。为了将其集成到asyncio事件循环中,我们使用了loop.run_in_executor(None, sync_function, ...),这会将同步函数放到一个默认的线程池中执行,从而不阻塞主事件循环。

    4. 总结

    通过本教程,我们了解了在处理JavaScript动态加载内容的网站时,传统爬虫工具如BeautifulSoup的局限性。而Selenium作为强大的无头浏览器自动化工具,能够模拟真实用户行为,有效获取这些动态内容,从而实现精确的库存监控。结合Python的asyncio和Discord Webhook,我们可以构建一个高效、实时的库存变动通知系统。在实际应用中,务必注意遵守网站的爬取政策,并采取合理的爬取频率和错误处理机制,确保程序的稳定性和合规性。

    相关专题

    更多
    python开发工具
    python开发工具

    php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

    759

    2023.06.15

    python打包成可执行文件
    python打包成可执行文件

    本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

    639

    2023.07.20

    python能做什么
    python能做什么

    python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

    761

    2023.07.25

    format在python中的用法
    format在python中的用法

    Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

    618

    2023.07.31

    python教程
    python教程

    Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

    1265

    2023.08.03

    python环境变量的配置
    python环境变量的配置

    Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

    548

    2023.08.04

    python eval
    python eval

    eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

    579

    2023.08.04

    scratch和python区别
    scratch和python区别

    scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

    709

    2023.08.11

    高德地图升级方法汇总
    高德地图升级方法汇总

    本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

    43

    2026.01.16

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    Sass 教程
    Sass 教程

    共14课时 | 0.8万人学习

    Bootstrap 5教程
    Bootstrap 5教程

    共46课时 | 2.9万人学习

    CSS教程
    CSS教程

    共754课时 | 20.2万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号