1. 程式人生 > >Java爬蟲入門實戰:爬取京東圖書資訊

Java爬蟲入門實戰:爬取京東圖書資訊

網路爬蟲框架

寫網路爬蟲,一個要有一個邏輯順序。本文主要講解我自己經常使用的一個順序,並且本人經常使用這個框架來寫一些簡單的爬蟲,複雜的爬蟲,也是在這個基礎上新增其他程式。

640

首先,我的工程都是使用maven建的。使用Spring MVC框架編寫過網站的同學,可以看出框架的重要性與邏輯性。在我的網路爬蟲框架中,包含的package有db、main、model、parse、util五個檔案。

db:主要放的是資料庫操作檔案,包含MyDataSource【資料庫驅動註冊、連線資料庫的使用者名稱、密碼】,MYSQLControl【連線資料庫,插入操作、更新操作、建表操作等】。

model:用來封裝物件,比如我要獲取京東書籍的ID、書名、價格,則需要在model寫入對應的屬性。說的直白一些,封裝的就是我要操作資料對應的屬性名。

util:主要放的是httpclient的內容,主要作用時將main方法,傳過來的url,通過httpclient相關方法,獲取需要解析的html檔案或者json檔案等。

parse:這裡面存放的是針對util獲取的檔案,進行解析,一般採用Jsoup解析;若是針對json資料,可採用正則表示式或者fastjson工具進行解析,建議使用fastjson,因其操作簡單,快捷。

main:程式起點,也是重點,獲取資料,執行資料庫語句,存放資料。

網路爬蟲的邏輯順序

針對我的網路爬蟲框架,網路爬蟲的邏輯順序,可以描述為:首先,main方法,將url傳給util獲取響應的html檔案,然後util將其獲得的html檔案,傳給parse進行解析,獲取最終資料,封裝在集合中。解析完畢後,資料返回到main,接著main操作db將資料匯入到mysql中。

網路爬蟲例項教學

通過上面的框架,我們可以看出寫一個網路爬蟲,其實很簡單(當然有很複雜的網路爬蟲哦)。下面,我將帶大家寫一個基於java爬蟲京東圖書資訊的網路爬蟲,只是做講解使用,供大家學習和參考。

首先,起點是什麼?你可能覺得是main方法,其實不然,起點是你要知道你要獲取網站中的哪些資料,然後針對要抓取的資料去寫model。如下圖,我要獲取京東上的圖書的價格,和圖書名,還有圖書的id(id是唯一標識,可作為資料表的主鍵) 

640

model

用來封裝物件,我要抓取一本書的資料包括,書籍的id,書名及價格。ecliplse中生成set、get方法的快捷鍵是shift+alt+s然後選擇生成setter、getter

package model;

public class JdModel {
   private String bookID;
   private String bookName;
   private String bookPrice;
   public String getBookID() {
       return bookID;
   }
   public void setBookID(String bookID) {
       this.bookID = bookID;
   }
   public String getBookName() {
       return bookName;
   }
   public void setBookName(String bookName) {
       this.bookName = bookName;
   }
   public String getBookPrice() {
       return bookPrice;
   }
   public void setBookPrice(String bookPrice) {
       this.bookPrice = bookPrice;
   }
}

main

主方法,儘量要求簡單,這裡我就這樣寫了。這裡面有註釋,很好理解。

package main;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import db.MYSQLControl;
import model.JdModel;
import util.URLFecter;

public class JdongMain {
   //log4j的是使用,不會的請看之前寫的文章
   static final Log logger = LogFactory.getLog(JdongMain.class);
   public static void main(String[] args) throws Exception {
       //初始化一個httpclient
       HttpClient client = new DefaultHttpClient();
       //我們要爬取的一個地址,這裡可以從資料庫中抽取資料,然後利用迴圈,可以爬取一個URL佇列
       String url="http://search.jd.com/Search?keyword=Python&enc=utf-8&book=y&wq=Python&pvid=33xo9lni.p4a1qb";
       //抓取的資料
       List<JdModel> bookdatas=URLFecter.URLParser(client, url);
       //迴圈輸出抓取的資料
       for (JdModel jd:bookdatas) {
           logger.info("bookID:"+jd.getBookID()+"\t"+"bookPrice:"+jd.getBookPrice()+"\t"+"bookName:"+jd.getBookName());
       }
       //將抓取的資料插入資料庫
       MYSQLControl.executeInsert(bookdatas);
   }
}

util

util中包含兩個檔案,URLFecter 與HTTPUtils,其中URLFecter 呼叫了HTTPUtils類。

package util;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.util.EntityUtils;
import model.JdModel;
import parse.JdParse;

public class URLFecter {
   public static List<JdModel> URLParser (HttpClient client, String url) throws Exception {
       //用來接收解析的資料
       List<JdModel> JingdongData = new ArrayList<JdModel>();
       //獲取網站響應的html,這裡呼叫了HTTPUtils類
       HttpResponse response = HTTPUtils.getRawHtml(client, url);      
       //獲取響應狀態碼
       int StatusCode = response.getStatusLine().getStatusCode();
       //如果狀態響應碼為200,則獲取html實體內容或者json檔案
       if(StatusCode == 200){
           String entity = EntityUtils.toString (response.getEntity(),"utf-8");    
           JingdongData = JdParse.getData(entity);
           EntityUtils.consume(response.getEntity());
       }else {
           //否則,消耗掉實體
           EntityUtils.consume(response.getEntity());
       }
       return JingdongData;
   }
}

上面程式呼叫的HTTPUtils這個類,以下是HTTPUtils這個類。

package util;

import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.message.BasicHttpResponse;

public abstract class HTTPUtils {
   public static HttpResponse getRawHtml(HttpClient client, String personalUrl) {
       //獲取響應檔案,即html,採用get方法獲取響應資料
       HttpGet getMethod = new HttpGet(personalUrl);
       HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
               HttpStatus.SC_OK, "OK");
       try {
           //執行get方法
           response = client.execute(getMethod);
       } catch (IOException e) {
           e.printStackTrace();

       } finally {
           // getMethod.abort();
       }
       return response;
   }

}

parse

parse主要是通過Jsoup來解析html檔案。並將解析後的資料,封裝在List集合中,將資料通過層層返回到main方法中。

package parse;

import java.util.ArrayList;
import java.util.List;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import model.JdModel;
/*
* 用於將上面傳下來的html解析,獲取我們需要的內容
* 解析方式,採用Jsoup解析,有不明白Jsoup的可以上網搜尋API文件
* Jsoup是一款很簡單的html解析器
*/

public class JdParse {
   public static List<JdModel> getData (String html) throws Exception{
       //獲取的資料,存放在集合中
       List<JdModel> data = new ArrayList<JdModel>();
       //採用Jsoup解析
       Document doc = Jsoup.parse(html);
       //獲取html標籤中的內容
       Elements elements=doc.select("ul[class=gl-warp clearfix]").select("li[class=gl-item]");
       for (Element ele:elements) {
           String bookID=ele.attr("data-sku");
           String bookPrice=ele.select("div[class=p-price]").select("strong").select("i").text();
           String bookName=ele.select("div[class=p-name]").select("em").text();
           //建立一個物件,這裡可以看出,使用Model的優勢,直接進行封裝
           JdModel jdModel=new JdModel();
           //物件的值
           jdModel.setBookID(bookID);
           jdModel.setBookName(bookName);
           jdModel.setBookPrice(bookPrice);
           //將每一個物件的值,儲存到List集合中
           data.add(jdModel);
       }
       //返回資料
       return data;
   }
}

db

db中包含兩個java檔案,MyDataSource,MYSQLControl。這兩個檔案的作用已在前面說明了。

package db;

import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;

public class MyDataSource {
   public static DataSource getDataSource(String connectURI){
       BasicDataSource ds = new BasicDataSource();
        //MySQL的jdbc驅動
       ds.setDriverClassName("com.mysql.jdbc.Driver");
       ds.setUsername("root");              //所要連線的資料庫名
       ds.setPassword("112233");                //MySQL的登陸密碼
       ds.setUrl(connectURI);
       return ds;
   }
}

下面是MYSQLControl,主要使用QueryRunner方法操作資料庫,使用時是batch方法。

package db;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.dbutils.QueryRunner;

import model.JdModel;
/*
* Mysql操作的QueryRunner方法
* 一個數據庫操作類,別的程式直接呼叫即可
*/

public class MYSQLControl {

   //根據自己的資料庫地址修改
   static DataSource ds = MyDataSource.getDataSource("jdbc:mysql://127.0.0.1:3306/moviedata");
   static QueryRunner qr = new QueryRunner(ds);
   //第一類方法
   public static void executeUpdate(String sql){
       try {
           qr.update(sql);
       } catch (SQLException e) {
           e.printStackTrace();
       }
   }
   //第二類資料庫操作方法
   public static void executeInsert(List<JdModel> jingdongdata) throws SQLException {
       /*
        * 定義一個Object陣列,行列
        * 3表示列數,根據自己的資料定義這裡面的數字
        * params[i][0]等是對陣列賦值,這裡用到集合的get方法
        *
        */

       Object[][] params = new Object[jingdongdata.size()][3];
       for ( int i=0; i<params.length; i++ ){
           params[i][0] = jingdongdata.get(i).getBookID();
           params[i][1] = jingdongdata.get(i).getBookName();
           params[i][2] = jingdongdata.get(i).getBookPrice();
       }
       qr.batch("insert into jingdongbook (bookID, bookName, bookPrice)"
               + "values (?,?,?)", params);
       System.out.println("執行資料庫完畢!"+"成功插入資料:"+jingdongdata.size()+"條");

   }
}

再看main方法

在main方法中有這樣一句程式,這便是呼叫了操作資料庫MYSQLControl程式,將抓取的資料插入到資料庫中了

MYSQLControl.executeInsert(bookdatas);

爬蟲效果展示

到此,便完成了這個簡單網路爬蟲的程式設計工作,下面來看看程式執行的結果吧。

640

資料庫中的結果如下:

640

(完)

PS:如果覺得我的分享不錯,歡迎大家隨手點贊、轉發。

原文:blog.csdn.net/qy20115549/article/details/52203722

Java團長

專注於Java乾貨分享

640

掃描上方二維碼獲取更多Java乾貨