Python第二章
一、webmagic入门程序
-
使用方法
-
1)创建工程
-
2)添加jar包
-
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.sgwks</groupId> <artifactId>crawler-webmagic</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!--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> <!--WebMagic对布隆过滤器的支持--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>16.0</version> </dependency> </dependencies> </project>
-
-
3)创建一个PageProcess对象,需要实现PageProcessor接口创建实现类。
-
package cn.sgwks.webmagic; 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.selector.Html; import us.codecraft.webmagic.selector.Selectable; public class MyPageProcessor implements PageProcessor { /** * 页面解析的业务逻辑 * @param page 下载结果封装到Page对象中 */ @Override public void process(Page page) { //抓取京东首页打印html Html html = page.getHtml(); //把Html对象装换成String对象 String strHtml = html.toString(); //<title>京东商场</title> System.out.println(strHtml.contains("title3"));//false //把解析结果传递给pipeline /*ResultItems resultItems = page.getResultItems(); resultItems.put("html", strHtml);*/ //page.putField("html", strHtml); //获取当前访问的链接地址 Selectable url = page.getUrl(); System.out.println(url);//https://www.jd.com/ } /** * 抓取站点信息的参数配置 * 抓取的频率 * 超时时间 * 编码格式 * 。。。 * @return */ @Override public Site getSite() { return Site.me(); } public static void main(String[] args) { Spider.create(new MyPageProcessor()) .addUrl("https://www.jd.com/") //启动爬虫 //同步方法,在当前线程中启动爬虫 .run(); //异步方法,创建新的线程启动爬虫 .start(); } }
-
-
4)在实现类中实现页面解析的业务逻辑。
-
5)创建一个main方法,在方法中初始化爬虫并启动。
-
二、组件介绍
-
Downloader组件
下载器组件,从互联网现在html页面,并封装成Page对象,传递给PageProcess组件。
就是使用HttpClient实现。
如果没有特殊需求,此组件无需自定义。 -
PageProcess组件
爬虫的核心组件,使用爬虫时,此组件 必须自定义。
1)Site
站点参数的配置。
抓取的间隔时间
超时时间
编码格式
UserAgent
…
一般情况下使用默认参数即可。
返回一个Site对象,使用Site.me();
2)Page
Html:Downloader组件抓取的页面封装成Html对象。也是一个Selectable对象
url:当前页面对应的url地址,是一个Selectable对象
ResultItems:从PageProcess向pipeline传递数据的对象。
本质上就是一个map
Request:一个Request就是一个url。
page对象:
addTargetRequest:向队列中添加一个url
addTargetRequests:向队列中批量添加url3)Selectable
-
– Html对象支持使用Jsoup原生api
-
css选择器解析页面:
css()、$()-
package cn.sgwks.webmagic; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; 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.selector.Html; import us.codecraft.webmagic.selector.Selectable; import java.util.List; public class MyPageProcessorCss implements PageProcessor { /** * 页面解析的业务逻辑 * @param page 下载结果封装到Page对象中 */ @Override public void process(Page page) { //抓取传智播客首页打印html Html html = page.getHtml(); //解析title Document document = html.getDocument(); Elements elements = document.getElementsByTag("title"); for (Element element : elements) { System.out.println(element);// <title>京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!</title> } Elements elements1 = document.select("title"); System.out.println(elements1);// <title>京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!</title> //使用css选择器解析1 Selectable title = html.css("title", "text"); System.out.println(title);//京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物! //使用css选择器解析2 Selectable title2 = html.$("title", "text"); System.out.println(title2);//京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物! //把Selectable对象转换成String对象 String title_s = title.get(); System.out.println(title_s);//京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物! //选择meta //这里是获取第一个meta节点的标签 String meta = html.css("meta").get(); System.out.println(meta);//<meta charset="utf8" version="1"> //获取全部带meta节点的标签 List<String> metaList = html.$("meta").all(); /*[<meta charset="utf8" version="1">, <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes">, <meta name="description" content="京东JD.COM-专业的综合网上购物商城,销售家电、数码通讯、电脑、家居百货、服装服饰、母婴、图书、食品等数万个品牌优质商品.便捷、诚信的服务,为您提供愉悦的网上购物体验!">, <meta name="Keywords" content="网上购物,网上商城,手机,笔记本,电脑,MP3,CD,VCD,DV,相机,数码,配件,手表,存储卡,京东">, <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">, <meta name="renderer" content="webkit">]*/ System.out.println(metaList); //取链接地址列表,得到第一个链接地址 String links = html.css("ul.fr").links().get(); System.out.println(links); //取全部链接地址列表 List<String> linkList = html.css("ul.fr").links().all(); System.out.println(linkList);//[, , https://order.jd.com/center/list.action, https://home.jd.com/, https://vip.jd.com/, https://b.jd.com/] //nodes方法,返回一个Selectable对象的集合 List<Selectable> nodes = html.css("meta").nodes(); System.out.println(nodes); //取节点的属性,去单个第一个得到的数据 String metaName = html.css("meta", "name").get(); System.out.println("该meta节点的属性是否包含name:"+metaName); //取节点的属性,包含得到全部数据 List<String> metaNameList = html.$("meta", "name").all(); System.out.println(metaNameList);//[, viewport, description, Keywords, , renderer] } /** * 抓取站点信息的参数配置 * 抓取的频率 * 超时时间 * 编码格式 * 。。。 * @return */ @Override public Site getSite() { return Site.me(); } public static void main(String[] args) { Spider.create(new MyPageProcessorCss()) .addUrl("https://www.jd.com/") //启动爬虫 //同步方法,在当前线程中启动爬虫 .run(); //异步方法,创建新的线程启动爬虫 .start(); } }
-
-
使用xpath解析页面:
xpath()
解析xml的一种方法。-
package cn.sgwks.webmagic; 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.selector.Html; import us.codecraft.webmagic.selector.Selectable; import java.util.List; public class MyPageProcessorXpath implements PageProcessor { /** * 页面解析的业务逻辑 * @param page 下载结果封装到Page对象中 */ @Override public void process(Page page) { Html html = page.getHtml(); //使用xpath解析title String title = html.xpath("title").get(); System.out.println(title);//<title>京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!</title> // text()就代表去标签内的文本值 String title2 = html.xpath("title/text()").get(); System.out.println(title2);//京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物! //取第一个link节点的href属性,属性前要加 @ String links = html.xpath("link/@href").get(); System.out.println(links);// //static.360buyimg.com //取全部link节点的href属性 List<String> linkList = html.xpath("link/@href").all(); /*[//static.360buyimg.com, //misc.360buyimg.com, //img10.360buyimg.com, //img11.360buyimg.com, // img12.360buyimg.com, //img13.360buyimg.com, //img14.360buyimg.com, // img20.360buyimg.com, //img30.360buyimg.com, //d.3.cn, //d.jd.com, // www.jd.com/favicon.ico, // misc.360buyimg.com/mtd/pc/index_2019/1.0.0/static/css/first-screen.chunk.css, // misc.360buyimg.com/mtd/pc/index_2019/1.0.0/static/css/index.chunk.css]*/ System.out.println(linkList); //取京东的LOGO里面的h1标记内容 String jdNameStr = html.xpath("/html/body/div[1]/div[4]/div/div[1]/h1/a/text()").get(); System.out.println(jdNameStr);//京东 } /** * 抓取站点信息的参数配置 * 抓取的频率 * 超时时间 * 编码格式 * 。。。 * @return */ @Override public Site getSite() { return Site.me(); } public static void main(String[] args) { Spider.create(new MyPageProcessorXpath()) .addUrl("https://www.jd.com/") //启动爬虫 //同步方法,在当前线程中启动爬虫 .run(); //异步方法,创建新的线程启动爬虫 .start(); } }
-
-
使用正则表达式
regex()
解析页面不推荐使用正则表达式。推荐使用css选择器。 -
把Selectable对象转换成字符串:
- toString()、get()
如果结果是一个列表的话,只取第一个元素。 - all()
可以吧Selectable转换成字符串,转换成一个List - links()
取当前节点中所有的链接地址。 - nodes()
返回一个List对象
- toString()、get()
-
-
pipeline
- 数据持久化组件。
- 框架默认提供三个实现。
ConsolePipeline:框架默认使用,向控制台输出
FilePipeline:向磁盘输出,可以把抓取到结果保存到磁盘。
JsonFilePipeline:向磁盘输出保存json数据。 - 使用方法:
1)创建一个FilePipeline对象
2)给FilePipeline对象设置文件输出路径。
3)使用Spider工具类初始化爬虫时装配组件。 - 如果需要把数据保存到数据库,需要自定义pipeline。
需要实现Pipeline接口即可。
-
Scheduler
-
url队列。
实现方法:
QueueScheduler:java内存队列(默认使用)
FileCacheQueueScheduler:使用文件做缓存的队列。
RedisScheduler:使用redis做队列,解决分布式爬虫问题 -
向队列中添加url时需要去重
-
1)使用java的hashset去重(默认使用)
-
2)使用redis的set去重
-
3)使用布隆过滤器去重。
-
优点: 成本低 效率高 速度快 占内存小 缺点: 可能误判 不能删除元素
-
-
布隆过滤器的使用方法:
- 1)创建一个QueryScheduler对象
- 2)创建一个布隆过滤器的对象。
- 3)给Scheduler对象设置布隆过滤器。
- 4)使用Spider初始化爬虫时,使用自定义的scheduler。
-
布隆过滤器需要一个jar包guava
-
package cn.sgwks.webmagic; import us.codecraft.webmagic.*; import us.codecraft.webmagic.pipeline.ConsolePipeline; import us.codecraft.webmagic.pipeline.FilePipeline; 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 java.util.List; public class MyPageProcessorGuava implements PageProcessor { /** * 页面解析的业务逻辑 * @param page 下载结果封装到Page对象中 */ @Override public void process(Page page) { //取页面内容 Html html = page.getHtml(); //把html文本信息传递给pipeline page.putField("html", html.get()); String jdName = html.css("title", "text").get(); //把title的信息传递给pipeline page.putField("jdName", jdName); //取页面中所有的url List<String> linkList = html.links().all(); //把地址添加到访问队列 page.addTargetRequests(linkList); } /** * 抓取站点信息的参数配置 * 抓取的频率 * 超时时间 * 编码格式 * 。。。 * @return */ @Override public Site getSite() { Site site = Site.me(); //设置抓取频率,一秒抓一次 site.setSleepTime(1000); return site; } /** * ConsolePipeline:框架默认使用,向控制台输出 * FilePipeline:向磁盘输出,可以把抓取到结果保存到磁盘。 * JsonFilePipeline:向磁盘输出保存json数据。 */ public static void main(String[] args) { //创建一个Pipeline对象 FilePipeline filePipeline = new FilePipeline(); filePipeline.setPath("C:\\Users\\acer\\Desktop\\sgw"); //创建一个Scheduler对象 QueueScheduler scheduler = new QueueScheduler(); //创建一个布隆过滤器对象 //构造参数就是布隆过滤器的容量。 BloomFilterDuplicateRemover remover = new BloomFilterDuplicateRemover(1000000); scheduler.setDuplicateRemover(remover); }
-
-
-
Spider工具类
初始化爬虫,可以装配各个组件,同时设置一些参数。
启动爬虫。-
Spider.create(new MyPageProcessorGuava()) //设置pipeline组件 .addPipeline(filePipeline) //向控制台输出,如果不设置自定义pipeline组件就不用设置ConsolePipeline,改组件是默认的 .addPipeline(new ConsolePipeline()) //设置自定义的scheduler .setScheduler(scheduler) //设置起始的url .addUrl("https://www.jd.com") .start();
-
三、综合案例
-
需求
抓取51job网站的招聘信息,把招聘信息保存到数据库。 -
功能分析
-
数据库:
job_infoCREATE TABLE `job_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', `company_name` varchar(100) DEFAULT NULL COMMENT '公司名称', `company_addr` varchar(200) DEFAULT NULL COMMENT '公司联系方式', `company_info` text COMMENT '公司信息', `job_name` varchar(100) DEFAULT NULL COMMENT '职位名称', `job_addr` varchar(50) DEFAULT NULL COMMENT '工作地点', `job_info` text COMMENT '职位信息', `salary_min` int(10) DEFAULT NULL COMMENT '薪资范围,最小', `salary_max` int(10) DEFAULT NULL COMMENT '薪资范围,最大', `url` varchar(150) DEFAULT NULL COMMENT '招聘信息详情页', `time` varchar(10) DEFAULT NULL COMMENT '职位最近发布时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='招聘信息';
-
步骤:
1)访问职位列表页面
2)解析职位详情的url地址,添加到队列中。
3)解析“下一页”的url,添加到队列中。
4)解析页面的业务逻辑中,判断是列表页面还是详情页面。
5)如果是列表页面,执行2-3的动作。
6)如果是详情页面,解析职位详情信息
7)把职位详情保存到数据库。
-
-
功能实现
-
1)创建工程
1、创建一个Springboot工程
2、添加依赖
添加SpringDataJpa的起步依赖
添加web模块的起步依赖
mysql数据库驱动
webmagic相关的jar包。-
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.sgwks</groupId> <artifactId>crawler-51job</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!--SpringMVC--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--SpringData Jpa--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--MySQL连接包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--WebMagic核心包--> <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> <!--WebMagic扩展--> <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> <!--工具包--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> </dependencies> </project>
3、创建一个配置文件application.properties
配置数据库连接信息-
#DB Configuration: spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/crawler-sgw?useUnicode=true&characterEncoding=utf8 username: root password: root #JPA Configuration: jpa: database: mysql show-sql: true generate-ddl: true hibernate: ddl-auto: update
4、对应数据库的表创建Entity、dao
-
package cn.sgwks.crawlerjob.entity; import javax.persistence.*; @Entity @Table(name = "job_info") public class Job { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="id") private Long id; @Column(name="company_name") private String companyName; @Column(name="company_addr") private String companyAddr; @Column(name="company_info") private String companyInfo; @Column(name="job_name") private String jobName; @Column(name="job_addr") private String jobAddr; @Column(name="job_info") private String jobInfo; @Column(name="salary_min") private Integer salaryMin; @Column(name="salary_max") private Integer salaryMax; @Column(name="url") private String url; @Column(name="time") private String time; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getCompanyName() { return companyName; } public void setCompanyName(String companyName) { this.companyName = companyName; } public String getCompanyAddr() { return companyAddr; } public void setCompanyAddr(String companyAddr) { this.companyAddr = companyAddr; } public String getCompanyInfo() { return companyInfo; } public void setCompanyInfo(String companyInfo) { this.companyInfo = companyInfo; } public String getJobName() { return jobName; } public void setJobName(String jobName) { this.jobName = jobName; } public String getJobAddr() { return jobAddr; } public void setJobAddr(String jobAddr) { this.jobAddr = jobAddr; } public String getJobInfo() { return jobInfo; } public void setJobInfo(String jobInfo) { this.jobInfo = jobInfo; } public Integer getSalaryMin() { return salaryMin; } public void setSalaryMin(Integer salaryMin) { this.salaryMin = salaryMin; } public Integer getSalaryMax() { return salaryMax; } public void setSalaryMax(Integer salaryMax) { this.salaryMax = salaryMax; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } }
-
package cn.sgwks.crawlerjob.dao; import cn.sgwks.crawlerjob.entity.Job; import org.springframework.data.jpa.repository.JpaRepository; public interface JobDao extends JpaRepository<Job,Long> { }
5、创建一个引导类。
-
package cn.sgwks.crawlerjob; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class JobApplication { public static void main(String[] args) { SpringApplication.run(JobApplication.class, args); } } package cn.sgwks.crawlerjob.controller; import cn.sgwks.crawlerjob.component.JobSpider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class JobController { @Autowired private JobSpider jobSpider; @RequestMapping("/start") public String startCrawler() { jobSpider.doCrawler(); return "OK"; } }
6、实现爬虫的业务逻辑
-
-
-
爬虫的业务逻辑
需要自定义的组件:-
PageProcess
-
工具类 MathSalary
-
package cn.sgwks.crawlerjob.utils; public class MathSalary { /** * 获取薪水范围 * * @param salaryStr * @return */ public static Integer[] getSalary(String salaryStr) { //声明存放薪水范围的数组 Integer[] salary = new Integer[2]; //"500/天" //0.8-1.2万/月 //5-8千/月 //5-6万/年 String date = salaryStr.substring(salaryStr.length() - 1, salaryStr.length()); //如果是按天,则直接乘以240进行计算 if (!"月".equals(date) && !"年".equals(date)) { salaryStr = salaryStr.substring(0, salaryStr.length() - 2); salary[0] = salary[1] = str2Num(salaryStr, 240); return salary; } String unit = salaryStr.substring(salaryStr.length() - 3, salaryStr.length() - 2); String[] salarys = salaryStr.substring(0, salaryStr.length() - 3).split("-"); salary[0] = mathSalary(date, unit, salarys[0]); salary[1] = mathSalary(date, unit, salarys[1]); return salary; } //根据条件计算薪水 private static Integer mathSalary(String date, String unit, String salaryStr) { Integer salary = 0; //判断单位是否是万 if ("万".equals(unit)) { //如果是万,薪水乘以10000 salary = str2Num(salaryStr, 10000); } else { //否则乘以1000 salary = str2Num(salaryStr, 1000); } //判断时间是否是月 if ("月".equals(date)) { //如果是月,薪水乘以12 salary = str2Num(salary.toString(), 12); } return salary; } private static int str2Num(String salaryStr, int num) { try { // 把字符串转为小数,必须用Number接受,否则会有精度丢失的问题 Number result = Float.parseFloat(salaryStr) * num; return result.intValue(); } catch (Exception e) { } return 0; } }
1)判断是列表页面还是详情页面
2)如果是列表页面,解析职位详情url列表
3)把列表添加到访问队列中。
4)解析下一页的url,添加到访问队列中。
5)如果是详情页面
6)解析职位详情信息
7)封装到Job对象中。
8)把job对象传递给pipeline。-
package cn.sgwks.crawlerjob.component; import cn.sgwks.crawlerjob.entity.Job; import cn.sgwks.crawlerjob.utils.MathSalary; import org.jsoup.nodes.Document; import org.springframework.stereotype.Component; import us.codecraft.webmagic.Page; import us.codecraft.webmagic.Site; import us.codecraft.webmagic.processor.PageProcessor; import us.codecraft.webmagic.selector.Html; import us.codecraft.webmagic.selector.Selectable; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @Component public class JobPageProcessor implements PageProcessor { @Override public void process(Page page) { Html html = page.getHtml(); //1)判断是列表页面还是详情页面 List<Selectable> nodes = html.css("#resultList div.el p.t1").nodes(); //如果得到的集合数量大于0就说明是列表页面 if (nodes.size() > 0) { //2)如果是列表页面,解析职位详情url列表 List<String> urlList = html.css("#resultList div.el p.t1").links().all(); //3)把列表添加到访问队列中。 page.addTargetRequests(urlList); //4)解析下一页的url,添加到访问队列中。 String nextPageUrl = html.css("#rtNext", "href").get(); page.addTargetRequest(nextPageUrl); //跳过pipeline处理 page.setSkip(true); } else { parseJobInfo(page); //5)如果是详情页面 //6)解析职位详情信息 //7)封装到Job对象中。 //8)把job对象传递给pipeline。 } } @Override public Site getSite() { //或者 return new Site(); return Site.me(); } /** * 职位详情解析业务逻辑 * * @param page */ private void parseJobInfo(Page page) { //取html对象 Html html = page.getHtml(); //公司名称 String compName = html.css("div.tHeader.tHjob p.cname a.catn", "text").get(); //公司联系方式 String compAddr = html.css(".tCompany_main > div:nth-child(2) > div:nth-child(2) > p:nth-child(1)", "text").get(); //公司信息 String compInfo = html.css("div.tBorderTop_box div.tmsg.inbox", "text").get(); //职位名称 String jobName = html.css("h1", "text").get(); //工作地点 // 深圳-南山区 | 3-4年经验 | 大专 | 招若干人 | 03-06发布 要分割 String jobLine = html.css("div.tHeader.tHjob div.in p.msg.ltype", "title").get(); //要转义 String[] elems = jobLine.split("\\|"); String jobAddr = ""; //如果得到的数组长度大有0,取第一个 if (elems.length > 0) { jobAddr = elems[0]; } //职位信息,这里也可以利用 html的getDocument()来获取指定的标签也可 Document document = html.getDocument(); String jobInfo = document.select("div.tCompany_main div.tBorderTop_box div.bmsg.job_msg.inbox").text(); //薪资范围,最小 String salaryStr = html.css(".cn > strong:nth-child(2)", "text").get(); System.out.println(salaryStr); //如果未获取到薪资 Integer[] salary = MathSalary.getSalary(salaryStr); int salaryMin = salary[0]; //薪资范围,最大 int salaryMax = salary[1]; //招聘信息详情页url String jobUrl = page.getUrl().get(); //职位最近发布时间 List<String> timeList = Stream.of(elems) .filter(e -> e.contains("发布")) .collect(Collectors.toList()); String time = ""; if (timeList.size() > 0) { time = timeList.get(0); } //把职位信息封装到Job对象中 Job job = new Job(); job.setCompanyName(compName); job.setCompanyAddr(compAddr); job.setCompanyInfo(compInfo); job.setJobName(jobName); job.setJobAddr(jobAddr); job.setJobInfo(jobInfo); job.setSalaryMin(salaryMin); job.setSalaryMax(salaryMax); job.setUrl(jobUrl); job.setTime(time); //把job对象传递给pipeline page.putField("job", job); } }
-
-
Pipeline:把数据保存到数据库
-
package cn.sgwks.crawlerjob.component; import cn.sgwks.crawlerjob.dao.JobDao; import cn.sgwks.crawlerjob.entity.Job; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import us.codecraft.webmagic.ResultItems; import us.codecraft.webmagic.Task; import us.codecraft.webmagic.pipeline.Pipeline; @Component public class JobPipelie implements Pipeline { @Autowired private JobDao jobDao; @Override //开启事务管理 @Transactional public void process(ResultItems resultItems, Task task) { Job job = resultItems.get("job"); //把job保存到数据库 jobDao.save(job); } }
-
-