@
WebMagic框架包含四個組件,
PageProcessor、
Scheduler、
Downloader和
Pipeline。
這四大組件對應爬蟲生命周期中的處理、管理、下載和持久化等功能。
這四個組件都是
Spider中的屬性,爬蟲框架通過
Spider啟動和管理。
WebMagic總體架構圖
一,WebMagic的四大組件
PageProcessor 負責解析頁面,抽取有用信息,以及發現新的鏈接。需要自己定義。
Scheduler 負責管理待抓取的URL,以及一些去重的工作。一般無需自己定制Scheduler。
Pipeline 負責抽取結果的處理,包括計算、持久化到文件、數據庫等。
Downloader 負責從互聯網上下載頁面,以便后續處理。一般無需自己實現。
二,用於數據流轉的對象
Request 是對URL地址的一層封裝,一個Request對應一個URL地址。
Page 代表了從Downloader下載到的一個頁面——可能是HTML,也可能是JSON或者其他文本格式的內容。
ResultItems 相當於一個Map,它保存PageProcessor處理的結果,供Pipeline使用。
三,項目開始前的熱身(解析頁面的方式)
項目maven添加了下面要求的maven坐標之后就可以寫下面的測試代碼了。
下面的測試很清楚的講解了解析頁面的方式,以及爬蟲的執行。
page.putField()是把爬取到的數據添加到了ResultItems中,默認在控制台打印出來。
public class JobProcessor implements PageProcessor {
//解析頁面
public void process(Page page) {
//解析返回的數據page,並且把解析的結果放在resultItems中
//css選擇器解析
//page.putField("爬取內容",page.getHtml().css("span.service_txt").all());//all()是返回所有數據,get()是返回第一條數據
//XPath解析
//page.putField("xpath方法解析結果",page.getHtml().xpath("//div[@id=J_cate]/ul/li/a").all());
//正則表達式解析(篩選內容帶“裝”字的所有信息)
page.putField("正則",page.getHtml().css("div#J_cate ul li a").regex(".*裝.*").all());
//獲取鏈接
// page.addTargetRequests(page.getHtml().css("div#shortcut-2014 div.w ul.fl li#ttbar-home a").links().all());
// page.putField("url",page.getHtml().css("div#shortcut div ul li a span").all());
}
private Site site=Site.me()
.setCharset("utf8") //設置編碼
.setTimeOut(10000) //設置超時時間 單位是ms毫秒
.setRetrySleepTime(3000) //設置重試的時間間隔
.setSleepTime(3); //設置重試次數
public Site getSite() {
return site;
}
/*設置request請求方式
Request requests=new Request("http://www.12371.cn/cxsm/gzbs/");
requests.setMethod(HttpConstant.Method.GET);
Spider.create(new JobProcessor())
//.addUrl(url)
.addRequest(requests)
.setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(100000)))
.thread(5)
.addPipeline(this.newsData)
.run();
*/
//主函數,執行爬蟲
public static void main(String[] args) {
Spider spider=Spider.create(new JobProcessor()).addUrl("https://www.jd.com")//設置爬取數據的頁面
// .addPipeline(new FilePipeline("D:\\result"))//使用Pipeline保存數據到指定文件夾中,自動生成文件
.thread(5) //多線程進行爬取,5個多線程,速度更快
.setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(10000000)));//設置布隆去重過濾器,指定最多對1000萬數據進行去重操作
//默認HashSet去重
//Scheduler scheduler=spider.getScheduler();
spider.run();//run()執行爬蟲
}
}
四,SpringBoot項目環境搭建
首先搭建好一個springboot項目,加入mysql,mybatis,webmagic的核心依賴以及擴展依賴,以及添加WebMagic對布隆過濾器的支持的依賴。
<!--springmvc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>0.7.3</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>0.7.3</version>
</dependency>
<!--WebMagic對布隆過濾器的支持-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0</version>
</dependency>
<!--工具包 StringUtils-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
五,配置文件
在resources文件夾新建log4j.properties文件配置日志
log4j.rootLogger=INFO,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
在新建application.properties文件配置數據庫
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=1234
六,Let's go WebMagic!
1,啟動類
@SpringBootApplication
@MapperScan("com.qianlong.dao") //掃描mapper文件
@EnableScheduling //開啟定時任務,定時抓取數據
@ComponentScan(value = "com.qianlong")//包掃描
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
2,實體類(存儲到數據庫表的字段)
public class JobInfo {
private Integer id;
private String companyName;
private String companyAddr;
private String companyInfo;
private String jobName;
private String jobAddr;
private String salary;
private String time;
}
3,爬蟲類
package com.qianlong.task;
import com.qianlong.entity.JobInfo;
import org.jsoup.Jsoup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.scheduler.BloomFilterDuplicateRemover;
import us.codecraft.webmagic.scheduler.QueueScheduler;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.Selectable;
import java.util.List;
@Component
public class JobProcessor implements PageProcessor {
//前程無憂網站的職位列表地址
private String url="https://search.51job.com/list/170200,000000,0000,00,9,99,%2B,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=";
@Override
public void process(Page page) {
//解析頁面,獲取招聘信息詳情的url地址
List<Selectable> list = page.getHtml().css("div#resultList div.el").nodes();
//判斷集合是否為空
if(list.size()==0){
//如果為空,表示這是招聘詳情頁,解析頁面,獲取招聘詳情信息,保存數據
this.saveJobInfo(page);
}else {
//如果不為空,表示這是列表頁,解析出詳情頁的url地址,放到任務隊列中
for(Selectable selectable:list){
String jobInfoUrl = selectable.links().toString();
//把獲取到的詳情頁的url地址放到任務隊列中
page.addTargetRequest(jobInfoUrl);
}
//獲取下一頁按鈕的url
String bkUrl=page.getHtml().css("div.p_in li.bk").nodes().get(1).links().toString();//get(1)拿到第二個
//把下一頁的url放到任務隊列中
page.addTargetRequest(bkUrl);
}
}
//解析頁面,獲取招聘詳情信息,保存數據
private void saveJobInfo(Page page) {
//創建招聘詳情對象
JobInfo jobInfo=new JobInfo();
//拿到解析的頁面
Html html = page.getHtml();
//獲取數據,封裝到對象中
//兩種獲取的方法,一種是直接html.css,另一種是使用Jsoup.parse解析html字符串
jobInfo.setCompanyName(html.css("div.cn p.cname a","text").toString());
String addrStr = Jsoup.parse(html.css("div.cn p.msg").toString()).text();
String addr=addrStr.substring(0,addrStr.indexOf("|"));
jobInfo.setCompanyAddr(addr);
jobInfo.setCompanyInfo(html.css("div.tmsg","text").toString());
jobInfo.setUrl(page.getUrl().toString());
jobInfo.setJobName(Jsoup.parse(html.css("div.cn h1","title").toString()).text());
jobInfo.setJobAddr(addr);
jobInfo.setSalary(Jsoup.parse(html.css("div.cn strong").toString()).text());
//把結果保存起來
page.putField("jobInfo",jobInfo);
}
private Site site=Site.me()
.setCharset("gbk")//設置編碼(頁面是什么編碼就設置成什么編碼格式的)
.setTimeOut(10*1000)//設置超時時間
.setRetrySleepTime(3000)//設置重試的間隔時間
.setRetryTimes(3);//設置重試的次數
@Override
public Site getSite() {
return site;
}
//這里注入SaveData
@Autowired
private SaveData saveData;
//initialDelay當任務啟動后,等多久執行方法
//fixedDelay每個多久執行方法
@Scheduled(initialDelay = 1000,fixedDelay = 100*1000)
public void process(){
Spider.create(new JobProcessor())
.addUrl(url)
.setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(100000)))
.thread(10)
.addPipeline(this.saveData)//指定把爬取的數據保存到SaveData類的ResultItems中
.run();
}
}
4,獲取爬到的數據並保存到數據庫
前面爬取的數據(封裝到了實體類)都保存在了ResultItems 對象中

這里取出前面保存的數據(實體類),然后把數據存到數據庫
package com.qianlong.task;
import com.qianlong.entity.JobInfo;
import com.qianlong.service.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;
@Component
public class SaveData implements Pipeline {
@Autowired
Service service;
@Override
public void process(ResultItems resultItems, Task task) {
//獲取封裝好的招聘詳情對象
JobInfo jobInfo=resultItems.get("jobInfo");
if(jobInfo!=null){
//保存數據到數據庫中
service.saveJobInfo(jobInfo);
}
}
}
5,dao和service
public interface Dao {
@Insert(value = "insert into jobinfo(companyName,companyAddr,companyInfo,jobName,jobAddr,salary,url) values(#{companyName},#{companyAddr},#{companyInfo},#{jobName},#{jobAddr},#{salary},#{url});")
int saveJobInfo(JobInfo jobInfo);
}
public interface Service {
int saveJobInfo(JobInfo jobInfo);
}
@Service
public class ServiceImpl implements Service {
@Autowired
private Dao dao;
@Override
public int saveJobInfo(JobInfo jobInfo) {
int i = dao.saveJobInfo(jobInfo);
return i;
}
}
然后運行啟動類,控制台出現下圖就是爬取成功了

爬取的數據保存到數據庫成功

七,后話
至於WebMagic的完整使用,還需要涉及到代理服務器,因為有一些網站是禁止爬取的,它會查到你的ip地址並給你禁掉,所以這時就需要代理服務器。以及爬取數據的去重問題,還要借助一些其他的工具平台進行處理整合,所以,有待完善。

每天進步一點點,有問題留言兄弟盟!
