【摘要】
集算器 SPL 支持抓取網頁數據,根據抓取定義規則,可將網頁數據下載到在本地進行統計分析。具體定義規則要求、使用詳細情況,請前往乾學院:集算器 SPL 抓取網頁數據!
網站上的數據源是我們進行統計分析的重要信息源。當我們瀏覽網頁,看到自己感興趣數據內容時,希望能夠快速抓取網頁上的數據,這對於數據分析相關工作來說極其重要,也是必備的技能之一。但是網絡數據抓取大多需要複雜的編程知識,操作也比較繁瑣。這裏介紹如何用集算器 SPL 快速抓取網頁數據。
1、基本流程圖
2、抓取網頁數據接口
3、定義規則
A、web_info
B、init_url
C、help_url
D、target_url
E、page_url
4、抓取股票歷史數據
5、用戶自定義程序
A、數據提取程序接口
B.數據保存程序接口
C、數據提取程序樣例
D、數據保存程序樣例
E、自定義程序的使用
1、基本流程圖
從給定的開始地址進行遍歷,將解析過濾後的網址放入下載地址隊列,分成網址頁 help_url 與下載頁 target_url, 網址頁只收集網址,下載頁即能收集網址,也能提取數據,把提取到的數據保存起來。抓取網頁數據直到遍歷地址爲空,則抓取工作結束。
2、抓取網頁數據接口
web_crawl(jsonstr) 是抓取網頁數據接口,參數 jsonstr 是定義規則的字符串,抓取數據時,根據定義規則遍歷 URL、下載、提取、保存相關內容數據。
本接口依賴集算器外部庫 webcrawlCli。它缺省安裝在集算器軟件的 esProc\extlib\webcrawlCli 路徑下,在集算器的外部庫設置中勾選 webcrawlCli 項, 重啓集算器後,就可以使用 web_crawl 接口。
web_crawl 簡單用法,如抓取指定股票數據,SPL 腳本 demo.dfx:
獲取股票代碼 600000 的數據文件:
文件內容:
3、定義規則
根據基本流程圖,將定義規則分成網站信息、初始網址、網址頁、下載頁、提取數據五部分。具體內容如下:
[
{web_info:{domain:‘www.banban.cn’, save_path:‘d:/tmp/data/webmagic’, thread_size:2, cookie:{name:“jacker”, laster:“2011”},
user_agent:‘Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:39.0) Gecko/20100101 Firefox/39.0’}},
{init_url:[‘_cybs.htmlhttps://www.banban.cn/gupiao/list’, ‘_sh.htmlhttps://www.banban.cn/gupiao/list’]},
{help_url:[‘gupiao/list_(sh|sz|cyb)\.html’, ‘/shujv/zhangting/’, ‘/agu/$’]},
{target_url:{reg_url:‘/agu/365\d’}},
{target_url:{filter:‘gupiao/list_(sh|sz|cyb)\.html’, reg_url:‘gupiao/[sz|sh]?(60000\d)/’,new_url:‘http://www.aigaogao.com/tools/history.html?s=%s’}},
{page_url:{filter:‘history.html\?s=\d{6}’, extractby: “//div[@id=‘ctl16_contentdiv’]/”}},
{page_url:{extractby: “//div[@id=‘content_all’]/”}},
{page_url:{filter:‘/agu/365\d’, extractby: “//div[@id=‘content’]/”}}
]
規則簡要說明:
web_info:網站信息, 根據要下載的網站,設置域名、本地存儲位置、用戶代理信息、用戶自定義程序等相關的信息。
init_url:初始網址, URL 遍歷的入口網址。
help_url:網址頁, 定義網址頁規則,收集網頁內容中的 URL,但不提取此頁面數據內容。
target_url:下載頁, 定義下載頁規則,收集網頁內容中的 URL,同時也提取此頁面的內容。
page_url:提取數據, 定義頁面內容提取規則,在下載頁 target_url 中根據此規則提取內容。
注意: json 書寫結構細節,節點 {} 中的 [] 表示 list 列表,節點 {} 中的 {} 表示 map 鍵值結構,書定時要注意,否則書寫不對易引起解析錯誤。
定義規則說明
A、web_info
設置要下載的信息,內容包括:
domain:設置域名。
save_path:文件存儲路徑。
user_agent:指用戶代理信息。 作用: 使服務器能夠識別客戶使用的操作系統及版本、CPU 類型、瀏覽器及版本、瀏覽器渲染引擎、瀏覽器語言、瀏覽器插件等。
sleep_time:抓取間隔。
cycle_retry_times:重試次數。
charset:設置編碼。
use_gzip:是否爲 gzip 壓縮。
time_out:抓取超時設置。
cookie_name:cookie 信息,鍵值結構。
thread_size:抓取時線程數。
save_post:是否要爲存儲的文件名稱追加編碼串,以防網名文件被覆蓋,缺省值爲 true。如 books/a.html, music/a.html, 都是要下載的頁面,保存時若此參數爲 true, 則存儲文件名分別爲 a_xxxcgk.txt,a_xabcdw.txt,文件不會被覆蓋;若爲 false, 保存文件名爲 a.txt, 後存儲的就會將已存在的同名文件覆蓋。
class_name:用戶自定義的存儲類。
class_argv:傳遞給 class_name 類的字符串參數。
B、init_url
初始的 URL。
爲 List 列表結構,可設置多個 URL.
C、help_url
網址頁主要是定義要收集的 URL 過濾規則, 符合規則的 URL 會被加入下載網址隊列,但是不會提取其具體內容。過濾規則支持正則表達式,如:
gupiao/list_(sh|sz|cyb)\.html 表示 URL 中只有包括字符串 gupiao/list_sh.html、gupiao/list_sz.html、gupiao/list_cyb.html 鏈接才能通過。
爲 List 列表結構,可定義多個規則。
D、target_url
下載頁是要抓取內容數據的 URL,需要從這個頁面裏提取內容。若此 URL 符合 help_url 過濾規則,那麼也會在本頁面中收集 URL。
約定定義規則格式:
{target_url:{filter: pageUrl, reg_url:urlRegex, new_url:newUrl}},
表示在符合 pageUrl 條件的頁面中,找出符合 urlRegex 條件的 href 鏈接,若 newUrl 定義了,則可與 urlRegex 過濾結果組合成新的 URL。
例如在頁面中找到鏈接 a_100.html 符合過濾條件 reg_url=a_(\d+)\.html, 則有 newUrl=b_%s.php, 那麼 urlRegex 過濾 a_100.html 的結果爲 100, 將與 newUrl 合併,新的下載頁爲 b_100.php。
其中 filter 表示定義過濾的 URL 規則;若無此定義,表示所有的 target_url 都要用此規則。
reg_url 表示要收集的 URL 規則,必寫;無 reg_url 的 target_url 規則則無意義。
new_url 表示定義新的頁面,需要與 reg_url 過濾結果結合成新的 URL。
舉例說明:
3.1 定義規則:{target_url:{filter:‘gupiao/list_(sh|sz|cyb)\.html’, reg_url:‘gupiao/([sz|sh]?6000\d{2})/’,new_url:‘http://www.raqsft.com/history.html?s=%s’}}
在下載頁 gupiao/list_sh.html 中包含如下內容:
- 包鋼股份 (600010)
- 四川路橋 (600039)
- 保利地產 (600048)
A、gupiao/list_sh.html 符合 filter 條件
B、href 串符合 reg_url 條件,將產生 [600010, 600039, 600048]
C、過濾結果與 newUrl 生成新的 URL:
http://www.raqsft.com/history.html?s=600010
http://www.raqsft.com/history.html?s=600039
http://www.raqsft.com/history.html?s=600048
new_url 中的 %s 爲合併字符串的佔位符。
3.2 定義規則:{target_url:{reg_url:‘/ gupiao/60001\d’}},
在下載頁 gupiao/list.html 中包含如下內容:
- 包鋼股份 (600010)
- 四川路橋 (600039)
- 保利地產 (600048)
href 中符合 reg_url 條件的,則收集到的 URL 爲: http://www.xxx.com/gupiao/600010/ 其它兩個 href 不符合過濾條件。 設置 filter 是爲了在過濾後的頁面中去收集 URL, 當 help_url 多時,過濾後縮小了範圍,提高了效率。 target_url 規則可定義多條,以適應不同的條件。
E、page_url
提取數據,主要作用於下載頁面內容提取,它表示使用這個抽取規則,將提取到的結果保存。定義此規則參考 xpath 使用說明。它只提取主要內容,但對內容細節還需要 className 類來抽取。
約定定義規則格式:
{page_url:{filter: pageUrl, extractby: contentReg, class: className }},
其中 filter 表示符合過濾條件的 url 規則,若無此定義,表示所有的 target_url 都要用此規則。
extractby 表示頁面內容提取規則。若定義 class,表示由 className 類執行內容提取;若 className=
”default”, 表示用當前缺省方式提取,也就是針對 table 表中的內容提取數據。若缺省提取不滿足需求,用戶可自定義類來實現,具體實現參考後面用戶自定義程序。
例如:extractby :“//div[@class=news-content]/text()”,從網頁中提取此節點下的數據。
page_url 可針對不同的頁面制定不同的規則。通過 filter 過濾後的頁面中去提取數據,減少要處理的 URL 數量,當 target_url 多時,能提高效率。
若無 extractby 規則,則表示提取 target_url 頁面中所有的內容。
若定義了多條 page_url 規則 ,則首個符合規則的內容將被提取。
假如 A 頁面內容的符合規則 R1,R2,R3, 提取內容時首先是 R2,則不再根據 R1、R3 規則提取數據。
說明:若沒有定義 target_url 規則,但當前頁面有適合的 page_url 規則,則此頁面的內容也會被提取。
4、抓取股票歷史數據
下面用抓取股票歷史數據來說明,web_crawl() 接口是如何應用的。基本操作:先獲取股票代碼,然後通過股票代碼查詢歷史數據,從下載頁面中提取數據後保存。A、在https://www.banban.cn/gupiao/list_xxx.html 頁面 help_url 提取上證、深證、創業板的股票代碼。
B 、將股票代碼與http://www.aigaogao.com/tools/history.html?s=%s 結合,生成需要下載網址 target_url.
C 、針對下載頁 target_url 中的內容提取。
D、顯示提取後的內容。
SPL 實現代碼 Stock.dfx:
加載其中的股票 600010 數據爲:
5、用戶自定義程序
對於內容提取,缺省提供了對 html 中的 table 內容進行抽取。 但是世界上沒有千篇一律的網頁一樣,也沒有一勞永逸的提取算法。在使用網頁數據抓取過程中,你會碰到各種類型的網頁,這個時候,你就要針對這些網頁,來實現對應抽取方法。存儲方式類似,缺省提供的是文件保存,若想其它方式如數據庫存儲,還需要用戶自己開發程序。參考下面接口,可將自定義程序融入網頁數據抓取流程中。
A、數據提取程序接口
下載頁的內容組織形式多樣,各具不同,爲了適應更多的內容提取需求,用戶可自定義提取數據程序。
接口程序:
package com.web;
import us.codecraft.webmagic.Page;
public interface StandPageItem {
// 數據提取處理。
void parse(Page p);
}
需要實現 com.web.StandPageItem 接口 parse(Page p),數據提取在此實現。
B、數據保存程序接口
提取數據存儲方式種類繁多,各具不同,爲了適應更多的數據存儲需求,用戶可自定義數據存儲程序。
接口程序:
package com.web;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;
public interface StandPipeline extends Pipeline {
public void setArgv(String argv);
public void process(ResultItems paramResultItems, Task paramTask);
}
同樣需要實現 com.web.StandPipeline 類中的 setArgv(), process()。
setArgv()輸入參數接口,process() 處理存儲數據接口。
C、數據提取程序樣例
實現 com.web.StandPage 接口 parse(Page p),
參考代碼:
package com.web;
import java.util.List;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.selector.Selectable;
public class StockHistoryData implements StandPageItem{
@Override
public void parse(Page page) {
StringBuilder buf = new StringBuilder();
List nodes = page.getHtml().xpath(“table/tbody/”).nodes();
for(Selectable node:nodes){
String day = node.xpath(“//a/text()”).get();
List title = node.xpath(“//a/text() | tr/td/text()”).all();
if (title.size()<5) continue;
String line = title.toString().replaceFirst(“, ,”, “,”);
buf.append(line+“\n”);
}
page.putField(“content”, buf.toString());
}
}
將要保存的數據存放到 page 的字段 "content" 中,在保存處理時,將從字段 "content" 中獲取。
D、數據保存程序樣例
實現 com.web.StandPageline 類中的 setArgv(),process()
參考代碼:
package com.web;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import org.apache.commons.codec.digest.DigestUtils;
import us.codecraft.webmagic.utils.FilePersistentBase;
public class StockPipeline extends FilePersistentBase implements StandPipeline {
private Logger logger = LoggerFactory.getLogger(getClass());
private String m_argv;
private String m_path;
public static String PATH_SEPERATOR = “/”;
static {
String property = System.getProperties().getProperty(“file.separator”);
if (property != null) {
PATH_SEPERATOR = property;
}
}
public StockPipeline() {
m_path = “/data/webcrawl”;
}
// 獲取存儲路徑與存儲文件名前綴
public void setArgv(String argv) {
m_argv = argv;
if (m_argv.indexOf(“save_path=”)>=0){
String[] ss = m_argv.split(“,”);
m_path = ss[0].replace(“save_path=”, ““);
m_argv = ss[1];
}
}
public void process(ResultItems resultItems, Task task) {
String saveFile = null;
Object o = null;
String path = this.m_path + PATH_SEPERATOR + task.getUUID() + PATH_SEPERATOR;
try {
do{
String url = resultItems.getRequest().getUrl();
o = resultItems.get(“content”);
if (o == null){
break;
}
int start = url.lastIndexOf(”/”);
int end = url.lastIndexOf(“?”);
if (end<0){
end=url.length();
}
String link = url.substring(start+1, end);
if (m_argv!=null && !m_argv.isEmpty()){
link = m_argv+“_”+link;
}
if (link.indexOf(“.”)>=0){
link = link.replace(“.”, “”);
}
// 加 md5Hex 爲防止重名
String hex = DigestUtils.md5Hex(resultItems.getRequest().getUrl());
saveFile = path + link+“”+ hex +“.json”;
}while(false);
if (saveFile!=null){
PrintWriter printWriter = new PrintWriter(new FileWriter(getFile(saveFile)));
printWriter.write(o.toString());
printWriter.close();
}
} catch (IOException e) {
logger.warn(“write file error”, e);
}
}
}
E、自定義程序的使用
將上述接口文件及 java 文件編譯後打包成 webStock.jar 文件,放在 esProc\extlib\webcrawlCli 路徑下重啓集算器。數據存儲程序,在 web_info 中配置;數據提取程序,在 page_url 中配置。下面是加載兩個自定義類程序的 dfx 腳本。
SPL 代碼腳本 mytest.dfx:
生成結果:
類似針對內容提取或數據存儲,參考上面程序的實現,用戶可自定義 java 程序。