
当`pandas.read_html`无法从网页中提取可见表格时,通常是因为数据通过javascript动态加载。本教程将演示如何利用浏览器开发者工具识别底层的api请求,然后使用`requests`库直接获取json数据,并将其转换为pandas dataframe,从而为提取此类动态web内容提供一个健壮的解决方案。
深入理解pandas.read_html的局限性
pandas.read_html是一个非常便捷的工具,用于从HTML页面中解析<table>标签并将其转换为DataFrame。然而,它的工作原理是解析页面的静态HTML源代码。如果网页中的表格内容是通过javaScript在页面加载完成后动态生成或从后端API异步获取的,那么read_html在检查原始HTML时将无法找到这些表格,从而返回一个空列表。
即使尝试使用Selenium等工具来渲染页面,然后获取元素属性,如果目标表格的HTML内容本身也是通过javascript填充的,并且在检查时还未完全渲染或获取到数据,也可能导致获取到空内容。在这种情况下,最有效的方法是绕过前端渲染逻辑,直接与数据源API进行交互。
识别动态数据源:开发者工具的妙用
解决此类问题的关键在于找出网页在后台是如何获取这些动态数据的。现代浏览器都提供了强大的开发者工具,其中“网络”(Network)标签页是我们的主要战场。
- 打开开发者工具: 在目标网页上,按下F12键(或右键点击页面选择“检查”)。
- 切换到“网络”标签页: 清空(Clear)现有请求,然后刷新页面。
- 筛选XHR/Fetch请求: 在“网络”标签页中,通常有一个筛选器,选择“XHR”或“Fetch/XHR”可以只显示异步http请求。这些请求通常是网页用于获取动态数据的。
- 分析请求: 仔细观察这些请求,寻找那些返回与表格数据相关内容的请求。对于本例,目标网站的表格数据是通过一个POST请求获取的。
通过上述步骤,我们可以精确地模拟浏览器获取数据的过程。
立即学习“Python免费学习笔记(深入)”;
使用requests和pandas获取并处理数据
一旦确定了API端点、请求方法、载荷和必要的请求头,就可以使用python的requests库来模拟这个HTTP请求,并用pandas来处理返回的数据。
以下是实现此目标的Python代码示例:
import requests import pandas as pd # 1. 定义API端点 # 这是通过开发者工具识别出的实际数据接口URL,而不是显示表格的页面URL url = 'https://anex.us/grades/getData/' # 2. 定义请求载荷 (Payload) # 这些参数是网站前端发送给后端API以获取特定数据所必需的 payload = {'dept': 'ENGR', 'number': '102'} # 3. 定义请求头 (Headers) # 模拟浏览器User-Agent可以帮助避免一些网站的反爬虫机制 headers = {'User-Agent': 'Mozilla/5.0 (X11; linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0'} # 4. 发送POST请求并获取响应 # 使用requests.post发送POST请求,并将载荷和请求头传递进去 try: response = requests.post(url, data=payload, headers=headers) response.raise_for_status() # 检查HTTP请求是否成功 (200 OK) # 5. 解析jsON响应 # 网站通常返回json格式的数据,使用.json()方法解析 data = response.json() # 6. 将数据转换为pandas DataFrame # 根据JSON结构,提取包含实际表格数据的部分(例如,'classes'键) if 'classes' in data: df = pd.DataFrame(data['classes']) print(df.head()) # 打印前几行数据进行验证 print(f"n成功获取 {len(df)} 行数据。") else: print("JSON响应中未找到 'classes' 键。") except requests.exceptions.RequestException as e: print(f"请求失败: {e}") except ValueError: print("无法解析JSON响应,可能响应内容不是有效的JSON格式。") except Exception as e: print(f"发生未知错误: {e}")
输出示例:
dept number section A B C D F I S U Q X prof year semester gpa 0 ENGR 102 20 18 17 8 2 3 0 0 0 1 0 AMINI N 2018 FALL 2.9375 1 ENGR 102 21 18 31 15 4 1 0 0 0 0 0 KOOLA P 2018 FALL 2.88405797101449 2 ENGR 102 22 10 28 16 2 3 0 0 0 0 0 SHAW S 2018 FALL 2.67796610169492 3 ENGR 102 26 9 24 10 4 6 0 0 0 0 0 SUBRAMANIAN R 2018 FALL 2.49056603773585 4 ENGR 102 201 21 12 1 1 0 0 0 0 0 0 IJAZ M 2018 FALL 3.51428571428571 成功获取 491 行数据。
注意事项与最佳实践
- 动态载荷: 如果需要获取不同参数(如本例中的dept和number)的数据,可以将payload字典参数化,通过循环或函数调用来获取多组数据。
- 错误处理: 在实际应用中,务必添加健壮的错误处理机制,例如try-except块来捕获网络连接错误、HTTP状态码非200错误或JSON解析错误。
- User-Agent: 并非所有网站都需要设置User-Agent,但一些网站会检查它来阻止自动化脚本。模拟常见的浏览器User-Agent通常是一个好的实践。
- 频率限制与robots.txt: 在抓取数据时,请务必遵守网站的robots.txt协议,并尊重网站的访问频率限制,避免对服务器造成不必要的负担。过度或恶意抓取可能导致IP被封禁。
- 数据结构变化: 网站API的数据结构可能会随着时间而改变。如果脚本突然失效,请重新检查开发者工具以确认API端点或JSON结构是否有更新。
总结
当pandas.read_html无法满足动态网页数据抓取的需求时,直接与网站的后端API交互是更强大和灵活的解决方案。通过熟练运用浏览器的开发者工具,我们可以有效地识别数据来源,然后结合requests库模拟HTTP请求,并利用pandas库高效地将获取的JSON数据转换为结构化的DataFrame,从而克服动态网页数据提取的挑战。