Как я могу извлечь определенные вложенные элементы с одним и тем же тегом в Beautiful Soup?

#python #web-scraping #beautifulsoup

#python #очистка веб-страниц #beautifulsoup

Вопрос:

Я действительно новичок в Python, поэтому я все еще пытаюсь разобраться в Beautiful Soup. Я пытаюсь очистить веб-сайт и извлечь пять элементов, которые сразу следуют за тегом, который я нашел в своем коде.

Я попробовал next.element, который извлекает только текст тега, который я использовал в моем soup.find, и я попробовал next.sibling, который возвращает пустым.

На странице есть несколько классов ‘first’ и ‘last’, поэтому я должен указать, какую строку я хочу вставить в текст. Вот что я пытаюсь очистить:

  <li>
        <ul>
            <li class="first">Maintenance</li>
                        <li>$number1</li>
                        <li>$number2</li>
                        <li>$number3</li>
                        <li>$number4</li>
                        <li>$number5</li>
                    <li class="last">$linetotal</li>
        </ul>
    </li>

  

Это то, что я пытаюсь:

 for x,y in zip(make, model):
    url = ('https://URL with variables goes here')
    headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}
    search = requests.get(url, headers = headers)
    html = search.text
    soup = BeautifulSoup(html, 'lxml')
    search_results = soup.find('li', class_ = 'first', text = re.compile('Maintenance'))
    try:
        d = search_results.next_element
        print(d)
    except:
        print('pass')  
  

Конечной целью будет добавление массива number1: number5 в список, но с приведенным выше кодом результатом будет просто «Обслуживание». Где я ошибаюсь? Кроме того, поскольку я такой новичок, я был бы очень признателен, если вы сможете также предоставить контекст.

Комментарии:

1. Вы хотите, чтобы сначала было каждое вхождение класса, а затем текстовое сопровождение дочерних элементов li? Или только первое вхождение? Можете ли вы поделиться URL-адресом? Выполняется ли техническое обслуживание только один раз и в фиксированном положении?

Ответ №1:

Учитывая ваш пример, самым простым способом было бы добавить в список результатов все li элементы, для которых не определен класс.

 from bs4 import BeautifulSoup

html = """ <li>
        <ul>
            <li class="first">Maintenance</li>
                        <li>$number1</li>
                        <li>$number2</li>
                        <li>$number3</li>
                        <li>$number4</li>
                        <li>$number5</li>
                    <li class="last">$linetotal</li>
        </ul>
    </li>"""

soup = BeautifulSoup(html, 'lxml')
start = soup.find('li', class_ = 'first').parent
result = []

for ele in start.find_all('li'):

    if not ele.get('class'):
        result.append(ele.text)

print(result)
  

Результаты:

 ['$number1', '$number2', '$number3', '$number4', '$number5']
  

Ответ №2:

Вы могли бы использовать выражение xpath с чем-то вроде tree.xpath

 //li[@class='first' and text()='Maintenance']/following-sibling::li[not(@class)]
  

Например.

 from lxml.html import fromstring
# url = ''
# tree = html.fromstring( requests.get(url).content)
h = '''
 <li>
    <ul>
        <li class="first">Maintenance</li>
        <li>$number1</li>
        <li>$number2</li>
        <li>$number3</li>
        <li>$number4</li>
        <li>$number5</li>
        <li class="last">$linetotal</li>
    </ul>
</li>
'''
tree = fromstring(h)
items = [item.text for item in tree.xpath("//li[@class='first' and text()='Maintenance']/following-sibling::li[not(@class)]")]
print(items)
  

Комментарии:

1. Вы пробовали это? В вашем вопросе указано, что вы хотели найти после технического обслуживания. Это решение делает это

Ответ №3:

Что-то вроде ответа QHarr, но несколько другое:

  h = '''
   <li>
     <ul>
       <li class="first">Maintenance</li>
       <li>$number1</li>
       <li>$number2</li>
       <li>$number3</li>
       <li>$number4</li>
       <li>$number5</li>
       <li class="last">$linetotal</li>
   </ul>
</li>

  '''
from lxml import etree
doc = etree.fromstring(h)
for cost in doc.xpath('//li'): 
   if not 'class' in cost.attrib:
      print(cost.text)
  

Вывод:

 $number1
$number2
$number3
$number4
$number5