无法抓取BSCScan动态表格,requests_html返回空、Selenium失效如何解决?
问题根源
你遇到的所有报错/返回空的核心原因是BSCScan的持币人表格是嵌套在独立iframe中的,直接在主页面DOM中查找元素必然返回空,其次你的两段代码都存在语法/逻辑错误。
原有代码的错误点
requests_html 版本错误
- 没有切换iframe逻辑,requests_html的render对iframe支持度差,且BSCScan对无头浏览器的反爬拦截概率很高,不推荐用这个方案
- xpath选择器没有定位到正确的表格范围,直接查找全局td自然匹配不到
Selenium版本错误
- 逻辑错误:调用
get_attribute('innerHTML')拿到的是字符串类型,不能继续调用Selenium的元素查找方法find_elements_by_xpath - 位置错误:
driver.close()放在for循环内部,第一次遍历就会关闭浏览器,后续遍历直接失效 - 选择器错误:持仓占比的xpath写法错误,
//td[4]/text无法匹配到元素,需要获取对应td的textContent属性 - 缺少等待:没有设置页面渲染等待/iframe加载等待,元素还没加载完成就开始查找,必然返回空
修复后的可运行Selenium代码
import pandas as pd 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 # 注意:请自行替换为你的chromedriver路径,且chromedriver版本要和你的Chrome浏览器版本匹配 driver_path = "/Users/XXX/Downloads/chromedriver" driver = webdriver.Chrome(driver_path) # 全局等待超时时间设置为10秒 wait = WebDriverWait(driver, 10) def get_top_holders(driver, contract_address): list_holders = [] url = f"https://bscscan.com/token/{contract_address}#balances" driver.get(url) # 第一步:切换到持币人表格的iframe,这是最关键的一步 iframe = wait.until(EC.presence_of_element_located((By.ID, "tokenholderiframe"))) driver.switch_to.frame(iframe) # 等待表格tbody加载完成 wait.until(EC.presence_of_element_located((By.XPATH, "//tbody/tr"))) # 遍历所有行,跳过表头 rows = driver.find_elements(By.XPATH, "//tbody/tr") for row in rows: try: # 提取三个字段,注意索引是从1开始的 address = row.find_element(By.XPATH, "./td[2]/span/a").get_attribute("textContent").strip() quantity = row.find_element(By.XPATH, "./td[3]").get_attribute("textContent").strip() percentage = row.find_element(By.XPATH, "./td[4]").get_attribute("textContent").strip() holder_dict = { '地址': address, '持仓数量': quantity, '持仓占比': percentage } list_holders.append(holder_dict) print(holder_dict) except Exception as e: # 跳过异常行(比如末尾的分页行) continue # 操作完成后切回主页面,不要放在循环里 driver.switch_to.default_content() driver.quit() # 转成DataFrame holders_df = pd.DataFrame(list_holders) return holders_df # 调用测试 contract = "0x84c0160d55a05a28a034e1e6776f84c5995aba3a" df = get_top_holders(driver, contract) print(df)
补充说明
如果想搭配BeautifulSoup解析,只需要在切换iframe、等待加载完成后,调用driver.page_source拿到iframe的完整源码,传给BeautifulSoup后用css选择器提取即可,逻辑更简单,不容易写错xpath。
如果遇到反爬拦截,可以给Chrome添加启动参数,设置普通用户的User-Agent,降低拦截概率。
内容的提问来源于stack exchange,提问作者heeervas
相关产品推荐
相关产品推荐

