webmagic_webmagic 获取文本 - CSDN
精华内容
参与话题
  • 一、WebMagic简介   参见网上其他介绍。 二、添加依赖 <!-- webmagic 核心包 --> <dependency> <...

    一、WebMagic简介
      参见网上其他介绍。

    二、添加依赖

    		<!-- webmagic 核心包 -->
    		<dependency>
    			<groupId>us.codecraft</groupId>
    			<artifactId>webmagic-core</artifactId>
    			<version>0.7.3</version>
    			<exclusions>
    				<exclusion>
    					<!-- 自带的commons-collections用不了 -->
    					<groupId>commons-collections</groupId>
    					<artifactId>commons-collections</artifactId>
    				</exclusion>
    			</exclusions>
    		</dependency>
    		<!-- webmagic 扩展包 -->
    		<dependency>
    			<groupId>us.codecraft</groupId>
    			<artifactId>webmagic-extension</artifactId>
    			<version>0.7.3</version>
    			<exclusions>
    				<exclusion>
    					<!-- 默认是log4j12,若要用其他日志框架,请排除 -->
    					<groupId>org.slf4j</groupId>
    					<artifactId>slf4j-log4j12</artifactId>
    				</exclusion>
    			</exclusions>
    		</dependency>
    		<!-- webmagic-selenium 对selenium支持 -->
    		<dependency>
    			<groupId>us.codecraft</groupId>
    			<artifactId>webmagic-selenium</artifactId>
    			<version>0.7.3</version>
    		</dependency>
    		<!-- commons-collections -->
    		<dependency>
    			<groupId>commons-collections</groupId>
    			<artifactId>commons-collections</artifactId>
    			<version>3.2.1</version>
    		</dependency>
    	</dependencies>
    

    注意要点1:webmagic-core 包下有 commons-collections 依赖包,但是该包在3.2.2版本有点问题,需要将其剔除出来,使用3.2.1版本的JAR包
    注意要点2:webmagic-extension 默认使用的日志框架是 slf4j-log4j12,若项目中使用了其他日志框架,需要将其排除。

    三、案例

    package org.pc.webmagic;
    
    import org.pc.webmagic.error.HttpClientDownloader;
    import us.codecraft.webmagic.Page;
    import us.codecraft.webmagic.Site;
    import us.codecraft.webmagic.Spider;
    import us.codecraft.webmagic.processor.PageProcessor;
    
    /**
     * @author 咸鱼
     * @date 2018/12/28 20:08
     */
    public class GithubRepoPageProcessor implements PageProcessor {
    
        /**
         * 部分一:抓去网站的相关配置,包括编码、抓去间隔、重试次数
         */
        private Site site = Site.me().setCycleRetryTimes(3).setSleepTime(1000).setTimeOut(10000);
    
        @Override
        public void process(Page page) {
            page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());
            page.putField("author", page.getUrl().regex("\"https://github\\.com/(\\w+)/.*\"").toString());
            page.putField("name", page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()").toString());
            if (page.getResultItems().get("name") == null){
                page.setSkip(true);
            }
            page.putField("readme", page.getHtml().xpath("//div[@id='readme']/tidyText()"));
        }
    
        @Override
        public Site getSite() {
            return site;
        }
        public static void main(String[] args) {
            Spider.create(new GithubRepoPageProcessor())
                    .addUrl("https://github.com/code4craft")
                    //覆盖默认的实现 HttpClientDownloader,因为使用webmagic爬取 https 网站时,会
                    .setDownloader(new HttpClientDownloader())
                    .thread(5)
                    .run();
        }
    }
    
    

    问题:这个案例在运行的时候会报javax.net.ssl.SSLException: Received fatal alert: protocol_versio错误?
    原因:绕过ssl时,没有支持版本。

    解决办法一:直接去 github clone修复后的 webmagic-core ,然后设置 commons-collections 的版本号为 3.2.1,然后重新打成Jar包(会打成webmagic-core-0.7.3.jar、webmagic-core-0.7.3-javadoc.jar、webmagic-core-0.7.3-sources.jar),然后替代本地仓库的Jar包(推荐使用!!!)。

    解决办法二:
    核心是重写HttpClientGenerator#buildSSLConnectionSocketFactory()方法

    private SSLConnectionSocketFactory buildSSLConnectionSocketFactory() {
            try {
                // 优先绕过安全证书
                return new SSLConnectionSocketFactory(createIgnoreVerifySSL(), new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"},
                        null,
                        new DefaultHostnameVerifier());
            } catch (KeyManagementException e) {
                logger.error("ssl connection fail", e);
            } catch (NoSuchAlgorithmException e) {
                logger.error("ssl connection fail", e);
            }
            return SSLConnectionSocketFactory.getSocketFactory();
        }
    

    第一步:重写 HttpClientGenerator

    package org.pc.webmagic.error;
    
    import org.apache.http.HttpException;
    import org.apache.http.HttpRequest;
    import org.apache.http.HttpRequestInterceptor;
    import org.apache.http.client.CookieStore;
    import org.apache.http.config.Registry;
    import org.apache.http.config.RegistryBuilder;
    import org.apache.http.config.SocketConfig;
    import org.apache.http.conn.socket.ConnectionSocketFactory;
    import org.apache.http.conn.socket.PlainConnectionSocketFactory;
    import org.apache.http.conn.ssl.DefaultHostnameVerifier;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.impl.client.*;
    import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
    import org.apache.http.impl.cookie.BasicClientCookie;
    import org.apache.http.protocol.HttpContext;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import us.codecraft.webmagic.Site;
    import us.codecraft.webmagic.downloader.CustomRedirectStrategy;
    
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    import java.io.IOException;
    import java.security.KeyManagementException;
    import java.security.NoSuchAlgorithmException;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import java.util.Map;
    
    /**
     * @author 咸鱼
     * @date 2018/12/28 21:19
     */
    public class HttpClientGenerator {
    
        private transient Logger logger = LoggerFactory.getLogger(getClass());
    
        private PoolingHttpClientConnectionManager connectionManager;
    
        public HttpClientGenerator() {
            Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", buildSSLConnectionSocketFactory())
                    .build();
            connectionManager = new PoolingHttpClientConnectionManager(reg);
            connectionManager.setDefaultMaxPerRoute(100);
        }
    
        private SSLConnectionSocketFactory buildSSLConnectionSocketFactory() {
            try {
                // 优先绕过安全证书
                return new SSLConnectionSocketFactory(createIgnoreVerifySSL(), new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"},
                        null,
                        new DefaultHostnameVerifier());
            } catch (KeyManagementException e) {
                logger.error("ssl connection fail", e);
            } catch (NoSuchAlgorithmException e) {
                logger.error("ssl connection fail", e);
            }
            return SSLConnectionSocketFactory.getSocketFactory();
        }
    
        private SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
            // 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
            X509TrustManager trustManager = new X509TrustManager() {
    
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }
    
                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }
    
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
    
            };
    
            SSLContext sc = SSLContext.getInstance("SSLv3");
            sc.init(null, new TrustManager[] { trustManager }, null);
            return sc;
        }
    
        public HttpClientGenerator setPoolSize(int poolSize) {
            connectionManager.setMaxTotal(poolSize);
            return this;
        }
    
        public CloseableHttpClient getClient(Site site) {
            return generateClient(site);
        }
    
        private CloseableHttpClient generateClient(Site site) {
            HttpClientBuilder httpClientBuilder = HttpClients.custom();
    
            httpClientBuilder.setConnectionManager(connectionManager);
            if (site.getUserAgent() != null) {
                httpClientBuilder.setUserAgent(site.getUserAgent());
            } else {
                httpClientBuilder.setUserAgent("");
            }
            if (site.isUseGzip()) {
                httpClientBuilder.addInterceptorFirst(new HttpRequestInterceptor() {
    
                    @Override
                    public void process(
                            final HttpRequest request,
                            final HttpContext context) throws HttpException, IOException {
                        if (!request.containsHeader("Accept-Encoding")) {
                            request.addHeader("Accept-Encoding", "gzip");
                        }
                    }
                });
            }
            //解决post/redirect/post 302跳转问题
            httpClientBuilder.setRedirectStrategy(new CustomRedirectStrategy());
    
            SocketConfig.Builder socketConfigBuilder = SocketConfig.custom();
            socketConfigBuilder.setSoKeepAlive(true).setTcpNoDelay(true);
            socketConfigBuilder.setSoTimeout(site.getTimeOut());
            SocketConfig socketConfig = socketConfigBuilder.build();
            httpClientBuilder.setDefaultSocketConfig(socketConfig);
            connectionManager.setDefaultSocketConfig(socketConfig);
            httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(site.getRetryTimes(), true));
            generateCookie(httpClientBuilder, site);
            return httpClientBuilder.build();
        }
    
        private void generateCookie(HttpClientBuilder httpClientBuilder, Site site) {
            if (site.isDisableCookieManagement()) {
                httpClientBuilder.disableCookieManagement();
                return;
            }
            CookieStore cookieStore = new BasicCookieStore();
            for (Map.Entry<String, String> cookieEntry : site.getCookies().entrySet()) {
                BasicClientCookie cookie = new BasicClientCookie(cookieEntry.getKey(), cookieEntry.getValue());
                cookie.setDomain(site.getDomain());
                cookieStore.addCookie(cookie);
            }
            for (Map.Entry<String, Map<String, String>> domainEntry : site.getAllCookies().entrySet()) {
                for (Map.Entry<String, String> cookieEntry : domainEntry.getValue().entrySet()) {
                    BasicClientCookie cookie = new BasicClientCookie(cookieEntry.getKey(), cookieEntry.getValue());
                    cookie.setDomain(domainEntry.getKey());
                    cookieStore.addCookie(cookie);
                }
            }
            httpClientBuilder.setDefaultCookieStore(cookieStore);
        }
    
    }
    
    

    第二步:重写 HttpClientDownloader

    package org.pc.webmagic.error;
    
    import org.apache.commons.io.IOUtils;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.util.EntityUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import us.codecraft.webmagic.Page;
    import us.codecraft.webmagic.Request;
    import us.codecraft.webmagic.Site;
    import us.codecraft.webmagic.Task;
    import us.codecraft.webmagic.downloader.AbstractDownloader;
    import us.codecraft.webmagic.downloader.HttpClientRequestContext;
    import us.codecraft.webmagic.downloader.HttpUriRequestConverter;
    import us.codecraft.webmagic.proxy.Proxy;
    import us.codecraft.webmagic.proxy.ProxyProvider;
    import us.codecraft.webmagic.selector.PlainText;
    import us.codecraft.webmagic.utils.CharsetUtils;
    import us.codecraft.webmagic.utils.HttpClientUtils;
    
    import java.io.IOException;
    import java.nio.charset.Charset;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author 咸鱼
     * @date 2018/12/28 21:22
     */
    public class HttpClientDownloader extends AbstractDownloader {
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        private final Map<String, CloseableHttpClient> httpClients = new HashMap<String, CloseableHttpClient>();
    
        private HttpClientGenerator httpClientGenerator = new HttpClientGenerator();
    
        private HttpUriRequestConverter httpUriRequestConverter = new HttpUriRequestConverter();
    
        private ProxyProvider proxyProvider;
    
        private boolean responseHeader = true;
    
        public void setHttpUriRequestConverter(HttpUriRequestConverter httpUriRequestConverter) {
            this.httpUriRequestConverter = httpUriRequestConverter;
        }
    
        public void setProxyProvider(ProxyProvider proxyProvider) {
            this.proxyProvider = proxyProvider;
        }
    
        private CloseableHttpClient getHttpClient(Site site) {
            if (site == null) {
                return httpClientGenerator.getClient(null);
            }
            String domain = site.getDomain();
            CloseableHttpClient httpClient = httpClients.get(domain);
            if (httpClient == null) {
                synchronized (this) {
                    httpClient = httpClients.get(domain);
                    if (httpClient == null) {
                        httpClient = httpClientGenerator.getClient(site);
                        httpClients.put(domain, httpClient);
                    }
                }
            }
            return httpClient;
        }
    
        @Override
        public Page download(Request request, Task task) {
            if (task == null || task.getSite() == null) {
                throw new NullPointerException("task or site can not be null");
            }
            CloseableHttpResponse httpResponse = null;
            CloseableHttpClient httpClient = getHttpClient(task.getSite());
            Proxy proxy = proxyProvider != null ? proxyProvider.getProxy(task) : null;
            HttpClientRequestContext requestContext = httpUriRequestConverter.convert(request, task.getSite(), proxy);
            Page page = Page.fail();
            try {
                httpResponse = httpClient.execute(requestContext.getHttpUriRequest(), requestContext.getHttpClientContext());
                page = handleResponse(request, request.getCharset() != null ? request.getCharset() : task.getSite().getCharset(), httpResponse, task);
                onSuccess(request);
                logger.info("downloading page success {}", request.getUrl());
                return page;
            } catch (IOException e) {
                logger.warn("download page {} error", request.getUrl(), e);
                onError(request);
                return page;
            } finally {
                if (httpResponse != null) {
                    //ensure the connection is released back to pool
                    EntityUtils.consumeQuietly(httpResponse.getEntity());
                }
                if (proxyProvider != null && proxy != null) {
                    proxyProvider.returnProxy(proxy, page, task);
                }
            }
        }
    
        @Override
        public void setThread(int thread) {
            httpClientGenerator.setPoolSize(thread);
        }
    
        protected Page handleResponse(Request request, String charset, HttpResponse httpResponse, Task task) throws IOException {
            byte[] bytes = IOUtils.toByteArray(httpResponse.getEntity().getContent());
            String contentType = httpResponse.getEntity().getContentType() == null ? "" : httpResponse.getEntity().getContentType().getValue();
            Page page = new Page();
            page.setBytes(bytes);
            if (!request.isBinaryContent()){
                if (charset == null) {
                    charset = getHtmlCharset(contentType, bytes);
                }
                page.setCharset(charset);
                page.setRawText(new String(bytes, charset));
            }
            page.setUrl(new PlainText(request.getUrl()));
            page.setRequest(request);
            page.setStatusCode(httpResponse.getStatusLine().getStatusCode());
            page.setDownloadSuccess(true);
            if (responseHeader) {
                page.setHeaders(HttpClientUtils.convertHeaders(httpResponse.getAllHeaders()));
            }
            return page;
        }
    
        private String getHtmlCharset(String contentType, byte[] contentBytes) throws IOException {
            String charset = CharsetUtils.detectCharset(contentType, contentBytes);
            if (charset == null) {
                charset = Charset.defaultCharset().name();
                logger.warn("Charset autodetect failed, use {} as charset. Please specify charset in Site.setCharset()", Charset.defaultCharset());
            }
            return charset;
        }
    }
    
    

    第三步:在Spider中设置重写后的HttpClientDownloader

    @Override
        public Site getSite() {
            return site;
        }
        public static void main(String[] args) {
            Spider.create(new GithubRepoPageProcessor())
                    .addUrl("https://github.com/code4craft")
                    //覆盖默认的实现 HttpClientDownloader,因为使用webmagic爬取 https 网站时,会报错
                    .setDownloader(new HttpClientDownloader())
                    .thread(5)
                    .run();
        }
    
    展开全文
  • Java爬虫(webmagic

    万次阅读 2016-10-18 16:30:00
    webMaigc学习地址(强烈推荐):...webmagic采用完全模块化的设计,功能覆盖整个爬虫的生命周期(链接提取、页面下载、内容抽取、持久化),支持多线程抓取,分布式抓取,并支持自动重试、

    webMaigc学习地址(强烈推荐):http://www.oschina.net/p/webmagic

    本人项目案例http://blog.csdn.net/u012385190/article/details/53393805

    本篇博客只是整理笔记。

      webmagic的是一个无须配置、便于二次开发的爬虫框架,它提供简单灵活的API,只需少量代码即可实现一个爬虫。webmagic采用完全模块化的设计,功能覆盖整个爬虫的生命周期(链接提取、页面下载、内容抽取、持久化),支持多线程抓取,分布式抓取,并支持自动重试、自定义UA/cookie等功能。

    一、概览

      WebMagic项目代码分为核心和扩展两部分。核心部分(webmagic-core)是一个精简的、模块化的爬虫实现,而扩展部分则包括一些便利的、实用性的功能(例如注解模式编写爬虫等)。
      WebMagic的结构分为Downloader、PageProcessor、Scheduler、Pipeline四大组件,并由Spider将它们彼此组织起来。这四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。而Spider则将这几个组件组织起来,让它们可以互相交互,流程化的执行,可以认为Spider是一个大的容器,它也是WebMagic逻辑的核心。
      
    WebMagic总体架构图如下:
    这里写图片描述

    1.1 WebMagic的四个组件

     1.Downloader

    Downloader负责从互联网上下载页面,以便后续处理。WebMagic默认使用了Apache HttpClient作为下载工具。

     2.PageProcessor

    PageProcessor负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用Jsoup作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup。在这四个组件中,PageProcessor对于每个站点每个页面都不一样,是需要使用者定制的部分。

     3.Scheduler

    Scheduler负责管理待抓取的URL,以及一些去重的工作。WebMagic默认提供了JDK的内存队列来管理URL,并用集合来进行去重。也支持使用Redis进行分布式管理。除非项目有一些特殊的分布式需求,否则无需自己定制Scheduler。

     4.Pipeline

    Pipeline负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic默认提供了“输出到控制台”和“保存到文件”两种结果处理方案。Pipeline定义了结果保存的方式,如果你要保存到指定数据库,则需要编写对应的Pipeline。对于一类需求一般只需编写一个Pipeline。

    1.2 用于数据流转的对象

     1. Request

    Request是对URL地址的一层封装,一个Request对应一个URL地址。它是PageProcessor与Downloader交互的载体,也是PageProcessor控制Downloader唯一方式。

     2. Page

    Page代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。Page是WebMagic抽取过程的核心对象,它提供一些方法可供抽取、结果保存等。

     3. ReusltItems

    ReusltItems相当于一个Map,它保存PageProcessor处理的结果,供Pipeline使用。它的API与Map很类似,值得注意的是它有一个字段skip,若设置为true,则不应被Pipeline处理。

    1.3 控制爬虫运转的引擎—Spider

      Spider是WebMagic内部流程的核心。Downloader、PageProcessor、Scheduler、Pipeline都是Spider的一个属性,这些属性是可以自由设置的,通过设置这个属性可以实现不同的功能。Spider也是WebMagic操作的入口,它封装了爬虫的创建、启动、停止、多线程等功能。
      对于编写一个爬虫,PageProcessor是需要编写的部分,而Spider则是创建和控制爬虫的入口。

    1.4 webMagic项目组成

    WebMagic项目代码包括几个部分,在根目录下以不同目录名分开。它们都是独立的Maven项目。
    WebMagic主要包括两个包,这两个包经过广泛实用,已经比较成熟:

     1.webmagic-core
    webmagic-core是WebMagic核心部分,只包含爬虫基本模块和基本抽取器。

     2.webmagic-extension
    webmagic-extension是WebMagic的主要扩展模块,提供一些更方便的编写爬虫的工具。包括注解格式定义爬虫、JSON、分布式等支持。

     3.实验性包
     包括webmagic-samples、webmagic-scripts、webmagic-selenium等;

    二、使用webMagic

      WebMagic主要包含两个jar包:webmagic-core-{version}.jar和webmagic-extension-{version}.jar。在项目中添加这两个包的依赖,即可使用WebMagic。
      
    maven中引入依赖jar包

    <dependency>
        <groupId>us.codecraft</groupId>
        <artifactId>webmagic-core</artifactId>
        <version>0.5.3</version>
    </dependency>
    <dependency>
        <groupId>us.codecraft</groupId>
        <artifactId>webmagic-extension</artifactId>
        <version>0.5.3</version>
    </dependency>

    不使用maven的用户,可以去http://webmagic.io中下载最新的jar包。

    三、基本的爬虫

      在WebMagic里,实现一个基本的爬虫只需要编写一个类,实现PageProcessor接口即可。这个类基本上包含了抓取一个网站,你需要写的所有代码。

    1 实现PageProcessor

    a、爬虫的配置

    // 部分一:抓取网站的相关配置,包括编码、抓取间隔、重试次数等
        private Site site = Site.me().setRetryTimes(3).setSleepTime(1000);

    b、页面元素的抽取
      对于下载到的Html页面,你如何从中抽取到你想要的信息?WebMagic里主要使用了三种抽取技术:XPath、正则表达式和CSS选择器。另外,对于JSON格式的内容,可使用JsonPath进行解析。
      
    b-1、XPath

    XPath本来是用于XML中获取元素的一种查询语言,但是用于Html也是比较方便的。例如:

    page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()")

    这段代码使用了XPath,它的意思是“查找所有class属性为’entry-title public’的h1元素,并找到他的strong子节点的a子节点,并提取a节点的文本信息”。

    b-2、CSS选择器

    CSS选择器是与XPath类似的语言。如果大家做过前端开发,肯定知道$(‘h1.entry-title’)这种写法的含义。客观的说,它比XPath写起来要简单一些,但是如果写复杂一点的抽取规则,就相对要麻烦一点。

    b-3、正则表达式

    正则表达式则是一种通用的文本抽取语言。  

    page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());

    这段代码就用到了正则表达式,它表示匹配所有”https://github.com/code4craft/webmagic“这样的链接。

    b-4、JsonPath

    JsonPath是于XPath很类似的一个语言,它用于从Json中快速定位一条内容。WebMagic中使用的JsonPath格式可以参考这里:https://code.google.com/p/json-path/

    c、链接的延伸

    一个站点的页面是很多的,一开始我们不可能全部列举出来,于是如何发现后续的链接,是一个爬虫不可缺少的一部分。

    page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());

    这段代码的分为两部分,page.getHtml().links().regex(“(https://github\.com/\w+/\w+)”).all()用于获取所有满足”(https:/ /github.com/\w+/\w+)”这个正则表达式的链接,page.addTargetRequests()则将这些链接加入到待抓取的队列中去。

    2 使用Selectable的链式API

      Selectable相关的链式API是WebMagic的一个核心功能。使用Selectable接口,你可以直接完成页面元素的链式抽取,也无需去关心抽取的细节。
      实现Selectable接口,这个接口包含一些重要的方法,我将它分为两类:抽取部分和获取结果部分。
      
    a-1 抽取部分API
      这里写图片描述
    例如,我现在要抓取github上所有的Java项目,这些项目可以在https://github.com/search?l=Java&p=1&q=stars%3A%3E1&s=stars&type=Repositories搜索结果中看到。为了避免抓取范围太宽,我指定只从分页部分抓取链接。这个抓取规则是比较复杂的,我会要怎么写呢?
    这里写图片描述

    首先看到页面的html结构是这个样子的:
    这里写图片描述
    那么我可以先用CSS选择器提取出这个div,然后在取到所有的链接。为了保险起见,我再使用正则表达式限定一下提取出的URL的格式,那么最终的写法是这样子的:

    List<String> urls = page.getHtml().css("div.pagination").links().regex(".*/search/\?l=java.*").all();

    然后,我们可以把这些URL加到抓取列表中去:

    List<String> urls = page.getHtml().css("div.pagination").links().regex(".*/search/\?l=java.*").all();
    page.addTargetRequests(urls);

    a-2 获取结果的API

      当链式调用结束时,我们一般都想要拿到一个字符串类型的结果。这时候就需要用到获取结果的API了。我们知道,一条抽取规则,无论是XPath、CSS选择器或者正则表达式,总有可能抽取到多条元素。WebMagic对这些进行了统一,你可以通过不同的API获取到一个或者多个元素。
      这里写图片描述
      
    3 保存结果

      WebMagic用于保存结果的组件叫做Pipeline。如我们通过“控制台输出结果”可以通过一个内置的ConsolePipeline来完成。想要把结果用Json的格式保存下来,可以将Pipeline的实现换成”JsonFilePipeline”。
      通过定制Pipeline,我们还可以实现保存结果到文件、数据库等一系列功能。

    4 爬虫的配置、启动和终止

    a-1 Spider

    Spider是爬虫启动的入口。在启动爬虫之前,我们需要使用一个PageProcessor创建一个Spider对象,然后使用run()进行启动。同时Spider的其他组件(Downloader、Scheduler、Pipeline)都可以通过set方法来进行设置。
    这里写图片描述
    这里写图片描述

    a-2  Site

    对站点本身的一些配置信息,例如编码、HTTP头、超时时间、重试策略等、代理等,都可以通过设置Site对象来进行配置。
    这里写图片描述

    5 抽取工具简介

    WebMagic的抽取主要用到了Jsoup和我自己开发的工具Xsoup。

    a-1 Jsoup

    Jsoup是一个简单的HTML解析器,同时它支持使用CSS选择器的方式查找元素。为了开发WebMagic,我对Jsoup的源码进行过详细的分析,具体文章参见Jsoup学习笔记

    a-2 Xsoup

    Xsoup是我基于Jsoup开发的一款XPath解析器。

    a-3 3 Saxon

    Saxon是一个强大的XPath解析器,支持XPath 2.0语法。webmagic-saxon是对Saxon尝试性的一个整合,但是目前看来,XPath 2.0的高级语法,似乎在爬虫开发中使用者并不多。

    6 爬虫的监控

    你可以查看爬虫的执行情况——已经下载了多少页面、还有多少页面、启动了多少线程等信息。该功能通过JMX实现,你可以使用Jconsole等JMX工具查看本地或者远程的爬虫信息。

    注意: 如果你自己定义了Scheduler,那么需要用这个类实现MonitorableScheduler接口,才能查看“LeftPageCount”和“TotalPageCount”这两条信息。

    6.1 为项目添加监控

    添加监控非常简单,获取一个SpiderMonitor的单例SpiderMonitor.instance(),并将你想要监控的Spider注册进去即可。你可以注册多个Spider到SpiderMonitor中。

    public class MonitorExample {
    
        public static void main(String[] args) throws Exception {
    
            Spider oschinaSpider = Spider.create(new OschinaBlogPageProcessor())
                    .addUrl("http://my.oschina.net/flashsword/blog");
            Spider githubSpider = Spider.create(new GithubRepoPageProcessor())
                    .addUrl("https://github.com/code4craft");
    
            SpiderMonitor.instance().register(oschinaSpider);
            SpiderMonitor.instance().register(githubSpider);
            oschinaSpider.start();
            githubSpider.start();
        }
    }

    6.2 查看监控信息

    WebMagic的监控使用JMX提供控制,你可以使用任何支持JMX的客户端来进行连接。我们这里以JDK自带的JConsole为例。我们首先启动WebMagic的一个Spider,并添加监控代码。然后我们通过JConsole来进行查看。

    我们按照4.6.1的例子启动程序,然后在命令行输入jconsole(windows下是在DOS下输入jconsole.exe)即可启动JConsole。
    这里写图片描述
    这里我们选择启动WebMagic的本地进程,连接后选择“MBean”,点开“WebMagic”,就能看到所有已经监控的Spider信息了!

    6.3 扩展监控接口

    除了已有的一些监控信息,如果你有更多的信息需要监控,也可以通过扩展的方式来解决。你可以通过继承SpiderStatusMXBean来实现扩展,具体例子可以看这里: 定制扩展demo

    四、使用注解编写爬虫

    WebMagic支持使用独有的注解风格编写一个爬虫,引入webmagic-extension包即可使用此功能。

    注解模式的开发方式是这样的:

    1.首先定义你需要抽取的数据,并编写类。
    2.在类上写明@TargetUrl注解,定义对哪些URL进行下载和抽取。
    3.在类的字段上加上@ExtractBy注解,定义这个字段使用什么方式进行抽取。
    4.定义结果的存储方式。
    代码如:

    @TargetUrl("https://github.com/\\w+/\\w+")
    @HelpUrl("https://github.com/\\w+")
    public class GithubRepo {
    
        @ExtractBy(value = "//h1[@class='entry-title public']/strong/a/text()", notNull = true)
        private String name;
    
        @ExtractByUrl("https://github\\.com/(\\w+)/.*")
        private String author;
    
        @ExtractBy("//div[@id='readme']/tidyText()")
        private String readme;
    
        public static void main(String[] args) {
            OOSpider.create(Site.me().setSleepTime(1000)
                    , new ConsolePageModelPipeline(), GithubRepo.class)
                    .addUrl("https://github.com/code4craft").thread(5).run();
        }
    }

    具体参考:http://webmagic.io/docs/zh/posts/ch5-annotation/README.html

    展开全文
  • WebMagic-使用入门

    万次阅读 多人点赞 2015-12-15 12:33:47
    WebMagic里,实现一个基本的爬虫只需要编写一个类,实现PageProcessor接口即可。这个类基本上包含了抓取一个网站,你需要写的所有代码。 同时这部分还会介绍如何使用WebMagic的抽取API,以及最常见的抓取结果保存...

    原文出自:http://webmagic.io/docs/zh 访问经常出错,于是把文档转到自己博客里

    基本的爬虫

    在WebMagic里,实现一个基本的爬虫只需要编写一个类,实现PageProcessor接口即可。这个类基本上包含了抓取一个网站,你需要写的所有代码。

    同时这部分还会介绍如何使用WebMagic的抽取API,以及最常见的抓取结果保存的问题。

    实现PageProcessor

    这部分我们直接通过GithubRepoPageProcessor这个例子来介绍PageProcessor的编写方式。我将PageProcessor的定制分为三个部分,分别是爬虫的配置、页面元素的抽取和链接的发现。

    public class GithubRepoPageProcessor implements PageProcessor {
    
        // 部分一:抓取网站的相关配置,包括编码、抓取间隔、重试次数等
        private Site site = Site.me().setRetryTimes(3).setSleepTime(1000);
    
        @Override
        // process是定制爬虫逻辑的核心接口,在这里编写抽取逻辑
        public void process(Page page) {
            // 部分二:定义如何抽取页面信息,并保存下来
            page.putField("author", page.getUrl().regex("https://github\\.com/(\\w+)/.*").toString());
            page.putField("name", page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()").toString());
            if (page.getResultItems().get("name") == null) {
                //skip this page
                page.setSkip(true);
            }
            page.putField("readme", page.getHtml().xpath("//div[@id='readme']/tidyText()"));
    
            // 部分三:从页面发现后续的url地址来抓取
            page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());
        }
    
        @Override
        public Site getSite() {
            return site;
        }
    
        public static void main(String[] args) {
    
            Spider.create(new GithubRepoPageProcessor())
                    //从"https://github.com/code4craft"开始抓
                    .addUrl("https://github.com/code4craft")
                    //开启5个线程抓取
                    .thread(5)
                    //启动爬虫
                    .run();
        }
    }
    

     爬虫的配置

    第一部分关于爬虫的配置,包括编码、抓取间隔、超时时间、重试次数等,也包括一些模拟的参数,例如User Agent、cookie,以及代理的设置,我们会在第5章-“爬虫的配置”里进行介绍。在这里我们先简单设置一下:重试次数为3次,抓取间隔为一秒。

    页面元素的抽取

    第二部分是爬虫的核心部分:对于下载到的Html页面,你如何从中抽取到你想要的信息?WebMagic里主要使用了三种抽取技术:XPath、正则表达式和CSS选择器。另外,对于JSON格式的内容,可使用JsonPath进行解析。

    1. XPath

      XPath本来是用于XML中获取元素的一种查询语言,但是用于Html也是比较方便的。例如:

       page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()")
      

      这段代码使用了XPath,它的意思是“查找所有class属性为'entry-title public'的h1元素,并找到他的strong子节点的a子节点,并提取a节点的文本信息”。 对应的Html是这样子的:

      xpath-html

    2. CSS选择器

      CSS选择器是与XPath类似的语言。如果大家做过前端开发,肯定知道$('h1.entry-title')这种写法的含义。客观的说,它比XPath写起来要简单一些,但是如果写复杂一点的抽取规则,就相对要麻烦一点。

    3. 正则表达式

      正则表达式则是一种通用的文本抽取语言。

       page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());
      

      这段代码就用到了正则表达式,它表示匹配所有"https://github.com/code4craft/webmagic"这样的链接。

    4. JsonPath

      JsonPath是于XPath很类似的一个语言,它用于从Json中快速定位一条内容。WebMagic中使用的JsonPath格式可以参考这里:https://code.google.com/p/json-path/

    链接的发现

    有了处理页面的逻辑,我们的爬虫就接近完工了!

    但是现在还有一个问题:一个站点的页面是很多的,一开始我们不可能全部列举出来,于是如何发现后续的链接,是一个爬虫不可缺少的一部分。

    page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());
    

    这段代码的分为两部分,page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all()用于获取所有满足"(https:/ /github\.com/\w+/\w+)"这个正则表达式的链接,page.addTargetRequests()则将这些链接加入到待抓取的队列中去。

    使用Selectable的链式API

    Selectable相关的链式API是WebMagic的一个核心功能。使用Selectable接口,你可以直接完成页面元素的链式抽取,也无需去关心抽取的细节。

    在刚才的例子中可以看到,page.getHtml()返回的是一个Html对象,它实现了Selectable接口。这个接口包含一些重要的方法,我将它分为两类:抽取部分和获取结果部分。

    抽取部分API:

    方法 说明 示例
    xpath(String xpath) 使用XPath选择 html.xpath("//div[@class='title']")
    $(String selector) 使用Css选择器选择 html.$("div.title")
    $(String selector,String attr) 使用Css选择器选择 html.$("div.title","text")
    css(String selector) 功能同$(),使用Css选择器选择 html.css("div.title")
    links() 选择所有链接 html.links()
    regex(String regex) 使用正则表达式抽取 html.regex("\
    (.*?)\")
    regex(String regex,int group) 使用正则表达式抽取,并指定捕获组 html.regex("\
    (.*?)\",1)
    replace(String regex, String replacement) 替换内容 html.replace("\","")

    这部分抽取API返回的都是一个Selectable接口,意思是说,抽取是支持链式调用的。下面我用一个实例来讲解链式API的使用。

    例如,我现在要抓取github上所有的Java项目,这些项目可以在https://github.com/search?l=Java&p=1&q=stars%3A%3E1&s=stars&type=Repositories搜索结果中看到。

    为了避免抓取范围太宽,我指定只从分页部分抓取链接。这个抓取规则是比较复杂的,我会要怎么写呢?

    selectable-chain-ui

    首先看到页面的html结构是这个样子的:

    selectable-chain

    那么我可以先用CSS选择器提取出这个div,然后在取到所有的链接。为了保险起见,我再使用正则表达式限定一下提取出的URL的格式,那么最终的写法是这样子的:

    List<String> urls = page.getHtml().css("div.pagination").links().regex(".*/search/\?l=java.*").all();
    

    然后,我们可以把这些URL加到抓取列表中去:

    List<String> urls = page.getHtml().css("div.pagination").links().regex(".*/search/\?l=java.*").all();
    page.addTargetRequests(urls);
    

    是不是比较简单?除了发现链接,Selectable的链式抽取还可以完成很多工作。我们会在第9章示例中再讲到。

    获取结果的API:

    当链式调用结束时,我们一般都想要拿到一个字符串类型的结果。这时候就需要用到获取结果的API了。我们知道,一条抽取规则,无论是XPath、CSS选择器或者正则表达式,总有可能抽取到多条元素。WebMagic对这些进行了统一,你可以通过不同的API获取到一个或者多个元素。

    方法 说明 示例
    get() 返回一条String类型的结果 String link= html.links().get()
    toString() 功能同get(),返回一条String类型的结果 String link= html.links().toString()
    all() 返回所有抽取结果 List links= html.links().all()
    match() 是否有匹配结果 if (html.links().match()){ xxx; }

    例如,我们知道页面只会有一条结果,那么可以使用selectable.get()或者selectable.toString()拿到这条结果。

    这里selectable.toString()采用了toString()这个接口,是为了在输出以及和一些框架结合的时候,更加方便。因为一般情况下,我们都只需要选择一个元素!

    selectable.all()则会获取到所有元素。

    好了,到现在为止,在回过头看看3.1中的GithubRepoPageProcessor,可能就觉得更加清晰了吧?指定main方法,已经可以看到抓取结果在控制台输出了。

    保存结果

    好了,爬虫编写完成,现在我们可能还有一个问题:我如果想把抓取的结果保存下来,要怎么做呢?WebMagic用于保存结果的组件叫做Pipeline。例如我们通过“控制台输出结果”这件事也是通过一个内置的Pipeline完成的,它叫做ConsolePipeline。那么,我现在想要把结果用Json的格式保存下来,怎么做呢?我只需要将Pipeline的实现换成"JsonFilePipeline"就可以了。

    public static void main(String[] args) {
        Spider.create(new GithubRepoPageProcessor())
                //从"https://github.com/code4craft"开始抓
                .addUrl("https://github.com/code4craft")
                .addPipeline(new JsonFilePipeline("D:\\webmagic\\"))
                //开启5个线程抓取
                .thread(5)
                //启动爬虫
                .run();
    }
    

    这样子下载下来的文件就会保存在D盘的webmagic目录中了。

    通过定制Pipeline,我们还可以实现保存结果到文件、数据库等一系列功能。这个会在第7章“抽取结果的处理”中介绍。

    至此为止,我们已经完成了一个基本爬虫的编写,也具有了一些定制功能。

    爬虫的配置、启动和终止

    Spider

    Spider是爬虫启动的入口。在启动爬虫之前,我们需要使用一个PageProcessor创建一个Spider对象,然后使用run()进行启动。同时Spider的其他组件(Downloader、Scheduler、Pipeline)都可以通过set方法来进行设置。

    方法 说明 示例
    create(PageProcessor) 创建Spider Spider.create(new GithubRepoProcessor())
    addUrl(String…) 添加初始的URL spider .addUrl("http://webmagic.io/docs/")
    addRequest(Request...) 添加初始的Request spider .addRequest("http://webmagic.io/docs/")
    thread(n) 开启n个线程 spider.thread(5)
    run() 启动,会阻塞当前线程执行 spider.run()
    start()/runAsync() 异步启动,当前线程继续执行 spider.start()
    stop() 停止爬虫 spider.stop()
    test(String) 抓取一个页面进行测试 spider .test("http://webmagic.io/docs/")
    addPipeline(Pipeline) 添加一个Pipeline,一个Spider可以有多个Pipeline spider .addPipeline(new ConsolePipeline())
    setScheduler(Scheduler) 设置Scheduler,一个Spider只能有个一个Scheduler spider.setScheduler(new RedisScheduler())
    setDownloader(Downloader) 设置Downloader,一个Spider只能有个一个Downloader spider .setDownloader(new SeleniumDownloader())
    get(String) 同步调用,并直接取得结果 ResultItems result = spider .get("http://webmagic.io/docs/")
    getAll(String…) 同步调用,并直接取得一堆结果 List<ResultItems> results = spider .getAll("http://webmagic.io/docs/", "http://webmagic.io/xxx")

    Site

    对站点本身的一些配置信息,例如编码、HTTP头、超时时间、重试策略等、代理等,都可以通过设置Site对象来进行配置。

    方法 说明 示例
    setCharset(String) 设置编码 site.setCharset("utf-8")
    setUserAgent(String) 设置UserAgent site.setUserAgent("Spider")
    setTimeOut(int) 设置超时时间,单位是毫秒 site.setTimeOut(3000)
    setRetryTimes(int) 设置重试次数 site.setRetryTimes(3)
    setCycleRetryTimes(int) 设置循环重试次数 site.setCycleRetryTimes(3)
    addCookie(String,String) 添加一条cookie site.addCookie("dotcomt_user","code4craft")
    setDomain(String) 设置域名,需设置域名后,addCookie才可生效 site.setDomain("github.com")
    addHeader(String,String) 添加一条addHeader site.addHeader("Referer","https://github.com")
    setHttpProxy(HttpHost) 设置Http代理 site.setHttpProxy(new HttpHost("127.0.0.1",8080))

    其中循环重试cycleRetry是0.3.0版本加入的机制。

    该机制会将下载失败的url重新放入队列尾部重试,直到达到重试次数,以保证不因为某些网络原因漏抓页面。

    抽取工具简介

    WebMagic的抽取主要用到了Jsoup和我自己开发的工具Xsoup

    Jsoup

    Jsoup是一个简单的HTML解析器,同时它支持使用CSS选择器的方式查找元素。为了开发WebMagic,我对Jsoup的源码进行过详细的分析,具体文章参见Jsoup学习笔记

    Xsoup

    Xsoup是我基于Jsoup开发的一款XPath解析器。

    之前WebMagic使用的解析器是HtmlCleaner,使用过程存在一些问题。主要问题是XPath出错定位不准确,并且其不太合理的代码结构,也难以进行定制。最终我自己实现了Xsoup,使得更加符合爬虫开发的需要。令人欣喜的是,经过测试,Xsoup的性能比HtmlCleaner要快一倍以上。

    Xsoup发展到现在,已经支持爬虫常用的语法,以下是一些已支持的语法对照表:

    Name Expression Support
    nodename nodename yes
    immediate parent / yes
    parent // yes
    attribute [@key=value] yes
    nth child tag[n] yes
    attribute /@key yes
    wildcard in tagname / yes
    wildcard in attribute /[@] yes
    function function() part
    or a | b yes since 0.2.0
    parent in path . or .. no
    predicates price>35 no
    predicates logic @class=a or @class=b yes since 0.2.0

    另外我自己定义了几个对于爬虫来说,很方便的XPath函数。但是请注意,这些函数式标准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

    Saxon

    Saxon是一个强大的XPath解析器,支持XPath 2.0语法。webmagic-saxon是对Saxon尝试性的一个整合,但是目前看来,XPath 2.0的高级语法,似乎在爬虫开发中使用者并不多。

    爬虫的监控

    爬虫的监控是0.5.0新增的功能。利用这个功能,你可以查看爬虫的执行情况——已经下载了多少页面、还有多少页面、启动了多少线程等信息。该功能通过JMX实现,你可以使用Jconsole等JMX工具查看本地或者远程的爬虫信息。

    如果你完全不会JMX也没关系,因为它的使用相对简单,本章会比较详细的讲解使用方法。如果要弄明白其中原理,你可能需要一些JMX的知识,推荐阅读:JMX整理。我很多部分也对这篇文章进行了参考。

    注意: 如果你自己定义了Scheduler,那么需要用这个类实现MonitorableScheduler接口,才能查看“LeftPageCount”和“TotalPageCount”这两条信息。

    为项目添加监控

    添加监控非常简单,获取一个SpiderMonitor的单例SpiderMonitor.instance(),并将你想要监控的Spider注册进去即可。你可以注册多个Spider到SpiderMonitor中。

    public class MonitorExample {
    
        public static void main(String[] args) throws Exception {
    
            Spider oschinaSpider = Spider.create(new OschinaBlogPageProcessor())
                    .addUrl("http://my.oschina.net/flashsword/blog");
            Spider githubSpider = Spider.create(new GithubRepoPageProcessor())
                    .addUrl("https://github.com/code4craft");
    
            SpiderMonitor.instance().register(oschinaSpider);
            SpiderMonitor.instance().register(githubSpider);
            oschinaSpider.start();
            githubSpider.start();
        }
    }
    

    查看监控信息

    WebMagic的监控使用JMX提供控制,你可以使用任何支持JMX的客户端来进行连接。我们这里以JDK自带的JConsole为例。我们首先启动WebMagic的一个Spider,并添加监控代码。然后我们通过JConsole来进行查看。

    我们按照4.6.1的例子启动程序,然后在命令行输入jconsole(windows下是在DOS下输入jconsole.exe)即可启动JConsole。

    jconsole

    这里我们选择启动WebMagic的本地进程,连接后选择“MBean”,点开“WebMagic”,就能看到所有已经监控的Spider信息了!

    这里我们也可以选择“操作”,在操作里可以选择启动-start()和终止爬虫-stop(),这会直接调用对应Spider的start()和stop()方法,来达到基本控制的目的。

    jconsole-show

    扩展监控接口

    除了已有的一些监控信息,如果你有更多的信息需要监控,也可以通过扩展的方式来解决。你可以通过继承SpiderStatusMXBean来实现扩展,具体例子可以看这里: 定制扩展demo



    展开全文
  • webmagic 中文开发文档

    2017-12-10 15:39:03
    webmagic的中文文档,里面详细的介绍了webmagic的各个模块的知识点和简单的例子
  • WebMagic从入门到放弃(1)

    千次阅读 2017-05-09 17:58:53
    WebMaigic demo运行WebMagic的demo地址: http://webmagic.io/ WebMagic的doc文档:http://webmagic.io/docs/zh/按照官方给的例子,运行会出现下面的错误,对于没有使用过log4j的人来说是一脸懵笔:

    WebMaigic demo运行

    WebMagic的demo地址: http://webmagic.io/
    WebMagic的doc文档:http://webmagic.io/docs/zh/

    按照官方给的例子,运行会出现下面的提示,对于没有使用过log4j的人来说是一脸懵笔:

    log4j:WARN No appenders could be found for logger (us.codecraft.webmagic.scheduler.QueueScheduler).
    log4j:WARN Please initialize the log4j system properly.
    log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

    这是由于WebMagic使用log4j来输出log,必须自己写一个配置文件,来规定log的输出格式,在src目录下新建文件 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=C:\\log4j\\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

    其实直接搜索 log4j.properties 能找到关于配置文件的说明,
    或者是直接参考网址:
    1. http://www.codeceo.com/article/log4j-usage.html
    2. http://blog.sina.com.cn/s/blog_5ed94d710101go3u.html
    3. http://blog.csdn.net/edward0830ly/article/details/8250412

    ################################################################################ 
    #①配置根Logger,其语法为: 
    # 
    #log4j.rootLogger = [level],appenderName,appenderName2,... 
    #level是日志记录的优先级,分为OFF,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,ALL 
    ##Log4j建议只使用四个级别,优先级从低到高分别是DEBUG,INFO,WARN,ERROR 
    #通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关 
    #比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来 
    #appenderName就是指定日志信息输出到哪个地方。可同时指定多个输出目的 
    ################################################################################ 
    ################################################################################ 
    #②配置日志信息输出目的地Appender,其语法为: 
    # 
    #log4j.appender.appenderName = fully.qualified.name.of.appender.class 
    #log4j.appender.appenderName.optionN = valueN 
    # 
    #Log4j提供的appender有以下几种: 
    #1)org.apache.log4j.ConsoleAppender(输出到控制台) 
    #2)org.apache.log4j.FileAppender(输出到文件) 
    #3)org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件) 
    #4)org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件) 
    #5)org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方) 
    # 
    #1)ConsoleAppender选项属性 
    # -Threshold = DEBUG:指定日志消息的输出最低层次 
    # -ImmediateFlush = TRUE:默认值是true,所有的消息都会被立即输出 
    # -Target = System.err:默认值System.out,输出到控制台(err为红色,out为黑色) 
    # 
    #2)FileAppender选项属性 
    # -Threshold = INFO:指定日志消息的输出最低层次 
    # -ImmediateFlush = TRUE:默认值是true,所有的消息都会被立即输出 
    # -File = C:\log4j.log:指定消息输出到C:\log4j.log文件 
    # -Append = FALSE:默认值true,将消息追加到指定文件中,false指将消息覆盖指定的文件内容 
    # -Encoding = UTF-8:可以指定文件编码格式 
    # 
    #3)DailyRollingFileAppender选项属性 
    # -Threshold = WARN:指定日志消息的输出最低层次 
    # -ImmediateFlush = TRUE:默认值是true,所有的消息都会被立即输出 
    # -File = C:\log4j.log:指定消息输出到C:\log4j.log文件 
    # -Append = FALSE:默认值true,将消息追加到指定文件中,false指将消息覆盖指定的文件内容 
    # -DatePattern='.'yyyy-ww:每周滚动一次文件,即每周产生一个新的文件。还可以按用以下参数: 
    #              '.'yyyy-MM:每月 
    #              '.'yyyy-ww:每周 
    #              '.'yyyy-MM-dd:每天 
    #              '.'yyyy-MM-dd-a:每天两次 
    #              '.'yyyy-MM-dd-HH:每小时 
    #              '.'yyyy-MM-dd-HH-mm:每分钟 
    # -Encoding = UTF-8:可以指定文件编码格式 
    # 
    #4)RollingFileAppender选项属性 
    # -Threshold = ERROR:指定日志消息的输出最低层次 
    # -ImmediateFlush = TRUE:默认值是true,所有的消息都会被立即输出 
    # -File = C:/log4j.log:指定消息输出到C:/log4j.log文件 
    # -Append = FALSE:默认值true,将消息追加到指定文件中,false指将消息覆盖指定的文件内容 
    # -MaxFileSize = 100KB:后缀可以是KB,MB,GB.在日志文件到达该大小时,将会自动滚动.如:log4j.log.1 
    # -MaxBackupIndex = 2:指定可以产生的滚动文件的最大数 
    # -Encoding = UTF-8:可以指定文件编码格式 
    ################################################################################ 
    ################################################################################ 
    #③配置日志信息的格式(布局),其语法为: 
    # 
    #log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class 
    #log4j.appender.appenderName.layout.optionN = valueN 
    # 
    #Log4j提供的layout有以下几种: 
    #5)org.apache.log4j.HTMLLayout(以HTML表格形式布局) 
    #6)org.apache.log4j.PatternLayout(可以灵活地指定布局模式) 
    #7)org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串) 
    #8)org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息) 
    #9)org.apache.log4j.xml.XMLLayout(以XML形式布局) 
    # 
    #5)HTMLLayout选项属性 
    # -LocationInfo = TRUE:默认值false,输出java文件名称和行号 
    # -Title=Struts Log Message:默认值 Log4J Log Messages 
    # 
    #6)PatternLayout选项属性 
    # -ConversionPattern = %m%n:格式化指定的消息(参数意思下面有) 
    # 
    #9)XMLLayout选项属性 
    # -LocationInfo = TRUE:默认值false,输出java文件名称和行号 
    # 
    #Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下: 
    # %m 输出代码中指定的消息 
    # %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL 
    # %r 输出自应用启动到输出该log信息耗费的毫秒数 
    # %c 输出所属的类目,通常就是所在类的全名 
    # %t 输出产生该日志事件的线程名 
    # %n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n” 
    # %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式 
    #    如:%d{yyyy年MM月dd日 HH:mm:ss,SSS},输出类似:2012年01月05日 22:10:28,921 
    # %l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数 
    #    如:Testlog.main(TestLog.java:10) 
    # %F 输出日志消息产生时所在的文件名称 
    # %L 输出代码中的行号 
    # %x 输出和当前线程相关联的NDC(嵌套诊断环境),像java servlets多客户多线程的应用中 
    # %% 输出一个"%"字符 
    # 
    # 可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如: 
    #  %5c: 输出category名称,最小宽度是5,category<5,默认的情况下右对齐 
    #  %-5c:输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格 
    #  %.5c:输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格 
    #  %20.30c:category名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉 
    ################################################################################ 
    ################################################################################ 
    #④指定特定包的输出特定的级别 
    #log4j.logger.org.springframework=DEBUG 
    ################################################################################ 
    
    #OFF,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB,ALL 
    log4j.rootLogger =ALL,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB 
    
    #输出到控制台 
    log4j.appender.systemOut = org.apache.log4j.ConsoleAppender 
    log4j.appender.systemOut.layout = org.apache.log4j.PatternLayout 
    log4j.appender.systemOut.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n 
    log4j.appender.systemOut.Threshold = DEBUG 
    log4j.appender.systemOut.ImmediateFlush = TRUE 
    log4j.appender.systemOut.Target = System.out 
    
    #输出到文件 
    log4j.appender.logFile = org.apache.log4j.FileAppender 
    log4j.appender.logFile.layout = org.apache.log4j.PatternLayout 
    log4j.appender.logFile.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n 
    log4j.appender.logFile.Threshold = DEBUG 
    log4j.appender.logFile.ImmediateFlush = TRUE 
    log4j.appender.logFile.Append = TRUE 
    log4j.appender.logFile.File = ../Struts2/WebRoot/log/File/log4j_Struts.log 
    log4j.appender.logFile.Encoding = UTF-8 
    
    #按DatePattern输出到文件 
    log4j.appender.logDailyFile = org.apache.log4j.DailyRollingFileAppender 
    log4j.appender.logDailyFile.layout = org.apache.log4j.PatternLayout 
    log4j.appender.logDailyFile.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n 
    log4j.appender.logDailyFile.Threshold = DEBUG 
    log4j.appender.logDailyFile.ImmediateFlush = TRUE 
    log4j.appender.logDailyFile.Append = TRUE 
    log4j.appender.logDailyFile.File = ../Struts2/WebRoot/log/DailyFile/log4j_Struts 
    log4j.appender.logDailyFile.DatePattern = '.'yyyy-MM-dd-HH-mm'.log' 
    log4j.appender.logDailyFile.Encoding = UTF-8 
    
    #设定文件大小输出到文件 
    log4j.appender.logRollingFile = org.apache.log4j.RollingFileAppender 
    log4j.appender.logRollingFile.layout = org.apache.log4j.PatternLayout 
    log4j.appender.logRollingFile.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n 
    log4j.appender.logRollingFile.Threshold = DEBUG 
    log4j.appender.logRollingFile.ImmediateFlush = TRUE 
    log4j.appender.logRollingFile.Append = TRUE 
    log4j.appender.logRollingFile.File = ../Struts2/WebRoot/log/RollingFile/log4j_Struts.log 
    log4j.appender.logRollingFile.MaxFileSize = 1MB 
    log4j.appender.logRollingFile.MaxBackupIndex = 10 
    log4j.appender.logRollingFile.Encoding = UTF-8 
    
    #用Email发送日志 
    log4j.appender.logMail = org.apache.log4j.net.SMTPAppender 
    log4j.appender.logMail.layout = org.apache.log4j.HTMLLayout 
    log4j.appender.logMail.layout.LocationInfo = TRUE 
    log4j.appender.logMail.layout.Title = Struts2 Mail LogFile 
    log4j.appender.logMail.Threshold = DEBUG 
    log4j.appender.logMail.SMTPDebug = FALSE 
    log4j.appender.logMail.SMTPHost = SMTP.163.com 
    log4j.appender.logMail.From = xly3000@163.com 
    log4j.appender.logMail.To = xly3000@gmail.com 
    #log4j.appender.logMail.Cc = xly3000@gmail.com 
    #log4j.appender.logMail.Bcc = xly3000@gmail.com 
    log4j.appender.logMail.SMTPUsername = xly3000 
    log4j.appender.logMail.SMTPPassword = 1234567 
    log4j.appender.logMail.Subject = Log4j Log Messages 
    #log4j.appender.logMail.BufferSize = 1024 
    #log4j.appender.logMail.SMTPAuth = TRUE 
    
    #将日志登录到MySQL数据库 
    log4j.appender.logDB = org.apache.log4j.jdbc.JDBCAppender 
    log4j.appender.logDB.layout = org.apache.log4j.PatternLayout 
    log4j.appender.logDB.Driver = com.mysql.jdbc.Driver 
    log4j.appender.logDB.URL = jdbc:mysql://127.0.0.1:3306/xly 
    log4j.appender.logDB.User = root 
    log4j.appender.logDB.Password = 123456 
    log4j.appender.logDB.Sql = INSERT INTOT_log4j(project_name,create_date,level,category,file_name,thread_name,line,all_category,message)values('Struts2','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
    展开全文
  • WebMagic

    2020-10-23 17:17:10
    WebMagic的四个组件 Downloader:负责从互联网上下载页面。WebMagic默认使用了Apache HttpClient作为下载工具。 PageProcessor:负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用Jsoup作为HTML解析...
  • WebMagic(一)-----初步使用

    千次阅读 2018-09-28 11:57:24
    参考文档:http://webmagic.io/docs/zh 1:创建maven项目(官方推荐使用maven) 我使用myeclipse创建后包结构如图所示: 也可以使用创建普通的Java项目导入相关jar包,如图所示 下载路径:...
  • webmagic需要jar包全部

    千次阅读 2018-11-16 22:15:47
    webmagic-0.7.3版本 官网:http://webmagic.io/docs/zh/posts/ch1-overview/thinking.html
  • 包含webmagic-core-0.5.2.jar,webmagic-extension-0.5.2.jar,这两个jar包,供爬虫爱好者提供帮助。
  • 基于Webmagic的Java爬虫(一) 配置Webmagic(基于Maven) 在创建的Maven项目的 pom.xml 文件中添加如下依赖: <dependency> <groupId>us.codecraft</groupId> <artifactId>...
  • Java爬虫框架WebMagic的使用总结

    万次阅读 2016-08-15 20:18:59
    最近,项目做一个公司新闻网站,分为PC&移动端(h5),数据来源是从HSZX与huanqiu2个网站爬取,主要使用java编写的WebMagic作为爬虫框架,数据分为批量抓取、增量抓取,批量抓当前所有历史数据,增量需要每10分钟定时...
  • 本篇文章主要讲解如何使用webmagic技术来实现网页的爬取, 以及使用selenium操作页面元素,实现点击、输入事件 所用技术 1.webmagic 添加需要爬取的url Spider.create(new MyProcessor()).addUrl(...
  • WebMagic写的网络爬虫

    2017-10-22 11:43:56
    WebMagic写的网络爬虫 一、前言  最近因为有爬一些招聘网站的招聘信息的需要,而我之前也只是知道有“网络爬虫”这个神奇的名词,具体是什么、用什么实现、什么原理、如何实现比较好都不清楚,因此最近...
  • webmagic

    2017-02-16 10:52:33
    1、retrytimes:请求失败后重试的次数,circleretrytimes是循环重试次数,该机制会将下载失败的url重新放入队列尾部重试,直到达到重试次数,以保证不因为某些网络原因漏抓页面,如代理ip不通。...
  • webmagic是个神奇的爬虫(二)-- webmagic爬取流程细讲

    万次阅读 多人点赞 2017-03-12 18:02:48
    webmagic流程图镇楼: 第一篇笔记讲到了如何创建webmagic项目,这一讲来说一说webmagic爬取的主要流程。 webmagic主要由Downloader(下载器)、PageProcesser(解析器)、Schedule(调度器)和Pipeline(管道)四...
  • webmagic使用手册

    千次阅读 2017-03-30 13:59:05
    webmagic是一个开源的Java垂直爬虫框架,目标是简化爬虫的开发流程,让开发者专注于逻辑功能的开发。webmagic的核心非常简单,但是覆盖爬虫的整个流程,也是很好的学习爬虫开发的材料。 web爬虫是一种技术,...
  • webmagic爬虫

    2017-12-20 17:16:57
    里面的task包下,是所有的爬虫程序,每个类代表一个城市的网站,学习的朋友可以先看成都,自贡,攀枝花的代码,自己亲自写的。主要使用webmagic,及xpath,css,jsoup,正则。解析页面
  • webmagic总体介绍

    万次阅读 2014-12-03 21:33:59
    1.WebMagic概览 WebMagic项目代码分为核心和扩展两部分。核心部分(webmagic-core)是一个精简的、模块化的爬虫实现,而扩展部分则包括一些便利的、实用性的功能。WebMagic的架构设计参照了Scrapy,目标是尽量的模块...
  • 本人从事爬虫工作整一年,在对爬虫一无所知的情况下接触到了webmagic,之后通过不断的学习和实践,发现了它的灵活和强大,渐渐地爱上了它,因此把心得整理出来,梳理自己思路也希望和众多爬虫爱好者一同交流成长。...
  • 初学webMagic

    2017-06-20 17:28:49
    初学者眼里的爬虫
1 2 3 4 5 ... 20
收藏数 3,594
精华内容 1,437
关键字:

webmagic