使用BeautifulSoup爬取加密货币数据调用find_all出现NoneType错误如何解决?
报错原因与解决方案
错误原因
这个AttributeError: 'NoneType' object has no attribute 'find_all'的直接原因是bitcoin_soup.find()没有找到匹配的table元素,返回了None值,后续调用None的find_all方法自然触发报错。导致find失效的核心原因有两个:
- 目标站点的历史数据表格是JavaScript动态渲染生成的,用requests直接发请求拿到的只是未渲染的初始HTML源码,里面根本没有表格内容
- 所用的class属性值是前端框架自动生成的哈希类名(比如h7vnx2-2这类),这类类名会随站点版本迭代频繁变动,稳定性极低,就算页面有表格也大概率匹配不到
另外现有代码还有两个隐藏bug: - 循环逻辑错误:拿到tr下的tds列表后又循环了每个td,再取td[0]的逻辑完全错误,应该直接取tds列表的对应索引
- pandas的append方法不会修改原DataFrame,只会返回新的对象,而且当前版本pandas已经废弃了append方法,需要用concat或者先存列表再统一转DataFrame的方式替代
解决方案
第一步:解决动态页面数据获取问题
替换静态的requests请求为支持JS渲染的工具,比如Selenium,示例代码如下:
from bs4 import BeautifulSoup import pandas as pd from selenium import webdriver from selenium.webdriver.chrome.options import Options # 配置无头浏览器 chrome_options = Options() chrome_options.add_argument("--headless=new") chrome_options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36") driver = webdriver.Chrome(options=chrome_options) bitcoin_url = "https://coinmarketcap.com/currencies/bitcoin/historical-data/" driver.get(bitcoin_url) # 等待页面渲染完成,可根据网络情况调整等待时长 driver.implicitly_wait(10) bitcoin_content = driver.page_source driver.quit()
第二步:优化元素匹配逻辑
不要使用哈希类名匹配,改用更稳定的匹配规则,比如直接匹配页面中所有table,再通过表头判断是否为目标表格:
bitcoin_soup = BeautifulSoup(bitcoin_content, "lxml") # 找到所有table,遍历判断表头 all_tables = bitcoin_soup.find_all("table") bitcoin_table = None for table in all_tables: headers = [th.text.strip() for th in table.find_all("th")] if set(['Date', 'Open', 'High', 'Low', 'Close', 'Volume', 'Market Cap']).issubset(set(headers)): bitcoin_table = table break if not bitcoin_table: raise Exception("未找到目标历史数据表格")
第三步:修正数据拼接逻辑
data_list = [] # 遍历表格行,跳过表头行 for tr in bitcoin_table.find_all("tr")[1:]: tds = tr.find_all("td") if len(tds) <7: continue row = { 'Date': tds[0].text.strip(), 'Open': tds[1].text.strip(), 'High': tds[2].text.strip(), 'Low': tds[3].text.strip(), 'Close': tds[4].text.strip(), 'Volume': tds[5].text.strip(), 'Market Cap': tds[6].text.strip() } data_list.append(row) # 一次性转成DataFrame,效率更高 bitcoin_df = pd.DataFrame(data_list)
内容的提问来源于stack exchange,提问作者Joseph_Koziol
相关产品推荐
相关产品推荐

