Bagaimana cara mendapatkan judul situs web menggunakan baris perintah?

50

Saya ingin program baris perintah yang mencetak judul situs web. Untuk misalnya:

Alan:~ titlefetcher http://www.youtube.com/watch?v=Dd7dQh8u4Hc

harus memberi:

Why Are Bad Words Bad? 

Anda memberikan url dan mencetak Judul.

Ufoguy
sumber
2
Ketika saya mengunduh judul itu, saya mendapatkan: "Mengapa Kata-Kata Buruk Buruk? - Youtube", apakah Anda ingin "- Youtube" juga terpotong?
slm

Jawaban:

44
wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' |
  perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si'

Anda dapat mengirimkannya ke GNU recodejika ada hal-hal seperti &lt;di dalamnya:

wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' |
  perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si' |
  recode html..

Untuk menghapus - youtubebagian:

wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' |
 perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)(?: - youtube)?\s*<\/title/si'

Untuk menunjukkan beberapa batasan:

portabilitas

Tidak ada perintah standar / portabel untuk melakukan permintaan HTTP. Beberapa dekade yang lalu, saya akan merekomendasikan di lynx -sourcesini. Tetapi saat ini, wgetlebih portabel karena dapat ditemukan secara default pada sebagian besar sistem GNU (termasuk sebagian besar sistem operasi desktop / laptop berbasis Linux). Yang cukup portabel lainnya termasuk GETperintah yang datang dengan perllibwww yang sering diinstal lynx -source,, dan pada tingkat lebih rendah curl. Lainnya umum yang meliputi links -source, elinks -source, w3m -dump_source, lftp -c cat...

Protokol HTTP dan penanganan pengalihan

wgetmungkin tidak mendapatkan halaman yang sama dengan yang misalnya firefoxakan ditampilkan. Alasannya adalah bahwa server HTTP dapat memilih untuk mengirim halaman yang berbeda berdasarkan informasi yang disediakan dalam permintaan yang dikirim oleh klien.

Permintaan yang dikirim oleh wget / w3m / GET ... akan berbeda dari yang dikirim oleh firefox. Jika itu masalah, Anda bisa mengubah wgetperilaku untuk mengubah cara mengirimkan permintaan melalui opsi.

Yang paling penting dalam hal ini adalah:

  • Acceptdan Accept-language: yang memberi tahu server tempat bahasa dan rangkaian karakter yang diinginkan klien untuk mendapatkan respons. wgettidak mengirim apa pun secara default sehingga server biasanya akan mengirim dengan pengaturan default. firefoxdi sisi lain kemungkinan dikonfigurasi untuk meminta bahasa Anda.
  • User-Agent: yang mengidentifikasi aplikasi klien ke server. Beberapa situs mengirim konten berbeda berdasarkan klien (meskipun itu sebagian besar untuk perbedaan antara interpretasi bahasa javascript) dan mungkin menolak untuk melayani Anda jika Anda menggunakan robot seperti agen pengguna wget.
  • Cookie: jika Anda pernah mengunjungi situs ini sebelumnya, browser Anda mungkin memiliki cookie permanen untuk itu. wgettidak akan.

wgetakan mengikuti pengalihan ketika mereka dilakukan di tingkat protokol HTTP, tetapi karena tidak melihat konten halaman, bukan yang dilakukan oleh javascript atau hal-hal seperti <meta http-equiv="refresh" content="0; url=http://example.com/">.

Kinerja / Efisiensi

Di sini, karena malas, kami telah perlmembaca seluruh konten dalam memori sebelum mulai mencari <title>tag. Mengingat bahwa judul ditemukan di <head>bagian yang ada di beberapa byte pertama file, itu tidak optimal. Pendekatan yang lebih baik, jika GNU awktersedia di sistem Anda bisa:

wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' |
  gawk -v IGNORECASE=1 -v RS='</title' 'RT{gsub(/.*<title[^>]*>/,"");print;exit}'

Dengan begitu, awk berhenti membaca setelah yang pertama </title, dan dengan keluar, menyebabkan wgetberhenti mengunduh.

Parsing HTML

Di sini, wgettulis halaman saat mengunduhnya. Pada saat yang sama perl,, slurps output ( -0777 -n) seluruh dalam memori dan kemudian cetak kode HTML yang ditemukan antara kejadian pertama <title...>dan </title.

Itu akan berfungsi untuk sebagian besar halaman HTML yang memiliki <title>tag, tetapi ada kasus di mana itu tidak akan berfungsi.

Sebaliknya , solusi coffeeMug akan mem-parsing halaman HTML sebagai XML dan mengembalikan nilai yang sesuai untuk title. Lebih tepat jika halaman dijamin XML yang valid . Namun, HTML tidak harus merupakan XML yang valid (versi bahasa yang lebih lama tidak), dan karena sebagian besar peramban di luar sana lunak dan akan menerima kode HTML yang salah, bahkan ada banyak kode HTML yang salah di luar sana.

Baik solusi saya dan coffeeMug akan gagal untuk berbagai kasus sudut, kadang-kadang sama, kadang tidak.

Misalnya, milik saya akan gagal pada:

<html><head foo="<title>"><title>blah</title></head></html>

atau:

<!-- <title>old</title> --><title>new</title>

Sementara itu akan gagal pada:

<TITLE>foo</TITLE>

(html valid, bukan xml) atau:

atau:

<title>...</title>
...
<script>a='<title>'; b='</title>';</script>

(lagi, valid html, <![CDATA[bagian yang hilang untuk menjadikannya XML yang valid).

<title>foo <<<bar>>> baz</title>

(html salah, tetapi masih ditemukan di sana dan didukung oleh sebagian besar browser)

interpretasi kode di dalam tag.

Solusi itu menampilkan teks mentah antara <title>dan </title>. Biasanya, seharusnya tidak ada tag HTML di sana, mungkin ada komentar (meskipun tidak ditangani oleh beberapa browser seperti firefox jadi sangat tidak mungkin). Mungkin masih ada beberapa penyandian HTML:

$ wget -qO- 'http://www.youtube.com/watch?v=CJDhmlMQT60' |
  perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si'
Wallace &amp; Gromit - The Cheesesnatcher Part 1 (claymation) - YouTube

Yang diurus oleh GNU recode:

$ wget -qO- 'http://www.youtube.com/watch?v=CJDhmlMQT60' |
  perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si' |
   recode html..
Wallace & Gromit - The Cheesesnatcher Part 1 (claymation) - YouTube

Tetapi klien web juga dimaksudkan untuk melakukan lebih banyak transformasi pada kode itu ketika menampilkan judul (seperti menyingkat beberapa bagian yang kosong, menghapus yang utama dan yang tertinggal). Namun tidak mungkin ada kebutuhan untuk itu. Jadi, seperti dalam kasus lain, terserah Anda memutuskan apakah itu sepadan dengan usaha.

Set karakter

Sebelum UTF-8, iso8859-1 digunakan untuk menjadi charset pilihan di web untuk karakter non-ASCII meskipun secara tegas mereka harus dituliskan sebagai &eacute;. Versi HTTP dan bahasa HTML yang lebih baru telah menambahkan kemungkinan untuk menentukan karakter yang ditetapkan dalam header HTTP atau dalam header HTML, dan klien dapat menentukan charset yang diterimanya. UTF-8 cenderung menjadi charset default saat ini.

Jadi, itu berarti bahwa di luar sana, Anda akan menemukan éditulis sebagai &eacute;, seperti &#233;, seperti UTF-8 é, (0xc3 0xa9), seperti iso-8859-1 (0xe9), dengan untuk 2 yang terakhir, kadang-kadang informasi di charset di header HTTP atau header HTML (dalam format berbeda), terkadang tidak.

wget hanya mendapatkan byte mentah, tidak peduli tentang artinya sebagai karakter, dan tidak memberi tahu server web tentang charset yang disukai.

recode html..akan berhati-hati untuk mengubah &eacute;atau &#233;menjadi urutan byte yang tepat untuk set karakter yang digunakan pada sistem Anda, tetapi untuk sisanya, itu lebih sulit.

Jika charset sistem Anda utf-8, kemungkinan itu akan baik-baik saja sebagian besar waktu karena cenderung menjadi charset default yang digunakan di luar sana saat ini.

$ wget -qO- 'http://www.youtube.com/watch?v=if82MGPJEEQ' |
 perl -l -0777 -ne 'print $1 if /<title.*?>\s*(.*?)\s*<\/title/si'
Noir Désir - L&#39;appartement - YouTube

Itu di éatas adalah UTF-8 é.

Tetapi jika Anda ingin menutupi untuk rangkaian karakter lain, sekali lagi, itu harus diurus.

Perlu juga dicatat bahwa solusi ini tidak akan berfungsi sama sekali untuk halaman yang disandikan UTF-16 atau UTF-32.

Untuk menyimpulkan

Idealnya, yang Anda butuhkan di sini, adalah browser web asli untuk memberi Anda informasi. Artinya, Anda perlu melakukan sesuatu untuk melakukan permintaan HTTP dengan parameter yang tepat, mengintepret respons HTTP dengan benar, sepenuhnya menafsirkan kode HTML seperti yang dilakukan browser, dan mengembalikan judul.

Karena saya tidak berpikir itu dapat dilakukan pada baris perintah dengan browser yang saya tahu (meskipun lihat sekarang trik ini denganlynx ), Anda harus menggunakan heuristik dan perkiraan, dan yang di atas sama baiknya dengan yang ada.

Anda mungkin juga ingin mempertimbangkan kinerja, keamanan ... Misalnya, untuk mencakup semua kasus (misalnya, halaman web yang memiliki beberapa javascript yang ditarik dari situs pihak ke-3 yang menetapkan judul atau mengalihkan ke halaman lain dalam suatu onload hook), Anda mungkin harus mengimplementasikan browser kehidupan nyata dengan mesin dom dan javascript yang mungkin harus melakukan ratusan pertanyaan untuk satu halaman HTML, beberapa di antaranya mencoba untuk mengeksploitasi kerentanan ...

Meskipun menggunakan regexps untuk mem-parsing HTML sering disukai , berikut ini adalah kasus khas di mana itu cukup baik untuk tugas (IMO).

Stéphane Chazelas
sumber
Apakah itu mengunduh gambar dari halaman juga? Apakah itu akan meninggalkan file html sampah?
Ufoguy
2
Anda mungkin ingin mengakhiri judul pada contoh pertama <karena judul tidak dijamin memiliki tag akhir dan tag lain apa pun harus memaksa terminasi tersebut. Anda mungkin juga ingin menghapus garis baru.
Brian Nickel
1
Tidak disarankan untuk menggunakan ekspresi reguler untuk mem-parsing HTML. Pernah. Bahkan dalam kasus ini. Itu kebiasaan buruk. Gunakan parser nyata sebagai gantinya. Ada jawaban Stackoverflow lucu terkenal tentang ini ...
Robin Green
4
@RobinGreen Posting itu tentang menggunakan regex untuk mengurai bahasa yang tidak biasa. Ada peringatan, tetapi ini adalah masalah yang mudah direduksi menjadi bahasa biasa. Saya sarankan menggunakan regex untuk mem-parsing HTML. Terkadang. Pada kasus ini.
Brian Nickel
2
Dan jumlah ekspresi reguler yang bekerja untuk hampir semuanya adalah sekitar 0.
Robin Green
27

Anda juga dapat mencoba hxselect(dari HTML-XML-Utils ) dengan wgetsebagai berikut:

wget -qO- 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' | hxselect -s '\n' -c  'title' 2>/dev/null

Anda dapat menginstal hxselectdi distro berbasis Debian menggunakan:
sudo apt-get install html-xml-utils.

Pengalihan STDERR adalah untuk menghindari Input is not well-formed. (Maybe try normalize?)pesan.

Untuk menyingkirkan "- YouTube", kirimkan output dari perintah di atas ke awk '{print substr($0, 0, length($0)-10)}'.

coffeMug
sumber
"hxselect" sepertinya tidak diinstal di Ubuntu secara default. Saya bahkan tidak dapat menemukannya di repositori yang ada. Bagaimana cara menginstalnya?
Ufoguy
7
sudo apt-get install html-xml-utils
coffeMug
Saya mendapatkan kesalahan ini di Ubuntu 12.10 "Input tidak terbentuk dengan baik. (Mungkin coba normalkan?)"
slm
1
Saya belum menemukan apa yang harus dilakukan dengan pesan. tentang menormalkan output. Tidak ada tombol seperti itu hxselect.
slm
1
Untuk Mac OS X, orang-orang Homebrew memiliki formula dengan hxselect di dalamnya. Instal dengan brew install html-xml-utils.
Sukima
18

Anda juga dapat menggunakan curldan grepmelakukan ini. Anda harus meminta penggunaan PCRE (Perl Compatible Regular Expressions) di grepuntuk mendapatkan tampilan belakang dan fasilitas tampilan depan sehingga kita dapat menemukan <title>...</title>tag.

Contoh

$ curl 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -so - | \
    grep -iPo '(?<=<title>)(.*)(?=</title>)'
Why Are Bad Words Bad? - YouTube

Detail

The curlswitch:

  • -s = diam
  • -o - = kirim output ke STDOUT

The grepswitch:

  • -i = tidak sensitif huruf
  • -o = Hanya kembalikan bagian yang cocok
  • -P = Mode PCRE

Pola untuk grep:

  • (?<=<title>) = cari string yang dimulai dengan ini di sebelah kiri itu
  • (?=</title>) = mencari string yang diakhiri dengan ini di sebelah kanannya
  • (.*)= semuanya di antaranya <title>..</title>.

Situasi yang lebih kompleks

Jika <title>...</titie>membentang beberapa baris, maka di atas tidak akan menemukannya. Anda dapat mengurangi situasi ini dengan menggunakan tr, untuk menghapus \nkarakter apa pun , yaitu tr -d '\n'.

Contoh

File sampel.

$ cat multi-line.html 
<html>
<title>
this is a \n title
</TITLE>
<body>
<p>this is a \n title</p>
</body>
</html>

Dan contoh dijalankan:

$ curl 'http://www.jake8us.org/~sam/multi-line.html' -so - | \
     tr -d '\n' | \
     grep -iPo '(?<=<title>)(.*)(?=</title>)'
this is a \n title

lang = ...

Jika <title>diset seperti ini, <title lang="en">maka Anda harus menghapus ini sebelum grepmemasukkannya. Alat sedini dapat digunakan untuk melakukan ini:

$ curl 'http://www.jake8us.org/~sam/multi-line.html' -so - | \
     tr -d '\n' | \
     sed 's/ lang="\w+"//gi' | \
     grep -iPo '(?<=<title>)(.*)(?=</title>)'
this is a \n title

Di atas menemukan string tidak peka huruf besar kecil lang=diikuti oleh urutan kata ( \w+). Ini kemudian dilucuti.

Parser HTML / XML nyata - menggunakan Ruby

Pada titik tertentu regex akan gagal dalam memecahkan masalah jenis ini. Jika itu terjadi maka Anda mungkin ingin menggunakan parser HTML / XML nyata. Salah satu pengurai seperti itu adalah Nokogiri . Ini tersedia di Ruby sebagai Permata dan dapat digunakan seperti:

$ curl 'http://www.jake8us.org/~sam/multi-line.html' -so - | \
    ruby -rnokogiri -e \
     'puts Nokogiri::HTML(readlines.join).xpath("//title").map { |e| e.content }'

this is a \n title

Di atas adalah parsing data yang datang melalui curlHTML ( Nokogiri::HTML). Metode ini xpathkemudian mencari node (tag) dalam HTML yang merupakan leaf node, ( //) dengan namanya title. Untuk setiap yang ditemukan, kami ingin mengembalikan kontennya ( e.content). The putskemudian mencetak mereka keluar.

Parser HTML / XML nyata - menggunakan Perl

Anda juga dapat melakukan sesuatu yang serupa dengan Perl dan modul HTML :: TreeBuilder :: XPath .

$ cat title_getter.pl
#!/usr/bin/perl

use HTML::TreeBuilder::XPath;

$tree = HTML::TreeBuilder::XPath->new_from_url($ARGV[0]); 
($title = $tree->findvalue('//title')) =~ s/^\s+//;
print $title . "\n";

Anda kemudian dapat menjalankan skrip ini seperti:

$ ./title_getter.pl http://www.jake8us.org/~sam/multi-line.html
this is a \n title 
slm
sumber
1
Solusi rapi! :)
coffeMug
3
Parsing HTML dengan ekspresi reguler tidak sesederhana itu. Tag yang ditulis sebagai “<TITLE>”, “<title lang = en>”, “<title \ n>” tidak akan cocok dengan ekspresi Anda. Masalah yang lebih besar, baik "<title> \ noops \ n </title>" akan menjadi.
manatwork
4
Mencoba mengurai html menggunakan regex cenderung disukai di sini.
user3490
1
@slm, <title>Unix\nLinux</title>seharusnya Unix Linux, bukan UnixLinux.
Stéphane Chazelas
1
+1 Untuk ruby ​​+ nokogiri. Saya telah menggunakannya untuk semua jenis pengikisan web, sungguh menakjubkan!
Rob
7

Menggunakan regex sederhana untuk mem-parsing HTML adalah naif. Misalnya dengan baris baru dan mengabaikan pengkodean karakter khusus yang ditentukan dalam file. Lakukan hal yang benar dan benar-benar mengurai halaman menggunakan salah satu parser nyata lainnya yang disebutkan dalam jawaban lain atau menggunakan liner berikut:

python -c "import bs4, urllib2; print bs4.BeautifulSoup(urllib2.urlopen('http://www.crummy.com/software/BeautifulSoup/bs4/doc/')).title.text"

(Di atas termasuk karakter Unicode).

BeautifulSoup juga menangani banyak HTML yang salah (mis. Tag penutup yang hilang), yang akan membuang regexing sederhana. Anda dapat menginstalnya dalam python standar menggunakan:

pip install beautifulsoup4

atau jika Anda tidak punya pip, dengan

easy_install beautifulsoup4

Beberapa sistem operasi seperti Debian / Ubuntu juga telah dikemas ( python-bs4paket pada Debian / Ubuntu).

Zelda
sumber
2
bs4tidak ada di pustaka standar python. Anda harus menginstalnya menggunakan easy_install beautfulsoup4(bukan easyinstall bs4).
Anthon
@Anthon menyertakan info Anda
Zelda
5

Mungkin itu "curang" tetapi satu opsi adalah pup, sebuah parser HTML baris perintah .

Berikut adalah dua cara untuk melakukannya:

Menggunakan metabidang dengan property="og:titleatribut

$ wget -q 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -O - | \
> pup 'meta[property=og:title] attr{content}'
Why Are Bad Words Bad?

dan cara lain menggunakan titlebidang secara langsung (dan kemudian memotong - YouTubestring di akhir).

$ wget -q 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc' -O - | \
> pup 'title text{}' | sed 's/ - YouTube$//'
Why Are Bad Words Bad?
abetusk
sumber
Untuk menghindari entitas karakter, pengguna mungkin ingin menggunakan --plainopsi pup .
puncak
3

Tampaknya dimungkinkan dengan lynxmenggunakan trik ini ( zsh, bashsintaks):

lynx -cfg=<(printf '%s\n' 'PRINTER:P:printf "%0s\\n" "$LYNX_PRINT_TITLE">&3:TRUE'
  ) lynx 3>&1 > /dev/null -nopause -noprint -accept_all_cookies -cmd_script <(
    printf '%s\n' "key p" "key Select key" "key ^J" exit
  ) 'http://www.youtube.com/watch?v=Dd7dQh8u4Hc'

Karena itu adalah peramban web kehidupan nyata, itu tidak menderita dari banyak keterbatasan yang saya sebutkan dalam jawaban saya yang lain .

Di sini, kami menggunakan fakta yang lynxmenetapkan $LYNX_PRINT_TITLEvariabel lingkungan ke judul halaman saat ini saat mencetak halaman.

Di atas, kami memberikan file konfigurasi (sebagai pipa) yang mendefinisikan "printer" lynx yang disebut Pyang hanya menampilkan konten variabel tersebut ke file descriptor 3(file deskriptor tersebut dialihkan ke lynxstdout dengan 3>&1sementara lynx stdout dengan sendirinya dialihkan ke / dev / null).

Kemudian kami menggunakan lynxfasilitas scripting untuk mensimulasikan pengguna menekan p, dan End(alias pilih), dan Enter( ^J).

-accept_all_cookies karena jika tidak, lynx akan meminta konfirmasi kepada pengguna untuk setiap cookie.

Stéphane Chazelas
sumber
3

Cara sederhana:

curl -s example.com | grep -o "<title>[^<]*" | tail -c+8

Beberapa alternatif:

curl -s example.com | grep -o "<title>[^<]*" | cut -d'>' -f2-
wget -qO- example.com | grep -o "<title>[^<]*" | sed -e 's/<[^>]*>//g'
kenorb
sumber
1
Ini adalah satu-satunya yang bekerja untuk saya!
Ahmad Awais
1

Saya menyukai gagasan Stéphane Chazelas untuk menggunakan Lynx dan LYNX_PRINT_TITLE, tetapi skrip itu tidak berfungsi untuk saya di bawah Ubuntu 14.04.5.

Saya telah membuat versi yang disederhanakan dengan menggunakan Lynx dan menggunakan file yang sudah dikonfigurasikan sebelumnya.

Tambahkan baris berikut ke /etc/lynx-cur/lynx.cfg (atau di mana pun lynx.cfg Anda berada):

PRINTER:P:printenv LYNX_PRINT_TITLE>/home/account/title.txt:TRUE:1000

Baris ini menginstruksikan untuk menyimpan judul, saat mencetak, ke "/home/account/title.txt" - Anda dapat memilih nama file yang Anda inginkan. Anda meminta SANGAT halaman yang besar, tambahkan nilai di atas dari "1000" ke sejumlah baris per halaman yang Anda inginkan, jika tidak Lynx akan membuat prompt tambahan "saat mencetak dokumen yang berisi halaman yang sangat besar".

Kemudian buat file /home/account/lynx-script.txt dengan konten berikut:

key p
key Select key
key ^J
exit

Kemudian jalankan Lynx menggunakan opsi baris perintah berikut:

lynx -term=vt100 -display_charset=utf-8 -nopause -noprint -accept_all_cookies -cmd_script=/home/account/lynx-script.txt "http://www.youtube.com/watch?v=Dd7dQh8u4Hc" >/dev/nul

Setelah menyelesaikan perintah ini, file /home/account/title.txt akan dibuat dengan judul halaman Anda.

Singkatnya, berikut adalah fungsi PHP yang mengembalikan judul halaman berdasarkan URL yang diberikan, atau false jika terjadi kesalahan.

function GetUrlTitle($url)
{
  $title_file_name = "/home/account/title.txt";
  if (file_exists($title_file_name)) unlink($title_file_name); // delete the file if exists
  $cmd = '/usr/bin/lynx -cfg=/etc/lynx-cur/lynx.cfg -term=vt100 -display_charset=utf-8 -nopause -noprint -accept_all_cookies -cmd_script=/home/account/lynx-script.txt "'.$url.'"';
  exec($cmd, $output, $retval);
  if (file_exists($title_file_name))
  {
    $title = file_get_contents($title_file_name);
    unlink($title_file_name); // delete the file after reading
    return $title;
  } else
  {
    return false;
  }
}

print GetUrlTitle("http://www.youtube.com/watch?v=Dd7dQh8u4Hc");
Maxim Masiutin
sumber
0

Menggunakan nokogiri, orang dapat menggunakan kueri berbasis CSS sederhana untuk mengekstrak teks bagian dalam tag:

 $ nokogiri -e 'puts $_.at_css("title").content'
 Why Are Bad Words Bad? - YouTube

Demikian pula, untuk mengekstrak nilai atribut "konten" dari tag:

$ nokogiri -e 'puts $_.at_css("meta[name=title]").attr("content")'
Why Are Bad Words Bad?
puncak
sumber