注: 上一篇《Python+Selenium 爬取动态加载页面(1)》讲了基本地如何获取动态页面的数据, 这里再讲一个稍微复杂一点的数据获取全国水雨情网 http://xxfb.hydroinfo.gov.cn/ssIndex.html . 数据的获取过程跟人手动获取过程类似, 所以也不会对服务器造成更大负荷. 本文的代码见 Selenium 获取动态页面数据 2.ipynb 或 Selenium 获取动态页面数据 2.py. 同样在开始前需要准备环境, 具体环境准备参考上一篇.
1, 数据获取目标
全国水雨情网的数据同样是动态加载出来的, 在浏览中打开网页后 http://xxfb.hydroinfo.gov.cn/ssIndex.html http://xxfb.hydroinfo.gov.cn/ssIndex.html , 可以看到回下图 1-1 所示的页面.
图 1-1 全国水雨情网
可以看到, 其中并没有显示任何的数据, 如果我们需要查看数据, 还需要点击一下其中的几个按钮. 比如, 我们需要得到其中大型水库的数据, 那么需要点击大型水库按钮, 得到如下图 1-2 所示的结果.
图 1-2 全国水雨情信息 - 大型水库
得到这个页面后, 就可以从其中的 HTML 页面源码中解析数据了.
2, 详细爬取过程
2.1 打开网页
运行下面代码, 会自动弹出 Chrome 浏览器的窗口; 如果用的 browser = webdriver.PhantomJS(), 则没有窗口出来. 浏览器的窗口出来后, 可以看到, 它加载出我们的页面了.
- import re
- import pandas as pd
- from bs4 import BeautifulSoup
- from selenium import webdriver
- from selenium.webdriver.support.ui import WebDriverWait
- from selenium.webdriver.common.by import By
- from selenium.webdriver.support import expected_conditions as EC
- # 打开 Chrome 浏览器(需提前安装好 chromedriver)
- browser = webdriver.Chrome()
- # browser = webdriver.PhantomJS()
- # 这里经常出现加载超的异常, 后面需要处理一下: 捕获异常后, 刷新浏览器
- browser.get("http://xxfb.hydroinfo.gov.cn/ssIndex.html")
2.2 查找目标数据
页面加载完成后, 接下需要定位我们的大型水库按钮, 通过按浏览器的 F12 查看源码, 可以得到这个按钮的 xpath://*[@id="sscontent"]/table/tbody/tr/td/table/tbody/tr[1]/td/table/tbody/tr/td[3]/a, 如图 2-1 所示.
图 2-1 xpath 定位
定位到按钮后, 需要再点击一下.
- # 找到大型水库的按钮 --- 通过 xpath
- btn_water_xpath = "//*[@id=\"sscontent\"]/table/tbody/tr/td/table/tbody/tr[1]/td/table/tbody/tr/td[3]/a"
- # 等待响应完成
- wait = WebDriverWait(browser, 10)
- wait.until(EC.presence_of_element_located((By.XPATH, btn_water_xpath)))
- # 查找目标按钮
- btn_water = browser.find_element_by_xpath(btn_water_xpath)
- # 找到按钮后单击
- btn_water.click()
2.2 得到页面源码
数据定位完成后, 接下来得到页面源码. 按照上面同样的方法, 我们需要定位其中的数据表, 并等待其加载完成, 加载完成后, 我们才能读取其页面源码.
- # 大型水库的数据表 --xpath
- water_table_xpath = "//*[@id=\"sktable\"]/table/tbody"
- # 得到新的页面, 并等待其数据表加载完成
- wait = WebDriverWait(browser, 10)
- wait.until(EC.presence_of_element_located(
- (By.XPATH, water_table_xpath)))
- soup = BeautifulSoup(browser.page_source, 'lxml')
2.3 提取数据
得到页面源码后, 可直接利用 Pandas 的 read_html 方法得以其中的表格数据, 非常方便. 同时我们还需要定位它的表头信息, 来得到我们的表头.
- # 表头信息
- table_head_csel = "#skcontent>table:nth-child(3)>tbody>tr"
- table_head = soup.select(table_head_csel)[0]
- # 通过 CSS 选择器, 找到水库表信息
- table_css_select = "#sktable"
- table_content = soup.select(table_css_select)[0]
- df_table = pd.read_html(str(table_content))[0]
- df_table.columns = [h.text for h in table_head.find_all("td")]
- # df_table.columns = ['流域', '行政区', '河名', '库名', '库水位(米)', \
- # '蓄水量(亿米 3)', '入库(米 3 / 秒)', '堤顶高程(米)']
查看提取数据的前 5 行, 如表 2.1 所示.
表 2.1 提取数据前 5 行
流域 | 行政区 | 河名 | 库名 | 库水位 (米) | 蓄水量 (亿米 3) | 入库 (米 3 / 秒) | 堤顶高程 (米) | |
---|---|---|---|---|---|---|---|---|
0 | 其他流域 | 新疆 | 乌伦古河 | 福海水库 | 575.14 ↓ | 1.756 | 0 | 579.00 |
1 | 其他流域 | 新疆 | 卡浪古尔河 | 喀浪古尔水库 | 988.26 — | 0.126 | 1.23 | 1007.50 |
2 | 其他流域 | 新疆 | 三屯河 | 三屯河水库 | 1024.49 — | 0.078 | 1.982 | 1039.60 |
3 | 其他流域 | 新疆 | 头屯河 | 头屯河水库 | 986.52 — | 0.108 | 1.75 | 995.20 |
4 | 其他流域 | 新疆 | 乌鲁木齐河 | 红雁池水库 | 995.54 ↓ | 0.234 | 0 | 1009.00 |
2.4 保存数据
数据提取完成后, 下面还将其表格的时间提取出来, 以作为文件名, 防止数据重复. 这里利用了一个简单的正则表达式提时间信息, 然后利用 pandas 的 to_csv 方法, 得到两种编码格式的. CSV 文件. 其中 rvr_tab_2019_1_22_ch.CSV 为中文编码格式, 可直接用 Excel 打开, 如图 2-2 所示.
- # 找到水库信息的时间
- table_time_csel = "#skdate"
- time_text = soup.select(table_time_csel)[0].text
- time_info = re.search(r"(\d{4})年 (\d{1,2}) 月(\d{1,2})", time_text).groups()
- df_table.to_csv("rvr_tab_%s_%s_%s.csv" % time_info, index=None)
- # 换一个编码方式, 此文件可以直接用 Excel 打开, 不会出现乱码
- df_table.to_csv("rvr_tab_%s_%s_%s_ch.csv" % time_info,encoding="GB18030", index=None)
图 2-2 获取的数据
总结
本文的方法与上一篇《Python+Selenium 爬取动态加载页面(1)》的方法类似, 只是多一个按钮点击的过程.
来源: https://www.cnblogs.com/endlesscoding/p/10306429.html