BeautifulSoup: классифицировать родительский и дочерний элементы - PullRequest
4 голосов
/ 09 марта 2020

У меня есть вопрос о BeautifulSoup в Python 3. Я потратил пару часов, чтобы попробовать, но я еще не решил его.

Это мой суп:

print(soup.prettify())
# REMEMBER THIS SOUP IS DYNAMIC
# <html>
#  <body>
#   <div class="title" itemtype="http://schema.org/FoodEstablishment">
#    <div class="address" itemtype="http://schema.org/PostalAddress">
#      <div class="address-inset">
#        <p itemprop="name">33 San Francisco</p>
#      </div>
#    </div>
#    <div class="image">
#      <img src=""/>
#      <span class="subtitle">image subtitle</p>
#    </div>
#    <a itemprop="name">The Dormouse's story</a>
#   </div>
#  </body>
# </html>

Мне нужно извлечь два текста с помощью itemprop="name": The Dormouse's story и 33 San Francisco Но мне нужен способ определить, какой класс является родительским.

Ожидаемый результат:

{
   "FoodEstablishment": "The Dormouse's story",
   "PostalAddress": "33 San Francisco"
}

Помните, что суп всегда динамичен c и в нем много элементов детского питания.

Ответы [ 3 ]

2 голосов
/ 09 марта 2020

Я получаю itemtype и содержимое каждого тега, затем создаю словарь с помощью update.

from bs4 import BeautifulSoup

html = """<html>
 <body>
  <div class="title" itemtype="http://schema.org/FoodEstablishment">
     <div class="address" itemtype="http://schema.org/PostalAddress">
     <p itemprop="name">33 San Francisco</p>
   </div>
   <p itemprop="name">The Dormouse's story</p>
  </div>

 </body>
</html>
"""
d = {}
soup = BeautifulSoup(html, 'html.parser')
for item in soup.findAll("div"):
    # get the last string in itemtype separated by /
    itemType = item.get("itemtype").split('/')[-1]
    # remove newline(\n) from contents
    itemProp = list(filter(lambda a: a != '\n', item.contents))
    # create a dictionary of key: value
    d.update({itemType: itemProp[-1].text}) 

print(d)

Result: {'FoodEstablishment': "The Dormouse's story", 'PostalAddress': '33 San Francisco'} 
1 голос
/ 10 марта 2020

Зачем использовать soup.find, если вы можете использовать soup.select, получить помощь от всех CSS wiz kids и , чтобы сначала проверить ваши критерии в браузере?

Есть тест производительности на SO и select быстрее или, по крайней мере, не значительно медленнее, так что это не так. Наверное, привычка.

(работает так же хорошо без квалификатора тега <p>, т.е. просто "[itemprop=name]")

found = soup.select("p[itemprop=name]")

results = dict()

for node in found:

    itemtype = node.parent.attrs.get("itemtype", "?")
    itemtype = itemtype.split("/")[-1]
    results[itemtype] = node.text

print(results)

output:

Это то, что вы просили, но если бы с FoodEstablishment существовало много узлов, последний выиграл бы, потому что вы используете словарь. Судья по умолчанию со списком может работать лучше.

{'PostalAddress': '33 San Francisco', 'FoodEstablishment': "The Dormouse's story"}

шаг 1, до Python: качайте CSS!

enter image description here

и если вам нужно проверить высших предков на itemtype:

, было бы полезно, если бы у вас было html с этим случаем:

    <div class="address" itemtype="http://schema.org/PostalAddress">
      <div>
        <p itemprop="name">33 San Francisco</p>  
      </div>

    </div>
found = soup.select("[itemprop=name]")

results = dict()

for node in found:

    itemtype = None
    parent = node.parent
    while itemtype is None and parent is not None:
      itemtype = parent.attrs.get("itemtype")
      if itemtype is None:
        parent = parent.parent


    itemtype = itemtype or "?"
    itemtype = itemtype.split("/")[-1]
    results[itemtype] = node.text

print(results)

тот же вывод.

с использованием defautdict

все остается прежним, за исключением объявления результатов и помещения в них данных.

from collections import defaultdict
...
results = defaultdict(list)
...

results[itemtype].append(node.text)
вывод (после того, как я добавил брата в 33 Сан-Франциско):
defaultdict(<class 'list'>, {'PostalAddress': ['33 San Francisco', '34 LA'], 'FoodEstablishment': ["The Dormouse's story"]})
1 голос
/ 09 марта 2020
from bs4 import BeautifulSoup


html = """<html>
 <body>
  <div class="title" itemtype="http://schema.org/FoodEstablishment">
   <div class="address" itemtype="http://schema.org/PostalAddress">
     <p itemprop="name">33 San Francisco</p>
   </div>
   <p itemprop="name">The Dormouse's story</p>
  </div>
 </body>
</html>
"""

soup = BeautifulSoup(html, 'html.parser')

a = [item.get("itemtype") for item in soup.findAll("div", {'itemtype': True})]
b = soup.find("div", {'itemtype': True}).get_text(
    strip=True, separator="|").split("|")

print(a)
print(b)

вывод:

['http://schema.org/FoodEstablishment', 'http://schema.org/PostalAddress']
['33 San Francisco', "The Dormouse's story"]

Обновление:

soup = BeautifulSoup(html, 'html.parser')

names = [item.text for item in soup.findAll("p", itemprop="name")]
print(names)

Выход:

['33 San Francisco', "The Dormouse's story"]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...