Извлечь теги заголовков из обычного текста - PullRequest
3 голосов
/ 07 июня 2010

Я работаю над одной задачей - извлечь тег заголовка из заданного обычного текста (это не HTML DOM). У меня есть ниже случаи, когда необходимо извлечь заголовок тега (ов):

Случай 1:

<html>
<head>
           <title>Title of the document</title>
</head>
<body>
The content of the document......
</body>
</html>

Ожидается: Название документа

Дело 2:

<html>
<head>
           <title>Title of the document</title>
           <title>Continuing title</title>
</head>
<body>
The content of the document......
</body>
</html>

Ожидаемое: Название документа Продолжение заголовка

Случай 3 (вложенные теги заголовков)

<html>
<head>
           <title>Title of the document
           <title>Continuing title</title></title>
</head>
<body>
The content of the document......
</body>
</html>

Ожидается: Название документа Продолжение заголовка

Я хотел извлечь теги заголовков, используя регулярные выражения в javascript. Reg-ex должен работать в вышеуказанном случае.

Кто-нибудь знает об этом ... пожалуйста, дайте мне знать ... Заранее спасибо

Ответы [ 2 ]

2 голосов
/ 07 июня 2010

Не анализируйте HTML с регулярным выражением! Серьезно, в общем случае это буквально невозможно. И на самом деле, вы не можете делать то, что вы хотите с помощью регулярных выражений. Это та же проблема, что и при сопоставлении сбалансированных вложенных пар скобок, за исключением того, что вы хотите сопоставить вложенные пары <title> / </title>, а это не обычный язык.

( Редактировать 1: Мне пришлось пересмотреть мой ответ, поскольку я увидел, что у вас нет доступа к DOM; то, что у меня изначально было, см. Ниже.)

Итак, зачем вам это делать? Возможно, есть лучший способ. Это помечено JavaScript, но вы никогда не упоминаете об этом в своем ответе. Если у вас нет JavaScript, вероятно, вы можете использовать HTML-парсер, который, вероятно, будет лучшим выбором. Если вы используете JavaScript, он все еще может быть, но я не гуру JavaScript.

Теперь обратите внимание: наличие нескольких или вложенных тегов title на самом деле не является допустимым HTML, поэтому не нужно беспокоиться об этом. Если это так, и если мы можем сделать еще некоторые предположения, вы можете создать вариант использования, который, вероятно, сработает. Например: без комментариев, без CDATA блоков и т. Д. (Хотя вы могли бы справиться с ними, потому что они не могут вкладываться.) Но могут быть крайние случаи, которые я забыл! Кроме того, ни Safari, ни Firefox не рассматривали ваш третий случай как вложенные теги заголовков, вместо этого просматривая его как один тег заголовка, содержащий буквенную строку Title of the document <title> Continuing title. Таким образом, если вы можете игнорировать этот случай, может иметь возможность взломать хрупкий набор регулярных выражений, которые будут работать. Возможно (слегка проверено!) Что-то вроде этого:

// Edit 2: Made this function case-insensitive where it needed to be.
// Edit 3: Used substring() instead of replace() to remove the extraneous
//         title tags and fixed the "not matching" case.
function getTitle(html) {
  return (html.replace( /<!\[CDATA\[(.+?)\]\]>/g
                      , function (_match, body) {
                          return body.replace(/&/g, '&amp;')
                                     .replace(/</g, '&lt;')
                                     .replace(/>/g, '&gt;')
                        } )
              .replace(/<!--.+?-->/g, '')
              .match(/<title>.+?<\/title>/ig) || [])
              .map(function (t) { return t.substring(7, t.length - 8) })
              .join(' ')
}

Я не гуру HTML, поэтому я, вероятно, пропустил пару крайних случаев, но вот что это делает. Сначала мы находим каждый раздел CDATA . Мы берем его внутренности и превращаем каждого нелегального персонажа в эквивалент его сущности, и избавляемся от <![CDATA[ и ]]>. Далее мы удаляем каждый комментарий. После этого мы сопоставляем каждый заголовок и получаем массив совпадений (получение массива совпадений несовместимо с извлечением подгрупп), в случае, если мы находимся в случае неверных кратных title s. Редактировать 3: Затем мы проверяем, ничего не найдено, в этом случае .match() возвращает null, и вместо этого возвращаем [], если это так; таким образом, у нас всегда есть массив. Затем мы обрезаем теги от начала и до конца ( edit 3: , больше не использующих regexen для этого шага), и, наконец, строим каждый фрагмент заголовка вместе с пробелом. Это справится, я думаю , ваш случай один и случай два. Если вам нужен только юридический случай (случай один), замените последние три строки (кроме }) одной строкой .match(/<title>(.+?)<\/title>/)[0]. Однако, хотя это будет работать (я думаю) во многих случаях, я делаю предположения (как относительно нашего ввода ( например , все теги заголовка отображаются вместе и где вы хотите их видеть), так и о том, что мы ' ищем только один (набор) <title>...</title> с и, вероятно, пропустили какой-то крайний случай или другой. Надеюсь, получится, что вы можете использовать более приятное решение.


Редактировать 1: Я упустил тот факт, что вам нужно работать с простым текстом; остальная часть моего первоначального ответа предполагала, что у вас есть доступ к DOM. Я оставлю это здесь для потомков, но это не особенно относится к вам.

Если у вас был доступ к DOM в JavaScript, вы могли бы сделать следующее, если бы у вас был правильный HTML с одним тегом title:

var titles    = document.getElementsByTagName('title')
var titleText = titles.length > 0 ? titles[0].text : ''

Однако, если у вас на самом деле есть HTML, который выглядит как вторые два случая, которые вы нам показали (надеюсь, нет, но вы никогда не знаете), вам придется заняться чем-то другим. Ни Firefox, ни Safari не рассматривали ваш третий случай как вложенные теги заголовков, вместо этого просматривая его как один тег заголовка, содержащий буквенную строку Title of the document <title> Continuing title. Таким образом, если вам нужно иметь дело только с первыми двумя случаями, это будет работать:

var titles    = document.getElementsByTagName('title')
var tlength   = titles.length
var titleText = ''
for (var i = 0; i < tlength; ++i)
  titleText += titles[i].text

А если у вас третий случай, то вам нужно удалить посторонний тег <title>, который может быть немного хитрым, но, вероятно, это не так. Если вы знаете, что <title> никогда не появится, кроме как из-за неправильно сформированного HTML, как описано выше, то вы можете использовать метод replace, чтобы избавиться от него. В одноместном корпусе - <title>, вы хотите

// Edit 2: Case-insensitivity
var titles    = document.getElementsByTagName('title')
var titleText = titles.length > 0 ? titles[0].text.replace(/<title>/ig,'') : ''

В искаженном случае нескольких автономных <title> вы хотите

// Edit 2: Case-insensitivity
var titles    = document.getElementsByTagName('title')
var tlength   = titles.length
var titleText = ''
for (var i = 0; i < tlength; ++i)
  titleText += titles[i].text.replace(/<title>/ig,'')

Если <title> может появиться как допустимая строка по другим причинам, то у вас проблемы; вам нужно выяснить почему это было в строке и заменить его, только если вы должны были. И, насколько я могу судить, нет хорошего общего способа сделать это. Но, надеюсь, (хотя и не обязательно) у вас есть законный HTML.

1 голос
/ 07 июня 2010

Это решение для этой конкретной проблемы с использованием этого сломанного «псевдо-HTML».Это не относится к обычному HTML:

function extractTitle(text) {
  var m = /<title>(.*)<\/title>/.exec(text); 
  if (m && m[1]) {
    return m[1].replace(/<\/?title>/g," ").replace(/\s+/," ");
  }
  return; // returns undefined
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...