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

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

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

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

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

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

Идеи

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

  • Контент - это проза на родном языке, поэтому я могу легко обнаружить
  • Должен ли я сначала удалить все пробелы, чтобы поймать "www .example.com"? Знают ли обычные пользователи, что нужно удалить пробел сами, или какие-нибудь браузеры «делают то, что я имею в виду» и удаляют его за вас?
  • Maybe multiple passes is a better strategy, with scans for:
    • Well-formed URLs
    • Все без пробелов, за которыми следует '.' за которым следует любой действующий TLD
    • Что-нибудь еще?

Связанные вопросы

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

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

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

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

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

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

person JasonSmith    schedule 31.03.2009    source источник
comment
Проверьте эти статьи: - Проблема с URL-адресами - Обнаружение URL-адресов в блоке текста   -  person Christian C. Salvadó    schedule 31.03.2009


Ответы (13)


Я концентрирую свой ответ на том, чтобы избегать спамеров. Это приводит к двум дополнительным предположениям: люди, использующие систему, поэтому будут активно пытаться нарушить вашу проверку, и ваша цель - только обнаружить наличие 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/]

Это получит:

  • buyfunkypharmaceutical s.it
  • googl e.com
  • http://stackoverflo ** w.com / ** questions / 700163 /

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

person Jon Bright    schedule 15.04.2009
comment
TLD - хорошее место для защиты, спасибо за ваш ответ! Я думаю объединить его с ответом capar и заменить точку на точечный символ Unicode. Сюда ... снова. это не работает, изменится незаметно, но URL-адрес все равно не будет работать, даже если кто-то удалит пространство. Для действительно непонятных вещей, возможно, я могу использовать флаг как неуместную обратную связь. - person JasonSmith; 19.04.2009
comment
В заключение: TLD - это ахиллесова пята для URL-адресов спама. В моем случае (параграф или два прозаического текста, где URL-адреса нежелательны) сканирование TLD - это простой способ обнаружить подозрительные строки. Оттуда могут применяться некоторые из великих эвристик и методов в других ответах. Но поскольку этот ответ является хорошей основой для многих других, я выберу его в качестве принятого ответа. - person JasonSmith; 19.04.2009

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

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

person Nathan    schedule 14.04.2009
comment
Какая байесовская библиотека на php самая лучшая? - person user4271704; 29.08.2016

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

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

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

person Arnold Spence    schedule 15.04.2009
comment
Это может не сработать для моего конкретного случая, но это, пожалуй, самый умный и самый выгодный ответ на данный момент! - person JasonSmith; 15.04.2009

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

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

person Benson    schedule 14.04.2009

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

person J c    schedule 15.04.2009

Проверьте возможный 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 time = 18,317 мс

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

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

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

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

person ramanujan    schedule 18.04.2009
comment
Извините, я наверняка действительно знаю Python! :) Но в любом случае составления списков вполне ... что за слово? Непонятно. (Программистам, не использующим Python.) - person JasonSmith; 19.04.2009
comment
Правильно. Вот почему я сказал, что нельзя :) - person ramanujan; 19.04.2009

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

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

person Bob Aman    schedule 15.04.2009
comment
Это очень интересная идея. Я могу использовать это (возможно, на втором этапе после построения базового алгоритма). - person JasonSmith; 17.04.2009
comment
Ссылка на ответ, в котором я более полно объяснил эту концепцию: stackoverflow.com/questions/8472/ - person Bob Aman; 22.10.2009

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

person Rad    schedule 15.04.2009

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

person McGovernTheory    schedule 18.04.2009

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

Однако есть удобный (), который точно так же прерывает вырезание и вставку. Его вертикальное выравнивание можно исправить ‹sub›, например:

stackoverflow com

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

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

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

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

person NickZoic    schedule 19.04.2009
comment
Индексная миддот. Гений! Я хочу протестировать его, но если он работает в IE7, FF3 и Safari, я бы сказал, что этого достаточно. Я думаю смешать это с идеей @Jon Bright о замене только подозрительных URL-адресов (то есть точки, за которой следует действительный TLD). - person JasonSmith; 19.04.2009
comment
Я пробовал только на FF3, дайте знать, работает ли! Это может быть хорошей техникой для ненормальных почтовых клиентов, которые используют URL-адреса или адреса электронной почты для всяких глупостей. - person NickZoic; 19.04.2009
comment
.TLD Я не так уверен в этом, в основном потому, что их довольно много, и они могли бы сделать одно уродливое регулярное выражение. Также не забывайте, что четырехугольники с точками (например, IP-адреса) являются допустимыми URL-адресами. - person NickZoic; 19.04.2009
comment
Да, определенно нужна многоуровневая эшелонированная защита, чтобы действительно улавливать как можно больше злоупотреблений. Особенность TLD в том, что, хотя их много, их не так много, и в моем конкретном случае (поле из 1 или 2 абзацев прозаического текста) мне, вероятно, удастся избежать неприятного регулярного выражения. . (Самые полезные регулярные выражения в любом случае уродливы!) - person JasonSmith; 19.04.2009

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

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

person Tracker1    schedule 19.04.2009

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

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

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

person Hauke    schedule 24.04.2018

Учитывая беспорядок «других забавных дел», который я вижу в спаме комментариев 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 и определения любых кодовых точек, в которых разложение или нижний / верхний регистр пары символов меняются независимо от того, изменяются ли они. считаются спутанными друг с другом.
person ssokolow    schedule 12.07.2019