Springboot+JPA下實現簡易爬蟲--爬取豆瓣電視劇數據
前言:今天聽到產品那邊討論一些需求,好像其中一點是用戶要求我們爬蟲,在網頁上抓取一些數據然后存到我們公司數據庫中,眾所周知,爬蟲的實現對於python語言可是專家,而對於我們使用的Java語言,我也不確定可不可以,趁着無事,上網參考了下資料,自己也寫了些demo,所幸爬取數據成功了,由於我使用的基礎demo項目是自己搭建的springboot+jpa的項目,因此也會在這個基礎上進行爬蟲的實現,文章會貼出具體的步驟以及重要的代碼,至於項目的搭建就不介紹了,我的完整代碼會同步至gitHub,大家可以參考使用,當然也可以使用自己的springboot項目。
gitHub地址:https://github.com/Slience-zae/mail-demo.git
1.材料准備
1.1首先打開豆瓣的網頁,同時F12打開控制台
1.2 在控制台中找到接口url,請求頭信息,以及相應數據格式和字段
在網頁上准備的材料這些就可以了,接下來,該動手撰寫代碼了。
2.代碼實現步驟
2.1 數據庫創建表格
CREATE TABLE `subjects` ( `id` varchar(255) NOT NULL COMMENT 'id', `title` varchar(255) DEFAULT NULL COMMENT '標題', `rate` decimal(10,2) DEFAULT NULL COMMENT '豆瓣評分', `url` varchar(255) DEFAULT NULL COMMENT '觀影地址', `playable` tinyint(4) DEFAULT NULL COMMENT '是否可以觀看:0是 1否', `cover` varchar(255) DEFAULT NULL COMMENT '封面圖片地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
通過相應數據可以得知共有九個字段,但是我們主要保存的是數據主要信息,其中cover_x,cover_y,is_new對於我們來說沒有什么意義,可以不保存到數據庫,因此略過。
2.2 hibernate逆向生成實體
選中右鍵點擊,選擇Generate persistence Mapping,再選擇By Database Schema,選中數據庫的表,再選擇生成實體的包位置,最后點擊ok就能生成到指定位置了,生成后我們再加以修改一下實體,大體是這個樣子。
import lombok.Data; import javax.persistence.*; import java.math.BigDecimal; @Data @Entity @Table(name = "subjects") public class Subjects { @Id private String id;//id
private String title;//標題
private BigDecimal rate;//豆瓣評分
private String url;//觀影地址
private Boolean playable;//是否能夠觀看
private String cover;//封面圖片URL
@Transient private Integer cover_x; @Transient private Integer cover_y; @Transient private Boolean is_new; }
在此說明一下,之所以將cover_x,cover_y,is_new三個字段也添加進實體了,是為了防止接下來json數組轉化List<Subjects>時發生異常,我已經在這三個字段上加了@Transient注解,因此三個字段不會映射數據庫。
2.3 編寫向指定網址抓取json數據的工具類
import org.springframework.boot.configurationprocessor.json.JSONObject; import org.springframework.stereotype.Component; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; @Component public class GetJson { public JSONObject getHttpJson(String url){ try { URL realUrl = new URL(url); HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection(); //設置HTTP的請求頭(參考材料准備截圖的請求頭信息)
connection.setRequestProperty("Accept", "*/*");//設置瀏覽器可以接收的媒體類型
connection.setRequestProperty("Connection", "keep-alive");//網頁打開,建立連接
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3928.4 Safari/537.36");//客戶端使用的操作系統和瀏覽器的名稱和版本 // 建立實際的連接
connection.connect(); //請求成功
if (connection.getResponseCode() == 200) { InputStream is = connection.getInputStream(); //創建字節數組輸出流對象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); //10MB的緩存
byte[] buffer = new byte[10485760]; int len = 0; while ((len = is.read(buffer))!= -1) { byteArrayOutputStream.write(buffer, 0, len); } String jsonString = byteArrayOutputStream.toString(); byteArrayOutputStream.close(); is.close(); //轉換成json數據返回
return new JSONObject(jsonString); } } catch (Exception e) { e.printStackTrace(); } return null; } }
注意一點,設置連接對象的請求頭信息的時候,要將我們在豆瓣上拿到的那幾個參數設置成一樣的,防止抓取數據失敗,具體那幾個參數是什么,已經在代碼注釋上加以說明了。
2.4 完整業務代碼糅合實現
(1).在application.properties配置文件中加入以下代碼
#豆瓣網址
douban_url= https://movie.douban.com/j/search_subjects
(2) dao層加入類
import com.maven.maildemo.entity.Subjects; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.CrudRepository; public interface SubjectsDao extends CrudRepository<Subjects, String>, JpaSpecificationExecutor<Subjects> { }
(3) service,serviceImpl,controller層代碼
import com.maven.maildemo.entity.Subjects; import java.util.List; public interface SubjectsService { /** * 在豆瓣網頁上抓取電視劇信息保存並返回 * @author zae * @param pageNumber 頁碼 */ List<Subjects> getAndSaveSubjectsList(Integer pageNumber) throws Exception; }
import com.alibaba.fastjson.JSON; import com.maven.maildemo.dao.SubjectsDao; import com.maven.maildemo.entity.Subjects; import com.maven.maildemo.service.SubjectsService; import com.maven.maildemo.utils.GetJson; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.configurationprocessor.json.JSONArray; import org.springframework.boot.configurationprocessor.json.JSONObject; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; @Slf4j @Service @Transactional public class SubjectsServiceImpl implements SubjectsService { @Value("${douban_url}") private String doubanUrl; @Autowired private GetJson getJson; private final String DOUBAN_PARM = "?type=tv&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start="; @Autowired private SubjectsDao subjectsDao; @Override public List<Subjects> getAndSaveSubjectsList(Integer pageNumber) throws Exception { if(pageNumber>=0 && pageNumber <= 10000){ String address = doubanUrl+DOUBAN_PARM+pageNumber; //獲取json對象數據
JSONObject httpJson = getJson.getHttpJson(address); if(httpJson != null){ //取出json數據數組
JSONArray subjectsArray = httpJson.getJSONArray("subjects"); if(subjectsArray!=null){ //將json數組轉化為List
List<Subjects> subjectsList = JSON.parseArray(subjectsArray.toString(),Subjects.class); for(Subjects subjects:subjectsList){ //爬出數據后,保存在數據庫中
subjectsDao.save(subjects); } return subjectsList; }else{ return new ArrayList<>(); } }else{ return new ArrayList<>(); } } return new ArrayList<>(); } }
import com.alibaba.fastjson.JSON; import com.maven.maildemo.entity.Subjects; import com.maven.maildemo.service.SubjectsService; import io.swagger.annotations.Api; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("subjects") @Api(description = "豆瓣") public class SubjectsController { @Autowired private SubjectsService subjectsService; /** * 在豆瓣網頁上抓取電視劇信息保存並返回 * @param pageNumber 頁碼 * @author zae * @return
*/ @GetMapping(value = "/getAndSaveSubjectsList") String getAndSaveSubjectsList(@RequestParam Integer pageNumber){ try { List<Subjects> subjectsList = subjectsService.getAndSaveSubjectsList(pageNumber); return subjectsList==null?null: JSON.toJSONString(subjectsList); } catch (Exception e) { return "爬取數據信息發生異常"+e.getMessage(); } } }
業務代碼沒啥復雜的,基本上都加注釋了,看一下就好了。
3.測試
啟動項目,打開swaager,找到剛剛寫的接口,輸入參數點擊進行測試。
此時不出意外應該是執行成功了,打開數據庫,看一下庫里有沒有我們爬下來的數據。
發現庫里有值,說明我們已經成功的將數據爬下來了。不僅僅豆瓣如此,其他網頁數據也是類似的實現。
本人java爬蟲學習借鑒博客:https://blog.csdn.net/qwe86314/article/details/91450098 博主是使用的mybatis結合實現,而我的是結合springboot+jpa的項目使用的。
如有問題,多多評論指教,文章編寫不易,期待您的推薦和贊,