从零开始:使用 Python + Selenium 爬取网页数据
·
大家好!在这个数据驱动的时代,获取网页上公开的数据变得越来越重要。今天,我将以一个实际的例子 —— 爬取桂林 2024 年 9 月的天气数据 —— 来详细讲解如何使用 Python 和 Selenium 库来编写一个爬虫脚本。(网站:桂林9月份天气|桂林9月份气温|桂林2024年9月份历史天气—全球天气网)
我们将实现的功能包括:
- 启动一个真实的浏览器(Microsoft Edge)。
- 自动访问目标网站。
- 模拟用户滚动页面和点击 “查看更多” 按钮的操作。
- 从加载完整的页面中提取所需的数据。
- 将提取到的数据保存到 CSV 文件中,方便后续分析。
最终的完整代码会在文末给出,你可以直接复制使用。
第 1 步:环境准备
在开始编写代码之前,我们需要确保你的电脑上已经安装了必要的工具和库。
-
Python 环境:首先,你需要安装 Python。如果还没有,可以从 Python 官网 下载并安装。建议选择 3.8 或更高版本。
-
安装必要的 Python 库:我们主要使用
selenium库来控制浏览器,以及beautifulsoup4库来解析网页内容,csv库用于写入文件。打开你的终端或命令行工具,运行以下命令来安装它们:pip install selenium beautifulsoup4 -
安装浏览器和对应的 WebDriver:
- 安装 Microsoft Edge 浏览器:如果你的电脑上还没有,请从 微软官网 下载并安装。
- 下载 EdgeDriver:这是 Selenium 用来控制 Edge 浏览器的 “桥梁”。
- 关键:EdgeDriver 的版本必须与你安装的 Edge 浏览器版本相匹配(主版本号一致即可)。
- 下载地址:访问 EdgeDriver 官方下载页面。
- 找到与你 Edge 浏览器版本对应的 Stable 稳定版,下载 Windows 对应的
msedgedriver.exe。 - 放置位置:将下载并解压得到的
msedgedriver.exe文件放到一个容易找到的位置。一个非常方便的做法是,将它与你的 Python 脚本放在同一个文件夹下。这样在代码中就可以直接使用文件名来引用它。
-
第 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 步:代码解析
-
导入库:
selenium相关的库用于浏览器控制和元素定位。BeautifulSoup来自bs4库,是一个强大的 HTML/XML 解析器,用于从网页源码中提取数据。csv库用于读写 CSV 文件。time库用于添加等待时间(sleep),模拟人类操作,防止因页面加载过快或过慢导致的问题。os库用于处理文件路径。
-
核心函数
crawl_guilin_30day_weather_edge():- 我们将所有逻辑封装在一个函数里,使代码更整洁。
-
初始化浏览器:
edgedriver_path:指定msedgedriver.exe的路径。因为我们把它和脚本放在了一起,所以直接写文件名即可。options:设置浏览器启动参数。例如--disable-gpu可以在某些环境下避免渲染问题。Service和webdriver.Edge():这是 Selenium 4.x 版本推荐的写法,用于创建一个浏览器驱动实例。
-
访问网页与模拟操作:
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 点击更稳定。
-
定位元素:
driver.find_element(By.XPATH, "xpath_expression"):通过 XPath 来精确定位一个元素。XPath 是一种在 XML 文档中查找信息的语言,非常适合在复杂的 HTML 结构中定位元素。你可以在浏览器开发者工具中右键点击元素,选择 “复制” -> “复制 XPath” 来快速获取。driver.find_element(By.TAG_NAME, "a"):通过标签名(如<a>)来定位元素。我们用它在 “查看更多” 的容器div内部找到了真正可点击的<a>标签。
-
异常处理:
- 代码中大量使用了
try...except块。这是编写健壮爬虫的关键。 NoSuchElementException:当find_element找不到元素时会抛出此异常。我们捕获它,并给出友好提示,而不是让程序直接崩溃。TimeoutException:当页面加载超过我们设定的 15 秒时触发。- 捕获通用的
Exception是为了防止一些未预料到的错误导致程序中断。
- 代码中大量使用了
-
数据提取与保存:
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可以解决这个问题。
-
资源释放:
finally块中的driver.quit()确保无论爬取成功还是失败,浏览器进程都会被关闭,避免资源泄露。
问题:在该网页下无法自己定位到查看更多,要在python运行时打开的网页自己点查看更多,后续会继续检查代码,给出完整结果。
注意事项与伦理:
- 遵守 robots.txt:在爬取任何网站前,请检查其根目录下的
robots.txt文件(如https://lishi.tianqi.com/robots.txt),了解网站的爬取规则。 - 控制爬取频率:不要对网站服务器造成过大压力。在你的请求之间添加适当的
sleep时间是个好习惯。 - 仅供学习和研究:爬取的数据应仅用于合法的学习和研究目的,请勿用于商业或其他可能侵犯他人权益的行为。
希望这篇博客对你有所帮助!
更多推荐
所有评论(0)