Я написал следующий простой HTML-фильтр с использованием requests
и BeautifulSoup
, который должен принимать список разрешенных тегов и атрибутов и удаляет все теги, которых нет в списке:
def filter_tags(soup, tags_to_keep, allowed_attrs=None):
for tag in soup.body.descendants:
if tag.name in tags_to_keep:
new_attrs = dict()
for k,v in tag.attrs.items():
if allowed_attrs and k in allowed_attrs:
new_attrs[k] = v
tag.attrs = new_attrs
elif isinstance(tag, NavigableString):
continue
else:
# insert one whitespace char so words on either side of tag aren't concat'ed together
tag.insert_before(" ")
tag.decompose()
return soup
Я вызываю функцию следующим образом: soup = filter_tags(soup, tags_to_keep=['html', 'body', 'div', 'a'], allowed_attrs=['href'])
.
Эта функция работает для простых входных данных, таких как:
<body>
<div id="cats">
This is a test
<a href="http://www.google.com">Google!</a>
More text
</div>
<div>
More text
<script>...oh javascript...</script>
</div>
</body>
Под "работает" я имею в виду, что она правильно удаляеттеги <script>
и <img>
и атрибут id=
, сохраняя при этом указанные теги и href=
attr), поэтому впоследствии это выглядит так:
<body>
<div>
This is a test
<a href="http://www.google.com">Google!</a>
More text
</div>
<div>
More text
</div>
</body>
Однако для более сложного HTML этополностью терпит неудачу (пример страницы, которая терпит неудачу - http://www.cnn.com
), и теги <script>
, атрибуты тега и т. д. не удаляются из HTML.Я получаю вывод, подобный этому:
import requests
from bs4 import BeautifulSoup
soup = BeautifulSoup(requests.get('http://www.cnn.com').text, 'lxml')
filter_tags(soup, tags_to_keep=['body', 'a', 'div'], allowed_attrs=['href'])
|
|
V
... <li class="m-legal__list__item last-child">
<a class="m-legal__links" data-analytics="footer_cnn-newsource" href="http://cnnnewsource.com">CNN Newsource</a></li>
</ul></div></div></div></div></div></footer>
<div class="OUTBRAIN" data-ob-template="cnn" data-src="" data-widget-id="TR_1"></div>
<script>(function (d) ...
Как вы можете видеть, он не удаляет ни один из тегов / атрибутов, таких как <script>
или class=
в более сложном HTML, как это, но я могуне могу понять, почему, основываясь на моих простых тестах, где, кажется, все работает нормально ...
Что не так с моей вышеописанной функцией, которая не позволяет удалять теги / атрибуты для сложного HTML?Моя интуиция заключается в том, что это может быть связано с изменением дерева DOM с помощью .decompose()
, когда я пересекаю .descendants
, но точно сказать не могу.Если это проблема, то какова альтернатива методу, который я пытаюсь использовать здесь?