Как изменить lxml autolink, чтобы он был более либеральным?

1

Я использую функцию автолинка большой библиотеки lxml, как описано здесь: http://lxml.de/api/lxml.html.clean-module.html

Моя проблема в том, что он обнаруживает только URL-адреса, начинающиеся с http://. Я хотел бы использовать более широкое регулярное выражение для обнаружения URL-адресов, подобное этому: http://daringfireball.net/2010/07/improved_regex_for_matching_urls

Я попытался сделать это регулярное выражение с помощью функции автоматической установки lxml без успеха. Я всегда получаю:

lxml\html\clean.py", line 571, in _link_text
host = match.group('host')
IndexError: no such group

Любые гуру python/regex, которые знают, как сделать эту работу?

Теги:
hyperlink
lxml

2 ответа

2
Лучший ответ

Есть две вещи, которые нужно сделать, чтобы адаптировать regexp к llml autolink. Сначала завершите совпадение всего шаблона url в группе (?P<body> .. ) - это позволяет lxml знать, что входит в атрибут href="".

Затем завершите хост-часть в группе (?<host> .. ) и передайте параметр avoid_hosts=[], когда вы вызываете функцию автолинка. Причиной этого является шаблон регулярного выражения, который вы используете, не всегда находит хост (иногда часть host будет None), так как она соответствует частичным URL-адресам и неоднозначным url-подобным шаблонам.

Я изменил regexp, чтобы включить приведенные выше изменения, и дал тестовый пример фрагмента:

import re
import lxml.html
import lxml.html.clean

url_regexp = re.compile(r"""(?i)\b(?P<body>(?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|(?P<host>[a-z0-9.\-]+[.][a-z]{2,4}/))(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»""‘’]))""")

DOC = """<html><body>
    http://foo.com/blah_blah
    http://foo.com/blah_blah/.
    http://www.extinguishedscholar.com/wpglob/?p=364.
    http://✪df.ws/1234
    rdar://1234
    rdar:/1234
    message://%[email protected]%3e
    What about &lt;mailto:[email protected]?subject=TEST&gt; (including brokets).
    bit.ly/foo
</body></html>"""

tree = lxml.html.fromstring(DOC)
body = tree.find('body')
lxml.html.clean.autolink(body, [url_regexp], avoid_hosts=[])
print lxml.html.tostring(tree)

Вывод:

<html><body>
    <a href="http://foo.com/blah_blah">http://foo.com/blah_blah</a>
    <a href="http://foo.com/blah_blah/">http://foo.com/blah_blah/</a>.
    <a href="http://www.extinguishedscholar.com/wpglob/?p=364">http://www.extinguishedscholar.com/wpglob/?p=364</a>.
    <a href="http://%C3%A2%C2%9C%C2%AAdf.ws/1234">http://&#226;&#156;&#170;df.ws/1234</a>
    <a href="rdar://1234">rdar://1234</a>
    <a href="rdar:/1234">rdar:/1234</a>
    <a href="message://%[email protected]%3e">message://%[email protected]%3e</a>
    What about &lt;<a href="mailto:[email protected]?subject=TEST">mailto:[email protected]?subject=TEST</a>&gt;
    (including brackets).
    <a href="bit.ly/foo">bit.ly/foo</a>
</body></html>
  • 0
    это чертовски регулярное выражение ... но это работает как шарм. Остается одна проблема: <a href="bit.ly/foo"> bit.ly/foo </a> будет ссылаться на подпапку, а не на домен ... как бы вы это исправили?
  • 0
    Предоставленное вами регулярное выражение будет соответствовать тексту URL-ссылки, но функция clean.autolink представляет собой черный ящик: он не позволит вам передать обратный вызов для изменения ссылок до того, как они их кодируют. Я бы порекомендовал скопировать функции clean.autolink и clean._link_text, вычеркнуть то, что вы не используете, и немного настроить поведение, особенно когда вы перебираете совпавшие URL-адреса, находите те, у которых нет части Host и добавляете http:// схема URL (и любые другие правила, которые вы хотите применить).
Показать ещё 1 комментарий
0

Вы действительно не даете достаточно информации, чтобы быть уверенным, но я уверен, что у вас возникают проблемы с обратными косыми чертами в регулярном выражении Gruber. Попробуйте использовать необработанную строку, которая позволяет обратную косую черту без экранирования и тройные кавычки, которые позволяют вам использовать кавычки в строке, не избегая их. Например.

re.compile(r"""(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»""‘’]))""")
  • 0
    это именно то, что пытался передать в функцию автосвязи;.), но это не удается с помощью: lxml \ html \ clean.py ", строка 571, в _link_text host = match.group ('host') IndexError: нет такой группы
  • 0
    Аааа, глядя больше на lxml, они ожидают, что он приведет к именованным группам совпадений, которых нет в регулярном выражении Грубера, предназначенном для соответствия целому URL. Для этого потребуется провести более обширную операцию на регулярном выражении; может быть, я смогу взглянуть на это сегодня вечером, предполагая, что кто-то еще не опубликовал решение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню