选择器在具有定义词的两个元素之间抓取列表
Selectors to scrape lists between two elements with defined words
尝试抓取部分目录 (toc),最好使用 SoupSieve:
<html>
<dl>
#many rows
<dd>4.2.1. Drivers</dd>
<dd>4.2.1.1. itemD1</dd>
<dd>4.2.1.2. itemD2</dd>
<dd>4.2.1.3. itemD3</dd>
<dd>4.2.2. Constraints</dd>
<dd>4.2.2.1. itemC1</dd>
#many more rows
</dl>
</html>
请注意,我需要的项目不是 4.2.1 的 children/descendants。驱动程序,只是因为编号看起来像。
现在,我需要抓取的元素是元素 Drivers 和 Constraints 之间的元素。并不总是其中的 3 个——它可能是 0 个或 3 个或 5 个,这取决于。稍后在我的代码中,我使用 pandas 将这些元素输出到 .csv.
中的单个单元格中
我试过这样的事情:
def get_drivers():
data.append({
'url': url,
'type': 'driver',
'list': [x.get_text(strip=True) for x in toc.select('dd:-soup-contains-own("Drivers") ~ dd')]
})
...但这只是给了我从驱动程序到文档末尾的所有元素,通常是我不需要的几十个元素。
问题:如何让选择器在 Drivers 之后开始选择并在 Constraints 停止选择?
您完全可以使用 css 选择器来做到这一点。使用 :-soup-contains
和 :not
,以及通用兄弟组合器 (~
) 和类型选择器 (dd
) 来过滤掉每个后面的内容(即减去 Constraints
从 Drivers
开始
from bs4 import BeautifulSoup as bs
html = '''<html>
<dl>
#many rows
<dd>4.2.1. Drivers</dd>
<dd>4.2.1.1. itemD1</dd>
<dd>4.2.1.2. itemD2</dd>
<dd>4.2.1.3. itemD3</dd>
<dd>4.2.2. Constraints</dd>
<dd>4.2.2.1. itemC1</dd>
#many more rows
</dl>
</html>'''
soup = bs(html, 'lxml')
filtered = [i.text for i in soup.select(
'dd:-soup-contains(" Drivers") ~ dd:not(dd:-soup-contains(" Constraints"), dd:-soup-contains(" Constraints") ~ dd)')]
我想一个循环也可以工作,但在我看来不太可取:
from bs4 import BeautifulSoup as bs
html = '''<html>
<dl>
#many rows
<dd>4.2.1. Drivers</dd>
<dd>4.2.1.1. itemD1</dd>
<dd>4.2.1.2. itemD2</dd>
<dd>4.2.1.3. itemD3</dd>
<dd>4.2.2. Constraints</dd>
<dd>4.2.2.1. itemC1</dd>
#many more rows
</dl>
<dl>
<dd>Error</dd>
</dl>
</html>'''
soup = bs(html, 'lxml')
start = soup.select_one('dd:-soup-contains(" Drivers") + dd')
next_node = start
while True:
if not next_node:
break
if 'Constraints' in next_node.text:
break
print(next_node.text)
next_node = next_node.find_next('dd')
尝试抓取部分目录 (toc),最好使用 SoupSieve:
<html>
<dl>
#many rows
<dd>4.2.1. Drivers</dd>
<dd>4.2.1.1. itemD1</dd>
<dd>4.2.1.2. itemD2</dd>
<dd>4.2.1.3. itemD3</dd>
<dd>4.2.2. Constraints</dd>
<dd>4.2.2.1. itemC1</dd>
#many more rows
</dl>
</html>
请注意,我需要的项目不是 4.2.1 的 children/descendants。驱动程序,只是因为编号看起来像。
现在,我需要抓取的元素是元素 Drivers 和 Constraints 之间的元素。并不总是其中的 3 个——它可能是 0 个或 3 个或 5 个,这取决于。稍后在我的代码中,我使用 pandas 将这些元素输出到 .csv.
中的单个单元格中我试过这样的事情:
def get_drivers():
data.append({
'url': url,
'type': 'driver',
'list': [x.get_text(strip=True) for x in toc.select('dd:-soup-contains-own("Drivers") ~ dd')]
})
...但这只是给了我从驱动程序到文档末尾的所有元素,通常是我不需要的几十个元素。
问题:如何让选择器在 Drivers 之后开始选择并在 Constraints 停止选择?
您完全可以使用 css 选择器来做到这一点。使用 :-soup-contains
和 :not
,以及通用兄弟组合器 (~
) 和类型选择器 (dd
) 来过滤掉每个后面的内容(即减去 Constraints
从 Drivers
开始
from bs4 import BeautifulSoup as bs
html = '''<html>
<dl>
#many rows
<dd>4.2.1. Drivers</dd>
<dd>4.2.1.1. itemD1</dd>
<dd>4.2.1.2. itemD2</dd>
<dd>4.2.1.3. itemD3</dd>
<dd>4.2.2. Constraints</dd>
<dd>4.2.2.1. itemC1</dd>
#many more rows
</dl>
</html>'''
soup = bs(html, 'lxml')
filtered = [i.text for i in soup.select(
'dd:-soup-contains(" Drivers") ~ dd:not(dd:-soup-contains(" Constraints"), dd:-soup-contains(" Constraints") ~ dd)')]
我想一个循环也可以工作,但在我看来不太可取:
from bs4 import BeautifulSoup as bs
html = '''<html>
<dl>
#many rows
<dd>4.2.1. Drivers</dd>
<dd>4.2.1.1. itemD1</dd>
<dd>4.2.1.2. itemD2</dd>
<dd>4.2.1.3. itemD3</dd>
<dd>4.2.2. Constraints</dd>
<dd>4.2.2.1. itemC1</dd>
#many more rows
</dl>
<dl>
<dd>Error</dd>
</dl>
</html>'''
soup = bs(html, 'lxml')
start = soup.select_one('dd:-soup-contains(" Drivers") + dd')
next_node = start
while True:
if not next_node:
break
if 'Constraints' in next_node.text:
break
print(next_node.text)
next_node = next_node.find_next('dd')