新闻中心

使用Python监控动态网页库存并发送Discord通知:从静态抓取到无头浏览器

2025-11-23
浏览次数:
返回列表

使用python监控动态网页库存并发送discord通知:从静态抓取到无头浏览器

本文旨在指导读者如何使用Python构建一个商品库存监控机器人,并实时通过Discord发送通知。文章将深入探讨在面对J*aScript动态加载内容的网站时,传统网页抓取工具(如BeautifulSoup)的局限性,并详细介绍如何利用无头浏览器(如Selenium)来模拟用户行为、获取动态数据,最终实现高效、准确的库存监控与通知机制。

1. 理解库存监控的需求与挑战

构建一个自动化的库存监控系统,其核心目标是定期检查特定商品(例如,特定尺码的鞋子)的库存状态,并在库存发生变化(例如,从缺货变为有货)时,通过即时通讯工具(如Discord)发送通知。

最初,开发者可能会尝试使用requests库获取网页内容,并结合BeautifulSoup进行解析。这种方法对于内容直接包含在HTML源代码中的静态网页非常有效。例如,对于一个表示商品库存状态的

  • 元素,如果其类名从unselectable(缺货)变为selectable(有货),那么通过查找这些类名可以判断库存。
    import requests
    from bs4 import BeautifulSoup
    
    def check_static_stock(url, target_size_title):
        try:
            response = requests.get(url)
            response.raise_for_status() # 检查HTTP请求是否成功
            soup = BeautifulSoup(response.text, 'html.parser')
    
            # 尝试查找表示有货的元素,例如:
            # 假设有货时,尺码选项的父元素是'selectable',且尺码选项本身有'swatchanchor'类和包含尺码的title
            # 如果网页是静态的,这可能是一个有效的查找方式
            *ailable_size_elements = soup.find('li', class_='selectable')
            if *ailable_size_elements:
                # 进一步查找包含特定尺码的链接
                size_40_*ailable = *ailable_size_elements.find_all('a', class_='swatchanchor', title=lambda t: t and target_size_title in t)
                return len(size_40_*ailable) > 0
            return False
        except requests.RequestException as e:
            print(f"请求网页时发生错误: {e}")
            return False
    
    # 示例:如果网页是静态的,可以这样调用
    # url = 'https://www.courir.com/fr/p/ugg-tasman-1499533.html'
    # is_in_stock = check_static_stock(url, '40')
    # if is_in_stock:
    #     print("尺码 40 有货!")
    # else:
    #     print("尺码 40 缺货或未找到。")

    然而,许多现代网站,包括示例中的电商网站,广泛使用J*aScript来动态加载和渲染页面内容。这意味着当您使用requests.get(url)获取页面时,返回的HTML源代码可能不包含所有最终在浏览器中可见的元素。尺码选项、库存状态等关键信息可能是在页面加载完成后,由J*aScript通过AJAX请求获取数据并插入到DOM中的。

    在这种情况下,BeautifulSoup只能解析原始的HTML文本,无法执行J*aScript,因此它无法“看到”这些动态生成的内容。这就是传统网页抓取方法面临的主要挑战。

    2. 解决方案:利用无头浏览器进行动态网页抓取

    为了克服动态内容的挑战,我们需要一个能够模拟真实浏览器行为的工具,即无头浏览器。无头浏览器可以在后台运行,执行J*aScript、加载CSS、处理AJAX请求,并最终呈现出完整的DOM结构,就像一个普通浏览器一样。

    PictoGraphic PictoGraphic

    AI驱动的矢量插图库和插图生成平台

    PictoGraphic 133 查看详情 PictoGraphic

    Selenium是一个流行的自动化测试工具,它也可以作为强大的无头浏览器抓取工具。

    2.1 Selenium环境搭建

    1. 安装Selenium库:
      pip install selenium
    2. 下载浏览器驱动: Selenium需要一个浏览器驱动来控制实际的浏览器。常用的有ChromeDriver(Google Chrome)、GeckoDriver(Mozilla Firefox)。请根据您系统上安装的浏览器版本下载对应的驱动,并将其放置在系统的PATH环境变量中,或者在代码中指定其路径。
      • ChromeDriver下载地址:https://www.php.cn/link/73715c097259c228af0648823d754407
      • GeckoDriver下载地址:https://www.php.cn/link/9a1ecce2d381e29ac81279bdae9886bd

    2.2 使用Selenium获取动态内容

    以下是使用Selenium检查特定尺码库存的示例代码:

    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    import time
    
    def check_dynamic_stock_selenium(url, target_size):
        chrome_options = Options()
        chrome_options.add_argument("--headless")  # 启用无头模式,不显示浏览器界面
        chrome_options.add_argument("--disable-gpu") # 禁用GPU硬件加速
        chrome_options.add_argument("--no-sandbox") # 禁用沙箱模式,某些Linux环境需要
        chrome_options.add_argument("--disable-dev-shm-usage") # 解决在某些Docker容器中内存不足的问题
    
        # 根据您的ChromeDriver路径进行修改
        # 例如:service = Service('/path/to/chromedriver')
        driver = webdriver.Chrome(options=chrome_options)
    
        try:
            driver.get(url)
    
            # 等待页面加载完成,或者等待特定元素出现
            # 这里我们等待一个包含尺码选项的父容器出现
            WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.CSS_SELECTOR, 'div.product-attributes__size-selector'))
            )
    
            # 查找所有尺码选项,这些选项通常是带有特定类名和title属性的<a>标签
            # 假设尺码选项的HTML结构为 <a class="swatchanchor" title="40">...</a>
            # 并且有货时,其父级<li>可能没有'unselectable'类,或者其自身没有'disabled'属性
    
            # 查找所有尺码的链接
            size_elements = driver.find_elements(By.CSS_SELECTOR, 'a.swatchanchor')
    
            for element in size_elements:
                if element.get_attribute('title') == target_size:
                    # 找到目标尺码
                    # 检查其父级<li>元素的类名,或者元素本身的属性来判断是否可选/有货
                    # 在Courir网站的例子中,如果一个尺码不可选(缺货),它的父级<li>会有'unselectable'类
                    parent_li = element.find_element(By.XPATH, '..') # 获取父元素
                    if 'unselectable' not in parent_li.get_attribute('class'):
                        print(f"尺码 {target_size} 有货!")
                        return True
                    else:
                        print(f"尺码 {target_size} 缺货。")
                        return False
    
            print(f"未找到尺码 {target_size} 的选项。")
            return False
    
        except Exception as e:
            print(f"使用Selenium检查库存时发生错误: {e}")
            return False
        finally:
            driver.quit() # 确保关闭浏览器实例
    
    # 示例调用
    # url = 'https://www.courir.com/fr/p/ugg-tasman-1499533.html'
    # is_size_40_in_stock = check_dynamic_stock_selenium(url, '40')
    # print(f"尺码 40 库存状态: {is_size_40_in_stock}")

    代码解释:

    • chrome_options.add_argument("--headless"):启用无头模式,浏览器将在后台运行,不显示图形界面。这对于服务器环境和提高性能非常有用。
    • WebDriverWait 和 expected_conditions:这是Selenium中处理动态加载内容的关键。它允许代码等待某个条件(例如,某个元素出现)满足后才继续执行,避免因元素尚未加载而导致的查找失败。
    • driver.find_elements(By.CSS_SELECTOR, 'a.swatchanchor'):使用CSS选择器查找所有匹配的尺码元素。
    • element.get_attribute('title'):获取元素的title属性,用于匹配目标尺码。
    • element.find_element(By.XPATH, '..'):通过XPath获取当前元素的父元素,以便检查其类名来判断库存状态。

    3. 集成Discord通知机制

    一旦我们能够准确地获取库存状态,就可以将其与Discord Webhook集成,发送实时通知。这部分可以使用aiohttp异步发送请求,以避免阻塞主程序。

    import discord
    import aiohttp
    import asyncio
    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    import time
    
    # 替换为您的Discord Webhook URL
    WEBHOOK_URL = 'YOUR_DISCORD_WEBHOOK_URL_HERE' 
    
    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消息发送成功。")
                    else:
                        print(f"发送Discord消息失败。状态码: {response.status}, 响应: {await response.text()}")
            except aiohttp.ClientError as e:
                print(f"发送Discord消息时发生网络错误: {e}")
    
    async def check_stock_and_notify(url, target_size, previous_stock_status):
        """
        检查指定尺码的库存,并在状态变化时发送Discord通知。
        """
        current_stock_status = check_dynamic_stock_selenium(url, target_size)
    
        # 检查库存状态是否从缺货变为有货
        if not previous_stock_status.get(url, {}).get(target_size, False) and current_stock_status:
            message = f"? 尺码 {target_size} 的商品已到货!请尽快查看: {url}"
            print(message)
            await send_webhook_message(message)
        elif previous_stock_status.get(url, {}).get(target_size, True) and not current_stock_status:
            # 如果之前有货,现在缺货,也可以选择发送通知
            message = f"⚠️ 尺码 {target_size} 的商品已缺货: {url}"
            print(message)
            # await send_webhook_message(message) # 根据需求决定是否通知缺货
    
        # 更新本次库存状态
        previous_stock_status.setdefault(url, {})[target_size] = current_stock_status
        return previous_stock_status
    
    async def main():
        product_to_monitor = [
            {'url': 'https://www.courir.com/fr/p/ugg-tasman-1499533.html', 'size': '40'},
            # 可以添加更多商品和尺码
            # {'url': '另一商品URL', 'size': '另一尺码'},
        ]
    
        previous_stock_status = {} # 存储上次检查的库存状态
    
        while True:
            print("\n开始检查库存...")
            for product_info in product_to_monitor:
                url = product_info['url']
                size = product_info['size']
                previous_stock_status = await check_stock_and_notify(url, size, previous_stock_status)
    
            print("所有商品检查完毕。下次检查将在10分钟后。")
            await asyncio.sleep(600) # 每10分钟检查一次
    
    if __name__ == "__main__":
        # 请确保将 WEBHOOK_URL 替换为您的实际 Discord Webhook URL
        # 确保ChromeDriver路径正确配置,或者在check_dynamic_stock_selenium函数中指定
        asyncio.run(main())
    

    4. 注意事项与最佳实践

    1. 网站使用条款和robots.txt: 在进行网页抓取之前,务必查阅目标网站的robots.txt文件(例如 https://www.courir.com/robots.txt)以及其服务条款。遵守网站的规定,避免对网站造成不必要的负担。
    2. 请求频率与IP封锁: 频繁的请求可能会被网站识别为恶意行为,导致IP地址被封锁。请合理设置检查间隔(例如,10分钟或更长),并考虑使用代理IP池来分散请求。
    3. 元素定位的稳定性: 网站的HTML结构可能会发生变化,导致您在Selenium中使用的CSS选择器或XPath失效。当脚本不再工作时,需要重新检查页面结构并更新定位器。使用更通用或更稳定的定位方式(如id属性,如果存在)可以提高稳定性。
    4. 错误处理: 在实际应用中,网络问题、元素未找到等异常情况时有发生。务必在代码中加入健壮的try-except块来处理这些异常,确保程序的稳定运行。
    5. 资源管理: 每次使用Selenium后,务必调用driver.quit()来关闭浏览器实例及其驱动进程,释放系统资源,防止内存泄漏。
    6. 异步编程: 对于需要同时监控多个商品或需要长时间运行的机器人,使用asyncio进行异步编程可以提高效率和响应性。
    7. 日志记录: 记录程序的运行状态、错误信息和库存变化,有助于问题排查和历史数据分析。

    总结

    通过本文的讲解,我们了解了在面对动态加载内容的现代网站时,传统网页抓取方法的局限性,并掌握了如何利用Selenium无头浏览器来模拟真实用户行为,成功获取并解析这些动态数据。结合Discord Webhook,我们能够构建一个高效、可靠的自动化库存监控与通知系统。在实际应用中,请务必注意遵守网站规定,并采取适当的策略来确保程序的稳定性和可持续性。

  • 以上就是使用Python监控动态网页库存并发送Discord通知:从静态抓取到无头浏览器的详细内容,更多请关注其它相关文章!


    # 是一个  # 医院药店营销推广  # seo教学咨询  # 无锡网站流量推广方式  # seo优化绩效考核  # seo关键词排名再咕15云速捷效果牛X  # 萧县网站seo优化价格  # 陕西企业网站线上推广  # 推广关键词排名老是掉  # seo课程教案  # 数码推广网站哪个好做些  # 下载地址  # 将在  # 选择器  # 如何使用  # 动态网页  # css  # 您的  # 加载  # 有货  # 无头  # do  # go  # ajax  # json  # git  # js  # html  # java  # python  # javascript  # linux 


    相关栏目: 【 科技资讯46185 】 【 网络学院92790


    相关推荐: vivo手机互传视频怎么操作_vivo手机互传视频详细传输方法  php源码怎么在电脑上测试_电脑测试php源码方法步骤【教程】  如何解决电商平台定制报价请求的“黑洞”问题,SprykerQuoteRequest模块助你提升客户体验与销售效率  Lar*el 递归关系中排除指定分支的教程  Golang如何优化内存分配与垃圾回收_Golang内存管理与GC优化实践  没有大陆身份证/银行卡如何实名微信? 亲测有效的几种方法分享  哔哩哔哩忘记密码了怎么找回_哔哩哔哩密码找回方法  C++如何解决segmentation fault_C++段错误调试与原因分析  淘宝支付提示失败如何解决 淘宝支付流程优化方法  铁路12306卧铺选择攻略 铁路12306下铺座位预定技巧  如何在CSS中使用浮动制作导航栏_float实现水平菜单  html5 app怎么运行环境_配html5 app运行环境【教程】  钉钉视频会议声音异常如何处理 钉钉会议音频修复技巧  poki网页游戏推荐_poki免费游戏平台入口  Django表单提交验证失败后保持字段值不刷新  msn官网入口地址手机版 msn官方网站手机最新链接  汽水音乐车机版横屏版7.1 汽水音乐车机版横屏版下载入口  163邮箱官方主页登录 直达网易邮箱登录核心页面  windows10怎么查看本机ip_windows10命令提示符ipconfig使用  CSS Flexbox如何实现多行排列_flex-wrap wrap自动换行显示  小红书怎么解除第三方平台绑定_小红书多平台登录解绑方法介绍  Spring Boot嵌入式服务器与J*a EE:功能支持深度解析  win11 Snap Layouts怎么用 Win11窗口布局与分屏多任务高效指南【必学】  一加 14R 快充无反应_一加 14R 充电优化  Win10系统怎么查看已安装更新_Win10卸载有问题的更新补丁  Adobe PDF表单中利用J*aScript解析与格式化日期组件的教程  MAC怎么在地图App里使用“四处看看”_MAC体验部分城市的3D实景街景  三星GalaxyZFold5怎样在相册制作折叠屏分镜_iPhone三星GalaxyZFold5相册制作折叠屏分镜【创意编辑】  sublime如何只显示或隐藏特定类型文件_sublime侧边栏文件过滤  冬*霸灯泡不亮怎么办_浴霸取暖灯一盏不亮的灯座清洁修复法  优酷会员付费后没到账怎么办_优酷会员充值异常及解决方法  J*aScript数据结构转换:将对象数组按类别分组  必由学在线入口 必由学网页版快速登录入口  Yandex官网免登录入口_俄罗斯Yandex搜索引擎一键访问  QQ邮箱在线使用入口 QQ邮箱个人账号网页版登录  如何设置Windows Defender的定时扫描_计划任务实现自动杀毒【安全】  处理动态列数据:J*a ArrayList的正确初始化与字符累加教程  J*aScript数组对象转换:按指定键分组与值收集  mc.js官网登录入口 mc.js官方登录入口最新版  天眼查怎么看公司融资情况 天眼查企业融资历史查询步骤【攻略】  Lar*el递归关系中排除子孙节点的策略  海量存储:机器视觉智能化的核心基石  PrimeNG Sidebar背景色自定义指南:CSS覆盖与主题化实践  解决macOS Tkinter应用双击启动崩溃:PyInstaller打包指南  使用CSS更改登录屏幕输入框中PNG图标颜色的策略与局限性  Highcharts 雷达图径向轴标签定制指南:利用多Y轴实现数值标注  C++的std::forward_list怎么用_C++ STL中单向链表容器的特点与应用  b站如何看历史记录_b站观看历史找回方法  php源码怎么看淘宝客系统_看php源码淘宝客系统技巧  PS5 Pro有点优势但不多! 《燕云十六声》PS5平台与PC性能画面对比 

    搜索