В пошуках нової музики для себе мені в голову прийшла ідейка проаналізувати сайт
( тексти пісень українськиї виконавців ). Аналізовувати та граббити різні сайти - це моє так сказати хоббі. Це дуже цікаво для мене - оскільки в цьому процесі я вивчаю програмування на bash більш детально і в потребі якогось унікального програмного рішення завжди дізнаюсь щось нове для себе.
Так ось: першим ділом треба було зясувати загальну кількість виконавців викладених на сайті. Відкривши вкладку виконавці на сайті я побачив 4 сторінки виконавців. Передивившись декілька посилань на сторінки виконавців було ясно що виконавців десь в районі 1000. На сайті вони розміщені по алфавітному зростанню. В випадку парсингу ID виконавців були б розміщені хаотично. Тому я вирішив грабити по зростанні ID кожного виконавця.Написав простий скрипт:
#!/bin/bash
for id in `seq 1 1200`; do
GET "http://nashe.com.ua/artist.htm?id=$id" > ./artists/$id.html
check=`cat "./artists/$id.html" | grep arttitle | cut -d"'" -f4`
if [ "$check" = "arttitle" ]; then
echo $id >> artist-list
echo "id=$id is ok!"
else
rm -rf ./artists/$id.html
echo "id=$id error..."
fi
done
який перевіряє ID від 1 до 1200 на наявність виконавця. Коли перевірка дійшла до 1000 скрипт почав видавати постійні "error..". Стало ясно що виконавців 953, а не 722 як пише на сайті.
Коли в параметрі id викликано неправильне значення то скрипт повертає усіх виконавців. За допомогою простого парсингу можна відрізнити яка сторінка видана: з існуючим ID чи неправильним.
flashbag@laptop:~/nashe.com.ua$ GET http://nashe.com.ua/artist2.htm?page=2 | grep arttitle
flashbag@laptop:~/nashe.com.ua$ GET http://nashe.com.ua/artist.htm?id=492 | grep arttitle
<div align='left' class='arttitle' style='width: 45%;'>K402</div>
flashbag@laptop:~/nashe.com.ua$ GET http://nashe.com.ua/artist.htm?id=492 | grep arttitle | cut -d"'" -f4
arttitle
Для виконавця в коді присутній блок div з класом arttitle. Якщо в команду ще добавити контейнер cut можна чітко виділити ключове слово
Звідси можна записати назву кожного виконавця в файл з такою послідовністю як вони розміщені у базі даних: від id=1 до 953. Ось скрипт get-artists.sh:
#!/bin/bash
rm -rf artist-names
touch artist-names
for id in `seq 1 953`; do
cat ./artists/${id}.html | grep arttitle | cut -d'>' -f2 | cut -d'<' -f1 >> artist-names
done
Це навіть смішно скриптом назвати, можна цей код в одну команду втиснути )))
Тепер я вирішив дістати усі стилі виконавців, код:
#!/bin/bash
rm -rf styles.list
touch styles.list
for id in `seq 1 953`; do
a=`cat "./artists/$id.html" | grep "<br><span class='title'>"`
b=`echo ${a//*<span} | cut -d'>' -f3 | cut -d'<' -f1`
echo $id $b
echo $b >> styles.list
done
після цього рядка
a=`cat "./artists/$id.html" | grep "<br><span class='title'>"`
змінна а набувала різного значення, у виконавців про яких була повна інформація змінна а приймала цю інформацію, а у тих в кого був лише стиль - приймала значення лише стилю.
Іншими словами змінна а приймала значеня html-кода блока про виконавця. Треба було якось виділити її і я побачив що усі пункти ( Дискографія, Оф.сайт, Email ) крім стилю були розміщені в тегах div. За допомогою цієї команди echo ${a//*<span}
можна виділити всю інфу після тега span. А далі cut-ом виділяється тільки потрібне значення стилю/стилів
Файл styles.list має відповідно 953 рядка і у кожному є стиль/стилі виконавця або нема стилю. Є і такі виконавці як 98,100,109,153,154,155 та інші.
Тепер в мене виникла ідейка виділити кожний унікальний стиль. Алгоритм скрипту зразу проскочив в голові: порожні рядки видаляються, в того виконавця в кого два або більше стилів, стилі розділяються, кожному стилю - окремий рядок, далі записуються в окремий файл один унікальний стиль тільки один раз. Для перевірки схожості стилів треба видаляти лишні пробіли в рядках.
Розписувати створення скрипту не буду, бо це буде довго і нудно, ось готовий скрипт:
#!/bin/bash -x
file="style-by-line";
rm -rf $file
touch $file
cat styles.list | while read line; do
if [ "$line" != "" ]; then
x=${line//[!,]/}
if [ "${#x}" = "0" ]; then
echo $line | tr -s " " | sed 's/^[ ]//g' >> $file
fi
if [ "${#x}" = "1" ]; then
echo $line | cut -d',' -f1 | tr -s " " | sed 's/^[ ]//g' >> $file
echo $line | cut -d',' -f2 | tr -s " " | sed 's/^[ ]//g' >> $file
fi
if [ "${#x}" = "2" ]; then
echo $line | cut -d',' -f1 | tr -s " " | sed 's/^[ ]//g'>> $file
echo $line | cut -d',' -f2 | tr -s " " | sed 's/^[ ]//g'>> $file
echo $line | cut -d',' -f3 | tr -s " " | sed 's/^[ ]//g'>> $file
fi
if [ "${#x}" = "3" ]; then
echo $line | cut -d',' -f1 | tr -s " " | sed 's/^[ ]//g'>> $file
echo $line | cut -d',' -f2 | tr -s " " | sed 's/^[ ]//g'>> $file
echo $line | cut -d',' -f3 | tr -s " " | sed 's/^[ ]//g'>> $file
echo $line | cut -d',' -f4 | tr -s " " | sed 's/^[ ]//g'>> $file
fi
fi
done
sort -k 1,1 -u style-by-line > uniq-styles
Зразу в очі кидається ондоманітні умови. Так це лишнє, але з циклами в цьому напрямку в мене поки що не дуже. Змінна x - це кількість ком в одному рядку. Експерементальний шляхом визначено - максимальне значення змінної x=3, три коми-розділювачі, отже максимально можливо 4 стилі для виконавця.
Остання команда мене просто вражає. Я стільки гуглив щоб зробити унікальне сортування кожного рядка в файлі. Вже надумав написате це сортування на PHP, але в останній момент найшов найоптимальніше рішення - унікальне сортування однією командою.
Так ось вийшло 37 унікальних стилів. Відсортовані по алфавіту в OpenOffice. Ось вони:
А капелла
Альтернатива
Альтернативний рок
Брутал-поп
Вільний український метал
Готик-фольк метал
Гранж
Джерело:
Ейсід-хоп
Електро
Електро-рок
Електро-фолк
Етно-арт-рок
Етно-ска-панк
Етно-хаус
Інді-ретро-поп
Латино
Ліричний рок
Нео-психоделія
Нью-ейдж
Нью-метал
Подільський фундаментал
Поп-рок
Ритм-н-блюз
Ритуальний фолк
Рок
Рок-н-рол
Рокабіллі
Симфо-арт-метал
Техно-денс
Фолк
Фолк-дарк-метал
Фолк-метал
Фолк-рок
Фолк-фанк-рок
Хіп-хоп
Християнський рок
Тут і найшовся перший баг: стиль "Джерело:" ( виділений жирним ). Коли я подивився в усі стилі то цей стиль належав виконавцю з id=452. Сторінка цього виконавця справді не така як усі, в середині тегу span там знаходиться посилання на джерело інформації. Значить скрипт без багів, це мене порадувало =)
Щоб це все не здавалсь маячнею, я написав прості скрипти index.php та show.php.
Лістинг index.php:
<form action=show.php method=get>
<select name=style>
<?php
$uniq_styles=file('uniq-styles');
for ($i=0; $i<count($uniq_styles); $i++ )
{ echo '<option name='.$i.'>'.$uniq_styles[$i].'</option><br>'; }
?>
</select>
<input type='submit' value="OK">
</form>
Лістинг show.php:
<?php
$style=$_GET['style'];
echo '<b>'.$style.'</b><br>';
$styles=file("styles.list");
$artists=file("artist-names");
for ($i=1; $i<count($styles); $i++ )
{
if (substr_count($styles[$i-1], $style)>0)
echo '<a target=_blank href="http://nashe.com.ua/artist.htm?id='.$i.'">'.$artists[$i-1].'<br>';
}
?>
Тут можна їх потестувати http://kepnung.host-ua.org.ua/nashe.com.ua
Цим скриптам потрібні файли uniq-styles, styles.list, artists-names.
Тепер все спочатку, коротко і ясно:
- Щоб не закачувати кожен раз для парсингу всі сторінки виконавців є скрипт download-artists.sh. Він закачує усі 953 сторінки у папку artists, у вигляді ${id}.html.
- Щоб записати в файл artist-names назву кожного виконавця є скрипт get-artists.sh
- Щоб дістати стиль/стилі кожного виконавця в файл styles.list є скрипт get-styles.sh
- І нарешті щоб виділи тільки унікальні стилі в файл uniq-styles є скрипт get-uniq-styles.sh
Наочний приклад цього грабингу це посилання вище. Іншими словами - це фільтр виконавців по стилю, на сайті такої штучки нема. Треба підказати їм таку ідейку.