- Регистрация
- 09.10.2015
- Сообщения
- 3 916
- Благодарностей
- 3 867
- Баллы
- 113
Вот и пришло время рассказать о моей "небольшой" уникальной разработке, которую я делал и тестировал более 2х недель
Предисловие
Наверняка многие из тех, кто тут занимается дорвеестроительством и SEO сталкивались с тем, что для получения качественных результатов необходимо создавать страницы с хорошими и читаемыми заголовками, но вот беда: где взять столько ключевиков, чтобы нагенерить как можно больше результатов? Ведь есть ниши, где количество ключевиков в базах - минимально. Использовать яндекс.wordstat? Использовать подсказки (suggests) от поисковиков? Использовать бесплатные базы ключевых слов? Использовать платные базы ключевых слов? Все эти способы имеют очень большой процент мусора, по ним уже делают доры множество других людей и поэтому по точному вхождению есть очень большой риск того, что вся выдача уже "засрана" кем-то другим.
Я тоже не раз сталкивался с этой проблемой: часто использовал spintax для генерации заголовков по моим спинтакс-шаблонам (сделать которые очень большая проблема, т.к. качество будет не самым лучшим); использовал платные и бесплатные базы ключевых слов, а затем часами сидел и чистил их от различного шлака (чтобы минимизировать процент страниц, которые будут созданы, но трафика с них не будет). И в итоге пришел к идее, что самым лучшим и оптимальным вариантом напарсить качественных ключей (читай: заголовков) будет у самого гугла. Ведь именно он, гугл, как никто другой знает как должен выглядеть заголовок (по его мнению), чтоб он приносил трафик.
Зарождение
Именно так родилась идея написать парсер заголовков с сайтов. Идея была до банального проста: забиваем в какой-нибудь парсер гугла список ключевых слов по нужной нише, собираем ссылки до топ100 (например), прогоняем результат через свой шаблон и вытаскиваем из страницы содержимое тега <title>. Но! При первых же попытках было замечено, что есть ещё множество сайтов, которые в title не пишут заголовок, а засовывают туда зачем-то только название сайта. А сам заголовок находится в h1 или мета-теге og:title или twitter:title. Но проблема не может придти одна. Ведь если мы всё равно вытянем заголовок, то в большинстве случаев там будет и название сайта, а как от него избавиться? Ведь нам оно совершенно не нужно и будет только мешать. Значит нужно "чистить" названия сайтов каким-то образом... Плюс ко всему, если задуматься, то на статейных страницах наверняка используются теги h2-h6, в которых могут быть релевантные и очень "вкусные" подзаголовки, которые с большой долей вероятности никто не использует в качестве основных заголовков. Значит нужно парсить и их! Но ведь среди этих тегов ещё крайне часто попадаются различные названия меню, разделов сайта и прочая белиберда, которая нам абсолютно не нужна. Удалять её по списку стоп-слов? Тогда получится, что это будет "шаг назад", возвращение к многочасовым очисткам итоговой базы. Это не по нашему! Нужно автоматизировать весь этот процесс, чтобы я мог сидеть и попивать чай, пока Zenno работает на благо одного из "санитаров интернета". Что ж, стоит посидеть с блокнотом и подумать о
Initial commit
Сразу стало понятно, что использовать "браузер" для такого рода задач - идея как минимум безумная и как максимум: очень непроизводительная. GET-запросы - наше всё! Но писать "километровые" регулярки желания никакого не было, хоть это и казалось вполне адекватной задачей. Давно уже чесались руки, чтобы распробовать и потестировать AngleSharp - что ж, пришло время.
Когда я изучал форум по вопросу подключения и использования этой библиотеки, то много раз сталкивался с тем, что все собирают его из исходников или качают nuget пакет и компилируют его оттуда. Собирать из исходников желания не было, а использовать nuget и разбираться не хотелось. Поэтому пришлось зайти "с тыла"... Погуглив описание nuget пакетов я встретил такую мысль, что некоторые пакеты - это просто zip архив с какими-то инструкциями, которые содержат уже скомпилированные *.dll под различные версии .NET.
И о чудо! С AngleSharp всё произошло именно так: если заглянуть на страницу пакета https://www.nuget.org/packages/AngleSharp , то можно увидеть, что для .NET v4.5 нет зависимостей (No dependencies), то есть вероятнее всего внутри архива лежит нужная dll. И это оказалось именно так! Везение? Возможно По ссылке "Manual download" со страницы пакета качаем странный "anglesharp.0.9.9.1.nupkg", переименовываем его в *.zip, разархивируем и видим, что в папке "lib/net45" как раз находится нужная и уже скомпилированная библиотека, которая, повторю - не нуждается в зависимостях. Просто? - Да. Удобно? - Более чем! Копируем её к себе в ExternalAssemblies в ZP и наслаждаемся минимизацией геморроя с этой библиотекой.
PS: не факт, что с другими библиотеками будет аналогичная ситуация, да и я не представляю возможны ли "подводные камни" от такого рода использования, поэтому не принимайте этот совет как первоначальную инструкцию. Может быть для требуемой вам библиотеки всё будет совсем по-другому, но с AngleSharp - всё работает.
И о чудо! С AngleSharp всё произошло именно так: если заглянуть на страницу пакета https://www.nuget.org/packages/AngleSharp , то можно увидеть, что для .NET v4.5 нет зависимостей (No dependencies), то есть вероятнее всего внутри архива лежит нужная dll. И это оказалось именно так! Везение? Возможно По ссылке "Manual download" со страницы пакета качаем странный "anglesharp.0.9.9.1.nupkg", переименовываем его в *.zip, разархивируем и видим, что в папке "lib/net45" как раз находится нужная и уже скомпилированная библиотека, которая, повторю - не нуждается в зависимостях. Просто? - Да. Удобно? - Более чем! Копируем её к себе в ExternalAssemblies в ZP и наслаждаемся минимизацией геморроя с этой библиотекой.
PS: не факт, что с другими библиотеками будет аналогичная ситуация, да и я не представляю возможны ли "подводные камни" от такого рода использования, поэтому не принимайте этот совет как первоначальную инструкцию. Может быть для требуемой вам библиотеки всё будет совсем по-другому, но с AngleSharp - всё работает.
Начальный конфиг был написан, за пару дней был сделан простой парсер тегов title, h1-h6, мета-тегов OpenGraph/Twitter, и сохранение всего добра в *.txt файл. Но вселенная решила, что такая задача не может решиться таким банальным подходом, и в этот момент я понял, что использование простых файлов абсолютно не поможет мне избавиться от "мусора": названий сайтов в заголовке (а они встречаются даже в meta-тегах og:title!!!), а также названий меню/разделов... Подумав какое-то время было принято волевое решение: использовать СУБД, но отказаться от MySQL/PostgreSQL базы данных, т.к. с большой долей вероятности это повлечет трудности у тех, кто захочет использовать данный проект у себя. Поэтому было решено посмотреть в сторону SQLite, которую я никогда до этого не использовал в desktop-решениях (только при веб-разработке, в рамках использования её как development базы данных). Из этого всего с помощью неиссякаемого желания и какой-то матери родилась библиотека FastSqliteHelper (легковесный C# wrapper), которую я тоже выложил как конкурсный шаблон + полностью открытый код на github.
После этого мысли стали более структурированы (и данные тоже), что по моим прикидкам должно было увеличить скорость (как же я ошибался) и улучшить качество итоговых результатов (а тут не ошибался).
Тупиковый подход?
Изначально было решено, что парсер должен состоять из 3 частей (шаблонов):
- Добавление ссылок из файла в базу sqlite.
- Получение ссылки из базы, парсинг всех данных со страницы, добавление этих данных в базу.
- Получение всех данных (заголовков) из базы и очистка их от мусора + запись результата в файл.
Два заголовка двух новостей со всеми известного сайта. Что их объединяет? Правильно, окончание заголовка. Это то самое "название сайта", от которого так хотелось бы избавиться. Если был бы один заголовок, то данная задача была бы практически не реализуемой, но вот когда есть два и более - это уже что-то! Обдумав такой стратегический поворот было решено написать алгоритм, который из списка заголовков удалял бы окончания (а в некоторых случаях и начала), которые повторяются в абсолютно каждом заголовке для одного домена. И чем больше страниц с домена мы имеем в нашей базе, тем точнее будут "выделены" правильные заголовки.Российские хоккеисты выиграли золото Олимпиады в Пхенчхане — Meduza
Венесуэла первой в мире выпустила национальную криптовалюту. В нее почти никто не верит — Meduza
Примерно такая же идея была продумана и по отношению к мусорным "разделам" сайта: если у нас есть несколько страниц, с которых мы спарсили содержимое тегов h1-h6, то с очень большой долей вероятности у нас есть такие данные, которые повторяются в рамках одного тега из страницы в страницу - именно это и есть "меню", "разделы" и прочие не нужные "словосочетания".
Далее пришлось погрузиться в давно забытый раздел информатики: олимпиадное программирование Вспомнил сортировку пузырьком, чуть-чуть подумал о графах и деревьях и пришел к выводу, что данная задача будет оптимально (по соотношению затраченное время / количество кода) решаться полным перебором так как, к счастью, данных то у нас не терабайты. Порисовав на листочке примеры, забросив их в SharpDevelop - за пару дней были сделаны оба алгоритма, которые удаляли именно то, что мне было необходимо, но плюсом ко всему были написаны различные варианты, при которых "что-то может пойти не так" и, к счастью, алгоритмы справлялись с ними на "ура" - ничего не падало в неожиданный Exception, результаты точно соответствовали тому, что нужно.
Фактически, этот этап ознаменовал победу над этой идеей и близость к конечной реализации, поэтому дальнейшие правки, объединение 3х частей/шаблонов в один (для удобства) хочется опустить, т.к. практического смысла в них нет. А поэтому перейдем к самому интересному и ожидаемому!
Конфигурация шаблона
Надеюсь, что я вас не шокирую количеством и возможностями для конфигурирования, т.к. их в ходе работы получилось ну очень много, но оно того стоило. Тут я приведу лишь часть из всего, полное описание настроек находится в config.ini
- Название проекта - используется как имя файла базы данных, папки с результатами, префиксов (а точнее частей пути) для других настроек.
- Путь к файлу со списком урлов для парсинга - все строки обязательно должны быть уникальными (без повторов).
- Сохранять ли объединенные результаты в один файл?
- Путь к файлу с объединенными результатами.
- Сохранять ли результаты, разбитые по названиям тегов?
- Путь к папке с результатами, разбитыми по названиям тегов.
- Фильтровать ли результаты? Тут стоит уточнить, что это список для "включения", а не для "исключения" (это не стоп-слова).
- Файл со списком строк для фильтрации.
- Директория для сохранения краткого лога, когда шаблон падает по BadEnd.
- Путь к файлу со списком UserAgent'ов.
- Путь к файлу со списком дополнительных заголовков - необходимо для проставления правильного языка (AcceptLanguage).
- Количество попыток получить данные со страницы.
- Максимальный размер страницы в байтах.
- Максимальная длина заголовка.
- Минимальная длина заголовка.
- Пропускать главные страницы сайта?
- Минимальное количество страниц с домена для правильной очистки.
- Огромное количество настроек для парсинга и очистки любых тегов со страниц (при желании вы сможете спарсить все <b>, <strong>, <em> и так далее)
- Название тега
- Селектор для парсинга
- Местонахождение данных (содержимое тега или содержимое атрибута)
- Алгоритм очистки (удаление названия сайта, удаление разделов меню, оставить "как есть")
- Параметры алгоритма
Советы и ограничения
- Самое важное замечание, которое хотелось бы отметить: данный шаблон не тестировался со страницами на русском языке. Мне это абсолютно не нужно, да и разбираться с "кракозябрами" в виде кодировки не хотелось. Поэтому я не даю никакой гарантии, что сайты в кодировке windows-1251 будут выдавать результаты, которые пригодны для использования. Но благодаря тому, что я покрыл комментариями ~95% кода - для тех, кому это жизненно необходимо - есть возможность исправить такое недоразумение и сделать, чтоб всё парсилось в нужной кодировке. Хотя есть подозрения, что AngleSharp может делать это автоматически, поэтому в любом случае - нужно тестировать и смотреть.
- Данный шаблон наиболее пригоден для "общенишевого" парсинга, а не "точечного". Что это значит: вам изначально нужно спарсить ссылки по определенным ключевым словам другим парсером, но ключевые слова (а точнее ниша, по которой вы работаете) должны быть вида "электроника и компьютеры", а не "macbook air 2016". Да, со вторым вариантом тоже всё будет работать, просто результат будет немного иной. Сложно объяснить в двух словах, но данный парсер заголовков будет работать лучше со статейными нишами, а не там, где есть отдельные объекты (товары, например) и вы будете использовать в качестве ключевиков для парсинга - разные объекты.
- Не стоит использовать для парсинга огромные базы урлов (по моим прикидкам - более 250 тысяч). К сожалению, sqlite оказалась такой базой, где скорость работы и множество блокировок внутри шаблона привели к тому, что увеличение потоков не даст прирост скорости (в первую очередь из-за записи результатов в базу). Это был технический просчет в плане того, что я выбрал её. Под MySQL всё бы работало быстрее, но возможности использования было бы меньше. Поэтому всё вышло именно так, как вышло. Чуть ниже я расскажу о скорости работы.
- При первом использовании лучше выключить фильтрацию результатов, чтобы посмотреть что получится в итоге. А уже потом решить для себя стоит ли использовать её или лучше фильтровать через другие программы.
- Так как внутри 1 шаблона пришлось уместить 3, то нагрузка на процессор в разных частях будет идти по разному: в первой части при добавлении урлов в базу будет незначительной, а вот когда будет идти парсинг - нагрузка возрастет. Поэтому мониторьте состояние шаблона (будет ясно по логу) и не выставляйте потоки "в максимум", т.к. это всё равно не даст сильного прироста по производительности.
- В ходе работы в логе будут появляться ошибки непосредственно из самого шаблона (когда окончили добавление ссылок, когда пропарсили все ссылки и когда очистили результаты), но могут вываливаться и совершенно неожиданные исключения. Если их количество не "зашкаливает", то ничего особо страшного не произойдет, но на всякий случай стоит проверить папку с логом BadEnd'a, там будут 3 файла: если в каком-то из них несколько урлов/идентификаторов, то в этом нет ничего страшного - потеря нескольких ссылок не принесет вам ощутимых проблем, но если там будут сотни строк, то это уже значит что что-то пошло не так (но надеюсь, что такого не будет).
- SQLite база будет достаточно "увесиста", поэтому имейте место на диске, где лежит шаблон, чтобы оно внезапно не кончилось.
- В ZP при выборе проекта есть вкладка "Остановка" - обязательно установите там в поле "Количество неуспехов подряд" значение от 10 до (количество_потоков / 5). Тем самым шаблон гарантировано остановится после окончания, а не будет "крутится" до конца жизни на земле.
- В результирующих файлах с заголовками будут повторы! Это сделано преднамеренно, вы должны очистить их от дублей самостоятельно.
- Парсинг происходит только с использованием прокси. Это не конфигурируемо и не отключаемо через настройки. Сделано в целях вашей же защиты от самих себя Чтобы потом не оказалось, что вы случайно "уронили" какой-то сайт парсингом, да ещё и со своего IP. Прокси берутся из ZennoPoster, поэтому учтите, что там они должны быть добавлены.
- Во время парсинга гугла (или любой другой ПС) для составления списка урлов для выдергивания заголовков, я советую не парсить все ссылки целиком. А ограничиться, например, топ50 или топ100. Потому что далее уже будет всё равно всякая хрень
- Все нужные директории создаются автоматически при первом запуске, поэтому не обязательно их делать руками. И пути к директориям должны быть в папке, где лежит шаблон, то есть не следует указывать полный путь.
- В итоговых результатах могут встречаться заголовки, которые не содержат пробелов. Это, к сожалению, так работает .TextContent в AngleSharp. Я хотел рекурсивно выделять под-ноды, чтобы избежать такого поведения, но так и не протестировал это, поэтому не стал выкладывать такое решение.
- 99% шаблона написано на C#, причем прокомментирована практически каждая строчка. Для тех, кто уже начал осваиваться в C#, но ещё жаждет знаний - думаю, будет полезно. Часть кода (по очистке) лежит в OwnCode отдельным классом, загляните туда тоже, если хотите
Установка
Установка с одной стороны тривиальна, а с другой - сложна. Для начала в любом случае нужно будет скопировать все необходимые библиотеки из архива в ExternalAssemblies, затем напарсить ссылок каким-то сторонним софтом, сконфигурировать необходимые настройки в файле config.ini и запустить парсер в многопоточном режиме в ZP. Работать будет долго, но терпимо.
Скорость работы
Хотелось бы привести некоторые численные показатели, которые были проведены в ходе последних 2х парсингов.
Параметр | База на 481 тысячу ссылок | База на 146 тысяч ссылок |
Точное количество ссылок в базе: | 481 904 | 146 000 |
Количество строк для фильтрации: | 37 | 37 |
Уникальных доменов: | 170 381 | 72 172 |
Всего заголовков в базе (с повторами!): | 7 240 266 | 1 722 535 |
Получено заголовков в результате: | 342 260 | 95 388 |
Уникальных заголовков в результате: | 316 604 | 85 534 |
Количество выполнений шаблона в ZP: | 1 203 137 | 392 179 |
Размер SQLite базы: | 723 МБ | 187 МБ |
Примерное время работы: | 5 дней | 20 часов |
Код:
(Количество ссылок в базе) + (Количество ссылок в базе * 1.2) + (Количество уникальных доменов) = Количество выполнений
Стоит ещё уточнить, что здесь в двух проходах использовалась фильтрация, поэтому количество итоговых заголовков получилось меньше количества ссылок в базе. Если убрать фильтрацию, то их будет гораздо больше (но и мусора попадет больше).
Post Scriptum
К концу статьи вы уже, наверное, забыли что я назвал эту разработку "конвейером" со звездочкой. И сейчас хотелось бы привнести ясность в используемую "игру слов". Дело в том, что я считаю, что "комбайн" в нашем понимании (автоматизации) - это такой продукт/шаблон/софт, который из множества разных сущностей делает одну. А вот "конвейер" - это когда из одного множества мы в итоге получим другое множество сущностей: из списка урлов мы получаем список заголовков. Не знаю приживется ли такая терминология в нашем "серо-черном" мире, но вдруг
Всю неделю, пока тут выкладывали шаблоны другие участники - я тестировал эту разработку. И думал. Много думал. Над тем стоит ли вообще выкладывать это в паблик. Поэтому что решение получилось крайне интересное и результаты его работы меня самого поразили. В итоге решил, что у меня и так слишком много разработок уходит "в стол". Банально жалко того, что делаю много всего, а выкладывать что-то - значит "убить тему". Думаю, что с таким шаблоном (из-за некоторых ограничений и скорости работы) - в итоге не выйдет "убить" в прямом понимании то, как сейчас делаются заголовки для доров.
Плюс, это был монструозный в плане реализации и в итоговом результате шаблон. Я потратил кучу времени и своих знаний, чтобы это всё заработало так, как необходимо. Вряд ли я буду делать такие публичные решения в дальнейшем (в первую очередь из-за кучи потраченных нервов), поэтому "куйте железо пока горячо"
Post Post Scriptum
Пример работы шаблона по сбору базы ключевиков для запроса "pancakes" (блинчики): http://zennolab.com/discussion/threads/konvejer-po-parsingu-baz-kljuchevikov-zagolovkov-statej-ot-gribnika.46636/#post-345905
- Категория
- Парсинг, SEO
- Номер конкурса шаблонов
- Первый конкурс шаблонов
- Уровень сложности
- Продвинутый
Вложения
-
1,2 МБ Просмотры: 1 107
-
143,6 КБ Просмотры: 984
Для запуска проектов требуется программа ZennoPoster или ZennoDroid.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...
Для того чтобы запустить шаблон, откройте нужную программу. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.
Последнее редактирование: