網路資訊擷取和分析是許多自動化任務的基礎。本文介紹如何使用 Shell 指令碼結合常見的命令列工具,例如 curl、awk 和 lynx,有效地抓取網頁資料並進行分析。從簡單的網頁內容擷取到解析 HTML 和 JSON 資料,這些技術能協助開發者快速取得所需資訊,並應用於各種自動化流程。文章中提供的指令碼範例涵蓋了 FTP 檔案下載、網頁連結提取、GitHub 使用者資訊擷取、ZIP 程式碼和電話區號查詢等實用場景,並針對每個指令碼進行了詳細的程式碼解析,方便讀者理解和應用。此外,文章也提出了指令碼改進方向,例如錯誤處理和功能擴充套件,鼓勵讀者進一步探索和最佳化這些技術。
網際網路與網頁使用者
Unix 在網際網路領域的表現非常出色。無論您想從桌下執行一個快速的伺服器,還是隻是想聰明高效地瀏覽網頁,對於網際網路互動,幾乎沒有什麼是不能嵌入 shell 指令碼的。
網際網路工具是可以被指令碼化的,即使您從未以這種方式思考過它們。例如,FTP(檔案傳輸協定)程式可以透過一些非常有趣的方式進行指令碼化,正如在第 174 頁的指令碼 #53 中所探討的那樣。Shell 指令碼通常可以提高大多數與網際網路某個方面相關的命令列工具的效能和輸出。
本文的第一版向讀者保證,網際網路指令碼編寫者工具箱中最好的工具是 lynx;現在我們建議使用 curl。兩個工具都提供了導向文字的網頁介面,但 lynx 試圖提供類別似瀏覽器的體驗,而 curl 是專門為指令碼設計的,可以輸出您想要檢查的任何頁面的原始 HTML 原始碼。
使用 curl 抓取網頁內容
例如,以下命令顯示了 Dave on Film 首頁原始碼的前七行,這得益於 curl:
$ curl -s http://www.daveonfilm.com/ | head -7
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<link rel="profile" href="http://gmpg.org/xfn/11" />
<link rel="pingback" href="http://www.daveonfilm.com/xmlrpc.php" />
<title>Dave On Film: Smart Movie Reviews from Dave Taylor</title>
內容解密:
curl -s http://www.daveonfilm.com/:使用curl命令靜默地(-s引數)抓取指定網址的內容。| head -7:將curl的輸出透過管道傳遞給head命令,顯示前七行內容。- 該命令用於檢查網站的 HTML 結構,可以用於網頁抓取或分析網站內容。
如果 curl 不可用,您可以使用 lynx 獲得相同的結果,但如果您同時擁有這兩個工具,我們建議使用 curl。這就是我們在本章中將要使用的工具。
網站抓取指令碼的限制
警告:本章中的網站抓取指令碼的一個限制是,如果指令碼所依賴的網站自本文編寫以來更改了其佈局或 API,那麼指令碼可能會被破壞。但是,如果您可以讀懂 HTML 或 JSON(即使您不完全理解它),您應該能夠修復這些指令碼。追蹤其他網站的問題正是可擴充套件標記語言(XML)被創造出來的緣由:它允許網站開發人員將網頁內容與其佈局規則分開提供。
使用 FTP 下載檔案
網際網路最初的殺手級應用之一是檔案傳輸,而最簡單的解決方案之一是 FTP(檔案傳輸協定)。從根本上講,所有網際網路互動都是根據檔案傳輸,無論是網頁瀏覽器請求 HTML 檔案及其附帶的影像檔案,聊天伺服器來回中繼討論行,或是電子郵件訊息從地球的一端傳送到另一端。
原始的 FTP 程式仍然存在,儘管其介面很粗糙,但該程式功能強大,值得利用。有很多較新的 FTP 程式,例如 FileZilla(http://filezilla-project.org/)和 NcFTP(http://www.ncftp.org/),以及許多不錯的圖形介面,可以使 FTP 更易於使用。不過,在一些 shell 指令碼封裝器的幫助下,FTP 可以很好地用於上傳和下載檔案。
ftpget 指令碼
#!/bin/bash
# ftpget--給定一個 ftp 風格的 URL,將其解開並嘗試使用匿名 ftp 取得檔案
anonpass="$LOGNAME@$(hostname)"
if [ $# -ne 1 ] ; then
echo "用法: $0 ftp://..." >&2
exit 1
fi
# 典型的 URL:ftp://ftp.ncftp.com/unixstuff/q2getty.tar.gz
if [ "$(echo $1 | cut -c1-6)" != "ftp://" ] ; then
echo "$0: URL 格式錯誤,需要以 ftp:// 開頭" >&2
exit 1
fi
server="$(echo $1 | cut -d/ -f3)"
filename="$(echo $1 | cut -d/ -f4-)"
basefile="$(basename $filename)"
echo ${0}: 從伺服器 $server 下載 $basefile
ftp -np << EOF
open $server
user ftp $anonpass
get "$filename" "$basefile"
quit
EOF
if [ $? -eq 0 ] ; then
ls -l $basefile
fi
exit 0
內容解密:
anonpass="$LOGNAME@$(hostname)":設定匿名 FTP 登入的密碼,通常是使用者的電子郵件地址。- 該指令碼檢查輸入引數是否正確,並解析 FTP URL 以取得伺服器地址和檔案名稱。
- 使用
ftp -np命令開啟與 FTP 伺服器的連線,並執行一系列指令來下載指定的檔案。 if [ $? -eq 0 ]:檢查最後一個命令的離開狀態,如果成功(0),則列出下載檔案的詳細資訊。
執行指令碼
該指令碼使用簡單,只需完全指定一個 FTP URL,它就會將檔案下載到當前工作目錄,如以下範例所示:
$ ftpget ftp://ftp.ncftp.com/unixstuff/q2getty.tar.gz
ftpget: 從伺服器 ftp.ncftp.com 下載 q2getty.tar.gz
-rw-r--r-- 1 taylor staff 4817 Aug 14 1998 q2getty.tar.gz
內容解密:
$ ftpget ftp://ftp.ncftp.com/unixstuff/q2getty.tar.gz:執行ftpget指令碼以下載指定的檔案。- 輸出顯示了下載的檔案名稱和檔案大小等資訊。
某些版本的 FTP 可能比其他版本更冗長,您可能會看到一些看似嚇人的錯誤訊息,但通常可以安全地忽略它們。如果您的 FTP 輸出過於冗長,可以透過在指令碼中的 FTP 呼叫中新增 -V 標誌來減少輸出。
從網頁中提取URL
在網路爬蟲的世界中,從特定網頁提取URL是一項基本且重要的任務。本篇文章將介紹如何使用shell指令碼和lynx工具來實作這一功能。
為什麼選擇lynx?
雖然現在很多教程推薦使用curl來抓取網頁內容,但當涉及到解析HTML並提取連結時,lynx顯得更加簡單易用。lynx是一個文字模式的網頁瀏覽器,它能夠自動解析HTML並以結構化的方式顯示網頁內容,包括其中的超連結。
安裝lynx
如果你的系統中尚未安裝lynx,可以透過套件管理器進行安裝。例如,在Red Hat系統上使用yum,在Debian系統上使用apt,在OS X上使用brew。你也可以直接從http://lynx.browser.org/下載原始碼自行編譯或取得預編譯的二進位制檔案。
getlinks指令碼詳解
以下是一個名為getlinks的bash指令碼,用於從給定的URL中提取相對和絕對連結。
#!/bin/bash
# getlinks--Given a URL, returns all of its relative and absolute links.
# Has three options: -d to generate the primary domains of every link,
# -i to list just those links that are internal to the site (that is,
# other pages on the same site), and -x to produce external links only
# (the opposite of -i).
if [ $# -eq 0 ] ; then
echo "Usage: $0 [-d|-i|-x] url" >&2
echo "-d=domains only, -i=internal refs only, -x=external only" >&2
exit 1
fi
if [ $# -gt 1 ] ; then
case "$1" in
-d) lastcmd="cut -d/ -f3|sort|uniq"
shift
;;
-i) basedomain="http://$(echo $2 | cut -d/ -f3)/"
lastcmd="grep \"^$basedomain\"|sed \"s|$basedomain||g\"|sort|uniq"
shift
;;
-x) basedomain="http://$(echo $2 | cut -d/ -f3)/"
lastcmd="grep -v \"^$basedomain\"|sort|uniq"
shift
;;
*) echo "$0: unknown option specified: $1" >&2
exit 1
esac
else
lastcmd="sort|uniq"
fi
lynx -dump "$1"|\
sed -n '/^References$/,$p'|\
grep -E '[[:digit:]]+\.'|\
awk '{print $2}'|\
cut -d\? -f1|\
eval $lastcmd
exit 0
內容解密:
- 引數檢查:首先檢查指令碼是否帶有引數,如果沒有,則輸出用法並離開。
- 選項處理:根據提供的選項(
-d,-i,-x),設定不同的處理命令儲存在lastcmd變數中,用於後續處理提取出的連結。-d:僅提取主網域名稱。-i:僅列出內部連結(即同一網站上的其他頁面)。-x:僅產生外部連結(與-i相反)。
lynx命令:使用lynx -dump "$1"來抓取指定URL的內容,並輸出到標準輸出。sed命令:透過sed -n '/^References$/,$p'篩選出"References"之後的內容,即網頁中的連結列表。grep,awk,cut命令組合:進一步處理連結列表,提取出實際的URL,去掉不必要的部分,如查詢引數。eval $lastcmd:執行之前根據選項設定的命令,對最終的連結列表進行排序、去重等處理。
使用範例
- 取得指定網頁的所有連結:
$ getlinks http://www.daveonfilm.com/ | head -10
- 取得指定網頁的所有主網域名稱:
$ getlinks -d http://www.amazon.com/ | head -10
網頁資料擷取與分析工具
本章節將介紹如何使用 Bash 指令碼結合 curl 和 awk 來擷取和分析網頁資料。我們將透過三個不同的指令碼範例,展示如何取得 GitHub 使用者資訊、查詢 ZIP 程式碼對應的城市和州,以及查詢區號對應的地區。
指令碼 #55:取得 GitHub 使用者資訊
程式碼
#!/bin/bash
# githubuser--Given a GitHub username, pulls information about the user
if [ $# -ne 1 ]; then
echo "Usage: $0 <username>"
exit 1
fi
curl -s "https://api.github.com/users/$1" | \
awk -F'"' '
/\"name\":/ {
print $4" 是 GitHub 使用者的名稱。"
}
/\"followers\":/{
split($3, a, " ")
sub(/,/, "", a[2])
print "他們有 "a[2]" 位關注者。"
}
/\"following\":/{
split($3, a, " ")
sub(/,/, "", a[2])
print "他們正在關注 "a[2]" 位其他使用者。"
}
/\"created_at\":/{
print "他們的帳戶建立於 "$4"。。"
}
'
exit 0
#### 內容解密:
- 引數檢查:指令碼首先檢查是否提供了正確數量的引數(即一個使用者名稱)。如果沒有,則顯示用法訊息並離開。
curl指令:使用curl以靜默模式擷取指定 GitHub 使用者的資訊。-s選項抑制了進度條和其他不必要的輸出。awk處理:將curl的輸出(JSON 格式)傳遞給awk,並設定欄位分隔符為雙引號(")。這樣可以簡化 JSON 資料的解析。- 正規表示式匹配:使用正規表示式匹配 JSON 中的特定欄位,例如
name、followers、following和created_at,並列印出相關資訊。
指令碼 #56:ZIP 程式碼查詢
程式碼
#!/bin/bash
# zipcode--Given a ZIP code, identifies the city and state. Use city-data.com,
# which has every ZIP code configured as its own web page.
baseURL="http://www.city-data.com/zips"
/bin/echo -n "ZIP 程式碼 $1 位於 "
curl -s -dump "$baseURL/$1.html" | \
grep -i '<title>' | \
cut -d\( -f2 | cut -d\) -f1
exit 0
#### 內容解密:
- URL 組合:根據輸入的 ZIP 程式碼動態生成對應的網頁 URL。
curl擷取網頁內容:使用curl以靜默模式擷取生成的 URL 對應的網頁內容。grep和cut處理:使用grep找到<title>標籤內的內容,然後使用cut提取括號內的城市和州資訊。
指令碼改進方向
- 錯誤處理:改進錯誤處理機制,例如當輸入無效的 ZIP 程式碼或 GitHub 使用者名稱時,提供更有用的錯誤訊息。
- 擴充套件功能:為指令碼新增更多功能,例如顯示更多關於地區的資訊(如人口、土地面積等)。
電話區號查詢與天氣資訊擷取的 Shell 指令碼實踐
前言
在現代生活中,查詢電話區號與取得即時天氣資訊是非常實用的功能。本文將介紹如何使用 Shell 指令碼實作這兩個功能,分別對應 areacode 與 weather 指令碼,並深入分析其實作原理與改進方法。
areacode 指令碼詳解
程式碼解析
#!/bin/bash
# areacode--根據三位數的美國電話區號,識別城市和州
source="http://www.bennetyee.org/ucsd-pages/area.html"
if [ -z "$1" ] ; then
echo "用法:areacode <三位數的美國電話區號>"
exit 1
fi
# 檢查輸入是否為三位數字
if [ "$(echo $1 | wc -c)" -ne 4 ] ; then
echo "areacode:錯誤的長度:僅適用於三位數的美國區號"
exit 1
fi
# 檢查輸入是否全部為數字
if [ ! -z "$(echo $1 | sed 's/[[:digit:]]//g')" ] ; then
echo "areacode:非數字:區號只能由數字組成"
exit 1
fi
# 查詢區號對應的城市和州
result="$(curl -s -dump $source | grep "name=\"$1" | \
sed 's/<[^>]*>//g;s/^ //g' | \
cut -f2- -d\ | cut -f1 -d\( )"
echo "區號 $1 = $result"
exit 0
#### 內容解密:
- 輸入驗證:首先檢查是否提供了輸入引數,接著驗證引數長度是否為三位數字,最後確認輸入是否全部為數字。
- 資料擷取:使用
curl命令從指定網址下載資料,並透過grep、sed和cut命令組合,提取出區號對應的城市和州資訊。 - 結果輸出:將查詢結果以易讀的方式輸出給使用者。
weather 指令碼詳解
程式碼解析
#!/bin/bash
# weather--使用 Wunderground API 取得指定 ZIP 碼的天氣資訊
if [ $# -ne 1 ]; then
echo "用法:$0 <郵政編碼>"
exit 1
fi
apikey="your_api_key_here" # 需要替換為自己的 API 金鑰
weather=`curl -s \
"https://api.wunderground.com/api/$apikey/conditions/q/$1.xml"`
state=`xmllint --xpath \
//response/current_observation/display_location/full/text\(\) \
<(echo $weather)`
zip=`xmllint --xpath \
//response/current_observation/display_location/zip/text\(\) \
<(echo $weather)`
current=`xmllint --xpath \
//response/current_observation/temp_f/text\(\) \
<(echo $weather)`
condition=`xmllint --xpath \
//response/current_observation/weather/text\(\) \
<(echo $weather)`
echo $state" ("$zip") : 目前溫度 "$current"F 和 "$condition" 天氣。"
exit 0
#### 內容解密:
- API 金鑰驗證:使用個人申請的 Wunderground API 金鑰進行驗證。
- 天氣資料擷取:透過
curl命令呼叫 Wunderground API,並使用xmllint解析傳回的 XML 資料,提取出所需的天氣資訊。 - 結果格式化輸出:將目前溫度、天氣狀況等資訊以友好的格式輸出給使用者。
改進與擴充套件
- areacode 改進:可增加反向查詢功能,根據城市或州查詢對應的區號。
- weather 改進:增加錯誤處理機制,例如處理無效的郵政編碼或 API 請求失敗的情況。同時,可擴充套件支援不同地區的天氣查詢。