網路機器人、網路蜘蛛與網路爬蟲 PHP/CURL程式設計指南(第二版)


Michael Schrenk 著 / 藍子軒 譯

Part I 基本概念與技術

Chapter 1. 網路機器人是什麼?能吃嗎?

  • 解放網際網路真正的淺力
  • 對程式開發者的用處:
    有趣、讓駭客行為變得有建設性
  • 對企業領導者的用處:
    客製化應用、小投資大收穫

Chapter 2. 關於網路機器人的一些構想

  • 從瀏覽器的限制中獲取靈感:
  1. TrackRates.com : 彙整篩選飯店房價
  2. WebSiteOptimization.com : 分析網站效能
  3. Pokerbot : 撲克牌機器人,模仿行為

Chapter 3. 下載網頁

  • 把它想成是檔案,而不是網頁
  • 利用PHP的內建函式來下載檔案

1. 使用fopen() + fgets()

<?php
$target = "http://www.webbotsspidersscreenscrapers.com/hello_world.html";
$file_handle = fopen($target, "r");

while(!feof($file_handle))
    echo fgets($file_handle, 4096);
fclose($file_handle);
?>

2. 使用file()

<?php
$target = "http://www.webbotsspidersscreenscrapers.com/hello_world.html";
$downloaded_page_array = file($target);

for($xx=0; $xx<cout($downloaded_page_array); $xx++)
    echo $downloaded_page_array[$xx]
?>
  • PHP/CURL簡介
    名稱由來:client+URL
  1. 支援多種傳輸協定
  2. 表單提交
  3. 基本身份認證
  4. Cookie
  5. 重導向
  6. 假冒代理程式的名稱
  7. 引用參照的管理
  8. Socket管理
  • 安裝PHP/CURL
  • LIB_http

Chapter 4. 基本解析技術

  • 資料就藏在一堆標籤中
  • 如果HTML寫得很爛,該如何解析?
    >使用HTMLTidy函式庫進行預處理
  • 標準解析常函式
  • 使用LIB_parse
  • 幾個有用的PHP函式
  1. stristr() : 測試字串是否包含在字串,在乾草堆(haystack,未解析的文字)中找縫衣針(needle,子字串),strstr()分大小寫
  2. str_replace() : 字串取代
  3. strip_tags() : 去除HTML格式
  4. similar_test() : 比較相似程度
  • 最終的想法
  1. 千萬不要信任一個原始碼寫得很差的網頁
  2. 解析時盡量區分成幾個小步驟
  3. 就算是除錯,也別用瀏覽器來展現解析結果
  4. 使用正規表示法時要特別謹慎

Chapter 5. 運用正規表達式的高級解析技術

  • 正規表示法的關鍵-模式匹配
  • PHP的正規表示法有兩種:
  1. PCRE(Perl相容正規表示法,以preg_開頭)
  2. POSIX(延伸正規表示法,以ereg開頭,PHP 5.3.0以後棄用)

PHP正規表示法函式:

preg_replace(pattern, replacement, subject)
preg_match(pattern, subject)
preg_match_all(pattern, subject, result_array)
preg_split(pattern, subject)
  • 透過範例學習模式
    \d: 數字
    \d+: 1個以上的數字
    \b: 邊界
    \D: 非數字字元
    \b\D\D\D\b or \b\D{3}\b: 匹配連續3個字母
    .: wildcard

  • 正規表示法中,對網路機器人開發者特別有趣的部分

  • 哪些情況適合(或不適合)使用正規表示法作為解析工具?
    正規表示法的缺點:不擅長表達資料間的關聯性、正規表示法提供太多選擇、正規表示法比較難以除錯、正規表示法會讓你的程式碼變複雜

Chapter 6. 表單提交的自動化

  • 對表單介面進行逆向工程

  • 表單處理器、資料欄位、傳遞方法、事件觸發

    1
    2
    3
    4
    <form name="frml" action="form_handler"(表單處理器) method="get"(傳遞方法)>
    <input type="textbox" name="email">(資料欄位)
    <input type="submit">(事件觸發)
    </form>

    表單處理器(Form Handler)
    action屬性,不指定的話則與處理表單為同一個頁面。
    資料欄位(Data Field)
    最重要的是名稱(name)與值(value)。
    傳遞方法(Method)
    GET:250字元長度左右、資料無法加密
    POST:好幾MB、資料可以加密
    Multipart Encoding:POST方法的延伸,可以傳送local檔案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <form name="frm1" method="POST" enctype="multipart/form-data" action="...">
    </form>

    //script版本:
    $post = array("uploadedfile" => "@".$full_path_name_of_file);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $form_action_URL);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    $response = curl_exec($ch);

    事件觸發(Event Trigger)

  • 無法預測的表單

  1. JavaScript:有可能改變表單
  2. 機器生成HTML的可讀性
  3. Cookie的存在
  • 表單的分析
    如果包含session id就需要先下載網頁進行解析

Chapter 7. 管理大量的資料

  • 資料的組織化
    根據資料特性選擇儲存結構
    命名慣例
  1. 切實執行
  2. 使用物件的類型命名而非本身的名稱
  3. 考慮使用此結構的對象(人?電腦?)
  4. 定義命名格式
  5. 對標籤使用同一詞性
  6. 命名考慮排序方便性
    把資料儲存在結構化的檔案目錄之中
    將文字儲存到資料庫中
    1
    2
    3
    4
    //LIB_mysql
    insert()
    update()
    exe_sql()
    將圖像儲存到資料庫中
    將圖檔轉成blob
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 透過Base-64編碼
    $data_array['IMAGE'] = base64_encode(file_get_contents($file_path));

    // 顯示資料庫內圖檔
    <img src="show_image.php?img_id=6">

    // show_img.php
    <?
    include("LIB_mysql.php");
    $image_id = $_GET['img_id'];
    $sql = "select IMAGE from table where IMAGE_ID='".$image_id."'";
    list($img) = exe_sql(DATABASE, $sql);

    header("Content-type: image/jpeg");
    echo base64_decode($img);
    exit;
    ?>
  • 把資料變小一點
  1. 儲存指向資料的參照
  2. 壓縮資料
    對傳進來的檔案進行壓縮:
    1
    2
    3
    4
    5
    6
    // 送出可接受壓縮檔的請求
    $header[] = "Accept-Encoding:compress, gzip";
    curl_setopt($curl_session, CURLOPT_HTTPHEADER, $header);

    // 對壓縮檔進行解壓縮
    $uncompressed_file = gzuncompress($compressed_file);
  3. 移除不需要的格式
    使用strip_tags()移除HTML標籤
  4. 為大型圖像檔案建立縮圖,或建立比較小的呈現方式
  • 運用縮圖
    1
    2
    3
    4
    5
    6
    //利用LIB_thumbnail函式庫  
    $org_file = "test.jpg";
    $new_file_name = "thumbnail.jpg";
    $max_width = 90;
    $max_height = 90;
    create_thumbnail($org_file, $new_file_name, $max_width, $max_height);

Part II 案例分享

Chapter 8. 價格監視用網路機器人

  • 設計一個能解析語意的script腳本
  • 目標網頁的初始化與下載
    ref: chapter8_price_monitoring_bot.php

Chapter 9. 圖像擷取用網路機器人

  • 圖像擷取用網路機器人範例
    相同檔案名稱、不同目錄
  • 建立圖像擷取用網路機器人
    ref: chapter9_image_capture_bot.php

Chapter 10. 鏈結查證用網路機器人

無效鏈結的偵測(Link-Verification)

  • 建立一個鏈結查證用網路機器人
HTTP Code 類別 意義
100-199 訊息相關 一般未使用
200-299 成功 請求成功
300-399 重導向 頁面被移走了
400-499 客戶端錯誤 客戶端發出錯誤請求
500-599 伺服器錯誤 伺服器錯誤
1
2
3
// $status_code_array於LIB_http_codes同時建立
$status_code_array[$download_link['STATUS']['http_code']] // 狀態碼轉文字
$download_link['STATUS']['http_code'] // 下載頁面花費時間
  • 網路機器人的運行
    1
    2
    3
    4
    5
    6
    // LIB_http_codes
    include(LIB_http_codes.php)
    echo $status_code_array[$YOUR_HEEP_CODE]['MSG'];

    //LIB_resolve_address
    resolve_address($link, $page_base);

Chapter 11. 搜尋排名用網路機器人

  • 對一個搜尋結果頁面進行描述
    付費區域/組織過的結果(SEO結果)
    “下一頁”的位置
  • 搜尋排名用網路機器人的工作方式
    輸入”關鍵字”與”要找的頁面”,尋找排名結果:頁面.筆數
  • 搜尋排名用網路機器人的執行
  • 搜尋排名用網路機器人的工作原理
  1. 變數初始化
  2. 使用”關鍵字”於搜尋引擎進行搜索
  3. 跳過網告與導覽文字
  4. 檢查搜尋頁面是否出現於結果中
  5. 報告結果
  • 搜尋排名用網路機器人的script腳本
    ref: chapter11_search_ranking_bot.php
    插入解析(insertion parse):於資料內部插入自定義的tag(如<data>)
  • 最終想法
    善待你的資料來源
    搜尋網站對待網路機器人時,可能與瀏覽器不同
    讓網路蜘蛛搜刮搜尋引擎的資料,可不是好主意
  • 進一步的探索

Chapter 12. 彙整用網路機器人

範例應用:Google Map結合房地產資訊,顯示彙整結果頁面

  • 為網路機器人選擇資料的來源
    API或RSS
  • 彙整用網路機器人的範例
    RSS饋送資料的方式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // RSS feed
    <title>
    RSS Feed Title
    </title>
    <link>
    www.Link_to_web_page.com
    </link>
    <description>
    Description of RSS feed
    </description>
    <copyright>
    Copyright notice
    </copyright>
    <lastBuildDate>
    Date of RSS publication
    </lastBuildDate>

    // RSS Item
    <item>
    <title> Title of Item </title>
    <link> URL of associated web page for item </link>
    <description> Description of item </description>
    <pubDate> Publication date of item </pubDate>
    </item>

ref: chapter12_aggregation_bot.php
CDATA: XML用CDATA標籤來識別不應該被解釋為XML的標籤
<![[…文字…]]>

  • 在你的彙整用網路機器人中加入篩選的功能
  • 進一步的探索

Chapter 13. FTP網路機器人

  • FTP網路機器人範例
    ref: chapter13_ftp_bot.php
  • PHP與FTP
    PHP支援的一般FTP指令
FTP函式($ftp:FTP File Stream) 用法
ftp_cdup($ftp) 使附目錄變成目前的目錄
ftp_chdir($ftp, “directory/path”) 改變目前的目錄
ftp_delete($ftp, “filename”) 刪除某個檔案
ftp_get($ftp, “local file”, “remote file”, MODE) 將遠端的檔案拷貝到本機檔案,MODE: FTP_ASCII/FTP_BINARY
ftp_mkdir($ftp, “directory name”) 建立一個新的目錄
ftp_rename($ftp, “file name”) 在FTP伺服器上更改檔案或目錄名稱
ftp_put($ftp, “remote file”, “local file”, MODE) 將本機的檔案拷貝至遠端的檔案,MODE: FTP_ASCII/FTP_BINARY
ftp_rmdir($ftp, “directory/path”) 移除一個目錄
ftp_rawlist($ftp, “directory/path”) 送回一個陣列,元素為檔案訊息
  • 進一步的探索

Chapter 14. 能讀取電子郵件的網路機器人

  • POP3協定
    利用telnet登入POP3郵件伺服器
    1
    telnet mail.server.net 110
    讀取郵件:
  1. LIST指令取得編號
  2. RETR <編號>:讀取內容
    刪除郵件:
  3. DELE <編號>
  4. QUIT //執行後才真正刪除
    RSET <編號>:QUIT前可還原刪除動作
  • 利用網路機器人來執行POP3協定
    利用PHP的opensocket()、fputs()和fgets()執行POP3指令
    ref. chapter14_email_reading_bot.php

  • 進一步的探索
    不相容系統之間的介面:發送檔案至mailbox讓機器人讀取後處理

Chapter 15. 能發送電子郵件的網路機器人

  • 電子郵件、網路機器人與垃圾郵件
    避免成為垃圾郵件指導原則:
  1. 允許收件人取消訂閱
  2. 避免多個重複的郵件
  3. 使用恰當的標題
  4. 表明你自己的身份
  5. 遵守法律規定
  • 利用SMTP與PHP來發送郵件
    編輯php.ini配置檔
    關閉郵件伺服器的中繼傳輸(relay host)功能,避免成為垃圾郵件跳板
    PHP的mail()函式
    1
    2
    3
    4
    $email_address = "account@someserver.com";
    $email_subject = "SUBJECT";
    $email_message = "MESSAGE";
    mail($email_address, $email_subject, $email_message);
    利用LIB_mail發送格式化的郵件
LIB_mail地址種類 功能 必要或選擇
To 主要收件人地址 必要
Reply-to 回覆地址 選擇
Return-path 無法傳送時,通知地址 選擇
From 寄件人地址 必要
Cc 其他收件人 選擇
Bcc 密件副本 選擇
  • 編寫一個能發送電子郵件通知的網路機器人
    網頁內容變化時,發送電子郵件的網路機器人:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    include("LIB_http.php");
    include("LIB_mysql.php");
    include("LIB_mail.php");

    $webbot_email_address = "mail@server.com";
    $notification_email_address = "mail@server.com";
    $target_web_site = "www.website.com";

    $download_array = http_get($target_web_site, $ref="");
    $web_page = $download_array['FILE'];

    $new_signature = sha1($web_page);

    $sql = "select SIGNATURE from signatures where WEB_PAGE='".$target_web_site"'";
    list($old_signature) = exe_sql(DATABASE, $sql);

    if($new_signature != $old_signature){
    if(isset($data_array)) unset($data_signature);
    $data_array['SIGNATURE'] = $new_signature;
    update(DATABASE, $table="signatures", $data_array, $key_column="WEB_PAGE", $id=$target_web_site);

    $subject = $target_web_site."內容改變";
    $message = $subject."\n";
    ...
    $address['from'] = $webbot_email_address;
    $address['replayto'] = $webbot_email_address;
    $address['to'] = $notification_email_address;
    formatted_mail($subject, $message, $address, $content_type="text/plain");
    }
    別讓垃圾郵件篩選程式,篩選掉合法郵件
    確保from與reply-to是相同網域
    發送HTML格式化的電子郵件
  1. 利用formatted_mail()函式
  2. 設定$content_type為text/html
  3. 使用完整形式的URL
  4. 使用標準格式技術(如:table)
  5. 避免使用CSS
  6. 將訊息建立成一個字串
  • 進一步的探索

Chapter 16. 將某個網站功能轉換成函式

  • 撰寫一個函式的介面
    提交郵遞區號,回傳詳細資訊
    ref. chapter16_website_to_function_bot.php
  • 最終的想法
  1. 分配資源:利用多台伺服器的資源
  2. 使用標準介面:跨平台的能力
  3. 設計一個自訂的輕量級”網路服務”

Part III 進階技術的相關考量

Chapter 17. 網路蜘蛛

網路蜘蛛: Spider, web spider, crawlers, web walker

  • 網路蜘蛛的工作原理
    seed URL 抓取第一個頁面(第一滲透層the first penetration level),重複抓取直到最大滲透層

  • 網路蜘蛛的範例
    ref. chapter17_simple_spider.php

  • LIB_simple_spider
    harvest_links($url):下載指定的網頁,回傳link array
    archive_links($spider_array, $penetration_level, $temp_link_array):標記level與排除exclusion_list中的url
    get_domain($url):抓取根網域
    exclude_link($spider_array, $link):排除不需要的鏈結

  • 利用網路蜘蛛進行一些實驗
    三個警告:

  1. $FETCH_DELAY至少設定1~2秒以避免DoS
  2. 滲透層的最大值保持1或2避免記憶體用盡
  3. 使用terminal進行,不要透過瀏覽器
  • 加上負載程式
    實際要執行的程式內容

  • 進一步的探索

  1. 將鏈結儲存到資料庫中
  2. 將取得鏈結的程式,與負載程式區隔開來
  3. 利用多部電腦分散工作
  4. 規範管理頁面請求

Chapter 18. 採購用網路機器人與狙擊程式

  • 採購用網路機器人理論
    流程:
    取得採購商品的條件 -> 買家身份認證 -> 對採購項目進行確認
    -> 價格可接受嗎?/庫存有需求嗎?/非買不可嗎? -> 進行採購
    -> 評估結果 -> 完成
  • 狙擊程式(Sniper)理論
    利用時間作為觸發事件的機器人
    流程:
    取得採購商品的條件 -> 買家身份認證 -> 對採購項目進行確認
    -> 時鐘同步 -> 出價的時間點到了嗎? -> 送出你的報價
    -> 評估結果 -> 完成

取得伺服器時間設定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
include("LIB_http.php");
include("LIB_parse.php");

$target = "http://www.schrenk.com";

$header_array = http_header($target, $ref="");

$local_server_time = return_between($header_array['FILE'], $start="Date:", $stop="\n", EXCL);
$local_server_time_ts = strtotime($local_server_time);

echo "\nReturned header:\n";
echo $header_array['FILE']."\n";
echo "Parsed server timestamp = ".$local_server_time_ts."\n";
echo "Formatted server time = ".date("r", $local_server_time_ts)."\n";
  • 對你自己的網路機器人與狙擊程式進行測試
  • 進一步的探索
  • 最終的想法

Chapter 19. 網路機器人與密碼術

  • 設計一個能使用加密功能的網路機器人
    1
    http_get("https://...", $referer)
  • 對Web網路加密的快速綜覽
    handshake過程確認使用的加密演算法,客戶端取得伺服器端回傳的CA(Certificate authority)、公鑰等資訊,客戶端使用公鑰進行加密回傳,伺服器利用回傳的訊息建立session憑證。

忽略認證動作:

1
2
curl_setopt($ch, CURLOPT_SSL_VERIFIYHOST, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

設定認證動作:

1
2
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
curl_setopt($ch, CURLOPT_CAINFO, $file_name); //憑證檔案
  • 最終的想法
    使用正確的協定,表單action有可能為http/https,

Chapter 20. 身份證認

  • 身份認證是什麼?
    證明你是誰的過程

  • script腳本範例與練習網頁
    練習網頁:

身份認證方法 練習網頁的位置
基本身份認證 http://www.WebbotsSpidersScreenScrapers.com/basic_authentication
Cookies Sessions http://www.webbotsspidersscreenscrapers.com/cookie_authentication
Query Sessions http://www.webbotsspidersscreenscrapers.com/query_authentication

帳號密碼:webbot / sp1der3

  • 基本身份認證
    realm(領域):受保護的範圍,一般來說為當前的伺服器目錄,以及子目錄下的所有網頁。
    基本身份證認的缺點:
    除了關閉瀏覽器,沒有其他登出的方法。
    表單外觀無法改變(根據瀏覽器實作)。
    明碼傳送資訊,安全性低。(可透過digest authentication補強,加入128-bits MD5,但支援度低)。
    ref. chapter20-1_basic_authentication.php

  • session認證
    兩種方法:cookie與query string

Cookie Session進行身份認證
Cookie的工作原理:
cookie於HTTP header中,當client端向server端發出請求時,檢查cookie值是否正確。
若不正確則伺服器向使用者要求憑證並將session值保存於cookie中。
ref. chapter20-2_cookie_session_authentication.php

以Query Session進行身份認證
有些瀏覽器限制cookie使用。
ref. chapter20-3_query_session_authentication.php

  • 最終的想法
    需要搭配加密傳輸。
    留意登入條件不要隨意散播,防止留下紀錄。
    網站可能結合多種認證,確認所有認證方案。

Chapter 21. 進階的Cookie管理

  • Cookie的工作原理
    網際網路為無狀態(stateless)環境
    Cookie兩種類型:
  1. 臨時性(temporary)
  2. 持久性(Permanent)
    1
    2
    setcookie("TemporaryCookie", "66");
    setcookie("PermanentCookie", "88", time()+3600);
    透過Http Header傳送cookie
  • PHP/CURL和Cookie

    1
    2
    3
    4
    //讀取cookie
    include("LIB_http.php"); //define("COOKIE_FILE", "c:\cookie.txt");
    $target="http://www.WebbotSpidersScreenSrapers.com/Listing_21_1.php"
    http_get($target, "");

    不同瀏覽器維護自定義的cookie結構,瀏覽器間的cookie不相容。

  • Cookie對網路機器人設計帶來了什麼挑戰?
    cookie可以由使用者關閉,非強制項目。
    在cookie傳遞的資料與Get/Post傳遞的內容一樣重要。
    清除臨時性Cookie
    PHP/CURL將cookie寫入檔案,需要自行檢查是否過期。
    或者設定下列屬性:

    1
    curl_setopt($s, CURLOPT_COOKIESESSION, TRUE);

管理多使用者的Cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$s = curl_init();

$cookie_file = "c:\bots\".$username."cookies.txt";
curl_setopt($s, CURLOPT_COOKIEFILE, $cookie_file); //讀取
curl_setopt($s, CURLOPT_COOKIEJAR, $cookie_file); //寫入

curl_setopt($s, CURLOPT_URL, $target);
curl_setopt($s, CURLOPT_RETURNTRANSFER, TURE); //以字串傳送

culr_setopt($s, CURLOPT_SSL_VERIFYPEER, FALSE);
culr_setopt($s, CURLOPT_FOLLOWLOCATION, TURE);
culr_setopt($s, CURLOPT_MAXREDIRS, 4);

$downloaded_page = curl_exec($s);
curl_close($s);
  • 進一步的探索
  1. 設計可讀取header的script,增加cookie管理能力。
  2. 若無法在運行伺服器上寫入檔案,可以寫入DB。
  3. 自動刪除臨時性cookie的script。
  4. 透過瀏覽器巨集自動管理cookie。

Chapter 22. 網路機器人與網路蜘蛛排程

  • 為網路機器人排程,做好準備工作
    使用批次檔
  • Windows XP工作排程器
    系統工具>工作排程器
  • Windows 7工作排程器
    控制台>工作排程器
  • 不以日曆為基礎的觸發方式
    使用e-mail進行觸發
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    include("LIB_pop3.php");
    define("SERVER", "your.mailserver.net");
    define("USER", "your@email.com");
    define("PASS", "your_passeord");
    $webbot_path = "C:\\webbots\\view_competitor.bat";

    $connection_array = POP3_connect(SERVER, USER, PASS);
    $POP3_connection = $connection_array['handle'];

    if($POP3_connection){
    $email_array = POP3_list($POP3_connection);
    for($xx=0; $xx<count($email_array); $xx++){
    list($mail_id, $size) = expode(" ", $email_array[$xx]);
    $message = POP3_retr($POP3_connection, $mail_id);

    if(stristr($message, "Subject: Run the webbot")){
    $output = shell_exec($webbot_path);
    echo "<pre>$output</pre>";
    POP3_delete($POP3_connection, $mail_id);
    }
    }
    }
  • 最終的想法
  1. 為機器人訂出最恰當的執行週期
  2. 避免單點故障
  3. 增加排成變化的多樣性

Chapter 23. 使用瀏覽器巨集擷取難度較高的網站

AJAX前,微軟的ActiveX落實XMLHTTP應用,搭配DHTML可提供毋須更新頁面的內容更新。
2006年W3C建立AJAX官方標準

  • 有效擷取網站資料時所遇到的障礙

AJAX:URL與內容不再是一對一關係。
可能難以達到以下工作:

  1. 將滑鼠游標移到某個日曆上,然後選定一個日期
  2. 捲動圖像側邊欄,選定某個特定的縮圖
  3. 用拖放的方式選擇某個物件
  4. 緩慢的輸入文字到文字框中,等待拼字檢查器給我們拼字的建議

怪異的JavaScript和Cookie行為:某些情況下,網頁利用JavaScript建立另一個JavaScript,最終被用來寫入cookie或是控制表單行為。或者網頁會將圖像寫入cookie。

Flash:內部為封閉的環境

  • 運用瀏覽器巨集,克服網路資料擷取的障礙
    使用script腳本來控制瀏覽器的動作。
    iMacros瀏覽器巨集:
    建議指令
    1
    2
    3
    4
    5
    6
    7
    8
    ' 註解的寫法
    SET !TIMEOUT 240
    SET !ERRORIGNORE YES //忽略錯誤訊息
    SET !EXTRACT_TEST_POPUP NO
    FILTER TYPE=IMAGES STATUS=ON
    CLEAR
    TAB T=1
    TAB CLOSEALLOTHERS
  • 最終的想法

Chapter 24. 深度應用iMacros

  • 深入到iMacros中,添加額外的功能
    寫一個script動態建立瀏覽器巨集
    利用iMacros載入本地端網站伺服器網頁

不使用iMacors腳本引擎(付費版)的原因:
此腳本以COM為基礎,跨平台性較差
雖然可以接受本機檔案,但與資料庫或其他網站的外部資料無發搭配很好
並沒有其他好處

建立動態巨集
範例:競品價格追蹤

  1. 使用iMacors和動態巨集,寫一個能模擬實際瀏覽器使用者的網路機器人。
  2. 開發一個能讀取內部資料庫的PHP script腳本,找出銷售最佳的前100項商品,然後寫出一個合適的巨集,取得這些商品在競爭者那邊所賣的價格。
  3. 寫個能從競爭者網頁中解析出價格的PHP script腳本。

寫一個能建立動態巨集的script腳本

1
2
3
4
5
6
7
8
9
10
11
12
$macro="";
$macro=$macro."'#####################\n";
$macro=$macro."'# HEADER (defaults, etc.)\n";
$macro=$macro."'#####################\n";
$macro=$macro."SET !TIMEOUT 240\n";
$macro=$macro."SET !ERRORIGNORE YES\n";
$macro=$macro."SET !EXTRACT_TEST_POPUP NO\n";
$macro=$macro."FILTER TYPE=IMAGES STATUS=ON\n";
$macro=$macro."CLEAR\n";
$macro=$macro."TABT= 1\n";
$macro=$macro."TAB CLOSEALLOTHERS\n";
$macro=$macro."'#####################\n";

將外部資料整合到動態建立的巨集中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
for($xx=0; $xx<count($product_array); $xx++){
$macro = $macro."' Get URL of competitor's product page\n";
$competing_product_information = //URL
$macro = $macro."'\n";
$macro = $macro."'Add random delay\n";
$macro = $macro."WAIT SECONDS=".sleep(rand(5,15))."\n";
$macro = $macro."'\n"
$macro = $macro."' Capture the competitor's web page with product information\n";
$macro = $macro."GOTO URL=$competitor_product_information \n";
$macro = $macro."SAVE TYPE=HTM FOLDER=* FILE=search_results \n";
$macro = $macro."'\n"
$macro = $macro."Run the parsing software in secondary browser tab\n"
$macro = $macro."TAB T=2"
$macro = $macro."URL GOTO=http://localhost/parser.php?if=".$product_arrat[$xx]['ASIN']."\n"; //呼叫本地端parser
$macro = $macro."'\n"
$macro = $macro."' Resume in original browser tab\n";
$macro = $macro."TAB T=1 \n";
}
file_put_contents("//PATH/MACRO_NAME.iim", $macro);

自動執行iMacros巨集
在windows中啟動iMacros

1
2
3
4
5
//run.bat
php create_macro.php
start /B "C:\Program Files\Mozilla Firefox\firefox.exe" http://127.0.0.1
ping 127.1.1.1 //delay
start /B "C:\Program Files\Mozilla Firefox\firefox.exe" http://run.imacros.net/?m=test.iim

在Linux中啟動iMacros

1
2
3
4
//run.php
<?php
system("firefox http://run.imacros.net/?m=test.iim");
?>
  • 進一步的探索
    其他技巧:
  1. 將firefox設為預設瀏覽器,且不做其他用途
  2. 多個iMacros script不能在同一個桌面帳號執行,必須錯開時間,或者錯開使用者
  3. 可能需要登入安全網站,多瞭解iMacros對登入憑證加密的能力
  4. IE版的iMacros不能支援分頁的功能

Chapter 25. 部署與規模擴展

  • 一對多的環境
    一個機器人接受多個輸入資源
    可以試著運用平行運算
  • 一對一的環境
    難以擴展規模
    最大的挑戰在於如何避免機器人被當作具有攻擊意圖的對象
  • 多對多的環境
    可能是同一個script腳本的多個實體
  • 多對一的環境
    最難以擴展的架構
    不小心就會發動DoS攻擊
  • 規模擴展與服務阻斷攻擊
    避免不小心發動了DoS
    就算是很簡單的網路機器人,也能塞爆網站
    目標網站本身沒效率
    規模越擴展,問題越嚴重:尤其多對一架構可能造成目標網站癱瘓,但是優點是可以使用多個IP位置
  • 為一個網路機器人建立多個執行實體
    三種方法:
  1. 用fork的方法複製出另一個額外的process
  2. 在作業系統中建立同一個script的多個實體
  3. 在多個硬體上執行相同的網路機器人:機器人網路(Botnet)
  • Botnet的管理
    Botnet的溝通方式:與管理的伺服器進行溝通,管理伺服器進行工作安排
  1. 以Polling的方式與Botnet伺服器溝通

    1
    2
    3
    4
    5
    6
    7
    <?php
    include('LIB_http.php');
    $post_array['STATUS'] = "TASK REQUEST";
    $post_array['BOT'] = "this webbot's name";
    $post_array['VERSION'] = "script version level";
    $botnet_server_address = "https://.BOTNET_SERVER_ADDRESS";
    $reply = http_post_form($botnet_server_address, $post_array);
  2. 判斷是否有工作要執行
    伺服器先判斷網路機器人的身份(名稱與憑證),衡量伺服器負載能力。

  3. Checkout程序
    伺服器指派工作給網路機器人,設定時限。

  4. 指派工作
    透過XML格式提供訊息

    1
    2
    3
    4
    5
    6
    7
    <XML>
    <TASK_ID>9999</TASK_ID>
    <TIMESTAMP>...</TIMESTAMP>
    <TARGET>www.SomeWebSite.com</TARGET>
    <USERNAME>username</USERNAME>
    <PASSWORD>password</PASSWORD>
    </XML>

    解析Botnet伺服器回傳的訊息

    1
    2
    3
    4
    5
    6
    <?php
    $task_id = return_between($reply['PAGE'], "<TASK_ID>", "</TASK_ID>", EXCL);
    $target = return_between($reply['PAGE'], "<TARGET>", "</TARGET>", EXCL);
    $username = return_between($reply['PAGE'], "<USERNAME>", "</USERNAME>", EXCL);
    $password = return_between($reply['PAGE'], "<PASSWORD>", "</PASSWORD>", EXCL);
    ?>

    沒任務的範例

    1
    2
    3
    4
    <XML>
    <TASK_ID>NO TASK</TASK_ID>
    <TIMESTAMP>...</TIMESTAMP>
    </XML>
  5. 執行工作

  6. 將擷取到的資料上傳給伺服器

    1
    2
    3
    4
    5
    6
    7
    <?php
    include<'LIB_http.php');
    $post_array['STATUS'] = "DATA_COLLECTED";
    $post_array['BOT'] = "this webbot's name";
    $post_array['VERSION'] = "script version level";
    $botnet_server_address = "https://".BOTNET_SERVER_ADDRESS;
    $reply = http_post_form($botnet_server_address, $post_array);
  7. 處理擷取到的資料
    將回傳資料保存在資料結構或者資料庫中。
    伺服器可能會留下一些log。

  • 進一步的探索
    如何增加待完成工作列表到Botnet伺服器?:透過API輸入?
    Botnet伺服器的最大能力?
    掩人耳目的網路機器人,使用代理或其他技術?
    自動從伺服器下載軟體更新。

Part IV 比較大的考量

Chapter 26. 能掩人耳目的網路機器人

  • 為什麼要設計出能掩人耳目的網路機器人?
    避免被當作駭客攻擊:
    Log記錄檔案的三種類型:存取log紀錄、錯誤log紀錄、自訂log紀錄
    Log監視軟體

  • “掩人耳目”,就意味著要模仿人類的行為模式
    善待你的資料來源
    選擇在比較繁忙時,執行你的網路機器人
    別老是在每天的同一個時間,執行你的網路機器人
    別在假期和週末執行你的網路機器人
    在取得網頁和網頁之間,插入隨機的延遲時間

  • 最終的想法

Chapter 27. 代理伺服器

  • 什麼是代理伺服器
    代理你做某件事的某個東西
  • 在虛擬世界中的代理伺服器
    列印機代理伺服器、內容過濾伺服器、防火牆
  • 為什麼網路機器人的開發者要使用代理伺服器
    運用代理伺服器達到匿名的效果
    使用代理伺服器可以假裝你在別的地方
  • 使用代理伺服器
    在瀏覽器中使用代理伺服器
    透過PHP/CURL來使用代理伺服器
    1
    2
    3
    // $session_id = curl_init()
    curl_setopt($session_id, CURLOPT_PROXY, $proxy_ip.": ".$proxy_port);
    curl_setopt($session_id, CURLOPT_PROXY_TYPE, CURLPROXY_SOCKS5); //default: CURL_PROXY_HTTP
  • 代理伺服器的種類
  1. 開放式代理伺服器(open proxy)
    開放式代理伺服器的種類:
    透明代理伺服器:通常會將IP位置放在HTTP_X_FORWARDED_FORWARDED變數中
    匿名代理伺服器:不會紀錄原始IP,不過仍然看偵測出經由代理伺服器轉送
    假冒代理伺服器:試圖隱藏真實伺服器
    開放式代理伺服器的黑暗面:
    很少是正當理由開放,有可能是政府機關/私人監控,或者私人電腦中毒。
    關於開放式代理伺服器清單服務的更多訊息:
    有提供開放代理伺服器清單的服務公司。

  2. Tor
    使用Tor:需要安裝Polipo(執行於電腦中的代理伺服器)
    設定PHP/CURL使用Tor:port為8118
    Tor的缺點:
    無法保證完全匿名,網站可能透過JavaScript或瀏覽器外掛跳過Tor的作用。
    速度較慢。

  3. 商業化代理伺服器
    例如:Anonymizer(http://www.Anonymizer.com)、HideMyIP(http://HideMyIP.com)

  • 最終的想法
    匿名只是一種有幫助的做法,而不是一種絕對有效的功能
    建立自己的代理伺服器

Chapter 28. 具有容錯能力的網路機器人

  • 具有容錯能力的網路機器人,幾種不同的類型

####適應URL的改變:

  1. 只下載目標網站中確實可取得的頁面
    避免對已經不存在的頁面提出請求:

透過HTTP代碼檢查

1
2
3
4
5
6
<?
include("LIB_http.php");
$page = http_get($target="www.schrenk.com", $ref="");
if($page['STATUS']['http_code']!=200)
error_handler("BAD RESULT", $page['STATUS']['http_code']);
?>

檢查連結路徑
確保經由正確路徑進入,而非已過期的連結

利用使用者名稱,確認表單提交結果

1
2
3
4
5
6
$username = "GClassmann";
$page = http_get($target, $ref="");
if(!stristr($page['FILE'], "$username")){
echo "authentication error";
error_handler("BAD AUTHENTICATION for ".$username, $target);
}
  1. 能隨著標頭的重導向,轉移到已被更新過的頁面
    HTTP標頭包含location:URL
    1
    2
    curl_setopt($curl_session, CURLOPT_FOLLOWLOCATION, TRUE);
    curl_setopt($curl_session, CURLOPT_MAXREDIRS, 4);

<head>標籤之間的頁面重導向
範例:

1
2
3
4
5
<html>
<head>
<meta http-equiv="refresh" countent="0; URL=http://www.nostarch.com">
</head>
</html>

ref. chapter28-1_meta_tag_redirection

  • 只搜尋head tag
  • 在meta tag中搜尋http-equiv屬性
  • 重導向的URL被轉換為一個完整的解析地址
  • 找到第一個重導向指令後,script就會停止

透過JavaScript的重導向

1
<script> document.location = 'http://www.schrenk.com'; </script>

透過JavaScript的重導向可能會很複雜,不容易被解析(例如function呼叫)。

  1. 能引用參照的值,指出你所跟隨的連結,是來自於某個仍然存在於網站上的頁面
    維護引用參照值的正確性。

####適用頁面內容的改變:

  1. 避免位置解析:可能由於網頁排版改變而無法運作
  2. 使用相對解析:找出關鍵元素後,再尋找關聯的資訊
  3. 尋找那些不太可能會發生變化的界標:改變頻率最低的大多是與伺服器應用有關的部分(type=”hidden”的資料,或者元素id等)

####適應表單中的改變:
先進行表單診斷
檢查表單變數,包含:使用的method、action、名稱與資料
ref. chapter28-2_form_parser.php

####適應cookie的改變:
LIP_http函式庫並不會自動刪除過期的cookie

####適應網路斷線與網路壅塞的狀況:
設定time-out值:

1
2
#default is 25s
curl_setopt($curl_session, CURLOPT_TIMEOUT, $timeout_value);

PHP延長超時設定

1
2
set_time_limit(60);
set_time_limit(0); // remove timeout setting
  • 錯誤處理程式
    錯誤訊息記錄應該包含以下內容:哪一個網路機器人出問題?為什麼出問題?
    出問題的日期與時間?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function webbot_error_handler($failure_mode){
    $email_address = "your.account@someserver.com";
    $email_subject = "Webbot Failure Notification";
    $email_message = "Webbot T-Rex encountered a fatal error<br>";
    $email_message = $email_message.$failure_mode."<br>";
    $email_message = $email_message."at".date("r")."<br>";
    mail($email_address, $email_subject, $email_message);
    exit;
    }

    為機器人增加狀態,執行前檢查是否需要執行,若發生錯誤時透過error handler改變狀態
    以移除排程。

  • 進一步的探索

Chapter 29. 對網路機器人友善的網站設計

  • 針對搜尋引擎的網路蜘蛛,進行網頁的最佳化(搜尋引擎最佳化, SEO)
    定義良好的鏈結
    Google Bombs與Spam Indexing:針對特定網站,利用相同的鏈結文字描述建立出多個鏈結
    Title標籤
    Meta標籤
    標題標籤(h1~h6)
    圖像的alt屬性

  • 會妨礙到搜尋引擎網路蜘蛛的網頁設計技術
    JavaScript:執行前難以被辨認
    非ASCII內容:如Flash

  • 設計”僅含資料”的介面

可擴展標記語言(XML)(ref. chapter29-1_xml_parser.php)

輕量級資料交換:需注意安全性問題(如使用eval()直接將回傳內容保存為變數的做法)
ref. chapter29-2_light-weight_data_exchange_interface.php

簡單物件存取協定(SOAP):

1
2
3
4
5
6
7
8
9
10
11
12
//SOAP呼叫範例
// PHP5內建SOAP, PHP4需要使用PEAR(PHP extension and Application Repository)
include("inc/PEAR/SOAP");
$params = array(
'manufacturer' => "XYZ CORP",
'mode' => 'development',
'sort' => '+product',
'type' => 'heavy',
'userkey' => $ACCESS_KEY);
$WSDL = new SOAP_WSDL($ADDRESS_OF_SOAP_INTERFACE);
client $client = $WSDL->getProxy();
$result_array = $client->SomeGenericSOAPRequest($params);

SOAP優點:跨平台
SOAP缺點:門檻較高

REST(Representational State Transfer)

  • 最終的想法

Chapter 30. 獵殺網路蜘蛛

網站擁有者可能為了下面幾種目的,希望隔絕網路機器人:
保護智慧財產
保護自己的電子郵件地址
規範管理網站的使用頻率
避免放在網路上的媒體被人保存起來
為所有使用者建立公平競爭的環境

  • 和善的提出要求
    建立服務條款協定
    使用robots.txt檔案
    使用Robots Meta標籤

    1
    2
    3
    <head>
    <meta name="robots" content="noindex, nofollow">
    </head>
  • 建立速度上的障礙
    選擇性允許某些網路代理程式,進行存取動作:記得在伺服器端進行檢查
    使用擾亂(obfuscation)的做法
    使用cookie、加密、JavaScript與重導向
    使用者身份認證
    經常更新你的網站(改變元素順序、表單方法、檔案名稱、參考點文字)
    將文字嵌入到其他媒體中,如CAPTCHA(Completely Automated Public Turing test to tell Computers and Humans Apart)。 破解:http://decaptcher.com

  • 設下陷阱
    建立一個捕捉網路蜘蛛的陷阱:人類無法進行的操作

    1
    <a href="spider_trap.php"><img src="spacer.gif" width="0" height="0"></a>

對不想要的網路蜘蛛,能做的一些好玩的事

策略 相應做法
驅逐 記錄IP之後進行阻擋
限制存取 記錄IP之後限制可存取的資源
誤導 將網路蜘蛛重導到另一個頁面中
分析 識別IP(如:http://www.arin.net),追蹤擁有者
忽略 忽略所有自動化操作
  • 最終的想法

Chapter 31. 讓你的網路機器人遠離是非

  • 一切都源自於尊重
    網站資源都是擁有者付費並非免費資源
  • 版權
    確實向各方進行諮詢
    暸解版權法的一些基本概念:
    版權並不需要經過註冊的程序
    在預設的情況下,創作總是”保留所有權利”
    不能對一個事實,主張擁有其版權
    如果能以具有創造性的方式,呈現整群的事實,就可以主張擁有版權
    在合理使用法(Fair Use Laws)的規範下,可使用部分的內容素材
  • 侵入動產
    過度消耗目標伺服器的頻寬,影響效能或其他人的使用
  • 網際網路的相關法律
    維吉尼亞洲反垃圾郵件法(anti-spam)
  • 最終的想法

Appendix A. PHP/CURL參考資料

  • 建立一個最精簡的PHP/CURL Session
    1
    2
    3
    4
    5
    6
    7
    8
    <?
    $s = curl_init();
    curl_setopt($s, CURLOPT_URL, "http://www.schrenk.com");
    curl_setopt($s, CURLOPT_RETURNTRANSFER, TRUE);

    $downloaded_page = curl_exec($s);
    curl_close($s);
    ?>
  • 初始化PHP/CURL Session
    1
    curl_init();
  • PHP/CURL選項設定
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    curl_setopt($s, CURLOPT_URL, "http://www.schrenk.com"); //定義目標網站
    curl_setopt($s, CURLOPT_RETURNTRANSFER, TRUE); //以字串回傳結果
    curl_setopt($s, CURLOPT_REFERER, "http://www.domain.com/index.php"); //設定參照網址
    curl_setopt($s, CURLOPT_FOLLOWLOCATION, TRUE); //跟隨網站重導向(僅header內容,不包含refresh meta或JavaScript中的設定),需設定CURLOPT_MAXREDIRS
    curl_setopt($s, CURLOPT_MAXREDIRS, 4); //最大重導向次數
    curl_setopt($s, CURLOPT_USERAGENT, "Webbot"); //設定代理程式名稱
    curl_setopt($s, CURLOPT_NOBODY, TRUE); //排除body部分
    curl_setopt($s, CURLOPT_HEADER, TRUE); //包含標頭(預設不包含)
    curl_setopt($s, CURLOPT_TIMEOUT, 30); //設定timeout值,單位秒
    curl_setopt($s, CURLOPT_COOKIEFILE, "c:\bots\cookies.txt"); //讀取cookie檔案
    curl_setopt($s, CURLOPT_COOKIEJAR, "c:\bots\cookies.txt"); //寫入cookie檔案
    curl_setopt($s, CURLOPT_HTTPHEADER, ["Mime-Version:1.0", "Content-type: text/html; charset=iso-8859-1", "Accept-Encoding: compress, gzip"]); //配置HTTP header
    curl_setopt($s, CURLOPT_SSL_VERIFYPEER, FALSE); //設定是否使用客戶端憑證
    curl_setopt($s, CURLOPT_USERPWD, "passwd"); //設定身份認證密碼
    curl_setopt($s, CURLOPT_UNRESTICTED_AUTH, TRUE); //若有設定重導向需要開啟
    curl_setopt($s, CURLOPT_POST, TRUE); //使用POST
    curl_setopt($s, CURLOPT_POSTFIELDS, "var1=1&var2=2"); //設定POST內容
    curl_setopt($s, CURLOPT_VERBOSE, FALSE); //關閉詳細模式
    curl_setopt($s, CURLOPT_PORT, 234); //設定使用的port
  • 執行PHP/CURL指令
    1
    2
    3
    curl_exec();
    curl_getinfo(); //檢索PHP/CRUL Session訊息
    curl_error(); //回傳可能發生在PHP/CURL session期間的錯誤
  • 關閉PHP/CURL Session
    1
    curl_close();

Resource

官方網站


網路機器人、網路蜘蛛與網路爬蟲 PHP/CURL程式設計指南(第二版)
https://chris-suo.github.io/ChrisComplete/2022/06/19/PHP-CURL-Programming/
Author
Chris Suo
Posted on
June 19, 2022
Licensed under