[BASH] Operacja na pliku HTML

Bash, C, C++, Java, PHP, Ruby, GTK, Qt i wiele innych - wszystko tutaj.
Awatar użytkownika
sauron
Sędziwy Jeż
Sędziwy Jeż
Posty: 45
Rejestracja: 13 cze 2009, 11:30
Płeć: Mężczyzna
Wersja Ubuntu: inny OS
Środowisko graficzne: GNOME
Architektura: x86_64

[BASH] Operacja na pliku HTML

Post autor: sauron »

Witam, piszę skrypt dla kolegi i potrzebuję pomocy.
Celem skryptu jest: wpisanie nazwy zespołu, a skrypt ma wywalić albumy jakie zespół wydał i rok. Dane mają zostać pobrane stąd: http://www.metal-archives.com/ (Encyclopaedia Metallum: The Metal Archives)

Załatwiłem już problem wyszukiwania oraz pobierania źródła strony o zespole.
Teraz muszę z tego źródła "wydobyć" info o albumach. Próbowałem grep'em, ale jako że to jest HTML i znaki specjalne się pojawiają to nie mogę dość do rozwiązania :(

Dam tu teraz linka do jednego przykładowego zespołu:
http://www.metal-archives.com/band.php?id=1

Zobaczcie w źródło strony, a to są info, które chcę z tego źródła wyciągnąć:
http://pastebin.com/UBHJKZLy

Poza tym skrypt powinien wyrzucać tylko albumy tylko Full-length, EP i Dema
Zawartość ma się zapisywać do HTML'a z pogrubieniem Full-length :(

Z góry dzięki za pomoc :)
Awatar użytkownika
mario_7
Administrator
Administrator
Posty: 8598
Rejestracja: 30 sie 2006, 13:11
Płeć: Mężczyzna
Wersja Ubuntu: 20.04
Środowisko graficzne: GNOME
Architektura: x86_64

Odp: [BASH] Operacja na pliku HTML

Post autor: mario_7 »

Poczytaj o programach sed i awk - tego potrzebujesz. :)
Awatar użytkownika
B.A.D.
Sędziwy Jeż
Sędziwy Jeż
Posty: 96
Rejestracja: 14 lis 2010, 21:16
Płeć: Mężczyzna
Wersja Ubuntu: 10.04
Środowisko graficzne: GNOME
Architektura: x86
Kontakt:

Odp: [BASH] Operacja na pliku HTML

Post autor: B.A.D. »

Kod: Zaznacz cały

http://www.metal-archives.com/search.php?string=iron+maiden&type=band
tak wygląda zapytanie do bazy danych przesyłane przez url. Przeglądarki graficzne ładnie łączą się z bazą i przetwarzają zapytanie na numer id zespołu w bazie, jednak ani w3m ani elinks nie poradziły sobie z tym więc nie wiem za bardzo jak zrobić to spod konsoli ale może się przyda:)
-Ice cream... I like an ice cream.
-Yeah? What flawor?
-Doesn't matter... it's for my ass.
Awatar użytkownika
mario_7
Administrator
Administrator
Posty: 8598
Rejestracja: 30 sie 2006, 13:11
Płeć: Mężczyzna
Wersja Ubuntu: 20.04
Środowisko graficzne: GNOME
Architektura: x86_64

Odp: [BASH] Operacja na pliku HTML

Post autor: mario_7 »

Znaczy... Nie wiesz jak stronę zapisać, aby dostać się do kodu html?
Program wget pomoże.

#laybythesea: Mamy nawet o tym art w czytelni:
http://czytelnia.ubuntu.pl/index.php/20 ... stron-www/
;)
Awatar użytkownika
sauron
Sędziwy Jeż
Sędziwy Jeż
Posty: 45
Rejestracja: 13 cze 2009, 11:30
Płeć: Mężczyzna
Wersja Ubuntu: inny OS
Środowisko graficzne: GNOME
Architektura: x86_64

Odp: [BASH] Operacja na pliku HTML

Post autor: sauron »

Kod: Zaznacz cały

read BAND
lynx -source "http://metal-archives.com/search.php?string=$BAND&type=band" > źródło
grep -w -o 'band.*[0-9]' źródło > źródło2
curl http://metal-archives.com/$(< źródło2) > źródło
To już rozwiązałem wcześniej. Dostałem z tego rozwiązania źródło strony w pliku źródło. Tylko teraz nie wychodzi mi wyciągnięcie informacji. Sed'em też już próbowałem, ale zazwyczaj nic nie zwracał, bo były problemy ze znakami HTML'a (. / < > [ ] ' ' ) i zazwyczaj nie działał sed tak ja powinien :(

Teraz z tego pliku źródła strony chcę wyciągnąć informacje (to o których pisałem w 1 poście).
Awatar użytkownika
B.A.D.
Sędziwy Jeż
Sędziwy Jeż
Posty: 96
Rejestracja: 14 lis 2010, 21:16
Płeć: Mężczyzna
Wersja Ubuntu: 10.04
Środowisko graficzne: GNOME
Architektura: x86
Kontakt:

Odp: [BASH] Operacja na pliku HTML

Post autor: B.A.D. »

. / < > [ ] ' '
znaki które podałeś są znakami specjalnymi, jeśli chcesz je zastosować musisz użyć maskowania czyli przed każdym takim znaczkiem postawić \
-Ice cream... I like an ice cream.
-Yeah? What flawor?
-Doesn't matter... it's for my ass.
Awatar użytkownika
sauron
Sędziwy Jeż
Sędziwy Jeż
Posty: 45
Rejestracja: 13 cze 2009, 11:30
Płeć: Mężczyzna
Wersja Ubuntu: inny OS
Środowisko graficzne: GNOME
Architektura: x86_64

Odp: [BASH] Operacja na pliku HTML

Post autor: sauron »

I tak też próbowałem, ale bez skutku :(

Chcę, aby grep wyszukał w pliku źródła strony wszystko od frazy

Kod: Zaznacz cały

<tr><td colspan='2' class='tt'>Discography
do frazy

Kod: Zaznacz cały

</a>]</td></tr></table>
To są niepowtarzalne frazy w źródle. Potem sobie tylko dam >> do pliku i mi się będzie sam tworzył plik HTML. Problem leży chyba w zapisaniu tego prawidłowo. Próbowałem chyba już na wszystkie sposoby, lecz bez skutku :(

Miałem koncepcję rozwiązania tego jakoś tak:

Kod: Zaznacz cały

grep 'piersza fraza (dowolna fraza) druga fraza' źródło > plik wyjściowy
Tylko nie bardzo udaje mi się to zapisać, aby działało.
kasjo
Serdeczny Borsuk
Serdeczny Borsuk
Posty: 175
Rejestracja: 31 paź 2007, 16:38
Płeć: Mężczyzna
Wersja Ubuntu: 11.10
Środowisko graficzne: KDE Plasma
Architektura: x86

Odp: [BASH] Operacja na pliku HTML

Post autor: kasjo »

Zamiast "-source" daj "-dump" pozbędziesz się htmla i jedynymi specjalnymi znakami będa [ i ] (odnośniki do linków)
Awatar użytkownika
sauron
Sędziwy Jeż
Sędziwy Jeż
Posty: 45
Rejestracja: 13 cze 2009, 11:30
Płeć: Mężczyzna
Wersja Ubuntu: inny OS
Środowisko graficzne: GNOME
Architektura: x86_64

Odp: [BASH] Operacja na pliku HTML

Post autor: sauron »

Jak dam -dump to mi nic nie da, bo dostanę wszystko w pliku tekstowym i będzie jeszcze trudniej wydostać nazwy albumów z tego pliku (bo w html'u jest tabela), a w pliku tekstowym mi wszystko "wyspacjowało". A z pliku jest chyba z HTML'a łatwiej wydobyć to info, bo można zrobić odnośniki na podstawie kodu (patrz fraza 2, z ostatniego mojego postu). A tu nie bardzo jak jest zrobić jakiś odnośnik :(

Zależałoby mi, na propozycjach zapisu tego:
Chcę, aby grep wyszukał w pliku źródła strony wszystko od frazy

Kod: Zaznacz cały

<tr><td colspan='2' class='tt'>Discography
do frazy

Kod: Zaznacz cały

</a>]</td></tr></table>
Awatar użytkownika
B.A.D.
Sędziwy Jeż
Sędziwy Jeż
Posty: 96
Rejestracja: 14 lis 2010, 21:16
Płeć: Mężczyzna
Wersja Ubuntu: 10.04
Środowisko graficzne: GNOME
Architektura: x86
Kontakt:

Odp: [BASH] Operacja na pliku HTML

Post autor: B.A.D. »

Kod: Zaznacz cały

sed -n -e '/\<tr\>\<td colspan\=\'2\' class\=\'tt\'\>Discography/,/\<\/a\>\]\<\/td\>\<\/tr\>\<\/table\>/p' /Twój/plik/z/kodem
może coś takiego?:) nie wiem czy nie przegiąłem z maskowaniem:) generalnie wykorzystaj konstrukcję:

Kod: Zaznacz cały

sed -n -e '/POCZATEK/,/KONIEC/p' plik
-Ice cream... I like an ice cream.
-Yeah? What flawor?
-Doesn't matter... it's for my ass.
Awatar użytkownika
sauron
Sędziwy Jeż
Sędziwy Jeż
Posty: 45
Rejestracja: 13 cze 2009, 11:30
Płeć: Mężczyzna
Wersja Ubuntu: inny OS
Środowisko graficzne: GNOME
Architektura: x86_64

Odp: [BASH] Operacja na pliku HTML

Post autor: sauron »

Niestety coś jest nie tak ze składnią :(
i po wywołaniu komendy w terminalu dostaję ">" od nowej i nic.
Próbowałem podstawiać pod ten wzór w różny sposób zapisane te frazy, ale też nic z tego nie wychodziło.
Wcześniej chciałem użyć sed'a do podmiany adresu zanim jeszcze użyłem:

Kod: Zaznacz cały

grep -w -o 'band.*[0-9]' źródło > źródło2
curl http://metal-archives.com/$(< źródło2) > źródło
To miałem taki sam problem, ale zamiast "/" podstawiłem "|" i się udało.
Wyglądało to tak:

Kod: Zaznacz cały

sed -e 's|fraza_początkowa|fraza_końcowa|g' plik
Jednak jak teraz spróbowałem podstawić znak "|"
sed: -e wyrażenie #1, znak 1: nieznane polecenie: `|'
Teraz już więc nie mam żadnego pomysłu jak ten problem rozwiązać :(
A tak poza tym to sed da mi całą linijkę, a nie tylko tekst od frazy do frazy.
Awatar użytkownika
borzole
Serdeczny Borsuk
Serdeczny Borsuk
Posty: 127
Rejestracja: 10 sie 2010, 11:48
Płeć: Mężczyzna
Wersja Ubuntu: inny OS
Środowisko graficzne: GNOME
Architektura: x86_64
Kontakt:

Odp: [BASH] Operacja na pliku HTML

Post autor: borzole »

Może powinieneś zmienić język.

Kod: Zaznacz cały

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import urllib, BeautifulSoup

url="http://www.metal-archives.com/band.php?id=25"
sock=urllib.urlopen(url)
source = sock.read()
sock.close()
soup = BeautifulSoup.BeautifulSoup(source)

for table in soup.findAll('table')[3]:
	try:
		a = table.findAll('a', {'class':'single'})[0]
		y = table.findAll('td', {'class':'single'})[0].string
		# tu masz wszystko
		print a['href'],'|', a.string,'|', y
	except Exception, e:
		pass
przykłady: http://www.crummy.com/software/Beautifu ... ation.html

[edit]
gubi niektóre tytuły, trzeba by dopracować

EDIT:
Postanowiłem spróbować oskrobać to w bash. Jak to zwykle bywa, trzeba było znaleźć unikalny dla strony sposób. Udało się :)

* Nazwę grupy podaj jako parametr (bez cudzysłowu)
skrypt.sh iron maiden
* Strona jest formatowana tak, aby każdy tag znajdował się w osobnej linii + usunięcie pustych wierszy
* Pomysł opiera się o wyłuskanie numerów id z linków do "release.php". Na szczęście odnośniki są na początku bloku informacji o albumie, a kończy je ]
* Z każdego bloku wyłuskałem przykładowo tytuł albumu, rok i typ reszta jest Twoim zadaniem. Każdy blok ma taką budowę:

Kod: Zaznacz cały

<a class='album' href='/release.php?id=276114'>
The Final Frontier
</a>
</td>
<td nowrap='true' class='album'>
Full-length, 2010
</td>
<td>
</td>
<td>
[
<a href='/review.php?id=276114'>
14 reviews, average 76%
</a>
]
blok jest wierszem tabeli na stronie, więc budowa jest stała. Władowałem to do tabeli w bash, więc każda linia jest komórką tabeli.

Kod: Zaznacz cały

#!/bin/bash

server='http://www.metal-archives.com'

# ------------------------------------------------------------------------------
die(){
	echo -e "[Error] $*" >&2
	exit 1
}
# ------------------------------------------------------------------------------
set_band_url(){
	args=$*
	query="$server"/"search.php?string=${args/ /+}&type=band"
	url="$server"/$(curl "$query" | egrep 'band\.php\?id=[0-9]+' | cut -d\' -f4)
	[ "$url" == "$server/" ] && die "Nie znaleziono adresu dla grupy: $args"
	echo URL: $url
}
# ------------------------------------------------------------------------------
get_raw_page(){
	# Funkcja zwraca sformatowaną stronę
	curl "$url" |
		# formatuje kod tak, aby każdy tag był w osobnej linii
		sed -e 's/</\n</g ; s/>/>\n/g ' |
		# usuwam puste linie
		sed -e '/^[[:space:]]*$/d'
}
# ------------------------------------------------------------------------------
get_raw_album(){
	id=$1
	echo -e "$raw" |
		# wyłuskujemy blok z albumem
		sed -n -e '/release.php?id='$id'\x27>/,/\]/p'
		# \x27 to pojedynczy cudzysłów'
}
# ------------------------------------------------------------------------------
echo 'Szukam adresu grupy' $*
set_band_url $*

echo 'Pobieram stronę grupy' $*
raw=$(get_raw_page)

echo 'Wyłuskuje id albumów'
ids=$(echo -e "$raw" | sed -n 's:^.*release\.php?id=\([0-9]\+\).*$:\1:p')

for id in $ids ; do
	release="$server/release.php?id=$id"
	review="$server/review.php?id=$id"
	# blok inforacji o albumie
	raw_album=$(get_raw_album $id)
	# ten sam blok jako tablica, każda komórka to linia
	IFS=$'\n' tab_album=( $(echo -e "$raw_album") )
	IFS=$' \n\t' # reset IFS

	if echo -e "${tab_album[0]}" | grep -q class ; then
		# jeśli istnieje atrybut "class"
		class=$(echo -e "${tab_album[0]}" | sed -n 's:.*class=\x27\([a-z]*\)\x27.*:\1:p')
	fi
	title=${tab_album[1]}
	year=${tab_album[5]}

	echo "$title | $year | $class | $release"
done |
	# na koniec ładne formatowanie na podstawie '|'
	column -s'|' -t
jakby coś było nie jasne to pytaj
Awatar użytkownika
sauron
Sędziwy Jeż
Sędziwy Jeż
Posty: 45
Rejestracja: 13 cze 2009, 11:30
Płeć: Mężczyzna
Wersja Ubuntu: inny OS
Środowisko graficzne: GNOME
Architektura: x86_64

Odp: [BASH] Operacja na pliku HTML

Post autor: sauron »

Witam, skrypt od borzole jest bardzo ładny :)
Dziś go trochę modyfikowałem :)
Dodałem eksport do HTML'a, wczytywanie zespołów z pliku, podawanie nazwy dla nowego pliku (tego HTML) oraz wyświetlanie tylko albumów Full-length, Demo i EP :)

Oto i gotowy skrypt:

Kod: Zaznacz cały

#!/bin/bash

# ------------------------------------------------------------------------------
die(){
	echo -e "[Error] $*" >&2
	exit 1
}
# ------------------------------------------------------------------------------
set_band_url(){
	args=$*
	query="$server"/"search.php?string=${args/ /+}&type=band"
	url="$server"/$(curl "$query" | egrep 'band\.php\?id=[0-9]+' | cut -d\' -f4)
	[ "$url" == "$server/" ] && die "Nie znaleziono adresu dla grupy: $args"
	echo URL: $url
}
# ------------------------------------------------------------------------------
get_raw_page(){
	# Funkcja zwraca sformatowaną stronę
	curl "$url" |
		# formatuje kod tak, aby każdy tag był w osobnej linii
		sed -e 's/</\n</g ; s/>/>\n/g ' |
		# usuwam puste linie
		sed -e '/^[[:space:]]*$/d'
}
# ------------------------------------------------------------------------------
get_raw_album(){
	id=$1
	echo -e "$raw" |
		# wyłuskujemy blok z albumem
		sed -n -e '/release.php?id='$id'\x27>/,/\]/p'
		# \x27 to pojedynczy cudzysłów'
}
# ------------------------------------------------------------------------------
main_script(){
	server='http://www.metal-archives.com'
	
	echo 'Szukam adresu grupy' $*
	set_band_url $*

	echo 'Pobieram stronę grupy' $*
	raw=$(get_raw_page)

	echo 'Wyłuskuję id albumów'
	ids=$(echo -e "$raw" | sed -n 's:^.*release\.php?id=\([0-9]\+\).*$:\1:p')

	echo "<font size="6"><center><b>$*</b></center></font>" >> $FILE
	echo "<table border="6">" >> $FILE
	for id in $ids ; do
		release="$server/release.php?id=$id"
		review="$server/review.php?id=$id"
		# blok inforacji o albumie
		raw_album=$(get_raw_album $id)
		# ten sam blok jako tablica, każda komórka to linia
		IFS=$'\n' tab_album=( $(echo -e "$raw_album") )
		IFS=$' \n\t' # reset IFS

		if echo -e "${tab_album[0]}" | grep -q class ; then
			# jeśli istnieje atrybut "class"
			class=$(echo -e "${tab_album[0]}" | sed -n 's:.*class=\x27\([a-z]*\)\x27.*:\1:p')
		fi
		title=${tab_album[1]}
		year=${tab_album[5]}
	
		if [[ $year = Full-length,* ]]; then
		echo "<tr><td><b>$title</b></td><td>$year</td><td><a href="$release">$release</a></td></tr>" >> $FILE
		elif [[ $year = EP,* ]] ; then
		echo "<tr><td><i>$title</i></td><td>$year</td><td><a href="$release">$release</a></td></tr>" >> $FILE
		elif [[ $year = Demo,* ]]; then
		echo "<tr><td>$title</td><td>$year</td><td><a href="$release">$release</a></td></tr>" >> $FILE
		fi
	done
	echo "</table>" >>$FILE
}
# ------------------------------------------------------------------------------

	echo "Podaj nazwę dla nowego pliku: "
	read FILE
	echo -e "Podaj nazwę zespołu lub wpisz słowo \E[31mlista\E[30m: "
	read WYBOR
	
	if [ $WYBOR = lista ] ; then
		echo "Wpisz ścieżkę pliku listy: "
		read PATH_LISTA
		cd ~
		LICZBA=`cat $PATH_LISTA | wc -l`
		echo "<html>" > $FILE
		for (( x=1; $x <= $LICZBA; x++ )) ; do
		BAND=`sed ''$x'q;d' lista`
		main_script $BAND
		done
		echo "</html>" >> $FILE
	else
		cd ~
		echo "<html>" > $FILE
		main_script $WYBOR
		echo "</html>" >> $FILE
	fi

Chcę jeszcze dodać obsługę argumentów, aby móc określić wyświetlanie tylko jakiegoś typu albumów. Dla pojedynczych argumentów zrobiłbym to z case, a jak zrobić obsługę np. 2 argumentów? Jakie macie propozycje? Bo łopatologiczne pisanie wszystkich możliwych kombinacji chyba jest nie zbyt efektywne :P
Chętnie posłucham również jakiś sugestii co do samego skryptu, co jeszcze wymaga lub dobrze byłoby gdyby zostało zmodyfikowane.
Awatar użytkownika
borzole
Serdeczny Borsuk
Serdeczny Borsuk
Posty: 127
Rejestracja: 10 sie 2010, 11:48
Płeć: Mężczyzna
Wersja Ubuntu: inny OS
Środowisko graficzne: GNOME
Architektura: x86_64
Kontakt:

Odp: [BASH] Operacja na pliku HTML

Post autor: borzole »

* Robienie skryptów interaktywnych (oczekujące na podanie danych lub potwierdzenie itp.) nie jest dobrą praktyką w świecie administratorów *nixa. Skrypt powinien posiadać wszystkie dane w momencie wywołania (przekazane jako parametry).
Jeśli uruchamiasz skrypt dla jednego zespołu to wszystko jest ok, ale dla 5 to odpowiadanie na pytania skryptu będzie już wkurzające, a przy 50 stratą czasu.
Każdy skrypt/program konsolowy powinien być robiony tak, aby można było go włączyć jako cegiełkę do innego programu.
Dlatego właśnie wszelkie operacje typu:
read WYBOR
read PATH_LISTA
itd.
uważam za złą praktykę.

* obsługę argumentów możesz zrobić za pomocą http://www.google.pl/search?q=bash+getopts
albo tak:

Kod: Zaznacz cały

#!/bin/bash

VERSION=0.1

# ------------------------------------------------------------------------------
# Jeśli parametr jest pusty lub nie ustawiony => VERBOSE=false
: ${VERBOSE:=false}
# http://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion
# dzięki temu, można do skryptu przekazać parametr, wykonując w powłoce
# export VERBOSE=true
# przed wywołaniem skryptu
# albo wywołać skrypt w ten sposób
# VERBOSE=true ./skrypt.sh
# ------------------------------------------------------------------------------
usage(){
cat <<__EOF__
 ${0##*/} -- przykładowy skrypt

Użycie:

	VERBOSE=true $0
	$0 -d ./temp/
	$0 -v -d ./temp/
	$0 --verbose -d ./temp/

	export VERBOSE=true
	$0 -d ./temp/

__EOF__
}
# ------------------------------------------------------------------------------
args(){
	if [ $# == 0 ] ; then
		echo -e " Pomoc:\n\t ${0##*/} --help"
		exit 0
	fi
	# funkcja działa w pętli po wszystkich argumentach

	while [ $# != 0 ] ; do
		case "$1" in
			-d|--dir)
				# --dir /path/to/dir
				DIR=$2
				shift # podbijamy dodatkowo o 1 (/path/to/dir)
				# aby podbić o więcej wpisz:
				# shift 2
				;;
			-v|--verbose)
				VERBOSE=true
				;;
			-V|--version)
				echo $VERSION
				exit 0
				;;
			-h|--help)
				usage
				exit 0
				;;
			*)
				echo -e "[ERROR] nieznany argument: $1" >&2
				exit 1
				;;
		esac
		shift # opuść 1 argument z początku listy
	done
}
# ------------------------------------------------------------------------------
set_msg(){
	if [[ $VERBOSE == true ]] ; then
		msg(){ echo -e "$*" ; } # funkcja jest gadatliwa
	else
		msg(){ :  ; }           # pusta instrukcja
	fi
}
# ------------------------------------------------------------------------------
args $*
set_msg

msg Hello World

if [ ${#DIR} != 0 ] && [ -d "$DIR" ] ; then
	msg "Podałeś katalog:"
	echo dir: $DIR
	msg "Ścieżka absolutna do katalogu:"
	echo abs: $(readlink -f "$DIR")
fi

Awatar użytkownika
sauron
Sędziwy Jeż
Sędziwy Jeż
Posty: 45
Rejestracja: 13 cze 2009, 11:30
Płeć: Mężczyzna
Wersja Ubuntu: inny OS
Środowisko graficzne: GNOME
Architektura: x86_64

Odp: [BASH] Operacja na pliku HTML

Post autor: sauron »

Dzięki za wskazówki :)
Doczytam sobie o getopts dokładniej i postaram się może jutro zrobić skrypt nieinteraktywny, a wszystko żeby podać jako parametry.
A tak poza tym do większej ilości zespołów stworzyłem obsługę pliku z listą.
ODPOWIEDZ

Wróć do „Programowanie”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 33 gości