教你如何用Java簡單爬取WebMagic
一、Java爬蟲——WebMagic
1.1 WebMagic總體架構(gòu)圖

1.2 WebMagic核心組件
1.2.1 Downloader
該組件負責從互聯(lián)網(wǎng)上下載頁面。WebMagic默認使用Apache HttpClient作為下載工具。
1.2.2 PageProcessor
該組件負責解析頁面,根據(jù)我們的業(yè)務進行抽取信息。WebMagic使用Jsoup作為HTML解析工具,并基于其開發(fā)了解析Xpath的工具Xsoup。
1.2.3 Scheduler
該組件負責管理待抓取的URL,以及去重的工作。WebMagic默認使用JDK內(nèi)存隊列管理URL,通過集合進行去重。
支持使用Redis進行分布式管理。
1.2.4 Pipeline
該組件負責抽取結(jié)果的處理,包括計算、持久化到文件、數(shù)據(jù)庫等等。
1.2.5 數(shù)據(jù)流轉(zhuǎn)對象
1. Request
Request是對URL地址的一層封裝,一個Request對應一個URL地址。
它是PageProcessor與Downloader交互的載體,也是PageProcessor控制Downloader唯一方式。
除了URL本身外,它還包含一個Key-Value結(jié)構(gòu)的字段extra。你可以在extra中保存一些特殊的屬性,然后在其他地方讀取,以完成不同的功能。例如附加上一個頁面的一些信息等。
2. Page
Page代表了從Downloader下載到的一個頁面——可能是HTML,也可能是JSON或者其他文本格式的內(nèi)容。
Page是WebMagic抽取過程的核心對象,它提供一些方法可供抽取、結(jié)果保存等。
3. ResultItems
ResultItems相當于一個Map,底層使用了LinkedHashMap進行存儲,它保存PageProcessor處理的結(jié)果,供Pipeline使用。它的API與Map很類似,值得注意的是它有一個字段skip,若設置為true,則不被Pipeline處理,跳過。
1.2.6 Spider——WebMagic核心引擎
Spider是WebMagic內(nèi)部流程的核心。Downloader、PageProcessor、Scheduler、Pipeline都是Spider的一個屬性,這些屬性是可以自由設置的,通過設置這個屬性可以實現(xiàn)不同的功能。Spider也是WebMagic操作的入口,它封裝了爬蟲的創(chuàng)建、啟動、停止、多線程等功能。
1.3 練習Demo
需求是爬取一篇廈門限行文章,文章來源:http://xm.bendibao.com/traffic/2018116/54311.shtm,具體需求如下:
1.刪除文章中超鏈接
2.文章中的圖片下載至本地
3.刪除文章末尾:溫馨提示...
1.3.1 定制Downloader
為預防頁面失效,定制一個Downloader,當鏈接地址不存在時,打印日志。
public class MyHttpClientDownloader extends HttpClientDownloader {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 提示頁面獲取狀態(tài)碼
*/
@Override
protected Page handleResponse(Request request, String charset, HttpResponse httpResponse, Task task) throws IOException {
Page page = super.handleResponse(request, charset, httpResponse, task);
if(httpResponse.getStatusLine().getStatusCode()!= ConstantsField.PAGE_STATUS_200){
page.setDownloadSuccess(false);
logger.warn("頁面獲取狀態(tài)碼錯誤,正在重試!");
}
return page;
}
}
1.3.2 定制PageProcessor
該頁面處理器實現(xiàn)了對頁面的抽取,符合上面的需求。
將處理完成的數(shù)據(jù)添加進入:Page對象,并設置鍵分別為:imgList與content。
public class XmPageProcessor implements PageProcessor {
/**
* 抓取網(wǎng)站的相關(guān)配置,包括編碼、抓取間隔、重試次數(shù)等
*/
private Site site = Site.me().setCycleRetryTimes(3).setSleepTime(1000);
/**
* 核心:編寫抽取邏輯
*/
@Override
public void process(Page page) {
// 抽取頁面文本數(shù)據(jù)
Selectable selectable = page.getHtml().css(ConstantsField.PAGE_CSS_CONTENT);
//處理圖片
List<String> pImgList = selectable.xpath(ConstantsField.XPATH_IMG).all();
List<String> imgUrl = new ArrayList<>();
if(pImgList.size()>0){
Pattern compile = Pattern.compile(ConstantsField.REX_IMG_SRC);
for (String img : pImgList) {
Matcher matcher = compile.matcher(img);
while (matcher.find()){
imgUrl.add(matcher.group(1));
}
}
}
if(imgUrl.size()>0){
page.putField("imgList",imgUrl);
}else {
page.putField("imgList",null);
}
//對內(nèi)容轉(zhuǎn)換為StringBuilder
String content = selectable.toString();
StringBuilder stringBuilder = new StringBuilder(content);
//處理超鏈接
StringBuilder newString = dealLink(stringBuilder);
//處理末尾
int startIndex = newString.indexOf(ConstantsField.END_CONTENT);
if(startIndex>0) {
newString.delete(startIndex, stringBuilder.length());
newString.append("</div>");
}
page.putField("content",newString.toString());
}
@Override
public Site getSite() {
return site;
}
/**
* 處理超鏈接
*/
private static StringBuilder dealLink(StringBuilder stringBuilder){
StringBuilder newString = new StringBuilder(stringBuilder);
int aIndex = newString.indexOf("<a href");
while (aIndex != -1){
int pStart = newString.lastIndexOf("<p>", aIndex);
int pEnd = (newString.indexOf("</p>", aIndex) + 4);
newString.delete(pStart,pEnd);
aIndex = newString.indexOf("<a href");
}
return newString;
}
}
1.3.3 定制Pipeline
Pipeline是處理結(jié)果的地方,這里我們對結(jié)果進行存儲文件的處理。網(wǎng)站文本存儲為:stm格式,圖片文本存儲為其網(wǎng)站源文件的格式。
public class MyFilePipeline extends FilePersistentBase implements Pipeline {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private StringBuilder filepath;
private MyFilePipeline() {
this.setPath(ConstantsField.DEFAULT_SAVE_LOCATION);
}
public MyFilePipeline(String path) {
if(path!=null){
this.setPath(path);
}else {
new MyFilePipeline();
}
filepath = new StringBuilder().append(this.path).
append(PATH_SEPERATOR).append(ConstantsField.FILE_NAME)
.append(ConstantsField.FILE_POSTFIX);
}
@Override
public void process(ResultItems resultItems, Task task) {
//文件內(nèi)容覆蓋
try(PrintWriter printWriter = new PrintWriter(new FileWriter(getFile(filepath.toString()),false))) {
printWriter.write(resultItems.get("content").toString());
logger.info("文件生成成功,存儲地址為:"+filepath);
//下載圖片
List<String> imgList = resultItems.get("imgList");
if(imgList!=null&&imgList.size()>0){
boolean dowload = DownloadImgUtils.download(imgList, this.getPath());
if(dowload){
logger.info("圖片下載成功,存儲地址為:" + this.getPath());
}
}
} catch (IOException e) {
logger.error("輸出文件出錯:" + e.getCause().toString());
}
}
}
這里實現(xiàn)了網(wǎng)頁存儲為stm格式與圖片存儲,圖片存儲使用了如下工具類DownloadImgUtils:
public class DownloadImgUtils {
/**
* 下載圖片
* @param imgList 圖片列表
* @param savePath 保存地址
* @return 成功返回true
*/
public static boolean download(List<String> imgList, String savePath) throws IOException {
URL url;
DataInputStream dataInputStream = null;
FileOutputStream fileOutputStream = null;
File file;
try {
for (String imgUrl : imgList) {
//截取文件名
Pattern pat=Pattern.compile(ConstantsField.REX_IMG_SUFFIX);
Matcher mc=pat.matcher(imgUrl);
while(mc.find()) {
String fileName= mc.group();
file = new File(savePath + fileName);
file.createNewFile();
fileOutputStream = new FileOutputStream(savePath + fileName);
}
url = new URL(imgUrl);
dataInputStream = new DataInputStream(url.openStream());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = dataInputStream.read(buffer))>0){
outputStream.write(buffer,0,length);
}
fileOutputStream.write(outputStream.toByteArray());
}
return true;
}catch (Exception e){
e.printStackTrace();
}finally {
dataInputStream.close();
fileOutputStream.close();
}
return false;
}
}
1.3.4 啟動類
public class WebMagicApplication {
private String url;
private String saveUrl;
/**
* 無參構(gòu)造使用默認值
*/
public WebMagicApplication() {
this.url = ConstantsField.XM_BDB_URL;
this.saveUrl = ConstantsField.DEFAULT_SAVE_LOCATION;
}
public WebMagicApplication(String url, String saveUrl) {
this.url = url;
this.saveUrl = saveUrl;
}
public void start(){
Spider.create(new XmPageProcessor()).addUrl(this.url).addPipeline(new MyFilePipeline(this.saveUrl)).setDownloader(new MyHttpClientDownloader()).run();
}
public static void main(String[] args) {
WebMagicApplication webMagicApplication = new WebMagicApplication("http://xm.bendibao.com/traffic/2018116/5431122.shtm","C:\\");
webMagicApplication.start();
}
}
這里啟動類可以使用帶參構(gòu)造或無參構(gòu)造,無參構(gòu)造默認使用URL與存儲地址為ConstantsField類中的XM_BDB_URL屬性和DEFAULT_SAVE_LOCATION。
練習中的ConstantsField具體如下:
public final class ConstantsField {
/**
* 爬取的CSS的文本
*/
public static final String PAGE_CSS_CONTENT = "div.content";
/**
* 結(jié)束文本起始位置
*/
public static final String END_CONTENT = "<div id=\"adInArticle\"></div>";
/**
* 廈門本地寶URL
*/
public static final String XM_BDB_URL = "http://xm.bendibao.com/traffic/2018116/54311.shtm";
/**
* 默認文件保存地址
*/
public static final String DEFAULT_SAVE_LOCATION = "C:\\";
/**
* 文件名
*/
public static final String FILE_NAME = "2021廈門限行最新消息(持續(xù)更新)";
/**
* 文件后綴
*/
public static final String FILE_POSTFIX = ".stm";
/**
* 頁面訪問狀態(tài)碼
*/
public static final int PAGE_STATUS_200 = 200;
/**
* 正則匹配src
*/
public static final String REX_IMG_SRC = "src\\s*=\\s*\"?(.*?)(\"|>|\\s+)";
/**
* 正則匹配文件后綴
*/
public static final String REX_IMG_SUFFIX = "[\\w]+[\\.](jpeg|jpg|png)";
/**
* 處理圖片XPATH
*/
public static final String XPATH_IMG = "http://*[@id=\"bo\"]/*/img";
}
1.3.5 源碼地址
練習Demo源碼地址: https://gitee.com/Xiaoxinnolabi/web-magic/settings
WebMagic中文文檔: http://webmagic.io/docs/zh/
WebMagic源碼地址: https://github.com/code4craft/webmagic/
到此這篇關(guān)于教你如何用Java簡單爬取WebMagic的文章就介紹到這了,更多相關(guān)Java爬取WebMagic內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot使用ThreadPoolTaskExecutor多線程批量插入百萬級數(shù)據(jù)的實現(xiàn)方法
這篇文章主要介紹了springboot利用ThreadPoolTaskExecutor多線程批量插入百萬級數(shù)據(jù),本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02
Spring Boot之FilterRegistrationBean-自定義Filter詳解
這篇文章主要介紹了Spring Boot之FilterRegistrationBean-自定義Filter詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
Android?Studio中創(chuàng)建java工程的完整步驟
Android?Studio創(chuàng)建java工程是非常麻煩的,因為Android?Studio沒有提供直接創(chuàng)建java工程的方法,下面這篇文章主要給大家介紹了關(guān)于Android?Studio中創(chuàng)建java工程的完整步驟,需要的朋友可以參考下2024-01-01
詳解java中面向?qū)ο笤O計模式類與類的關(guān)系
這篇文章主要介紹了java面向?qū)ο笤O計模式中類與類之間的關(guān)系,下面小編和大家一起來學習一下吧2019-05-05

