Java爬虫框架WebMagic入门管理——爬取列表类网站文章

 本文紧要基于marathon-lb已毕mesos+marathon平台的的劳动自发现与负载均衡。

条件布置

行使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();
    }
}

输出结果:

管理 1

如果您和自身同一此前未曾用过log4j,可能会并发上面的警示:

管理 2

这是因为少了计划文件,在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

方今试一下,没有警示了吧。

2)访问服务

#web服务下的两个instance都被重置过,这里修改新instance容器的index.html文件
[root@slave-node2 ~]# echo "This is Nginx Server: 10.11.4.147:31288" > index.html
[root@slave-node2 ~]# docker cp /root/index.html 9489b51370fd:/usr/share/nginx/html
[root@slave-node2 ~]# echo "This is Nginx Server: 10.11.4.147:31646" > index.html
[root@slave-node2 ~]# docker cp /root/index.html 2aee995a6ce7:/usr/share/nginx/html

(1)访问vip的10002端口;

URL:http://10.11.4.145:10002

管理 3

管理 4

(2)通过vip访问marathon-lb状态页。

URL:http://10.11.4.145:9090/haproxy?stats

管理 5

爬取列表类网站例子

列表爬取的思辨都很相近,首先判定是还是不是为列表页,即使的话将小说url加入爬取队列,不是的话就代表此时为文章页,直接爬取你要的始末就可以。

选拔一个列表类作品的网站:https://voice.hupu.com/nba

管理 6

率先判断是文章仍旧列表,查看多少个页面后可以找到规律,利用正则表明式区分。

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

如果知足上面的正则表明式,则该url对应的是一个篇章页面。

接下去要对急需爬取的情节开展抽取,我选取了xPath(浏览器自带直接粘贴就可以)。

管理 7

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。

管理 8

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

那儿,你早已收获要爬取url的list,把她们经过addTargetRequests办法参加到行列中即可。

末段完毕翻页,同理,WebMagic会自动添加到爬取队列中。

管理 9

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();
        }
    }
}

查阅数据库:

管理 10

爬好的数额已经静静地躺在数据库中了。

官方文档中还介绍了经过申明来促成各类功效,卓殊便利灵活。

行使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

1. 环境认证

管理 11

根据《基于zookeeper+mesos+marathon的docker集群管理平台》的条件,差别处:

  1. 自发现与负载均衡服务由镜像marathon-lb生成的器皿完结,下发到自由1台salve节点;
  1. 怀有salve节点安装keepalived服务做劳务的高可用,vip:10.11.5.145。 

用以数据流转的靶子

Request 是对URL地址的一层封装,一个Request对应一个URL地址。

Page 代表了从Downloader下载到的一个页面——可能是HTML,也说不定是JSON或者其它文本格式的内容。

ResultItems 一定于一个Map,它保存PageProcessor处理的结果,供Pipeline使用。

1)设置iptables

#marathon-lb生成的haproxy容器映射到宿主机的tcp 9090端口,可以在相应宿主机通过”netstat -tunlp | grep haproxy”查看;
#需要在所有的宿主机,即slave节点放行tcp 9090端口,因为在没有指定特定主机的时候(在slave启动参数中设置机器属性 --attributes=VALUE),通过marathon下发的marathon-lb容器是随机下发到任意slave节点的;
#不重启iptables,如果重启则docker daemon也需要重启,否则iptables相关的docker的forward规则会丢失;或者在没有容器的情况下提前放行相应端口再做容器相关操作
[root@slave-node1 ~]# iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 9090 -j ACCEPT

 初学爬虫,WebMagic作为一个Java开发的爬虫框架很不难上手,上面就经过一个简单易行的小例子来看一下。

四.keepalived高可用配置

对此marathon-lb或者其后端的real-server来说,对外提供的劳务都依靠marathon-lb容器所在slave节点本身的ip,就算marathon能长久保持服务在线,但对外提供劳动的ip的生成依旧内需高可用的装置,那里运用keepalived已毕,vip:10.11.4.145。

Keepalived服务须求安装配置在具有marathon-lb可能下发到的slave节点。

四大组件

PageProcessor 担当解析页面,抽取有用新闻,以及发现新的链接。要求自己定义。

Scheduler 顶住管理待抓取的URL,以及一些去重的干活。一般无需协调定制Scheduler。

Pipeline 承担抽取结果的处理,包含总计、持久化到文件、数据库等。

Downloader 顶住从互连网上下载页面,以便后续处理。一般无需自己落成。

2. keepalived配置文件

#各slave节点的keepalived配置文件根据情况做微小调整
[root@slave-node1 ~]# vim /usr/local/keepalived/etc/keepalived/keepalived.conf

! Configuration File for keepalived

global_defs {
   notification_email {
     root@localhost.local
   }
   notification_email_from root@localhost.local
   smtp_server 10.11.4.146
   smtp_connect_timeout 30
   router_id MARATHON_DEVEL
}

vrrp_script chk_marathonlb {
    script "/usr/local/keepalived/etc/chk_marathonlb.sh"
    interval 1
    weight 2          //脚本检测返回值为”0”时,权重上升
    rise 1
}

vrrp_instance VI_1 {
    state BACKUP
    interface br0
    virtual_router_id 201
    priority 101
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 987654
    }
    virtual_ipaddress {
        10.11.4.145
    }
    track_script {
        chk_marathonlb
    }
}

参考资料 

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

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

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

三.验证自动发现与负载均衡

因而布署nginx服务可以印证自动发现与负载均衡两项职能。

WebMagic框架简介

WebMagic框架包罗三个零部件,PageProcessorSchedulerDownloaderPipeline。

那四大组件对应爬虫生命周期中的处理管理下载持久化等功能。

那八个零件都是Spider中的属性,爬虫框架通过Spider启动和管理。

WebMagic总体架构图如下:

管理 12

二.部署marathon-lb

1)启动并查看vip

#验证过程中,marathon-lb所在的slave1节点宕机,marathon为了保持服务,将marathon-lb重新下发到在salve2节点
[root@slave-node2 ~]# systemctl daemon-reload
[root@slave-node2 ~]# systemctl restart keepalived
[root@slave-node2 ~]# ip a show br0

管理 13

4. 查看marathon-lb

一.环境

2)方式2:通过marathon web下发json配置

管理 14

3. marathon-lb检测脚本

#根据配置文件中的配置设定检测脚本
[root@slave-node1 ~]# touch /usr/local/keepalived/etc/chk_marathonlb.sh
[root@slave-node1 ~]# chmod +x /usr/local/keepalived/etc/chk_marathonlb.sh
[root@slave-node1 ~]# vim /usr/local/keepalived/etc/chk_marathonlb.sh

#!/bin/bash
# check if thers is a matathon-lb container running, then this is a keepalived master.
# 2017-09-18 v0.1 

MARATHONLB_LOG="/tmp/marathon-lb.log"

MARATHONLB=`netstat -tunlp | grep "haproxy" | grep ":80" | awk '{print $4}' | awk  'BEGIN{FS=":"} {print $2}'`

#检测marathon-lb服务的80端口,若存在则返回”0”,不存在返回”1”;
#记录日志,非必须项
if [ ${MARATHONLB} -eq 80 ]; then
     echo -e "`date +%F\ %T`: Matathon-lb is here, exit!\n" >> $MARATHONLB_LOG
     exit 0

else
     echo -e "`date +%F\ %T`: Matathon-lb is not here, the keepalived weight will be downgraded." >> $MARATHONLB_LOG
     exit 1
fi

2)mesos web

mesos
active tasks中突显marathon-lb任务下发成功,如下:

管理 15

3)访问haproxy提供的劳动

(1)做客marathon-lb所在slave节点的10002端口(在marathon
ui服务的configuration能够查询分配的servicePort);

URL:http://10.11.4.146:10002

管理 16

管理 17

PS:10002端口须要在相应节点不重启iptables的意况下放行,可以实施iptables
-I INPUT -m state –state NEW -m tcp -p tcp –dport 10002 -j
ACCEPT

(2)查看marathon-lb
haproxy的配置;

URL:http://10.11.4.146:9090/_haproxy_getconfig

管理 18

PS:或者登陆到marathon-lb容器中查看haproxy.cfg文件(效果一样)。

(3)查看marathon-lb
haproxy的情景页;

URL:http://10.11.4.146:9090/haproxy?stats

管理 19

1)方式1:通过curl调用json配置

#调用保存有json文件的maste节点8080端口下的json文件,即调用marathon的api
[root@master-node1 ~]# curl -X POST http://10.11.4.156:8080/v2/apps -d@/root/marathon-lb.json -H "Content-type:application/json"

1. 安装keepalived

Keepalived服务必要安装配置在享有marathon-lb可能下发到的slave节点。

keepalived版本:keepalived-1.3.6

切切实实的设置步骤请见:http://www.cnblogs.com/netonline/p/7598744.html

PS:注意各keepalived节点之间打招呼拔取组播,iptables须求放行组播或vrrp相关协商。

参照文档:

1. 准备marathon-lb镜像

#在3个slave节点提前准备镜像,以节省容器部署时间
[root@slave-node1 ~]# docker pull mesosphere/marathon-lb
[root@slave-node1 ~]# docker images

管理 20

1. 编辑生成nginx服务的json文件

#在任意marathon master节点上编写json文件
[root@master-node1 ~]# vim marathon-nginx.json

#“labels”中的“HAPROXY_GROUP”,与相关的marathon-lb绑定即可,“group”在下发marathon-lb容器时已经定义;
#“labels”中"HAPROXY_0_VHOST"主要起标签作用;对于web服务可以加上VHOST标签,让marathon-lb设置WEB虚拟主机;标签名字自定义,目的是为了便于区别应用容器,一般可以用业务域名来描述标签;
#"instances",实例数;
#"healthChecks",对应示例健康状态检测点;
#"portMappings"中的"containerPort"是容器应用端口;"hostPort"是映射的宿主机的端口(设置为”0”即随机);"servicePort"是marathon-lb配置的haproxy代理端口(设置为”0”即随机),设置"servicePort"对一组实例服务非常有用;
{
  "id":"web",
  "labels": {
     "HAPROXY_GROUP":"external",
     "HAPROXY_0_VHOST":"web.nginx.com"
  },
  "cpus":0.2,
  "mem":20.0,
  "instances": 2,
  "healthChecks": [{ "path": "/" }],
  "container": {
    "type":"DOCKER",
    "docker": {
     "image": "nginx",
     "network": "BRIDGE",
     "portMappings":[{"containerPort":80,"hostPort":0,"servicePort":0,"protocol":"tcp"}]
    }
  }
}

3. 下发marathon-lb容器

1)marathon web

marathon-lb作为一种长服务,已经下发,运行如常,如下:

管理 21

marathon-lb已下发到slave节点10.11.4.146:31187,如下:

管理 22

4. 验证

2. 拓扑编写marathon-lb的json文件

#在任意marathon master节点上编写关于marathon-lb的json文件
[root@master-node1 ~]# vim marathon-lb.json

#”network”采用HOST模式,与宿主机共享namespace;
#”args”参数将3个marathon master节点的ip:8080都带上,即marahon的api接口地址,因为不确定marathon下发marathon-lb容器时,会下发到哪一台宿主机;
#”group”参数为”external”,可以定义多个不同的”group”(即不同的marathon-lb),针对不同的应用可以绑定到不同的”group”;
#请注意json文件的格式,如标点符号等,marathon web下的”JSON Mode”框有检查语法正确与否的功能。
{
  "id":"marathon-lb",
  "cpus": 1,
  "mem": 128,
  "instances": 1,
  "constraints": [["hostname", "UNIQUE"]],
  "container": {
  "type":"DOCKER",
  "docker": {
     "image": "mesosphere/marathon-lb",
     "privileged": true,
     "network": "HOST"
    }
  },
   "args": ["sse", "-m", "http://10.11.4.156:8080", "-m", "http://10.11.4.157:8080", "-m", "http://10.11.4.158:8080", "--group", "external"]
}

2)haproxy监控页面

浏览器查看:http://10.11.4.146:9090/haproxy?stats

管理 23

除此以外还有部分haproxy监控页面(ip地址是宿主机,即相应slave节点的ip),如下:

2. 下发nginx服务

#采用上述方式1下发服务即可;
#nginx镜像已经提前准备
[root@master-node1 ~]# curl -X POST http://10.11.4.156:8080/v2/apps -d@/root/marathon-nginx.json -H "Content-type:application/json"

1)查看marathon&mesoso ui

(1)marathon
ui突显web服务已经是”Running”状态,且服务名后带标签;

管理 24

(2)点击查看服务的切实可行新闻,”Instances”标签中体现了2个instance容器安排的slave节点与映射的端口音讯(能够在切切实实的slave节点通过”docker
ps”命令查看),同时健康检查状态正常;

管理 25

(3)
“Configuration”标签中有显示服务的切实可行布置消息,同json文件,关切标红框的”servicePort”是10002,此服务端口是随意分配的,可经过marathon-lb所在节点的ip+port的章程访问服务,而不是造访单独的某部instance节点提供的劳动;

管理 26

(4)mesos
ui展示呈现web服务的七个instance职分现已下发。

管理 27

5. 查看haproxy

3. 验证

2)访问容器instance提供的服务

#可以先将2个nginx instance的web页面提前修改;
#如marathon ui展示,1个nginx容器在slave节点10.11.4.146:31005
#在10.11.4.146,即slave-node1节点编写index.html文件,使用”docker cp”将文件覆盖对应容器的默认web站点目录”/usr/share/nginx/html”下的index.html文件
[root@slave-node1 ~]# echo "This is Nginx Server: 10.11.4.146:31005" >> index.html
[root@slave-node1 ~]# docker cp /root/index.html 2c7448e2d185:/usr/share/nginx/html

管理 28

#如marathon ui展示,另1个nginx容器在slave节点10.11.4.147:31071;
#10.11.4.147,即slave-node2节点
[root@slave-node2 ~]# echo "This is Nginx Server: 10.11.4.147:31071" >> index.html
[root@slave-node2 ~]# docker cp /root/index.html 11060abfad4f:/usr/share/nginx/html

管理 29

  1. Marathon-lb介绍:https://docs.mesosphere.com/1.9/networking/marathon-lb/
  2. 参考:http://www.cnblogs.com/kevingrace/p/6845980.html
  3. 按照脚本实现服务自发现与负载均衡(供参考,marathon-lb此前的方案):

    http://dockone.io/article/439

    https://github.com/draculavlad/SetUpMesosphereOnCentos7WithServiceDiscovery/

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注