Как заставить атрибут BeautifulSoup «replace_with» работать с объектом «Юникод»? - PullRequest
1 голос
/ 06 марта 2019

Вот мой html:

<html>
<body>
<h2>Pizza</h2>
<p>This is some random paragraph without child tags.</p>
<p>Delicious homebaked pizza.<br><em></em>$8.99 pp</em></p>
<h2>Eggplant Parmesan</h2>
<p>Try the authentic <i>Italian flavor</i> of baked aubergine.<br><em>$6.99 pp</em></p>
<h2>Italian Ice Cream</h2>
<p>Our dessert specialty.<br><em>$3.99 pp</em></p>
</body>
</html>

Используя BeautifulSoup, я хочу получить текст, отображаемый для тегов h2 и p, заменить их префиксной версией в дереве, а также распечатать их на экране. Для тегов h2 это прекрасно работает:

from bs4 import BeautifulSoup

with open("/var/www/html/Test/index.html", "r") as f:
 soup = BeautifulSoup(f, "lxml")

f = open("/var/www/html/Test/I18N_index.html", "w+")

for h2 in soup.find_all('h2'):
    i18n_string = "I18N_"+h2.string
    h2.string.replace_with(i18n_string)
    print(h2.string)

f.write(str(soup))


###Output:##############################################
# $ python ./test.py
# I18N_Pizza
# I18N_Eggplant Parmesan
# I18N_Italian Ice Cream
########################################################

В моем I18N_index.html все 3 строки отображаются правильно с префиксом «I18N _».

Однако мои теги p содержат дочерние теги, и для них тип возвращаемого значения - «Нет». В результате конкатенация больше не работает:

    for p in soup.find_all('p'):
        i18n_string = "I18N_"+p.string
        p.string.replace_with(i18n_string)
        print(p.string)

    f.write(str(soup))

###Output:##################################################
# $ python ./test.py
# I18N_Pizza
# I18N_Eggplant Parmesan
# I18N_Italian Ice Cream
# I18N_This is some random paragraph without child tags.
# Traceback (most recent call last):
  # File "./test.py", line 15, in <module>
    # i18n_string = "I18N_"+p.string
# TypeError: cannot concatenate 'str' and 'NoneType' objects
############################################################

Из этой темы Я узнал о функции join. Это позволит мне выполнить конкатенацию и распечатать получившиеся строки на экране, но не заменять их в дереве супа:

for p in soup.find_all('p'):
    joined = ''.join(p.strings)
    i18n_string = "I18N_"+joined
    #joined.replace_with(i18n_string)
    print (i18n_string)

###Output with 'joined.replace_with(i18n_string)' DISABLED:###
# I18N_Pizza
# I18N_Eggplant Parmesan
# I18N_Italian Ice Cream
# I18N_This is some random paragraph without child tags.
# I18N_Delicious homebaked pizza.$8.99 pp
# I18N_Try the authentic Italian flavor of baked aubergine.$6.99 pp
# I18N_Our dessert specialty$3.99 pp
############################################################

###Output with 'joined.replace_with(i18n_string)' ENABLED:#####
# I18N_Pizza
# I18N_Eggplant Parmesan
# I18N_Italian Ice Cream
# Traceback (most recent call last):
  # File "./test.py", line 41, in <module>
    # joined.replace_with(i18n_string)
# AttributeError: 'unicode' object has no attribute 'replace_with'
############################################################

В этой теме упоминается другое решение, основанное на isinstance, но я не смог заставить это работать.

Если я правильно понимаю, функция join объединяет строки, но возвращает объект 'unicode', а не объект string, и именно поэтому атрибут replace_with не работает. Как я могу обойти это? Любая помощь очень ценится.

Ответы [ 2 ]

2 голосов
/ 06 марта 2019
Метод

replace_with() не работает не потому, что joined - это объект Unicode, а потому, что это метод, специфичный для объекта bs4.Смотрите это: BeautifulSoup-replace_with

Кстати, метод join() возвращает str Смотрите это: python3-join

Сейчасчтобы дать вам решение, я бы просто удалил string после тега p:

from bs4 import BeautifulSoup

with open("index.html", "r") as f:
 soup = BeautifulSoup(f, "lxml")

f = open("I18N_index.html", "w+")

for h2 in soup.find_all('h2'):
    i18n_string = "I18N_"+h2.string
    h2.string.replace_with(i18n_string)
    print(h2.string)

for p in soup.find_all('p'):
    joined = ''.join(p.strings)
    i18n_string = "I18N_"+joined
    p.replace_with(i18n_string)
    print (i18n_string)


f.write(str(soup))

ВЫХОД:

I18N_Pizza I18N_Eggplant Parmesan I18N_Italian Ice Cream I18N_This is some random paragraph without child tags. I18N_Delicious homebaked pizza.$8.99 pp I18N_Try the authentic Italian flavor of baked aubergine.$6.99 pp I18N_Our dessert specialty.$3.99 pp

1 голос
/ 06 марта 2019

С упрощенной версией вашего кода (то есть, просто заботясь о проблеме p тегов), похоже, что вы должны заменить p.string на p.text:

soup = BeautifulSoup([ваш html], "lxml")

 for p in soup.find_all('p'):
   print('before: ',p.text)
   i18n_string = "I18N_"+p.text
   print('after ',i18n_string)

Вывод:

before:  This is some random paragraph without child tags.
after  I18N_This is some random paragraph without child tags.
before:  Delicious homebaked pizza.$8.99 pp
after  I18N_Delicious homebaked pizza.$8.99 pp
before:  Try the authentic Italian flavor of baked aubergine.$6.99 pp
after  I18N_Try the authentic Italian flavor of baked aubergine.$6.99 pp
before:  Our dessert specialty.$3.99 pp
after  I18N_Our dessert specialty.$3.99 pp
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...