WebMagic爬虫框架(爬取前程无忧网站的招聘信息保存到mysql数据库)
@
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地址并给你禁掉,所以这时就需要代理服务器。以及爬取数据的去重问题,还要借助一些其他的工具平台进行处理整合,所以,有待完善。
每天进步一点点,有问题留言兄弟盟!
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)