如何用Python结合JavaScript抓取网页表格及解决NaN无数据问题
嘿,我来帮你搞定这两个问题——先用Python结合JavaScript抓取网页表格,再解决你遇到的NaN/数字不显示的坑。
很多网页的表格数据是通过JavaScript动态渲染的,直接用requests抓静态源码根本拿不到真实数据,这时候就需要让JS先执行完,再抓取渲染后的内容。常见的方案有这三种:
1. 用Selenium模拟浏览器(最直观)
Selenium可以完全模拟真实浏览器的行为,自动执行页面上的JS,等表格加载完成后再抓取。举个简单的例子:
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 # 初始化浏览器(这里用Chrome,需要对应版本的chromedriver) driver = webdriver.Chrome() try: driver.get("目标网页URL") # 等待表格加载完成(最多等10秒) WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.TAG_NAME, "table")) ) # 获取渲染后的页面源码 page_source = driver.page_source soup = BeautifulSoup(page_source, "html.parser") # 解析表格 table = soup.find("table") rows = table.find_all("tr") for row in rows: cols = row.find_all(["td", "th"]) print([col.get_text(strip=True) for col in cols]) finally: driver.quit()
2. 用Pyppeteer(无头浏览器,更轻量)
Pyppeteer是Chrome DevTools协议的Python实现,不需要额外装浏览器驱动,用无头模式运行更隐蔽:
import asyncio from pyppeteer import launch from bs4 import BeautifulSoup async def scrape_table(): browser = await launch(headless=True) page = await browser.newPage() await page.goto("目标网页URL", waitUntil="networkidle2") # 等待网络空闲,确保JS加载完 page_source = await page.content() await browser.close() soup = BeautifulSoup(page_source, "html.parser") table = soup.find("table") # 后续解析逻辑和上面一样 rows = table.find_all("tr") for row in rows: cols = row.find_all(["td", "th"]) print([col.get_text(strip=True) for col in cols]) asyncio.get_event_loop().run_until_complete(scrape_table())
3. 直接提取JS变量(最高效,适合静态页面内嵌JS数据)
如果表格数据是存在页面的JS变量里(比如var tableData = [...];),可以用requests抓源码,再用execjs提取变量:
import requests import execjs url = "目标网页URL" response = requests.get(url) html = response.text # 提取JS变量(这里假设变量名为tableData) js_code = """ function getTableData() { // 这里复制页面里定义tableData的代码,或者直接返回变量 return tableData; } """ ctx = execjs.compile(js_code) table_data = ctx.call("getTableData") print(table_data)
你遇到的NaN问题,大概率是数据没被正确抓取到,常见原因和对应解法如下:
1. 数据是动态加载的(最常见)
很多网站的表格不是一开始就渲染在HTML里的,而是页面加载后通过AJAX请求后端接口,再用JS把数据插入DOM。这时候直接用requests抓静态源码,拿到的只是空的表格框架,解析后自然是NaN。
解法:用上面说的Selenium/Pyppeteer等无头浏览器,等待数据加载完成后再抓取;或者打开浏览器F12的Network标签,找到返回表格数据的XHR接口,直接用requests请求这个接口(速度更快)。
2. 数字是JS动态计算生成的
有些网站的表格数字是通过JS计算后插入的(比如从其他数据推导、格式化),静态源码里只有空标签或者占位符,直接抓取就会拿到空值,转成NaN。
解法:要么用无头浏览器让JS执行完再抓渲染后的内容;要么找到计算数字的JS逻辑,自己用Python实现(如果逻辑不复杂的话)。
3. 选择器定位错误
可能你写的CSS/XPath选择器没命中正确的表格元素,或者选中了错误的子节点,导致拿到的是空内容。
解法:打开浏览器F12,用元素选择器定位表格,检查真实的DOM结构,调整你的选择器。比如有时候表格嵌套在div里,或者有多个table标签,要选对目标表格。
4. 网站有反爬机制
有些网站会检测请求是否来自真实浏览器,如果是requests的默认请求头,可能会返回空数据或者假数据,导致你拿到NaN。
解法:模拟浏览器的请求头(比如加User-Agent、Referer),带上必要的Cookie;或者用无头浏览器模拟正常用户行为(比如加随机等待时间、滚动页面)。
假设你用requests抓表格拿到NaN,换成Selenium等待数据加载:
from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By import pandas as pd driver = webdriver.Chrome() try: driver.get("目标网页URL") # 等待表格里的第一个数字单元格加载完成 WebDriverWait(driver, 15).until( EC.text_to_be_present_in_element((By.CSS_SELECTOR, "table td:nth-child(2)"), "") ) # 直接用pandas读取表格,更方便 df = pd.read_html(driver.page_source)[0] print(df) # 这时候数字应该能正常显示了 finally: driver.quit()
内容的提问来源于stack exchange,提问作者saga

