大家好!在这个数据驱动的时代,获取网页上公开的数据变得越来越重要。今天,我将以一个实际的例子 —— 爬取桂林 2024 年 9 月的天气数据 —— 来详细讲解如何使用 Python 和 Selenium 库来编写一个爬虫脚本。(网站:桂林9月份天气|桂林9月份气温|桂林2024年9月份历史天气—全球天气网

我们将实现的功能包括:

  1. 启动一个真实的浏览器(Microsoft Edge)。
  2. 自动访问目标网站。
  3. 模拟用户滚动页面和点击 “查看更多” 按钮的操作。
  4. 从加载完整的页面中提取所需的数据。
  5. 将提取到的数据保存到 CSV 文件中,方便后续分析。

最终的完整代码会在文末给出,你可以直接复制使用。

第 1 步:环境准备

在开始编写代码之前,我们需要确保你的电脑上已经安装了必要的工具和库。

  1. Python 环境:首先,你需要安装 Python。如果还没有,可以从 Python 官网 下载并安装。建议选择 3.8 或更高版本。

  2. 安装必要的 Python 库:我们主要使用 selenium 库来控制浏览器,以及 beautifulsoup4 库来解析网页内容,csv 库用于写入文件。打开你的终端或命令行工具,运行以下命令来安装它们:

    pip install selenium beautifulsoup4

  3. 安装浏览器和对应的 WebDriver

    • 安装 Microsoft Edge 浏览器:如果你的电脑上还没有,请从 微软官网 下载并安装。
    • 下载 EdgeDriver:这是 Selenium 用来控制 Edge 浏览器的 “桥梁”。
      • 关键:EdgeDriver 的版本必须与你安装的 Edge 浏览器版本相匹配(主版本号一致即可)。
      • 下载地址:访问 EdgeDriver 官方下载页面
      • 找到与你 Edge 浏览器版本对应的 Stable 稳定版,下载 Windows 对应的 msedgedriver.exe
      • 放置位置:将下载并解压得到的 msedgedriver.exe 文件放到一个容易找到的位置。一个非常方便的做法是,将它与你的 Python 脚本放在同一个文件夹下。这样在代码中就可以直接使用文件名来引用它。
  4. 第 2 步:编写代码

    现在,我们来逐段分析并编写代码。我会将最终的完整代码拆解开,解释每一部分的作用。

    完整代码预览(最终版):

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.edge.service import Service
    from selenium.common.exceptions import (
        TimeoutException,
        WebDriverException,
        NoSuchElementException,
    )
    from bs4 import BeautifulSoup
    import csv
    import time
    import os
    
    def crawl_guilin_30day_weather_edge():
        url = "https://lishi.tianqi.com/guilin/202409.html"
        edgedriver_path = "msedgedriver.exe"  # 确保驱动程序与脚本在同目录下
    
        # 配置Edge浏览器选项
        options = webdriver.EdgeOptions()
        options.add_argument("--disable-gpu")
        options.add_argument("--no-sandbox")
        options.add_argument("--disable-dev-shm-usage")
    
        try:
            # 初始化浏览器驱动服务
            service = Service(executable_path=edgedriver_path)
            driver = webdriver.Edge(service=service, options=options)
            driver.set_page_load_timeout(15)  # 设置页面加载超时时间
    
            # 1. 访问目标网页
            print("页面加载中...")
            driver.get(url)
            time.sleep(3)  # 等待页面基本元素加载完成
    
            # 2. 模拟滚动页面,使"查看更多"按钮可见
            print("正在滚动页面以查找'查看更多'按钮...")
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            time.sleep(2)
    
            # 3. 定位并点击“查看更多”按钮
            more_container_xpath = "/html/body/div[7]/div[1]/div[4]/ul/div"
            try:
                more_container = driver.find_element(By.XPATH, more_container_xpath)
                driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", more_container)
                time.sleep(1)
                
                more_btn = more_container.find_element(By.TAG_NAME, "a")
                driver.execute_script("arguments[0].click();", more_btn)
                print("已触发'查看更多',等待数据加载...")
                time.sleep(3)  # 等待新数据加载
            except NoSuchElementException:
                print("未找到'查看更多'按钮,可能已默认展示全部数据!")
            except Exception as e:
                print(f"点击'查看更多'时出错:{str(e)}")
    
            # 4. 解析页面,提取数据
            print("开始解析页面数据...")
            soup = BeautifulSoup(driver.page_source, "html.parser")
    
            weather_ul_xpath = "/html/body/div[7]/div[1]/div[4]/ul"
            weather_ul = None
            try:
                weather_ul_element = driver.find_element(By.XPATH, weather_ul_xpath)
                weather_ul_html = weather_ul_element.get_attribute("outerHTML")
                weather_ul = BeautifulSoup(weather_ul_html, "html.parser")
            except NoSuchElementException:
                print("XPath定位天气容器失败,尝试用class定位...")
                weather_ul = soup.find("ul", class_="thrui")
    
            if not weather_ul:
                print("错误:未找到天气数据容器!")
                return
    
            weather_items = weather_ul.find_all("li")
            if not weather_items:
                print("错误:未找到具体天气数据项!")
                return
    
            # 5. 整理和清洗数据
            weather_data = []
            for idx, item in enumerate(weather_items, 1):
                date = item.find("div", class_="th200").get_text(strip=True) if item.find("div", class_="th200") else f"未知日期_{idx}"
                
                details = item.find_all("div", class_="th140")
                if len(details) >= 4:
                    high_temp = details[0].get_text(strip=True)
                    low_temp = details[1].get_text(strip=True)
                    weather = details[2].get_text(strip=True)
                    wind = details[3].get_text(strip=True)
                else:
                    high_temp = low_temp = weather = wind = "数据缺失"
                
                weather_data.append({
                    "日期": date,
                    "最高气温": high_temp,
                    "最低气温": low_temp,
                    "天气": weather,
                    "风向": wind
                })
    
            # 6. 保存数据到CSV文件
            output_file = "桂林2024年9月30天天气预报_最终版.csv"
            with open(output_file, "w", newline="", encoding="utf-8-sig") as f:
                fieldnames = ["日期", "最高气温", "最低气温", "天气", "风向"]
                writer = csv.DictWriter(f, fieldnames=fieldnames)
                writer.writeheader()
                writer.writerows(weather_data)
    
            print(f"\n✅ 爬取成功!共获取 {len(weather_data)} 天数据")
            print(f"📁 数据保存路径:{os.path.abspath(output_file)}")
    
        except TimeoutException:
            print("❌ 页面加载超时,请检查网络或目标网站响应速度!")
        except WebDriverException as e:
            print(f"❌ 浏览器驱动异常:{str(e)},请检查msedgedriver版本是否与Edge浏览器匹配!")
        except Exception as e:
            print(f"❌ 未知异常:{str(e)}")
        finally:
            # 确保浏览器无论成功与否都被关闭
            if 'driver' in locals():
                driver.quit()
                print("浏览器已关闭")
    
    if __name__ == "__main__":
        crawl_guilin_30day_weather_edge()
        input("\n按回车键退出...")
    第 3 步:代码解析
  5. 导入库

    • selenium 相关的库用于浏览器控制和元素定位。
    • BeautifulSoup 来自 bs4 库,是一个强大的 HTML/XML 解析器,用于从网页源码中提取数据。
    • csv 库用于读写 CSV 文件。
    • time 库用于添加等待时间(sleep),模拟人类操作,防止因页面加载过快或过慢导致的问题。
    • os 库用于处理文件路径。
  6. 核心函数 crawl_guilin_30day_weather_edge()

    • 我们将所有逻辑封装在一个函数里,使代码更整洁。
  7. 初始化浏览器

    • edgedriver_path:指定 msedgedriver.exe 的路径。因为我们把它和脚本放在了一起,所以直接写文件名即可。
    • options:设置浏览器启动参数。例如 --disable-gpu 可以在某些环境下避免渲染问题。
    • Service 和 webdriver.Edge():这是 Selenium 4.x 版本推荐的写法,用于创建一个浏览器驱动实例。
  8. 访问网页与模拟操作

    • driver.get(url):让浏览器打开指定的 URL。
    • time.sleep(3):等待 3 秒。这是非常重要的一步,因为网页加载需要时间,特别是动态加载的内容。
    • driver.execute_script("..."):这是一个强大的功能,可以在当前页面的上下文中执行任意的 JavaScript 代码。我们用它来:
      • window.scrollTo(0, document.body.scrollHeight):滚动到页面底部。
      • arguments[0].scrollIntoView(...):将指定的元素(这里是 “查看更多” 按钮)滚动到视图中。
      • arguments[0].click():模拟点击操作。有时直接用 .click() 会失败(例如元素被遮挡),用 JS 点击更稳定。
  9. 定位元素

    • driver.find_element(By.XPATH, "xpath_expression"):通过 XPath 来精确定位一个元素。XPath 是一种在 XML 文档中查找信息的语言,非常适合在复杂的 HTML 结构中定位元素。你可以在浏览器开发者工具中右键点击元素,选择 “复制” -> “复制 XPath” 来快速获取。
    • driver.find_element(By.TAG_NAME, "a"):通过标签名(如 <a>)来定位元素。我们用它在 “查看更多” 的容器 div 内部找到了真正可点击的 <a> 标签。
  10. 异常处理

    • 代码中大量使用了 try...except 块。这是编写健壮爬虫的关键。
    • NoSuchElementException:当 find_element 找不到元素时会抛出此异常。我们捕获它,并给出友好提示,而不是让程序直接崩溃。
    • TimeoutException:当页面加载超过我们设定的 15 秒时触发。
    • 捕获通用的 Exception 是为了防止一些未预料到的错误导致程序中断。
  11. 数据提取与保存

    • driver.page_source:获取当前浏览器页面的完整 HTML 源码。
    • BeautifulSoup(driver.page_source, "html.parser"):将获取到的 HTML 源码交给 BeautifulSoup 进行解析。
    • soup.find("ul", class_="thrui"):使用 BeautifulSoup 的 find 方法查找一个 ul 标签,且这个标签的 class 属性包含 thrui。这比使用绝对 XPath 更具鲁棒性,如果页面结构有微小变动,它可能仍然有效。
    • weather_ul.find_all("li"):找到 ul 容器内的所有 li 标签,每个 li 代表一天的天气数据。
    • 遍历 li 列表,再次使用 find 和 find_all 提取出日期、最高温、最低温等具体数据,并将它们存入一个字典列表 weather_data 中。
    • csv.DictWriter:使用 csv 库将字典列表写入 CSV 文件。fieldnames 定义了 CSV 文件的表头。encoding="utf-8-sig" 是一个常见的坑,使用 utf-8 编码保存的 CSV 文件用 Excel 打开可能会出现中文乱码,而 utf-8-sig 可以解决这个问题。
  12. 资源释放

    • finally 块中的 driver.quit() 确保无论爬取成功还是失败,浏览器进程都会被关闭,避免资源泄露。

问题:在该网页下无法自己定位到查看更多,要在python运行时打开的网页自己点查看更多,后续会继续检查代码,给出完整结果。

注意事项与伦理

  • 遵守 robots.txt:在爬取任何网站前,请检查其根目录下的 robots.txt 文件(如 https://lishi.tianqi.com/robots.txt),了解网站的爬取规则。
  • 控制爬取频率:不要对网站服务器造成过大压力。在你的请求之间添加适当的 sleep 时间是个好习惯。
  • 仅供学习和研究:爬取的数据应仅用于合法的学习和研究目的,请勿用于商业或其他可能侵犯他人权益的行为。

希望这篇博客对你有所帮助!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐