Springboot+JPA下實現簡易爬蟲--爬取豆瓣電視劇資料

長歌懷采薇發表於2020-10-15

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的專案使用的。

如有問題,多多評論指教,文章編寫不易,期待您的推薦和贊,

 

相關文章