从Scrapy转用BeautifulSoup提取OER数据的三类问题求助
我来帮你逐个解决这些从Scrapy切换到BeautifulSoup时遇到的问题,顺便解答你关于select方法的疑问:
1. 带空格ID的元素与访问量/保存次数提取
首先明确:BeautifulSoup完全支持ID带空格的元素定位,你之前的代码可能是没正确定位到目标文本节点。
你的Scrapy XPath是通过fa fa-eye类的i标签找后续兄弟span,对应到BeautifulSoup,应该用更贴合原逻辑的写法:
# 提取访问量(对应你Scrapy的cant_visitas) cant_visitas = soup.find('i', class_='fa fa-eye').find_next_sibling('span').get_text(strip=True) # 如果是通过ID为"Number of saves"的li元素提取保存次数 cant_guardados = soup.find('li', id='Number of saves').find('span').get_text(strip=True)
注意点:
- 用
find_next_sibling('span')对应XPath的following-sibling::span[1],直接定位目标兄弟节点 - 加
strip=True自动去除文本前后的空格、换行符,对应Scrapy的normalize-space效果 - 若担心元素不存在报错,可以加判断:
cant_visitas = elem.get_text(strip=True) if (elem := soup.find('i', class_='fa fa-eye').find_next_sibling('span')) else None
2. 嵌套列表(学位列表)提取:模拟getall()效果
BeautifulSoup的select方法返回的是ResultSet对象(本质是元素列表),要实现类似Scrapygetall()的效果,只需要遍历列表提取每个元素的文本即可:
# 获取目标dd元素列表 dd_elements = soup.find('div', 'material-details').select('dl:nth-of-type(2)>dd:nth-of-type(3)') # 提取每个元素的文本,处理空格后得到列表 degrees = [dd.get_text(strip=True) for dd in dd_elements]
如果原页面中该dd标签内是多行文本(比如换行分隔的学位),想要把单元素内的文本拆分成列表,可以这样处理:
# 假设dd内的文本是换行分隔的 degree_text = soup.find('div', 'material-details').select_one('dl:nth-of-type(2)>dd:nth-of-type(3)').get_text(strip=True) degrees = [item.strip() for item in degree_text.split('\n') if item.strip()]
这样就能得到和Scrapygetall()类似的结构化列表。
3. 提取评分平均值:直接获取data-rating-value属性
你不需要通过统计激活星星数量来间接获取评分,BeautifulSoup可以直接提取元素的自定义属性:
# 定位到带data-rating-value的div元素 rating_div = soup.find('div', 'item-rating').find('div', 'stars') # 提取属性值,转成float类型(如果需要数值计算) average_rating = float(rating_div.get('data-rating-value'))
用get('属性名')方法可以安全获取属性值(如果属性不存在会返回None,避免报错),比直接用rating_div['data-rating-value']更稳妥。
关于select方法的疑问
你说得没错!BeautifulSoup的select方法是最接近XPath的定位方式,它支持CSS选择器语法,和XPath一样可以实现复杂的层级定位、伪类选择(比如:nth-of-type、:first-child)等操作。很多时候,CSS选择器比XPath更简洁易读,完全可以作为Scrapy XPath的替代方案在BeautifulSoup中使用。
内容的提问来源于stack exchange,提问作者Cristian

