Теги в извлеченном содержимом должны располагаться в том же порядке, что и в исходном HTML-файле

Я пытаюсь создать веб-скребок. Мой парсер должен найти все строки, соответствующие выбранным тегам, и сохранить их в том же порядке, что и исходный HTML в новый файл file.md.

Теги указываются в массиве:

list_of_tags_you_want_to_scrape = ['h1', 'h2', 'h3', 'p', 'li']

то это дает мне только содержимое указанного тега:

soup_each_html = BeautifulSoup(particular_page_content, "html.parser")
inner_content = soup_each_html.find("article", "container")

скажем, что это результат:

<article class = "container">
  <h1>this is headline 1</h1>
  <p>this is paragraph</p>
  <h2>this is headline 2</h2>
  <a href = "bla.html">this won't be shown bcs 'a' tag is not in the array</a>
</article>

то у меня есть метод, который отвечает за запись строки в файл file.md, если тег из массива существует в содержимом

with open("file.md", 'a+') as f:
    for tag in list_of_tags_you_want_to_scrape:
        inner_content_tag = inner_content.find_all(tag)

        for x in inner_content_tag:
            f.write(str(x))
            f.write("\n")

и это так. Но проблема в том, что он проходит через массив (для каждого) и сохраняет сначала все <h1>, все <h2> на втором месте и т. д. И это потому, что именно такой порядок указан в массиве list_of_tags_you_want_to_scrape.

это будет результат:

<article class = "container">
  <h1>this is headline 1</h1>
  <h2>this is headline 2</h2>
  <p>this is paragraph</p>
</article>

поэтому я хотел бы, чтобы они были в правильном порядке, как в исходном HTML. После первого <h1> должен быть элемент <p>.

Это означает, что мне, вероятно, нужно будет сделать для каждого цикла также с inner_content и проверить, равна ли каждая строка из этого inner_content хотя бы одному из тегов из массива. Если да, то сохраните, а затем перейдите на другую строку. Я попытался сделать это и сделал для каждого для inner_content получение строки за строкой, но это дало мне ошибку, и я не уверен, что это правильный способ, как это сделать. (Первый день использования модуля BeautifulSoup)

Любые советы или советы, как изменить мой метод для достижения этого, пожалуйста? Спасибо!

Две вещи: во-первых, как правило - если это вообще возможно, вы должны вставлять код, а не его изображения (в данном случае ваш вывод). Во-вторых, вы должны добавить к своему вопросу свой inner_content (или хотя бы репрезентативную часть), чтобы люди могли видеть, с чем вы имеете дело.

Jack Fleeting 28.05.2019 00:22

@JackFleeting Я сделал. Спасибо, что заметили меня.

Michal Moravik 28.05.2019 00:33

@MapeSVK В вашем последнем примере порядок не должен быть [h1, h2, p], поскольку это структура самого HTML?

Ajax1234 28.05.2019 00:36

@ Ajax1234 Нет, не должно. Исходный порядок: h1 -> p -> h2. Это просто пример. Мне нужно придерживаться правильного порядка. Это не тема о том, как должен быть структурирован HTML. Это тема о правильном порядке после парсинга.

Michal Moravik 28.05.2019 00:45

@MapeSVK Ввод всегда в виде <article>{something}</article>?

Ajax1234 28.05.2019 00:54

@ Ajax1234 да, это так. И я только что попробовал ваше решение, и оно работает! Спасибо за ваши усилия!

Michal Moravik 28.05.2019 00:55

@MapeSVK Нет проблем, рад помочь!

Ajax1234 28.05.2019 00:56

@ Ajax1234 Ajax1234, не могли бы вы очень кратко объяснить, как работает ваш метод «анализа»? Просто как бы вы прочитали метод.

Michal Moravik 28.05.2019 01:14

@MapeSVK Пожалуйста, посмотрите мое недавнее редактирование.

Ajax1234 28.05.2019 01:32
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
9
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы сохранить исходный порядок ввода html, вы можете использовать рекурсию для перебора атрибута soup.contents:

from bs4 import BeautifulSoup as soup
def parse(content, to_scrape = ['h1', 'h2', 'h3', 'p', 'li']):
   if content.name in to_scrape:
      yield content
   for i in getattr(content, 'contents', []):
      yield from parse(i)

Пример:

html = """   
<html>
  <body>
      <h1>My website</h1>
      <p>This is my first site</p>
      <h2>See a listing of my interests below</h2>
      <ul>
         <li>programming</li>
         <li>math</li>
         <li>physics</li>
      </ul>
      <h3>Thanks for visiting!</h3>
  </body>
</html>
"""

result = list(parse(soup(html, 'html.parser')))

Выход:

[<h1>My website</h1>, <p>This is my first site</p>, <h2>See a listing of my interests below</h2>, <li>programming</li>, <li>math</li>, <li>physics</li>, <h3>Thanks for visiting!</h3>]

Как видите, исходный порядок html сохраняется, и теперь его можно записать в файл:

with open('file.md', 'w') as f:
   f.write('\n'.join(map(str, result)))

Каждый объект bs4 содержит, среди прочего, атрибуты name и contents. Атрибут name — это само имя тега, а атрибут contents хранит весь дочерний HTML. parse использует генератор, чтобы сначала проверить, есть ли у переданного bs4 объекта тег, который принадлежит к списку to_scrape, и если да, то yield имеет это значение. Наконец, parse перебирает содержимое content и вызывает себя для каждого элемента.

Я просто изменил строку: result = list(parse(soup(html, 'html.parser'))) на result = list(parse(inner_content)) так как мой контент уже был после супа. Спасибо еще раз!

Michal Moravik 28.05.2019 00:57

Другие вопросы по теме