Подтвердить что ты не робот

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

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

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

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

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

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

Идеи

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

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

Вопросы, относящиеся

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

Обновление и сводка

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

  • @Jon Яркая методика обнаружения TLD (хороший защитный chokepoint)
  • Для этих подозрительных строк замените точку точкой-символом в соответствии с параметром @capar
  • Хороший точечный символ - @Sharkey subscripted & amidd; middot; (т.е. " & middot;" ). & Амп; Мидот; также является границей слов, поэтому сложнее случайно скопировать и вставить.

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

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

Ответ 1

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

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

[^\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-адреса, которые копируются и вставляются в адресную строку, сохраняя при этом побочный ущерб до минимума.

Ответ 3

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

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

Ответ 4

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

Ниже приведен пример:

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

Ping возможный 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

Здесь:

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

  • 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 статистика ping --- 1 переданный пакет, 1 полученный пакет, 0% потери пакетов мин./авг/макс/stddev = 18.317/18.317/18.317/0.000 мс

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

    ping: невозможно разрешить fooalksdflajkd.com: Неизвестный хост

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

Рассмотрите возможность включения API OWASP AntiSAMY...

Ответ 11

I like capar answer the best so far, but dealing with unicode fonts can be a bit fraught, with older browsers often displaying a funny thing or a little box... and the location of the U+05B4 is a bit odd... for me, it appears outside the pipes here |ִ| even though it between them.

Там есть удобный и миддот; (& middot;), хотя, что ломает вырезать и вставлять таким же образом. Его вертикальное выравнивание может быть скорректировано с помощью <sub> , например:

StackOverflow <суб > & Мидот;суб > ком

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

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

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

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

Ответ 12

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

P.S. извините за саморегуляторные заглушки;)

Ответ 13

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

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

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

Ответ 14

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

К счастью, юникод-люди вас охватили. Найдите реализацию алгоритма скелета TR39 для Unicode Confusables на выбранном вами языке программирования и соедините его с некоторой нормализацией 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 и определения любых кодовых точек, в которых изменяется разложение или нижний регистр/верхний регистр пары символов, независимо от того, являются ли они считается смешанным друг с другом.