Теги в очищенном содержимом должны иметь тот же порядок, что и в исходном HTML-файле. - PullRequest
2 голосов
/ 28 мая 2019

Я пытаюсь построить веб-скребок. Мой скребок должен найти все строки, соответствующие выбранным тегам, и сохранить их в том же порядке, что и исходный 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)

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

1 Ответ

1 голос
/ 28 мая 2019

Чтобы сохранить исходный порядок ввода 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 и вызывает себя для каждого элемента.

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