獲取 www.51.job.com 上的招聘信息。只爬取「計算機軟件」和「互聯網電子商務」兩個行業的信息。css
1. 查詢頁面,獲取查詢到列表的中的urlhtml
2. 跳轉到相應頁面,獲取須要的數據java
建立數據庫,建立存儲對應數據的表node
開始 —— > 列表頁面 —— > 獲取url —— > url加入任務 —— > 結束mysql
在解析頁面時,極可能會解析出相同url地址,若是不進行處理,一樣的url解析處理屢次,浪費資源。須要一個url去重功能。web
Scheduler 是 WebMagic 中進行URL管理的組件。包括兩個功能:spring
1. 對抓取的URL頁面隊列進行管理;sql
2. 對已抓取的URL進行去重數據庫
- WebMagic 內置來幾個經常使用的Scheduler,再本地執行規模比較小的爬蟲,那麼基本無需定製Scheduler:apache
DuplicateRemoveScheduler:抽象基類,提供一些模板方法;
QueueScheduler:使用內存隊列保存待抓取的URL。(內存空間小,易形成內存溢出)
FileCacheQueueScheduler:使用文件保存抓取URL,能夠在關閉程序並下次啓動時,從以前抓取到的URL繼續抓取(需指定路徑,會創建 .urls.txt 和 .cusor.txt兩個文件)
PriorityScheduler:使用帶有優先級的內存隊列保存待抓取的URL
RedisScheduler:使用 Redis 保存抓取隊列,能夠進行多臺機器同時合做抓取(須要安裝並啓動 Redis)
- 去重部分被單獨抽象成了一個接口:DuplicateRemove。從而能夠爲同一個 Scheduler 選擇不一樣的去重方式,以適應不一樣的需求。目前提供了兩種去重方式:
HashSetDuplicateRemove(默認):使用 HashSet 來進行去重,佔用內存比較大
BloomFilterDuplicateRemove:使用 BloomFilter 來進行去重,佔用內存比較小,但可能漏抓頁面
若是使用 BloomFilter ,必須加入依賴:
<!-- WebMagic 對布隆過濾器的支持 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> </parent> <groupId>com.xiaojian</groupId> <artifactId>crawler-jobinfo</artifactId> <version>1.0-SNAPSHOT</version> <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> <version>8.0.17</version> </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>18.0</version> </dependency> <!--工具包--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> </dependencies> </project>
#DB Configuration: spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/db_crawler?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull spring.datasource.username=root spring.datasource.password=243600 #JPA Configuration: spring.jpa.database=mysql spring.jpa.show-sql=true
pojo類
@Entity @Table(name = "t_jobinfo") public class JobInfo { // 主鍵 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 公司名稱 private String companyName; ... set,get.toString.....
dao
public interface JobInfoDao extends JpaRepository<JobInfo,Long> { }
service
public interface JobInfoService { /** * 保存招聘信息 * @param jobInfo */ void save(JobInfo jobInfo); /** * 根據條件查詢招聘信息 * @param jobInfo */ List<JobInfo> findJobInfo(JobInfo jobInfo); }
serviceImpl
@Service public class JobInfoServiceImpl implements JobInfoService { @Resource private JobInfoDao jobInfoDao; @Override @Transactional public void save(JobInfo jobInfo) { // 根據招聘url和發佈時間查詢數據 JobInfo param = new JobInfo(); param.setUrl(jobInfo.getUrl()); param.setTime(jobInfo.getTime()); // 查詢 List<JobInfo> list = this.findJobInfo(param); // 判斷數據是否已存在 if(list.size() == 0){ // 若是數據庫爲空,表示招聘信息數據不存在,或者已經更新了,須要新增或更新數據 this.jobInfoDao.saveAndFlush(jobInfo); } jobInfoDao.save(jobInfo); } @Override public List<JobInfo> findJobInfo(JobInfo jobInfo) { // 設置查詢條件 Example<JobInfo> example = Example.of(jobInfo); return jobInfoDao.findAll(example); } }
引導類
@SpringBootApplication // 使用定時任務,須要開啓定時任務,須要加註解 @EnableScheduling public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); } }
JobProcessor.java
@Component public class JobProcessor implements PageProcessor { private String url = "https://search.51job.com/list/030200%252C110200,000000,0000,01%252C32,9,99,java,2,1.html" + "?lang=c&stype=&postchannel=0000&workyear=99&cotype=99°reefrom=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="; @Override public void process(Page page) { // 解析頁面,獲取招聘信息詳情的url地址 List<Selectable> list = page.getHtml().css("div#resultList div.el").nodes(); // 判斷list集合是否爲空 if(list.size() == 0){ // 爲空: 表示詳情頁 // 保存招聘詳情信息 this.saveJobInfo(page); } else{ // 不爲空: 表示招聘列表頁 // 解析出詳情頁url,放到任務隊列中 for(Selectable s : list){ // 獲取url地址 String link = s.links().toString(); // 把獲取到的ulr地址放到任務隊列中 page.addTargetRequest(link); } // 獲取下一頁url地址 String nextUrl = page.getHtml().css("div.p_in li.bk").nodes().get(1).links().toString(); // 把下一頁url放到任務隊列中 page.addTargetRequest(nextUrl); } String html = page.getHtml().toString(); } private Site site = Site.me() .setTimeOut(10 * 1000) // 設置超時時間 .setCharset("gbk") // 編碼 .setRetryTimes(3) // 重試次數 .setRetrySleepTime(3000) // 重試間隔時間 ; @Override public Site getSite() { return site; } /** * 保存招聘詳情信息 * @param page */ private void saveJobInfo(Page page) { Html html = page.getHtml(); JobInfo jobInfo = new JobInfo(); // 將信息封裝到對象中 // 公司名稱 jobInfo.setCompanyName(html.css("div.cn p.cname a","text").toString()); ... 根據需求,抓取對應的數據 // 把結果保存起來 page.putField("jobInfo",jobInfo); } // initialDelay,任務開啓後,等多久執行方法 // fixedDelay,每隔多久執行一次 @Scheduled(initialDelay = 1000,fixedDelay = 600 * 1000) public void processor(){ Spider.create(new JobProcessor()) .addUrl(url) .setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(10000))) .addPipeline(springDataPipeline) // 數據要保存到數據庫中 .thread(10) .run() ; } }
SpringDataPipeline.java
@Component public class SpringDataPipeline implements Pipeline { @Resource private JobInfoService jobInfoService; @Override public void process(ResultItems resultItems, Task task) { // 獲取封裝好的JobInfo對象 JobInfo jobInfo = resultItems.get("jobInfo"); // 判斷數據不爲空 if(jobInfo != null){ this.jobInfoService.save(jobInfo); } } }
4. 啓動 Application.java
完成 !