Красивый суп метод find_all () захватывает больше тегов, чем указывает фильтр - PullRequest
1 голос
/ 26 апреля 2019

У меня есть следующий xml,

<url>
     <loc>https://mystore.com/products-t-shirt.xml</loc>
     <lastmod>2019-04-11T00:01:42-04:00</lastmod>
     <changefreq>daily</changefreq>
     <image:image>
         <image:loc> http://some-imageurl.com
         </image:loc>
         <image:title>GIFTS</image:title>
         <image:caption>quirky caption</image:caption>
     </image:image>
</url>

и я пытаюсь извлечь только теги "loc".

Я использовал следующий код для этого products_list = soup.find_all(lambda tag: tag.name == "loc") и я попытался использовать soup.find_all(re.compile("\\bloc\\b")), но когда я возвращаю этот результат массива, у меня есть тег loc, а также тег image: loc в результатах (вместе с текстом этих тегов, конечно). Кто-нибудь знает, что красивый суп захватывает изображение: loc, даже когда я указываю, что хочу точную строку?

Ответы [ 2 ]

2 голосов
/ 26 апреля 2019

Предполагается, что вы используете Beautiful Soup 4.7 +.

Вы можете использовать селекторы для этого.То, что вы показываете, выглядит как XML, поэтому я предполагаю, что где-то в вашем документе image определено пространство имен.В этом примере мы будем предполагать, что пространство имен определено как xmlns:image="http://somenamespace.com", что означает, что префикс image (предшествующий :) представляет пространство имен http://somenamespace.com.Предположим, что loc без пространства имен не имеет.И наконец, мы будем использовать |loc, чтобы указать, что мы хотим loc без пространства имен:

from bs4 import BeautifulSoup
xml = """
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:image="http://somenamespace.com">
<url>
     <loc>https://mystore.com/products-t-shirt.xml</loc>
     <lastmod>2019-04-11T00:01:42-04:00</lastmod>
     <changefreq>daily</changefreq>
     <image:image>
         <image:loc> http://some-imageurl.com
         </image:loc>
         <image:title>GIFTS</image:title>
         <image:caption>quirky caption</image:caption>
     </image:image>
</url>
</root>
"""

soup = BeautifulSoup(xml, 'xml')

print(soup.select('|loc'))

Вывод

[<loc>https://mystore.com/products-t-shirt.xml</loc>] 

Но если loc имеетпространство имен, которому не назначен префикс, мы все еще можем нацелить его.Давайте предположим, что оно имеет пространство имен по умолчанию xmlns="http://default.com".Желаемому loc не присваивается префикс, поэтому в этом примере он унаследует наше пространство имен по умолчанию.

Префикс в документе действительно имеет значение только для синтаксического анализатора, поэтому мы можем дать нашему целевому пространству имен произвольный префикс.название для нашего селектора, мы назовем его default.Затем мы можем нацелиться на тег loc с помощью default|loc.

from bs4 import BeautifulSoup
xml = """
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://default.com" xmlns:image="http://somenamespace.com">
<url>
     <loc>https://mystore.com/products-t-shirt.xml</loc>
     <lastmod>2019-04-11T00:01:42-04:00</lastmod>
     <changefreq>daily</changefreq>
     <image:image>
         <image:loc> http://some-imageurl.com
         </image:loc>
         <image:title>GIFTS</image:title>
         <image:caption>quirky caption</image:caption>
     </image:image>
</url>
</root>
"""

soup = BeautifulSoup(xml, 'xml')

print(soup.select('default|loc', namespaces={'default': 'http://default.com'}))

Output

[<loc>https://mystore.com/products-t-shirt.xml</loc>]    

Вы можете даже определить его как пространство имен по умолчанию без префикса, а затем просто настроить таргетингэто как loc:

from bs4 import BeautifulSoup
xml = """
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://default.com" xmlns:image="http://somenamespace.com">
<url>
     <loc>https://mystore.com/products-t-shirt.xml</loc>
     <lastmod>2019-04-11T00:01:42-04:00</lastmod>
     <changefreq>daily</changefreq>
     <image:image>
         <image:loc> http://some-imageurl.com
         </image:loc>
         <image:title>GIFTS</image:title>
         <image:caption>quirky caption</image:caption>
     </image:image>
</url>
</root>
"""

soup = BeautifulSoup(xml, 'xml')

print(soup.select('loc', namespaces={'': 'http://default.com'}))

Выход

[<loc>https://mystore.com/products-t-shirt.xml</loc>]    

Для тех, кто не хочет использовать селекторы, вы также можете проверить prefix элемента.В этом случае нам нужен loc без префикса:

from bs4 import BeautifulSoup
import re
xml = """
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://default.com" xmlns:image="http://somenamespace.com">
<url>
     <loc>https://mystore.com/products-t-shirt.xml</loc>
     <lastmod>2019-04-11T00:01:42-04:00</lastmod>
     <changefreq>daily</changefreq>
     <image:image>
         <image:loc> http://some-imageurl.com
         </image:loc>
         <image:title>GIFTS</image:title>
         <image:caption>quirky caption</image:caption>
     </image:image>
</url>
</root>
"""

soup = BeautifulSoup(xml, 'xml')

print([el for el in soup.find_all('loc') if not el.prefix])
0 голосов
/ 26 апреля 2019

Я попытался с этой настройкой, и мой вывод: [<loc>https://mystore.com/products-t-shirt.xml</loc>]

Сначала я загружаю файл с вашими строками внутри.Однако мне пришлось внести некоторые исправления: File: test.xml

<?xml version="1.0" encoding="UTF-8"?>
    <url xmlns:image=" ">
        <loc>https://mystore.com/products-t-shirt.xml</loc>
        <lastmod>2019 - 04 - 11
        T00: 01:42 - 04: 00
        </lastmod>
        <changefreq>daily</changefreq>
        <image: image="">
        <image loc="">http://some-imageurl.com
        </image>
        <image: title="">GIFTS</image:>
        <image: caption="">quirky caption</image:>
        </image:>
    </url>

и здесь код на python

import bs4 as BS

if __name__ == "__main__":

    with open("test.xml", "r") as f:
        xml = f.read()
    soup = BS.BeautifulSoup(xml, "lxml")
    tag_selection = soup.find_all(lambda tag: tag.name == "loc")
    print(tag_selection)

Как вы можете видеть в выводе, единственная полученная строка - этоТолько тег loc.

Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...