杰瑞科技汇

Python 爬 gridview

GridView 本身不是一个网页,而是一种服务器端控件,常见于 ASP.NET 等后端技术框架生成的网页中。

这意味着,你不能直接用 Python 像抓取普通 HTML 标签(如 <div>, <table>)那样去“爬”一个 GridView,你需要理解 GridView 是如何最终呈现在浏览器中的。

GridView 在服务器端生成数据,然后将其渲染成标准的 HTML 表格 (<table>) 发送到浏览器,我们的爬虫策略就是:找到 GridView 渲染成的 HTML 表格,然后解析它。

下面我将分步讲解,并提供详细的代码示例。


第一步:理解 GridView 在网页中的表现形式

当你用浏览器开发者工具(F12)查看一个包含 GridView 的网页时,你看到的 HTML 大概是这样的:

<!-- 这是 GridView 控件渲染出来的最终 HTML -->
<table id="GridView1" border="1" style="border-collapse:collapse;">
    <thead>
        <tr>
            <th scope="col">ID</th>
            <th scope="col">姓名</th>
            <th scope="col">年龄</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1</td>
            <td>张三</td>
            <td>25</td>
        </tr>
        <tr>
            <td>2</td>
            <td>李四</td>
            <td>30</td>
        </tr>
        <tr>
            <td>3</td>
            <td>王五</td>
            <td>28</td>
        </tr>
    </tbody>
</table>

我们的目标就是解析这个 <table> 元素,这和爬取任何其他网页上的表格数据在技术上没有本质区别。


第二步:选择合适的 Python 库

对于爬虫任务,我们通常使用以下两个库:

  1. requests: 用于发送 HTTP 请求,获取网页的 HTML 内容。
  2. BeautifulSoup4: 用于解析 HTML 文档,方便地查找和提取我们需要的数据。

安装

pip install requests
pip install beautifulsoup4
pip install lxml  # 一个高效的 HTML 解析器,推荐使用

第三步:编写爬虫代码(完整示例)

假设我们要爬取的网页 URL 是 http://example.com/data,里面就包含上面那个 GridView。

静态页面(GridView 数据直接加载)

如果页面加载时,GridView 的所有数据就已经在 HTML 中了,这是最简单的情况。

import requests
from bs4 import BeautifulSoup
# 1. 目标网页 URL
url = 'http://example.com/data'  # 请替换为你的目标网址
# 2. 发送 HTTP GET 请求
try:
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
    }
    response = requests.get(url, headers=headers, timeout=10)
    response.raise_for_status()  # 如果请求失败 (状态码不是 200), 则抛出异常
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")
    exit()
# 3. 使用 BeautifulSoup 解析 HTML
# 'lxml' 是一个更快的解析器,需要提前安装
soup = BeautifulSoup(response.text, 'lxml')
# 4. 定位到 GridView 渲染的表格
# 你需要通过开发者工具找到表格的 id 或 class
# 假设表格的 id 是 "GridView1"
gridview_table = soup.find('table', id='GridView1')
if not gridview_table:
    print("在页面中未找到 id 为 'GridView1' 的表格。")
    exit()
# 5. 解析表格数据
all_data = []
# 找到表头
header_row = gridview_table.find('tr')
headers = [th.get_text(strip=True) for th in header_row.find_all('th')]
all_data.append(headers)
# 找到所有数据行
data_rows = gridview_table.find('tbody').find_all('tr') if gridview_table.find('tbody') else gridview_table.find_all('tr')[1:]
for row in data_rows:
    # 提取每一行的所有单元格
    cells = row.find_all('td')
    # 获取单元格的文本内容,并去除首尾空格
    row_data = [cell.get_text(strip=True) for cell in cells]
    all_data.append(row_data)
# 6. 打印或处理数据
print("爬取到的数据:")
for row in all_data:
    print(row)
# 也可以将数据保存到 CSV 文件
import csv
with open('gridview_data.csv', 'w', newline='', encoding='utf-8-sig') as f:
    writer = csv.writer(f)
    writer.writerows(all_data)
print("\n数据已成功保存到 gridview_data.csv")

第四步:处理更复杂的情况

在实际项目中,GridView 往往不会这么简单。

GridView 分页

GridView 有分页功能,数据通常是分页加载的,你需要模拟点击“下一页”来获取所有数据。

解决方法

  1. 分析“下一页”按钮的链接,它通常是一个指向同一页面的 POST 请求,并带有分页参数(如 __EVENTTARGET, __EVENTARGUMENT)。
  2. 你需要使用 requestsSession 对象来保持 cookies。
  3. 在第一次请求后,解析页面中隐藏的 <input> 字段(如 __VIEWSTATE, __EVENTVALIDATION),这些是 ASP.NET WebForms 用于维持状态和验证请求的。
  4. 在构造下一次请求时,将这些隐藏字段和分页参数一起发送。

这是一个非常复杂的任务,通常需要借助 Selenium 来模拟浏览器行为,因为它能更好地处理 JavaScript 和页面状态。

GridView 数据通过 AJAX 动态加载

现代网站越来越多地使用 AJAX 来动态加载数据,页面初始加载时可能没有数据,或者只有第一页数据。

解决方法

  1. 使用浏览器开发者工具(F12)的 "Network" (网络) 选项卡
  2. 刷新页面,然后尝试翻页或触发数据加载。
  3. 在网络请求列表中,找到一个 XHR (XMLHttpRequest) 或 Fetch 请求,这个请求就是真正获取 GridView 数据的 API。
  4. 分析这个请求的 URL、请求方法(GET/POST)、请求头和请求体。
  5. 使用 requests 库直接模拟这个 API 请求,这通常比解析整个页面要简单、高效得多。

示例: 假设你发现了一个 API http://example.com/api/getdata?page=1

import requests
import json
api_url = 'http://example.com/api/getdata'
all_data = []
page = 1
while True:
    params = {'page': page}
    headers = {'User-Agent': 'My-Crawler/1.0'}
    try:
        response = requests.get(api_url, params=params, headers=headers)
        response.raise_for_status()
        data = response.json() # 假设返回的是 JSON 格式
        if not data['items']: # 假设数据在 'items' 键下,如果为空则结束
            break
        all_data.extend(data['items'])
        print(f"成功获取第 {page} 页数据,共 {len(data['items'])} 条。")
        page += 1
    except requests.exceptions.RequestException as e:
        print(f"请求 API 失败: {e}")
        break
    except json.JSONDecodeError:
        print("返回的不是有效的 JSON 格式。")
        break
print(f"爬取完成,共获取 {len(all_data)} 条数据。")

第五步:终极武器 - Selenium

当遇到复杂的分页、AJAX 加载、或者有反爬虫机制(如验证码)时,直接使用 requests 会变得非常困难,这时,Selenium 是你的最佳选择。

Selenium 可以模拟一个真实的浏览器(如 Chrome),它可以执行 JavaScript,点击按钮,等待内容加载,然后获取渲染后的完整 HTML。

安装

pip install selenium
# 还需要下载对应浏览器的 WebDriver,ChromeDriver
# 确保你的 Chrome 浏览器版本和 ChromeDriver 版本匹配

Selenium 爬取 GridView 分页示例

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
# 1. 设置 WebDriver (这里以 Chrome 为例)
# 确保你已经下载了 chromedriver 并将其路径放在这里,或者确保它在你的系统 PATH 中
driver = webdriver.Chrome()
# 2. 打开目标网页
url = 'http://example.com/paged_data'
driver.get(url)
# 3. 循环点击“下一页”直到没有下一页为止
all_data = []
page_count = 0
while True:
    page_count += 1
    print(f"正在处理第 {page_count} 页...")
    # 4. 等待 GridView 表格加载完成 (使用显式等待)
    try:
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.ID, "GridView1"))
        )
    except Exception as e:
        print(f"等待 GridView 加载超时: {e}")
        break
    # 5. 获取当前页面的 HTML
    soup = BeautifulSoup(driver.page_source, 'lxml')
    # 6. 解析当前页的数据 (解析逻辑和前面一样)
    gridview_table = soup.find('table', id='GridView1')
    if not gridview_table:
        break
    data_rows = gridview_table.find('tbody').find_all('tr') if gridview_table.find('tbody') else gridview_table.find_all('tr')[1:]
    for row in data_rows:
        cells = row.find_all('td')
        row_data = [cell.get_text(strip=True) for cell in cells]
        all_data.append(row_data)
    # 7. 尝试点击“下一页”按钮
    try:
        # 假设“下一页”按钮的 id 或 class 是 'NextButton'
        next_button = WebDriverWait(driver, 5).until(
            EC.element_to_be_clickable((By.ID, 'NextButton'))
        )
        next_button.click()
        # 等待页面跳转和数据加载
        time.sleep(2) # 简单等待,更稳健的方式是等待某个元素出现
    except Exception as e:
        print(f"找不到或无法点击“下一页”按钮,可能已经是最后一页,错误: {e}")
        break
# 8. 关闭浏览器
driver.quit()
# 9. 输出结果
print(f"爬取完成,共获取 {len(all_data)} 条数据。")
for row in all_data:
    print(row)

总结与建议

  1. 先分析,再编码:永远先用浏览器开发者工具(F12)分析网页结构,搞清楚数据是如何加载的(静态、AJAX、分页)。
  2. 从简到繁:优先尝试使用 requests + BeautifulSoup,因为它更轻量、更快。
  3. 拥抱 AJAX:如果数据是 AJAX 加载的,直接找到 API 并模拟请求,这是最高效的方式。
  4. Selenium 是大招:当页面交互复杂(如分页、弹窗、验证码)时,不要犹豫,直接使用 Selenium,它能帮你解决 90% 的难题,但速度相对较慢。
  5. 合法爬虫:遵守网站的 robots.txt 协议,控制爬取频率,不要给对方服务器造成过大压力。
分享:
扫描分享到社交APP
上一篇
下一篇