WebMagic学习笔记

it2023-07-30  69

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。

2、WebMagic坐标

<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> </dependency>

3、WebMagic的使用

1. 第一步,需要先实现PageProcessor接口,并实现process与getSite方法,process方法主要用来选择页面的数据并放入page对象中。具体案例如下:

JobProcessor类,用于抓取并解析网页内容获取需要的数据

@Component public class JobProcessor implements PageProcessor { private Logger logger = LoggerFactory.getLogger(this.getClass()); /** 自定以的数据处理类,该类继承了Pipeline接口,并实现了其process方法 */ @Autowired private SpringDataPipeline springDataPipeline; /** 需要抓取的url列表页 */ private String url = "https://search.51job.com/list/000000,000000,0000,32%252C01,9,99," + "Java,2,1.html?lang=c&stype=&postchannel=0000&workyear=99&cotype=99&degreefrom=99" + "&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1&ord_field=0" + "&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare="; /** 设置请求的页面解析的编码,请求的超时时间,每台服务的请求时间与重试次数 */ private Site site = Site.me() .setCharset("gbk") .setTimeOut(10000) .setRetrySleepTime(3000) .setRetryTimes(3); @Override public void process(Page page) { //解析页面,获取招聘信息详情页的url地址 List<Selectable> nodes = page.getHtml().css("div#resultList div.el").nodes(); //判断获取到的集合是否weikong,如果为空表示这个招聘详情页,如果不为空则表示这是列表页 if (nodes == null || nodes.size() == 0) { // 如果为空,则表示这是招聘详情页,解析页面获取招聘详情信息保存数据 this.saveJobInfo(page); } else { // 如果不为空,则表示这是列表页解析出详情页的URL地址,放到任务队列中 for (Selectable selectable : nodes) { // 获取URL地址 String jobInfoUrl = selectable.links().toString(); // 将获取到的URL地址放到任务队列中 page.addTargetRequest(jobInfoUrl); } // 获取下一页的url String nextPageUrl = page.getHtml().css("div.p_in li.bk").nodes().get(1).links().toString(); // 把url放到任务队列中 page.addTargetRequest(nextPageUrl); } } /** * 功能描述:saveJobInfo,解析页面获取招聘详情信息,保存数据 <br> * @param page * @return void * * @Author xu * @Date 2020-03-15 21:38 * @Version 1.0.0 */ private void saveJobInfo(Page page) { // 创建招聘详情对象 JobInfo jobInfo = new JobInfo(); // 解析页面 Html html = page.getHtml(); // 获取数据封装到对象中 jobInfo.setCompanyName(html.css("div.cn p.cname a", "text").toString()); String companyAddr = Jsoup.parse(html.css("div.bmsg").nodes().get(1).toString()).text(); jobInfo.setCompanyAddr(companyAddr.substring(0, companyAddr.length()-3)); jobInfo.setCompanyInfo(Jsoup.parse(html.css("div.tmsg").toString()).text()); jobInfo.setJobName(html.css("div.cn h1", "text").toString()); String[] jobAndTime = html.css("div.cn p.ltype", "text").toString().split(" "); jobInfo.setJobAddr(jobAndTime[0]); jobInfo.setJobInfo(Jsoup.parse(html.css("div.job_msg").toString()).text()); jobInfo.setUrl(page.getUrl().toString()); String text = html.css("div.cn strong", "text").toString(); Integer[] salary = SalatyUtils.getSalary(text); jobInfo.setSalaryMin(salary[0]); jobInfo.setSalaryMax(salary[1]); String time = null; for (int i=0; i< jobAndTime.length; i++) { String s = jobAndTime[i]; if (s.contains("发布")) { time = s.substring(0, s.length()-2); } } jobInfo.setTime(time); // 保存结果 page.putField("jobInfo", jobInfo); } @Override public Site getSite() { return site; } /** * 功能描述:initialDelay任务启动后等待多久执行,fixedDelay任务执行的间隔时间 <br> * @param * @return void * * @Author xu * @Date 2020-03-15 21:21 * @Version 1.0.0 */ @Scheduled(initialDelay = 1000, fixedDelay = 100000) public void doProcess() { logger.info("============ 任务开始执行 ============"); Spider.create(new JobProcessor()) .addUrl(url) .setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(100000))) .thread(10) .addPipeline(this.springDataPipeline) .run(); logger.info("============ 任务执行完成 ============"); } }

SpringDataPipeline类,用于数据处理后放入数据库中

@Component public class SpringDataPipeline implements Pipeline { @Autowired private JobInfoService jobInfoService; @Override public void process(ResultItems resultItems, Task task) { // 获取封装好的招聘信息对象 JobInfo jobInfo = resultItems.get("jobInfo"); // 判断数据是否为空,如果不为空则将数据保存到数据库中 if (jobInfo != null) { this.jobInfoService.save(jobInfo); } } }

SalatyUtils类,JobProcessor中调用的方法用于处理网页中的数据

public class SalatyUtils { public static Integer[] getSalary(String salaryString) { Integer[] salaryArr = new Integer[2]; String itemSalaty = salaryString.substring(0, salaryString.length()-3); String itemMath = salaryString.substring(salaryString.length()-3, salaryString.length()); String[] salary = itemSalaty.split("-"); if (itemMath.contains("千")) { salaryArr[0] = new BigDecimal(salary[0]).multiply(new BigDecimal(1000)).intValue(); salaryArr[1] = new BigDecimal(salary[1]).multiply(new BigDecimal(1000)).intValue(); } else { if (itemMath.contains("月")) { salaryArr[0] = new BigDecimal(salary[0]).multiply(new BigDecimal(10000)).intValue(); salaryArr[1] = new BigDecimal(salary[1]).multiply(new BigDecimal(10000)).intValue(); } else { salaryArr[0] = new BigDecimal(salary[0]).multiply(new BigDecimal(10000/12)).intValue(); salaryArr[1] = new BigDecimal(salary[1]).multiply(new BigDecimal(10000/12)).intValue(); } } return salaryArr; } }

以上为个人总结的一些笔记,若有雷同纯属巧合,若有错误欢迎指出,谢谢!

最新回复(0)