webmagic自定义存储(mysql、redis存储)
在很多时候,我们使用webmagic爬取网站的时候,爬取的数据希望存储在mysql、redis中。因此需要对其扩展,实行自定义PipeLine。首先我们了解一下webmagic 的四个基本组件
一、 WebMagic的四个组件
1、Downloader
Downloader负责从互联网上下载页面,以便后续处理。WebMagic默认使用了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。
二、自定义Pipeline,实现Pipeline接口,从写process方法。
package com.mdd.pip.pipeLine; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.mdd.pip.model.ProxyIp; import com.mdd.pip.service.ProxyIpService; import us.codecraft.webmagic.ResultItems; import us.codecraft.webmagic.Task; import us.codecraft.webmagic.pipeline.Pipeline; @Component public class DataPipeLine implements Pipeline { @Autowired private ProxyIpService proxyIpService; /** * mysql 存储 */ /* * public void process(ResultItems resultItems, Task task) { * List<ProxyIp>proxyIpList = resultItems.get("proxyIpList"); * if(proxyIpList!=null&&!proxyIpList.isEmpty()){ * proxyIpService.saveProxyIpList(proxyIpList); } * * } */ /** * redis 存储 */ public void process(ResultItems resultItems, Task task) { List<ProxyIp> proxyIpList = resultItems.get("proxyIpList"); if (proxyIpList != null && !proxyIpList.isEmpty()) { proxyIpService.saveProxyListIpInRedis(proxyIpList); } } }
ResultItems 对象本质是一个Map。因此要我们保存对象的时候,只需要在爬取时把爬取得数据封装成对象,保存在
ResultItems 里即可。如果有很多数据,则可以考虑用List保存。
package com.mdd.pip.crawler; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.springframework.stereotype.Component; import com.mdd.pip.model.ProxyIp; import us.codecraft.webmagic.Page; import us.codecraft.webmagic.Site; import us.codecraft.webmagic.processor.PageProcessor; /** * 热刺代理网站ip抓取 * * @author xwl 2017.6.3 */ @Component public class XiCiProxyIpCrawler implements PageProcessor { private Logger logger = Logger.getLogger(XiCiProxyIpCrawler.class); // 部分一:抓取网站的相关配置,包括编码、抓取间隔、重试次数等 private Site site = Site.me().setCycleRetryTimes(3).setRetryTimes(3).setSleepTime(1000) .setUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0"); public Site getSite() { return site; } public void process(Page page) { Document html = page.getHtml().getDocument(); // 结果集 List<ProxyIp> proxyIpList = new ArrayList<ProxyIp>(); Elements trElements = html.getElementById("ip_list").getElementsByTag("tr"); for (Element trEle : trElements) { Elements tdElements = trEle.getElementsByTag("td"); if (tdElements == null||tdElements.size()<=0) { continue; } try { ProxyIp proxyIp = new ProxyIp(); String ip = tdElements.get(1).text(); String proxyPort = tdElements.get(2).text(); String ipAddress = tdElements.get(3).text(); String anonymity = tdElements.get(4).text(); String proxyType = tdElements.get(5).text(); String aliveTime = tdElements.get(6).text(); proxyIp.setProxyIp(ip); proxyIp.setProxyPort(Integer.parseInt(proxyPort)); proxyIp.setAliveTime(aliveTime); proxyIp.setAnonymity(anonymity); proxyIp.setIpAddress(ipAddress); proxyIp.setProxyType(proxyType); logger.info(proxyIp.getProxyIp()+":"+proxyIp.getProxyPort()); proxyIpList.add(proxyIp); } catch (Exception e) { logger.error("IP代理解析出错!", e); } } page.putField("proxyIpList", proxyIpList); } }
page.putField("proxyIpList", proxyIpList);本质是设值到了ResultItems对象里了。
这样插件式、定制化很值的我们借鉴。希望下一步看源码。
参考:
http://webmagic.io/docs/zh/posts/ch1-overview/architecture.html