天天看點

使用HttpClient+Jsoup實作網絡爬蟲抓取京東商品資料資訊

作者:Java機械師

添加依賴

<!--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>5.1.49</version>
        </dependency>
        <!-- HttpClient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!--Jsoup-->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
             <version>1.15.2</version>
        </dependency>
         <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
複制代碼           

配置application.properties

# MySQL配置
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456


# JPA配置
spring.jpa.database=MySQL
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy

複制代碼           

POJO

@Entity
@Table(name = "item")
@Data
public class Item {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    //标準産品機關
    private Long spu;
    //庫存量機關
    private Long sku;
    //商品标題
    private String title;
    //商品價格
    private Double price;
    //商品圖檔
    private String pic;
    //商品詳情位址
    private String url;
    //店鋪;
    private String shop;
    //建立時間
    private Date created;
    //更新時間
    private Date updated;
}
複制代碼           

Dao

public interface ItemDao extends JpaRepository<Item,Long> {
}
複制代碼           

Service

public interface ItemService {

    /**
     * 儲存商品
     *
     * @param item
     */
    void save(Item item);

    /**
     * 删除所有商品
     */
    void deleteAll();
}


@Service
public class ItemServiceImpl implements ItemService {

    @Autowired
    private ItemDao itemDao;

    @Override
    @Transactional
    public void save(Item item) {
        this.itemDao.save(item);
    }

    @Override
    public void deleteAll() {
        this.itemDao.deleteAll();
    }
}
複制代碼           

封裝HttpClient

@Component
public class HttpUtils {

    private static final String FILEPATH = "D:\\demo\\";

    private PoolingHttpClientConnectionManager cm;

    public HttpUtils() {
        this.cm = new PoolingHttpClientConnectionManager();
        //設定最大連接配接數
        this.cm.setMaxTotal(100);
        //設定每個主機的最大連接配接數
        this.cm.setDefaultMaxPerRoute(10);
    }

    /**
     * 根據請求位址下載下傳頁面資料
     *
     * @param url
     * @return 頁面資料
     */
    public String doGetHtml(String url) {
        //擷取HttpClient對象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
        //建立httpGet請求對象,設定url位址
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36");
        //設定請求資訊
        httpGet.setConfig(this.getConfig());
        CloseableHttpResponse response = null;
        try {
            //使用HttpClient發起請求,擷取響應
            response = httpClient.execute(httpGet);
            //解析響應,傳回結果
            if (response.getStatusLine().getStatusCode() == 200) {
                //判斷響應體Entity是否不為空,如果不為空就可以使用EntityUtils
                if (response.getEntity() != null) {
                    String content = EntityUtils.toString(response.getEntity(), "utf8");
                    return content;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //關閉response
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //傳回空串
        return "";
    }


    /**
     * 下載下傳圖檔
     *
     * @param url
     * @return 圖檔名稱
     */
    public String doGetImage(String url) {
        //擷取HttpClient對象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
        //建立httpGet請求對象,設定url位址
        HttpGet httpGet = new HttpGet(url);
        //設定請求資訊
        httpGet.setConfig(this.getConfig());
        CloseableHttpResponse response = null;
        try {
            //使用HttpClient發起請求,擷取響應
            response = httpClient.execute(httpGet);
            //解析響應,傳回結果
            if (response.getStatusLine().getStatusCode() == 200) {
                //判斷響應體Entity是否不為空
                if (response.getEntity() != null) {
                    //擷取圖檔的字尾
                    String extName = url.substring(url.lastIndexOf("."));
                    //建立圖檔名,重命名圖檔
                    String picName = UUID.randomUUID() + extName;
                    //聲明OutPutStream
                    OutputStream outputStream = new FileOutputStream(new File(FILEPATH + picName));
                    response.getEntity().writeTo(outputStream);
                    //傳回圖檔名稱
                    return picName;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //關閉response
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //如果下載下傳失敗,傳回空串
        return "";
    }

    /**
     * 設定請求資訊
     *
     * @return
     */
    private RequestConfig getConfig() {
        RequestConfig config = RequestConfig.custom()
                //建立連接配接的最長時間
                .setConnectTimeout(1000)
                // 擷取連接配接的最長時間
                .setConnectionRequestTimeout(500)
                //資料傳輸的最長時間
                .setSocketTimeout(10000)
                .build();

        return config;
    }
}
複制代碼           

SPU與SKU

SPU

SPU是商品資訊聚合的最小機關,是一組可複用、易檢索的标準化資訊的集合,該集合描述了一個産品的特性。
屬性值、特性相同的商品就可以稱為一個SPU。

如:某型号某配置某顔色的筆記本電腦就對應一個SPU,它有多種配置,或者多種顔色

SKU

SKU即庫存進出計量的機關, 可以是以件、盒、托盤等為機關。SKU是實體上不可分割的最小存貨單元。在使用時要根據不同業态,不同管理模式來處理。

如:某型号的筆記本電腦有多種配置,8G+512G筆記本電腦就是一個SKU。

爬取分析

爬取筆記本電腦搜尋頁面。進行分頁操作,得到分頁請求位址:https://search.jd.com/search?keyword=%E7%94%B5%E8%84%91&wq=%E7%94%B5%E8%84%91&pvid=56a110735c6c491c91416c194aed4c5b&cid3=672&cid2=671&s=56&click=0&page=
使用HttpClient+Jsoup實作網絡爬蟲抓取京東商品資料資訊
所有商品由一個class=J_goodsList的div包裹。div中則是由ul标簽包裹的li标簽,每一個li标簽對應一個商品資訊。
使用HttpClient+Jsoup實作網絡爬蟲抓取京東商品資料資訊
li标簽包含的需要的商品資訊
使用HttpClient+Jsoup實作網絡爬蟲抓取京東商品資料資訊

爬取邏輯

@Component
public class ItemTask {

    @Autowired
    private HttpUtils httpUtils;
    @Autowired
    private ItemService itemService;

    /**
     * 使用定時任務抓取最新資料
     *
     * @throws Exception
     */
    @Scheduled(fixedDelay = 50 * 1000)
    public void itemTask() throws Exception {
    	// 每次執行前請客資料
        itemService.deleteAll();
        
        //聲明需要解析的初始位址
        String url = "https://search.jd.com/search?keyword=%E7%94%B5%E8%84%91&wq=%E7%94%B5%E8%84%91&pvid=56a110735c6c491c91416c194aed4c5b&cid3=672&cid2=671&s=56&click=0&page=";

        // 按照頁面對搜尋結果進行周遊解析,注意頁面是奇數
        for (int i = 1; i < 10; i = i + 2) {
            String html = httpUtils.doGetHtml(url + i);
            // 解析頁面,擷取商品資料并存儲
            this.parse(html);
        }
        System.out.println("商品資料抓取完成!");
    }

    /**
     * 解析頁面,擷取商品資料并存儲
     *
     * @param html
     * @throws Exception
     */
    private void parse(String html) {
        // 解析html擷取Document
        Document doc = Jsoup.parse(html);
        // 擷取spu資訊
        Elements spuEles = doc.select("div#J_goodsList > ul > li");

        // 循環清單中的SPU資訊
        for (int i = 0; i < spuEles.size(); i++) {
            Element element = spuEles.get(i);
            //擷取spu
            String strSpu = element.attr("data-spu");
            if (strSpu == null || strSpu.equals("")) {
                continue;
            }
            long spu = Long.parseLong(strSpu);
            //擷取sku
            long sku = Long.parseLong(element.attr("data-sku"));

            Item item = new Item();
            //設定商品的spu
            item.setSpu(spu);
            //設定商品的sku
            item.setSku(sku);
            //擷取商品的詳情的url
            String itemUrl = "https://item.jd.com/" + sku + ".html";
            item.setUrl(itemUrl);

            // 擷取商品的圖檔
            String picUrl = "https:" + element.select("div.p-img").select("a").select("img").attr("data-lazy-img");
            String picName = this.httpUtils.doGetImage(picUrl);
            item.setPic(picName);

            //擷取商品的價格
            String strPrice = element.select("div.p-price").select("i").text();
            item.setPrice(Double.parseDouble(strPrice));

            //擷取商品的标題
            String title = element.select("div.p-name").select("a").attr("title");
            item.setTitle(title);

            // 店鋪名稱
            String shopName = element.select("div.p-shop a").text();
            item.setShop(shopName);

            item.setCreated(new Date());
            item.setUpdated(item.getCreated());

            //儲存商品資料到資料庫中
            this.itemService.save(item);
        }
    }
}
複制代碼           

配置啟動類

@SpringBootApplication
// 開啟定時任務
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
複制代碼           

執行測試

啟動項目,執行測試。檢視資料庫與本地下載下傳照片。

使用HttpClient+Jsoup實作網絡爬蟲抓取京東商品資料資訊
使用HttpClient+Jsoup實作網絡爬蟲抓取京東商品資料資訊

繼續閱讀