
本文旨在解决使用beautifulsoup进行网页抓取时,因选择器不准确或数据提取方式不当导致返回空列表的常见问题。通过分析错误示例,我们将重点介绍如何利用css选择器进行更精确的元素定位,并采用迭代父元素、逐个提取子元素的高效策略,辅以`.get_text()`方法确保文本内容的正确获取,从而构建稳定可靠的网页数据抓取程序。
网页抓取中空列表问题的根源
在使用Python的requests和BeautifulSoup库进行网页数据抓取时,一个常见的困扰是程序最终输出一个空列表。这通常不是因为网络请求失败(尽管这也是一个需要检查的因素),而是因为BeautifulSoup未能根据我们提供的选择器找到目标元素。
以以下代码片段为例,它尝试从inshorts.com抓取新闻标题和内容:
import requests
from bs4 import BeautifulSoup
url = 'https://inshorts.com/en/read/technology'
news_data = []
news_category = url.split('/')[-1]
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124124 Safari/537.36'}
data = requests.get(url, headers=headers)
if data.status_code == 200:
soup = BeautifulSoup(data.content, 'html.parser')
# 原始代码中的选择器
headlines = soup.find('div', class_=['news-card-title', 'news-right-box'])
articles = soup.find('div', class_=['news-card-content', 'news-right-box'])
if headlines and articles and len(headlines) == len(articles):
news_articles = [
{
'news_headline': headline.find_all('span', attrs={'itemprop': 'headline'}).string,
'news_article': article.find_all('div', attrs={'itemprop': 'articleBody'}).string,
'news_category': news_category
}
for headline, article in zip(headlines, articles)
]
news_data.extend(news_articles)
print(news_data)这段代码返回空列表的主要原因在于其选择器存在问题。soup.find('div', class_=['news-card-title', 'news-right-box'])和soup.find('div', class_=['news-card-content', 'news-right-box'])这两个语句很可能返回None。find()方法只返回匹配的第一个元素,并且在处理class_参数时,传入列表通常表示匹配所有这些类,而不是匹配其中任意一个。更重要的是,如果选择器不准确,find()将直接返回None,导致后续对headlines或articles进行操作时引发错误,或者在if headlines and articles条件判断时直接失败,最终导致news_data保持为空。此外,即使元素被找到,find_all(...).string也可能无法正确提取文本,因为find_all返回的是一个列表,即使只有一个元素,也需要进一步处理,且.string属性仅适用于只有一个子节点的标签。
优化选择器与数据提取策略
为了解决上述问题,我们需要采用更精确的选择器和更健壮的数据提取逻辑。关键在于:
数据本地化解决接口缓存数据无限增加,读取慢的问题,速度极大提升更注重SEO优化优化了系统的SEO,提升网站在搜索引擎的排名,增加网站爆光率搜索框本地化不用远程读取、IFRAME调用,更加容易应用及修改增加天气预报功能页面增加了天气预报功能,丰富内容增加点评和问答页面增加了点评和问答相关页面,增强网站粘性电子地图优化优化了电子地图的加载速度与地图功能酒店列表增加房型读取酒店列表页可以直接展示房型,增
- 利用CSS选择器: Beautiful Soup支持强大的CSS选择器,通过select()和select_one()方法,我们可以编写更简洁、更准确的定位规则。
- 定位父级容器: 优先找到包含所有目标信息的共同父级容器。这样可以确保我们处理的是一个个完整的数据单元。
- 迭代与局部提取: 遍历每个父级容器,然后在该容器的范围内进一步提取其子元素的信息。
- 使用.get_text(): 确保正确提取元素的文本内容,避免.string属性可能带来的问题。
改进后的抓取代码示例
以下是优化后的代码,展示了如何应用这些策略:
import requests
from bs4 import BeautifulSoup
url = 'https://inshorts.com/en/read/technology'
news_data = []
news_category = url.split('/')[-1]
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124124 Safari/537.36'}
data = requests.get(url, headers=headers)
if data.status_code == 200:
soup = BeautifulSoup(data.content, 'html.parser')
# 使用CSS选择器定位每个新闻文章的父级容器
# 经过网页检查,发现每个新闻文章都由一个具有特定itemtype属性的div包裹
for article in soup.select('[itemtype="http://schema.org/NewsArticle"]'):
# 在每个article容器内,使用select_one定位标题和文章内容
headline_element = article.select_one('[itemprop="headline"]')
article_body_element = article.select_one('[itemprop="articleBody"]')
# 检查元素是否存在,并使用.get_text()提取文本
news_headline = headline_element.get_text(strip=True) if headline_element else "N/A"
news_article = article_body_element.get_text(strip=True) if article_body_element else "N/A"
news_data.append(
{
'news_headline': news_headline,
'news_article': news_article,
'news_category': news_category
}
)
print(news_data)代码解析:
- soup.select('[itemtype="http://schema.org/NewsArticle"]'): 这是核心改进。通过检查目标网页的HTML结构(通常使用浏览器开发者工具),我们发现每篇新闻文章都由一个div标签包裹,该标签具有itemtype="http://schema.org/NewsArticle"属性。这是一个非常精确且稳定的CSS选择器,能够直接选中所有新闻文章的根容器。select()方法会返回一个匹配所有元素的列表。
- for article in ...: 我们遍历select()返回的每个article元素,确保我们逐个处理每篇新闻。
- article.select_one('[itemprop="headline"]') 和 article.select_one('[itemprop="articleBody"]'): 在每个article容器的内部,我们再次使用select_one()方法来定位新闻标题(itemprop="headline")和文章主体(itemprop="articleBody")。select_one()只返回第一个匹配的元素,适用于我们期望只找到一个标题或一个文章主体的情况。
- .get_text(strip=True): 这是提取文本内容的推荐方法。get_text()会获取元素及其所有子元素的可见文本内容,strip=True参数可以去除文本两端的空白字符,使结果更整洁。同时,我们添加了if headline_element else "N/A"这样的检查,以防某些元素确实缺失,增强代码的鲁棒性。
注意事项与最佳实践
- 审查HTML结构: 在编写任何选择器之前,务必使用浏览器的开发者工具(F12)仔细检查目标网页的HTML结构。理解元素的层级关系、ID、类名、属性等是编写有效选择器的基础。
- CSS选择器优先: 对于复杂的选择需求,CSS选择器通常比find()和find_all()的组合更强大、更简洁。它们支持ID、类名、属性、子元素、兄弟元素等多种组合方式。
- 处理动态内容: 如果目标网站使用JavaScript动态加载内容,requests和BeautifulSoup可能无法获取到这些内容。此时,您可能需要考虑使用Selenium等工具来模拟浏览器行为。
- User-Agent: 在请求头中设置User-Agent是一个良好的实践,可以模拟浏览器访问,降低被网站识别为爬虫并阻止的风险。
- 错误处理: 始终考虑网络请求失败(data.status_code != 200)和元素未找到(选择器返回None或空列表)的情况,并进行相应的错误处理或默认值设置。
- 遵守Robots协议: 在进行网页抓取时,请务必查看网站的robots.txt文件,了解网站是否允许爬取以及允许爬取的范围。尊重网站的使用条款。
总结
当BeautifulSoup返回空列表时,通常意味着您的选择器未能准确匹配目标元素。通过采用更精确的CSS选择器(如select()和select_one()),定位包含完整数据单元的父级容器,并在其内部进行迭代和局部提取,结合使用.get_text()方法,可以显著提高网页抓取程序的稳定性和准确性。始终牢记,深入理解目标网页的HTML结构是编写高效、健壮爬虫的关键。









