Обнаружение (непослушного или приятного) URL или ссылки в текстовой строке

Как я могу обнаружить (с помощью регулярных выражений или эвристики) ссылку на веб-сайт в текстовой строке, такой как комментарий?

Цель состоит в том, чтобы предотвратить спам. HTML разделен, поэтому мне нужно обнаружить приглашения для копирования и вставки. Для спамера не должно быть экономичным размещать ссылки, потому что большинство пользователей не могут успешно попасть на страницу. Я хотел бы получить предложения, рекомендации или обсуждение лучших практик.

Некоторые цели:

  • Низко висящие фрукты, как правильно сформированные URL (http://some-fqdn/some/valid/path.ext)
  • URL-адреса, но без http:// префикс (т. е. действительное полное доменное имя + действительный путь HTTP)
  • Любые другие забавные дела

Конечно, я блокирую спам, но тот же процесс можно использовать для автоматической ссылки на текст.

идеи

Вот некоторые вещи, о которых я думаю.

  • Содержание написано на родном языке, так что я могу быть счастливым при обнаружении
  • Должен ли я сначала убрать все пробелы, чтобы пойматьwww .example.com"? Знают ли обычные пользователи, чтобы убрать пространство самостоятельно, или какие-либо браузеры" делают то, что я имею в виду "и убирают его для вас?
  • Возможно, несколько проходов - лучшая стратегия со сканированием для:
    • Правильно сформированные URL
    • Все не пропуски, сопровождаемые '.' сопровождаемый любым действительным TLD
    • Что-нибудь еще?

Смежные вопросы

Я прочитал их, и теперь они задокументированы здесь, так что вы можете просто ссылаться на регулярные выражения в этих вопросах, если хотите.

Обновление и Резюме

Вау, здесь есть несколько очень хороших эвристик! Для меня лучший результат - это синтез следующего:

  1. Техника @Jon Bright по обнаружению TLD (хороший защитный барьер)
  2. Для этих подозрительных строк замените точку символом, похожим на точку, в соответствии с @capar
  3. Хорошим персонажем, похожим на точку, является @Sharkey's subspted & middot; (т.е. "·"). & Мидот; также является границей слова, поэтому сложнее случайно скопировать и вставить.

Это должно сделать CPM спаммера достаточно низким для моих нужд; обратная связь "пометить как неприемлемую" должна отразить все остальное. Другие перечисленные решения также очень полезны:

  • Удалите все пунктирные квадраты (комментарий @ Шарки к его собственному ответу)
  • @ Требование Sporkmonger для клиентского Javascript, который вставляет обязательное скрытое поле в форму.
  • Пингуйте URL-адрес на стороне сервера, чтобы определить, является ли это веб-сайтом. (Возможно, я мог бы запустить HTML через SpamAssassin или другой байесовский фильтр согласно @Nathan..)
  • Посмотрите на источник Chrome для его умной адресной строки, чтобы увидеть, какие хитрые трюки использует Google
  • Обращение к OWASP AntiSAMY или другим веб-сервисам для обнаружения спама / вредоносных программ.

13 ответов

Решение

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

Я думаю, что ваш лучший выбор будет с TLD. Существуют двухбуквенные нДВУ и сравнительно небольшой список (в настоящее время). Они должны начинаться с точки или с суффикса или какой-либо границы слова. Как отметили другие, это не будет идеальным. Нет никакого способа получить "покупать забавные фармацевтические препараты. Это", не отказываясь от законного "я попробовал еще раз. Это не работает" или подобного. Все это сказал, это было бы мое предложение:

[^\b]\.([a-zA-Z]{2}|aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel)[\b/]

Вещи, которые это получит:

Это, конечно, сломается, как только люди начнут запутывать свои URL, заменяя "." с "точкой". Но, опять же, если предположить, что ваша цель - спаммеры, то если они начнут делать подобные вещи, их рейтинг кликов упадет еще на пару порядков до нуля. Множество людей, достаточно информированных, чтобы деобфусцировать URL, и множество людей, недостаточно информированных, чтобы посещать спам-сайты, я думаю, имеет крошечное пересечение. Это решение должно позволить вам обнаруживать все URL-адреса, которые можно копировать и вставлять в адресную строку, сохраняя при этом побочный ущерб на минимальном уровне.

Я знаю, что это не поможет с текстом автоматической ссылки, но что если вы выполните поиск и замените все точки полного останова символом, который выглядит как одно и то же, например символом юникода для точки иврита hiriq (U+05B4)?

Следующий абзац является примером:

Это может сработать ִ Период выглядит немного странно, но все еще читаемо ִ Преимущество, конечно, в том, что любой, кто копирует и вставляет www ִ google ִ com, не станет слишком далеко ִ:)

Я не уверен, что обнаружение URL с помощью регулярных выражений является правильным способом решения этой проблемы. Обычно вы пропускаете какой-то непонятный крайний случай, который спаммеры смогут использовать, если они достаточно мотивированы.

Если ваша цель - просто отфильтровывать спам из комментариев, вы можете подумать о байесовской фильтрации. Он очень точно пометил электронную почту как спам, возможно, он сможет сделать то же самое для вас, в зависимости от объема текста, который вам нужно отфильтровать.

Ну, очевидно, низко висящие фрукты - это вещи, которые начинаются с http:// и www. Попытка отфильтровать такие вещи, как "www. G mail. Com", приводит к интересным философским вопросам о том, как далеко вы хотите зайти. Вы хотите сделать следующий шаг и отфильтровать "www dot gee mail dot com"? Как насчет абстрактных описаний URL, таких как "Аббревиатура для всемирной паутины, за которой следует точка, за которой следует буква g, за которой следует слово mail, за которым следует точка, заканчивающаяся аббревиатурой TLD для коммерческого использования".

Важно провести черту, какие вещи вы собираетесь пытаться отфильтровать, прежде чем продолжать пытаться разработать свой алгоритм. Я думаю, что линия должна быть проведена на уровне, где "gmail.com" считается URL, а "gmail.com" - нет. В противном случае вы, вероятно, получите ложные срабатывания каждый раз, когда кто-то не сможет использовать первую букву в предложении.

Поскольку вы в первую очередь ищете приглашения для копирования и вставки в адресную строку браузера, возможно, стоит взглянуть на код, используемый в браузерах с открытым исходным кодом (например, Chrome или Mozilla), чтобы решить, введен ли текст в адресную строку. эквивалент "- это поисковый запрос или попытка перехода по URL.

Пинг возможного URL

Если вы не возражаете против небольшого вычисления на стороне сервера, как насчет этого?

urls = []
for possible_url in extracted_urls(comment):
    if pingable(possible_url):
       urls.append(url)  #you could do this as a list comprehension, but OP may not know python

Вот:

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

  2. pingable фактически использует системный вызов, чтобы определить, существует ли имя хоста в сети. Вы могли бы иметь простую оболочку для анализа вывода ping.

    [ramanujan: ~ / base] $ ping -c 1 www.google.com

    PING www.l.google.com (74.125.19.147): 56 байтов данных 64 байта из 74.125.19.147: icmp_seq=0 ttl=246 время =18,317 мс

    --- www.l.google.com статистика пинга --- 1 переданных пакетов, 1 полученных пакетов, 0% потери пакетов в оба конца, мин /avg/max/stddev = 18,317/18,317/18,317/0,000 мс

    [ramanujan: ~ / base] $ ping -c 1 fooalksdflajkd.com

    ping: не удается разрешить fooalksdflajkd.com: неизвестный хост

Недостатком является то, что если хост выдаст 404, вы его не обнаружите, но это довольно неплохой первый шаг - лучший способ проверить, является ли адрес веб-сайта, состоит в том, чтобы попытаться перейти на него. Вы также можете попробовать wget'ing этот URL, но это более тяжелый вес.

Сделав несколько попыток написания этого точного фрагмента кода, я могу однозначно сказать, что вы не сможете сделать это с абсолютной надежностью, и вы определенно не сможете обнаружить все формы URI, разрешенные RFC. К счастью, поскольку у вас очень ограниченный набор URL-адресов, которые вас интересуют, вы можете использовать любой из описанных выше методов.

Тем не менее, я могу с большой уверенностью сказать, что если вы действительно хотите победить спамеров, лучший способ сделать это - использовать JavaScript. Отправьте кусок JavaScript, который выполняет некоторые вычисления, и повторите вычисления на стороне сервера. JavaScript должен скопировать результат вычисления в скрытое поле, чтобы при отправке комментария также передавался результат вычисления. Убедитесь в правильности расчета на стороне сервера. Единственный способ обойти эту технику - спаммеры могут вручную вводить комментарии или запускать движок JavaScript только для вас. Я использовал эту технику, чтобы уменьшить количество спама на моем сайте со 100+ в день до одного или двух в год. Теперь единственный спам, который я когда-либо получаю, вводится людьми вручную. Странно получать спам по теме.

Здесь уже есть несколько отличных ответов, поэтому я не буду больше публиковать. Я дам пару ошибок, хотя. Во-первых, не забудьте проверить известные протоколы, все остальное может быть непослушным. Как человек, чье хобби касается ссылок telnet, вы, вероятно, захотите включить в поиск больше, чем http (s), но, возможно, захотите запретить, например, цель: или некоторые другие URL-адреса. Во-вторых, многие люди разграничивают свои ссылки в угловых скобках (gt / lt), таких как < http://theroughnecks.net/> или в скобках "(url)", и нет ничего хуже, чем щелкнуть ссылку и закрыть > или) идти вместе с остальной частью URL.

PS извините за самореферентные штекеры;)

Подумайте о включении OWASP AntiSAMY API...

Мне больше нравится ответ capar, но иметь дело с юникодными шрифтами может быть немного чревато, так как старые браузеры часто показывают забавную вещь или маленькую коробочку... а расположение U+05B4 немного странно... для меня это появляется вне труб здесь |ִ| хотя это между ними.

Есть удобный & миддот; (·) Хотя, который ломает вырезать и вставить таким же образом. Его вертикальное выравнивание может быть исправлено с помощью , например:

stackru· com

Извращенный, но в любом случае эффективный в FF3, его нельзя вырезать и вставить как URL. на самом деле довольно приятный, так как он делает визуально очевидным, почему URL не может быть вставлен.

Точки, которых нет в подозрительных URL, могут быть оставлены в покое, так что, например, вы можете сделать

s/\b\.\b/<sub>&middot;<\/sub>/g

Другой вариант - вставить некий объект нулевой ширины рядом с подозрительными точками, но такие вещи, как ‍ и ‌ и &zwsp; не похоже на работу в FF3.

Конечно, вы понимаете, что если спамеры решат использовать tinuyrl или подобные сервисы, чтобы сократить свои URL, ваша проблема только усугубится. Возможно, вам придется написать некоторый код для поиска реальных URL-адресов в этом случае, используя такой сервис, как TinyURL- декодер.

Мне нужно было просто обнаружить простые http-URL с протоколом /out, предполагая, что либо указан протокол, либо префикс "www". Я нашел вышеупомянутую ссылку весьма полезной, но в итоге я вышел с этим:

http(s?)://(\S+\.)+\S+|www\d?\.(\S+\.)+\S+

Это, очевидно, не проверяет соответствие стандарту DNS.

Учитывая беспорядок "других забавных дел", который я вижу в спаме комментариев Disqus в виде похожих персонажей, первое, что вам нужно сделать, это разобраться с этим.

К счастью, люди Unicode вас охватили. Найдите реализацию скелетного алгоритма TR39 для сбивающих с толку Unicode на выбранном вами языке программирования и соедините ее с некоторой нормализацией Unicode и верхним / нижним регистром с учетом Unicode.

Скелетный алгоритм использует таблицу поиска, поддерживаемую людьми Unicode, чтобы делать что-то концептуально похожее на сворачивание регистра.

(В выводе могут не использоваться разумные символы, но, если вы примените его к обеим сторонам сравнения, вы получите совпадение, если символы визуально достаточно похожи, чтобы человек мог понять намерение.)

Вот пример из этой реализации Java:

// Skeleton representations of unicode strings containing 
// confusable characters are equal 
skeleton("paypal").equals(skeleton("paypal")); // true
skeleton("paypal").equals(skeleton("ỿℓ")); // true
skeleton("paypal").equals(skeleton("ρ⍺уן")); // true
skeleton("ρ⍺уן").equals(skeleton("ỿℓ")); // true
skeleton("ρ⍺уן").equals(skeleton("ỿℓ")); // true

// The skeleton representation does not transform case
skeleton("payPal").equals(skeleton("paypal")); // false

// The skeleton representation does not remove diacritics
skeleton("paypal").equals(skeleton("pàỳpąl")); // false

(Как видите, сначала вам нужно выполнить другую нормализацию.)

Учитывая, что вы выполняете обнаружение URL-адресов с целью определения того, является ли что-то спамом, это, вероятно, одна из тех необычных ситуаций, когда было бы безопасно начать с нормализации Unicode до NFKD, а затем удаления кодовых точек, объявленных как объединяющие символы.

(Затем вы захотите нормализовать случай, прежде чем передавать его в алгоритм скелета.)

Я бы посоветовал вам сделать одно из следующего:

  1. Напишите свой код для запуска проверки сбивающих с толку символов как до, так и после разложения символов, на случай, если что-то считается сбивающим с толку до разложения, но не после, и проверьте строки с верхним и нижним регистром, если таблицы сбивающих с толку не симметричны между верхним и строчные формы.
  2. Выясните, действительно ли проблема №1 вызывает беспокойство (нет необходимости тратить время процессора, если это не так), написав небольшой скрипт для проверки таблиц Unicode и определения любых кодовых точек, где разложение или нижний / верхний регистр пары символов меняются независимо от того, считаются спутанными друг с другом.
Другие вопросы по тегам