Java爬蟲框架WebMagic入門——爬取列表類網站文章


 初學爬蟲,WebMagic作為一個Java開發的爬蟲框架很容易上手,下面就通過一個簡單的小例子來看一下。

WebMagic框架簡介

WebMagic框架包含四個組件,PageProcessorSchedulerDownloaderPipeline。

這四大組件對應爬蟲生命周期中的處理管理下載持久化等功能。

這四個組件都是Spider中的屬性,爬蟲框架通過Spider啟動和管理。

WebMagic總體架構圖如下:

四大組件

PageProcessor 負責解析頁面,抽取有用信息,以及發現新的鏈接。需要自己定義。

Scheduler 負責管理待抓取的URL,以及一些去重的工作。一般無需自己定制Scheduler。

Pipeline 負責抽取結果的處理,包括計算、持久化到文件、數據庫等。

Downloader 負責從互聯網上下載頁面,以便后續處理。一般無需自己實現。

用於數據流轉的對象

Request 是對URL地址的一層封裝,一個Request對應一個URL地址。

Page 代表了從Downloader下載到的一個頁面——可能是HTML,也可能是JSON或者其他文本格式的內容。

ResultItems 相當於一個Map,它保存PageProcessor處理的結果,供Pipeline使用。

環境配置

使用Maven來添加依賴的jar包。

<dependency>
    <groupId>us.codecraft</groupId>
    <artifactId>webmagic-core</artifactId>
    <version>0.7.3</version>
</dependency>
<dependency>
    <groupId>us.codecraft</groupId>
    <artifactId>webmagic-extension</artifactId>
    <version>0.7.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

或者直接摸我下載。

添加完jar包就完成了所有准備工作,是不是很簡單。

下面來測試一下。

package edu.heu.spider;

import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.pipeline.ConsolePipeline;
import us.codecraft.webmagic.processor.PageProcessor;

/**
 * @ClassName: MyCnblogsSpider
 * @author LJH
 * @date 2017年11月26日 下午4:41:40
 */
public class MyCnblogsSpider implements PageProcessor {

    private Site site = Site.me().setRetryTimes(3).setSleepTime(100);

    public Site getSite() {
        return site;
    }

    public void process(Page page) {
        if (!page.getUrl().regex("http://www.cnblogs.com/[a-z 0-9 -]+/p/[0-9]{7}.html").match()) {
            page.addTargetRequests(
                    page.getHtml().xpath("//*[@id=\"mainContent\"]/div/div/div[@class=\"postTitle\"]/a/@href").all());
        } else {
            page.putField(page.getHtml().xpath("//*[@id=\"cb_post_title_url\"]/text()").toString(),
                    page.getHtml().xpath("//*[@id=\"cb_post_title_url\"]/@href").toString());
        }
    }
public static void main(String[] args) { Spider.create(new MyCnblogsSpider()).addUrl("http://www.cnblogs.com/justcooooode/") .addPipeline(new ConsolePipeline()).run(); } }

輸出結果:

如果你和我一樣之前沒有用過log4j,可能會出現下面的警告:

這是因為少了配置文件,在resource目錄下新建log4j.properties文件,將下面配置信息粘貼進去即可。

目錄可以定義成你自己的文件夾。

# 全局日志級別設定 ,file
log4j.rootLogger=INFO, stdout, file

# 自定義包路徑LOG級別
log4j.logger.org.quartz=WARN, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{MM-dd HH:mm:ss}[%p]%m%n

# Output to the File
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=D:\\MyEclipse2017Workspaces\\webmagic\\webmagic.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%n%-d{MM-dd HH:mm:ss}-%C.%M()%n[%p]%m%n

現在試一下,沒有警告了吧😊。

爬取列表類網站例子

列表爬取的思想都很類似,首先判定是否為列表頁,若是的話將文章url加入爬取隊列,不是的話就代表此時為文章頁,直接爬取你要的內容就可以。

選擇一個列表類文章的網站:https://voice.hupu.com/nba

首先判斷是文章還是列表,查看幾個頁面后可以找到規律,利用正則表達式區分。

page.getUrl().regex("https://voice\\.hupu\\.com/nba/[0-9]{7}\\.html").match()

如果滿足上面的正則表達式,則該url對應的是一個文章頁面。

接下來要對需要爬取的內容進行抽取,我選擇了xPath(瀏覽器自帶直接粘貼就可以)。

WebMagic框架支持多種抽取方式,包括xPath、css選擇器、正則表達式,還可以通過links()方法選擇所有鏈接。

記住抽取之前要獲得通過getHtml()來獲取html對象,通過html對象來使用抽取方法。

ps:WebMagic好像不支持xPath中的last()方法,如果用到的話可以想其他方法代替。

然后利用page.putFiled(String key, Object field)方法來將你想要的內容放到一個鍵值對中。

page.putField("Title", page.getHtml().xpath("/html/body/div[4]/div[1]/div[1]/h1/text()").toString());
page.putField("Content", page.getHtml().xpath("/html/body/div[4]/div[1]/div[2]/div/div[2]/p/text()").all().toString());

如果不滿足文章頁的正則,就說明這是一個列表頁,此時要通過xPath來定位頁面中文章的url。

page.getHtml().xpath("/html/body/div[3]/div[1]/div[2]/ul/li/div[1]/h4/a/@href").all();

這時,你已經得到要爬取url的list,把他們通過addTargetRequests方法加入到隊列中即可。

最后實現翻頁,同理,WebMagic會自動添加到爬取隊列中。

page.getHtml().xpath("/html/body/div[3]/div[1]/div[3]/a[@class='page-btn-prev']/@href").all()

下面是完整代碼,自己實現了一個MysqlPipeline類,用到了Mybatis,可以將爬下來的數據直接持久化到數據庫中。

也可以用自帶的ConsolePipeline或者FilePipeline等。

package edu.heu.spider;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import edu.heu.domain.News;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;
import us.codecraft.webmagic.processor.PageProcessor;

/**
 * @ClassName: HupuNewsSpider
 * @author LJH
 * @date 2017年11月27日 下午4:54:48
 */
public class HupuNewsSpider implements PageProcessor {

    // 抓取網站的相關配置,包括編碼、抓取間隔、重試次數等
    private Site site = Site.me().setRetryTimes(3).setSleepTime(100);

    public Site getSite() {
        return site;
    }

    public void process(Page page) {
        // 文章頁,匹配 https://voice.hupu.com/nba/七位數字.html
        if (page.getUrl().regex("https://voice\\.hupu\\.com/nba/[0-9]{7}\\.html").match()) {
            page.putField("Title", page.getHtml().xpath("/html/body/div[4]/div[1]/div[1]/h1/text()").toString());
            page.putField("Content",
                    page.getHtml().xpath("/html/body/div[4]/div[1]/div[2]/div/div[2]/p/text()").all().toString());
        }
        // 列表頁
        else {
            // 文章url
            page.addTargetRequests(
                    page.getHtml().xpath("/html/body/div[3]/div[1]/div[2]/ul/li/div[1]/h4/a/@href").all());
            // 翻頁url
            page.addTargetRequests(
                    page.getHtml().xpath("/html/body/div[3]/div[1]/div[3]/a[@class='page-btn-prev']/@href").all());
        }
    }
public static void main(String[] args) { Spider.create(new HupuNewsSpider()).addUrl("https://voice.hupu.com/nba/1").addPipeline(new MysqlPipeline()) .thread(3).run(); } } // 自定義實現Pipeline接口 class MysqlPipeline implements Pipeline { public MysqlPipeline() { } public void process(ResultItems resultitems, Task task) { Map<String, Object> mapResults = resultitems.getAll(); Iterator<Entry<String, Object>> iter = mapResults.entrySet().iterator(); Map.Entry<String, Object> entry; // 輸出到控制台 while (iter.hasNext()) { entry = iter.next(); System.out.println(entry.getKey() + ":" + entry.getValue()); } // 持久化 News news = new News(); if (!mapResults.get("Title").equals("")) { news.setTitle((String) mapResults.get("Title")); news.setContent((String) mapResults.get("Content")); } try { InputStream is = Resources.getResourceAsStream("conf.xml"); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is); SqlSession session = sessionFactory.openSession(); session.insert("add", news); session.commit(); session.close(); } catch (IOException e) { e.printStackTrace(); } } }

查看數據庫:😊

爬好的數據已經靜靜地躺在數據庫中了。

官方文檔中還介紹了通過注解來實現各種功能,非常簡便靈活。

使用xPath時要留意,框架作者自定義了幾個函數:

Expression Description XPath1.0
text(n) 第n個直接文本子節點,為0表示所有 text() only
allText() 所有的直接和間接文本子節點 not support
tidyText() 所有的直接和間接文本子節點,並將一些標簽替換為換行,使純文本顯示更整潔 not support
html() 內部html,不包括標簽的html本身 not support
outerHtml() 內部html,包括標簽的html本身 not support
regex(@attr,expr,group) 這里@attr和group均可選,默認是group0 not support

 使用起來很方便。


轉載請注明原文鏈接:http://www.cnblogs.com/justcooooode/p/7913365.html

參考資料 

官方文檔:https://github.com/code4craft/webmagic

http://webmagic.io/docs/zh/

xPath教程:http://www.w3school.com.cn/xpath/index.asp


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM