Хобрук: Ваш путь к мастерству в программировании

Получить весь контент между закрывающим и открывающим HTML-тегом с помощью Beautiful Soup

Я анализирую контент с помощью Python и Beautiful Soup, затем записываю его в файл CSV и столкнулся с проблемой получения определенного набора данных. Данные проходят через реализацию TidyHTML, которую я создал, а затем удаляются другие ненужные данные.

Проблема в том, что мне нужно получить все данные между набором тегов <h3>.

Пример данных:

<h3><a href="Vol-1-pages-001.pdf">Pages 1-18</a></h3>
<ul><li>September 13 1880. First regular meeting of the faculty;
 September 14 1880. Discussion of curricular matters. Students are
 debarred from taking algebra until they have completed both mental
 and fractional arithmetic; October 4 1880.</li><li>All members present.</li></ul>
 <ul><li>Moved the faculty henceforth hold regular weekkly meetings in the
 President's room of the University building; 11 October 1880. All
 members present; 18 October 1880. Regular meeting 2. Moved that the
 President wait on the property holders on 12th street and request
 them to abate the nuisance on their property; 25 October 1880.
 Moved that the senior and junior classes for rhetoricals be...</li></ul>
 <h3><a href="Vol-1-pages-019.pdf">Pages 19-33</a></h3>`

Мне нужно получить все содержимое между первым закрывающим тегом </h3> и следующим открывающим тегом <h3>. Это не должно быть сложно, но моя тупая голова не находит нужных связей. Я могу захватить все теги <ul>, но это не работает, потому что между тегами <h3> и тегами <ul> нет однозначного отношения.

Результат, которого я хочу достичь, таков:

Страницы 1-18|Vol-1-pages-001.pdf|содержимое между тегами и.

Первые две части не вызвали затруднений, но контент между набором тегов для меня сложен.

Мой текущий код выглядит следующим образом:

import glob, re, os, csv
from BeautifulSoup import BeautifulSoup
from tidylib import tidy_document
from collections import deque

html_path = 'Z:\\Applications\\MAMP\\htdocs\\uoassembly\\AssemblyRecordsVol1'
csv_path = 'Z:\\Applications\\MAMP\\htdocs\\uoassembly\\AssemblyRecordsVol1\\archiveVol1.csv'

html_cleanup = {'\r\r\n':'', '\n\n':'', '\n':'', '\r':'', '\r\r': '', '<img src="UOSymbol1.jpg"    alt="" />':''}

for infile in glob.glob( os.path.join(html_path, '*.html') ):
    print "current file is: " + infile

    html = open(infile).read()

    for i, j in html_cleanup.iteritems():
            html = html.replace(i, j)

    #parse cleaned up html with Beautiful Soup
    soup = BeautifulSoup(html)

    #print soup
    html_to_csv = csv.writer(open(csv_path, 'a'), delimiter='|',
                      quoting=csv.QUOTE_NONE, escapechar=' ')  
    #retrieve the string that has the page range and file name
    volume = deque()
    fileName = deque()
    summary = deque()
    i = 0
    for title in soup.findAll('a'):
            if title['href'].startswith('V'):
             #print title.string
             volume.append(title.string)
             i+=1
             #print soup('a')[i]['href']
             fileName.append(soup('a')[i]['href'])
             #print html_to_csv
             #html_to_csv.writerow([volume, fileName])

    #retrieve the summary of each archive and store
    #for body in soup.findAll('ul') or soup.findAll('ol'):
    #        summary.append(body)
    for body in soup.findAll('h3'):
            body.findNextSibling(text=True)
            summary.append(body)

    #print out each field into the csv file
    for c in range(i):
            pages = volume.popleft()
            path = fileName.popleft()
            notes = summary
            if not summary: 
                    notes = "help"
            if summary:
                    notes = summary.popleft()
            html_to_csv.writerow([pages, path, notes])
04.01.2012

  • Попробуйте использовать это выражение Xpath html/body/h3[1]/a/@href | //ul[1]/li/text() | //ul[2]/li/text() | //h3[2]/a/@href 04.01.2012
  • Не совсем, он не возвращает никаких результатов, но я не знал, что вы можете использовать Xpath внутри findAll. Я поиграю с этим. Спасибо. 04.01.2012
  • и почему бы вам не попробовать lxml, потому что BSoup не поддерживается, работает медленно и имеет уродливый API. 04.01.2012
  • даже собственный сопровождающий рекомендует переходить на другие библиотеки 04.01.2012
  • @RanRag: сопровождающий говорит: tl;dr: Вместо этого используйте серию 4.0. и Эта страница была первоначально написана в марте 2009 года. С тех пор была выпущена серия 3.2, заменившая 3.1. серия, и разработка серии 4.x началась. Эта страница останется доступной в _исторических целях_. 05.01.2012

Ответы:


1

Извлечь содержимое между тегами </h3> и <h3>:

from itertools import takewhile

h3s = soup('h3') # find all <h3> elements
for h3, h3next in zip(h3s, h3s[1:]):
  # get elements in between
  between_it = takewhile(lambda el: el is not h3next, h3.nextSiblingGenerator())
  # extract text
  print(''.join(getattr(el, 'text', el) for el in between_it))

В коде предполагается, что все элементы <h3> являются одноуровневыми. Если это не так, вы можете использовать h3.nextGenerator() вместо h3.nextSiblingGenerator().

04.01.2012
  • Будучи новичком в Python, я не знал о библиотеке itertools. Это гладкий набор функций. Код действительно хорошо извлекает содержимое между тегами </h3> и <h3>. Использование sup.find всегда захватывает только первый тег h3 в документе. Есть ли способ увеличить функцию поиска супа, чтобы я мог получить все содержимое на странице между тегами <h3>? Цикл for со счетчиком дает те же результаты, что и цикл без использования функции find. Я пробовал findAll() безуспешно, потому что он конфликтует с findNext(). 05.01.2012
  • @theMusician: напиши цикл: h3s = soup('h3'); \n for h3, h3next in zip(h3s, h3s[1:]): \n between_it = # the same as above ... 05.01.2012
  • Насколько я понимаю предложенный код, zip создает набор кортежей на основе закрывающих и открывающих тегов h3. h3 = soup.find('h3') # find the first <h3> h3next = h3.findNext('h3') # find next <h3> h3s = soup('h3') for h3, h3next in zip(h3s, h3s[1:]): # get elements in between between_it = takewhile(lambda el: el is not h3next, h3.nextSiblingGenerator()) # extract text summary.append(''.join(getattr(el, 'text', el) for el in between_it)) 06.01.2012
  • Насколько я понимаю предложенный код, zip создает набор кортежей на основе закрывающих и открывающих тегов h3. Кажется, что zip вызывает ошибку типа, когда встречает пустые элементы, проблема, которая предположительно была устранена в версии 2.4. docs.python.org/library/functions.html#zip `h3 = суп.найти('h3') # найти первый ‹h3› h3next = h3.findNext('h3’) # найти следующий ‹h3› h3s = суп('h3') for h3, h3next in zip(h3s, h3s[ 1:]): # получить элементы между ними between_it = takewhile(lambda el: el is not h3next, h3.nextSiblingGenerator()) 06.01.2012
  • @theMusician: документы относятся к случаю zip() (без аргументов). Здесь это не применимо. Код не должен производить TypeError. Я обновил ответ. 06.01.2012
  • @theMusician: Попытайтесь понять, что делает каждый элемент кода, поэкспериментируйте в интерактивной оболочке Python, например, попробуйте L = range(10); print zip(L, L[1:]). Прочтите руководство по Python. Если это слишком сложно, попробуйте learnpythonthehardway.org (введите код и посмотрите результаты). 06.01.2012
  • Спасибо за помощь. Он по-прежнему выдает TypeError, но это может быть из-за входных данных. Пока самый лаконичный ответ. 06.01.2012
  • Создайте минимальный пример (входные данные, код, точная трассировка, которую вы получите), которая показывает TypeError и обновите свой вопрос, например, ''.join([1]) 06.01.2012
  • Минимальный пример здесь: pastebin.com/RCgzV22v, так как ошибка возникает только при достижении определенного объема данных. . Точная ошибка — это Traceback (последний последний вызов): Файл //psf/Host/Applications/MAMP/htdocs/uoassembly/python/archive_test_parse.py, строка 529, в ‹module› print(''.join(getattr(el, 'text', el) для el in between_it)) TypeError: элемент последовательности 1: ожидаемая строка или Unicode, найден NoneType Вводится unicode, Beautiful Soup должен выводить только unicode. 07.01.2012
  • @theMusician: 1. сумма не имеет значения, имеет значение структура html-документа (код предполагает, что все элементы <h3> являются одноуровневыми, в противном случае используйте h3.nextGenerator()). 2. Не используйте html_cleanup, используйте возможности BeautifulSoup для изменения html. 07.01.2012

  • 2

    Если вы попытаетесь извлечь данные между тегами <ul><li></ul></li> в lxml, это обеспечит отличную функциональность использования CSSSelector

    import lxml.html
    import urllib
    data = urllib.urlopen('file:///C:/Users/ranveer/st.html').read() //contains your html snippet
    doc = lxml.html.fromstring(data)
    elements = doc.cssselect('ul li') // CSSpath[using firebug extension]
    for element in elements:
          print element.text_content()    
    

    после выполнения приведенного выше кода вы получите весь текст между тегами ul,li. Это намного чище, чем красивый суп.

    Если вы случайно планируете использовать lxml, вы можете оценить выражения XPath следующим образом:

    import lxml
    from lxml import etree
    content = etree.HTML(urllib.urlopen("file:///C:/Users/ranveer/st.html").read())
    content_text = content.xpath("html/body/h3[1]/a/@href | //ul[1]/li/text() | //ul[2]/li/text() | //h3[2]/a/@href")
    print content_text
    

    Вы можете изменить XPath в соответствии с вашими потребностями.

    04.01.2012
  • Я попробовал библиотеку lxml, и, хотя синтаксически она может быть лаконичной, кажется, что она просто захватывает каждый элемент ul, а не элементы ul между тегами заголовка. Я собираюсь продолжить ввод xpath. Образец, который я предоставил, является лишь частью документа, я извиняюсь, если это было неясно, но когда я перебираю документ, я не могу споткнуться о нескольких uls между тегами заголовков. 05.01.2012
  • Спасибо RanRag за помощь. Однако xpath, использующий lxml, извлекает определенные элементы, итерация, похоже, не работает. xpath не принимает переменные, по крайней мере, я еще не нашел их в документации lxml. Хотя это была хорошая дорога для путешествий. 05.01.2012
  • Новые материалы

    Учебные заметки JavaScript Object Oriented Labs
    Вот моя седьмая неделя обучения программированию. После ruby ​​и его фреймворка rails я начал изучать самый популярный язык интерфейса — javascript. В отличие от ruby, javascript — это более..

    Разбор строк запроса в vue.js
    Иногда вам нужно получить данные из строк запроса, в этой статье показано, как это сделать. В жизни каждого дизайнера/разработчика наступает момент, когда им необходимо беспрепятственно..

    Предсказание моей следующей любимой книги 📚 Благодаря данным Goodreads и машинному обучению 👨‍💻
    «Если вы не любите читать, значит, вы не нашли нужную книгу». - J.K. Роулинг Эта статья сильно отличается от тех, к которым вы, возможно, привыкли . Мне очень понравилось поработать над..

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..