基於webmagic的爬蟲小應用--爬取知乎使用者資訊
阿新 • • 發佈:2019-01-07
最近跟舍友@小瘋一起研究爬蟲
他寫了個小應用-CSDN部落格爬蟲 有興趣的朋友可以點進去看看哦~
一起學習。
一起進步。
想要原始碼的朋友點選這裡下載哦~
聽到“爬蟲”,是不是第一時間想到python/php ? 多少想玩爬蟲的java學習者就因為語言不通而止步。Java是真的不能做爬蟲嗎?
當然不是。
只不過python的3行程式碼能解決的問題,而Java要30行。
這裡推薦大家一個大牛做的java爬蟲框架 【WebMagic】
文件簡單易懂!java爬蟲開發的福利啊!
一起來動手做一個小應用吧!
爬蟲小應用–知乎使用者資訊
爬蟲思想有3步
1. 抽取目標連結
2. 抽取需要的資訊
3. 處理資料
接下來檢視html結構,確定待爬取的目標連結。(這裡我的目標連結是【前10個使用者的詳細資訊頁面的url】)
二、抽取需要的資訊(webmagic提供了3種方式,xpath,css選擇,正則表示式。具體可以檢視下[WebMagic文件](http://webmagic.io/docs/zh/))
確定好【目標的資訊】,如下圖。
建立對應的實體物件
package entity;
/**
* 知乎使用者資訊
* @author antgan
*
*/
public class ZhihuUser {
private String key;//keyword
private String name;//使用者名稱
private String identity;//身份
private String location;//所在地
private String profession;//行業
private int sex;//性別
private String school;//學校
private String major;//專業
private String recommend;//個人簡介
private String picUrl;//頭像url
private int agree;//贊同
private int thanks;//感謝
private int ask;//提問數
private int answer;//回答數
private int article;//文章數
private int collection;//收藏數
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdentity() {
return identity;
}
public void setIdentity(String identity) {
this.identity = identity;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getProfession() {
return profession;
}
public void setProfession(String profession) {
this.profession = profession;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
public String getRecommend() {
return recommend;
}
public void setRecommend(String recommend) {
this.recommend = recommend;
}
public String getPicUrl() {
return picUrl;
}
public void setPicUrl(String picUrl) {
this.picUrl = picUrl;
}
public int getAgree() {
return agree;
}
public void setAgree(int agree) {
this.agree = agree;
}
public int getThanks() {
return thanks;
}
public void setThanks(int thanks) {
this.thanks = thanks;
}
public int getAsk() {
return ask;
}
public void setAsk(int ask) {
this.ask = ask;
}
public int getAnswer() {
return answer;
}
public void setAnswer(int answer) {
this.answer = answer;
}
public int getArticle() {
return article;
}
public void setArticle(int article) {
this.article = article;
}
public int getCollection() {
return collection;
}
public void setCollection(int collection) {
this.collection = collection;
}
@Override
public String toString() {
return "ZhihuUser [name=" + name + ", identity=" + identity + ", location=" + location + ", profession="
+ profession + ", sex=" + sex + ", school=" + school + ", major=" + major + ", recommend=" + recommend
+ ", picUrl=" + picUrl + ", agree=" + agree + ", thanks=" + thanks + ", ask=" + ask + ", answer="
+ answer + ", article=" + article + ", collection=" + collection + "]";
}
}
編寫PageProcessor(Processor中的process方法是webmagic的核心,負責抽取目標url的邏輯)
package repo;
import dao.ZhihuDao;
import dao.impl.ZhihuDaoImpl;
import entity.ZhihuUser;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
/**
* 知乎使用者小爬蟲<br>
* 輸入搜尋使用者關鍵詞(keyword),並把搜出來的使用者資訊爬出來<br>
* @date 2016-5-3
* @website ghb.soecode.com
* @csdn blog.csdn.net/antgan
* @author antgan
*
*/
public class ZhiHuUserPageProcessor implements PageProcessor{
//抓取網站的相關配置,包括:編碼、抓取間隔、重試次數等
private Site site = Site.me().setRetryTimes(10).setSleepTime(1000);
//使用者數量
private static int num = 0;
//搜尋關鍵詞
private static String keyword = "JAVA";
//資料庫持久化物件,用於將使用者資訊存入資料庫
private ZhihuDao zhihuDao = new ZhihuDaoImpl();
/**
* process 方法是webmagic爬蟲的核心<br>
* 編寫抽取【待爬取目標連結】的邏輯程式碼在html中。
*/
@Override
public void process(Page page) {
//1. 如果是使用者列表頁面 【入口頁面】,將所有使用者的詳細頁面的url放入target集合中。
if(page.getUrl().regex("https://www\\.zhihu\\.com/search\\?type=people&q=[\\s\\S]+").match()){
page.addTargetRequests(page.getHtml().xpath("//ul[@class='list users']/li/div/div[@class='body']/div[@class='line']").links().all());
}
//2. 如果是使用者詳細頁面
else{
num++;//使用者數++
/*例項化ZhihuUser,方便持久化儲存。*/
ZhihuUser user = new ZhihuUser();
/*從下載到的使用者詳細頁面中抽取想要的資訊,這裡使用xpath居多*/
/*為了方便理解,抽取到的資訊先用變數儲存,下面再賦值給物件*/
String name = page.getHtml().xpath("//div[@class='title-section ellipsis']/span[@class='name']/text()").get();
String identity = page.getHtml().xpath("//div[@class='title-section ellipsis']/span[@class='bio']/@title").get();
String location = page.getHtml().xpath("//div[@class='item editable-group']/span[@class='info-wrap']/span[@class='location item']/@title").get();
String profession = page.getHtml().xpath("//div[@class='item editable-group']/span[@class='info-wrap']/span[@class='business item']/@title").get();
boolean isMale = page.getHtml().xpath("//span[@class='item gender']/i[@class='icon icon-profile-male']").match();
boolean isFemale = page.getHtml().xpath("//span[@class='item gender']/i[@class='icon icon-profile-female']").match();
int sex = -1;
/*因為知乎有一部分人不設定性別 或者 不顯示性別。所以需要判斷一下。*/
if(isMale&&!isFemale) sex=1;//1代表男性
else if(!isMale&&isFemale) sex=0;//0代表女性
else sex=2;//2代表未知
String school = page.getHtml().xpath("//span[@class='education item']/@title").get();
String major = page.getHtml().xpath("//span[@class='education-extra item']/@title").get();
String recommend = page.getHtml().xpath("//span[@class='fold-item']/span[@class='content']/@title").get();
String picUrl = page.getHtml().xpath("//div[@class='body clearfix']/img[@class='Avatar Avatar--l']/@src").get();
int agree = Integer.parseInt(page.getHtml().xpath("//span[@class='zm-profile-header-user-agree']/strong/text()").get());
int thanks = Integer.parseInt(page.getHtml().xpath("//span[@class='zm-profile-header-user-thanks']/strong/text()").get());
int ask = Integer.parseInt(page.getHtml().xpath("//div[@class='profile-navbar clearfix']/a[2]/span[@class='num']/text()").get());
int answer = Integer.parseInt(page.getHtml().xpath("//div[@class='profile-navbar clearfix']/a[3]/span[@class='num']/text()").get());
int article = Integer.parseInt(page.getHtml().xpath("//div[@class='profile-navbar clearfix']/a[4]/span[@class='num']/text()").get());
int collection = Integer.parseInt(page.getHtml().xpath("//div[@class='profile-navbar clearfix']/a[5]/span[@class='num']/text()").get());
//物件賦值
user.setKey(keyword);
user.setName(name);
user.setIdentity(identity);
user.setLocation(location);
user.setProfession(profession);
user.setSex(sex);
user.setSchool(school);
user.setMajor(major);
user.setRecommend(recommend);
user.setPicUrl(picUrl);
user.setAgree(agree);
user.setThanks(thanks);
user.setAsk(ask);
user.setAnswer(answer);
user.setArticle(article);
user.setCollection(collection);
System.out.println("num:"+num +" " + user.toString());//輸出物件
zhihuDao.saveUser(user);//儲存使用者資訊到資料庫
}
}
@Override
public Site getSite() {
return this.site;
}
public static void main(String[] args) {
long startTime ,endTime;
System.out.println("========知乎使用者資訊小爬蟲【啟動】嘍!=========");
startTime = new Date().getTime();
//入口為:【https://www.zhihu.com/search?type=people&q=xxx 】,其中xxx 是搜尋關鍵詞
Spider.create(new ZhiHuUserPageProcessor()).addUrl("https://www.zhihu.com/search?type=people&q="+keyword).thread(5).run();
endTime = new Date().getTime();
System.out.println("========知乎使用者資訊小爬蟲【結束】嘍!=========");
System.out.println("一共爬到"+num+"個使用者資訊!用時為:"+(endTime-startTime)/1000+"s");
}
}
三、處理資料 (這裡我儲存在本地資料庫中)
Dao層介面
package dao;
import entity.ZhihuUser;
/**
* 知乎 資料持久化 介面
* @author 甘海彬
*
*/
public interface ZhihuDao {
/**
* 儲存使用者資訊
* @param user
* @return
*/
public int saveUser(ZhihuUser user);
}
Dao實現類
package dao.impl;
import java.util.ArrayList;
import java.util.List;
import dao.ZhihuDao;
import entity.ZhihuUser;
import util.DBHelper;
/**
* 知乎 資料庫持久化介面 實現
* @author 甘海彬
*
*/
public class ZhihuDaoImpl implements ZhihuDao{
@Override
public int saveUser(ZhihuUser user) {
DBHelper dbhelper = new DBHelper();
StringBuffer sql = new StringBuffer();
sql.append("INSERT INTO spider_zhihu_user ( `key`,`name`,identity,location,profession,sex,school,major,recommend,picUrl,agree,thanks,ask,answer,article,collection)")
//`key`,`name`,identity,location,profession,sex,school,major,recommend,picUrl,agree,thanks,ask,answer,article,collection
.append("VALUES (? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? ) ");
//設定 sql values 的值
List<String> sqlValues = new ArrayList<>();
sqlValues.add(user.getKey());
sqlValues.add(user.getName());
sqlValues.add(user.getIdentity());
sqlValues.add(user.getLocation());
sqlValues.add(user.getProfession());
sqlValues.add(""+user.getSex());
sqlValues.add(user.getSchool());
sqlValues.add(user.getMajor());
sqlValues.add(user.getRecommend());
sqlValues.add(user.getPicUrl());
sqlValues.add(""+user.getAgree());
sqlValues.add(""+user.getThanks());
sqlValues.add(""+user.getAsk());
sqlValues.add(""+user.getAnswer());
sqlValues.add(""+user.getArticle());
sqlValues.add(""+user.getCollection());
try{
int result = dbhelper.executeUpdate(sql.toString(),sqlValues);
}catch(Exception e){
}finally{
dbhelper.close();
}
return result;
}
}
這裡我封裝了個DbHelpler類,方便進行持久化操作,使用單例模式,併線程同步。
package util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
/**
* JDBC通用類
* @author GANAB
*
*/
public class DBHelper {
public static final String driver_class = "oracle.jdbc.OracleDriver";
public static final String driver_url = "jdbc:oracle:thin:@ita-031-w7:1521:xe";
public static final String user = "abel";
public static final String password = "123";
private static Connection conn = null;
private PreparedStatement pst = null;
private ResultSet rst = null;
public DBHelper() {
try {
conn = getConnInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
private Connection getConnInstance() {
if(conn == null){
try {
Class.forName(driver_class);
conn = DriverManager.getConnection(driver_url, user, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("Connect success.");
}
return conn;
}
public void close() {
try {
if (pst != null) {
this.pst.close();
}
if (rst != null) {
this.rst.close();
}
if (conn != null) {
conn.close();
}
System.out.println("Close connection success.");
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* query
*
* @param sql
* @param sqlValues
* @return ResultSet
*/
public ResultSet executeQuery(String sql, List<String> sqlValues) {
try {
pst = conn.prepareStatement(sql);
if (sqlValues != null && sqlValues.size() > 0) {
setSqlValues(pst, sqlValues);
}
rst = pst.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return rst;
}
/**
* update
*
* @param sql
* @param sqlValues
* @return result
*/
public int executeUpdate(String sql, List<String> sqlValues) {
int result = -1;
try {
pst = conn.prepareStatement(sql);
if (sqlValues != null && sqlValues.size() > 0) {
setSqlValues(pst, sqlValues);
}
result = pst.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
/**
* sql set value
*
* @param pst
* @param sqlValues
*/
private void setSqlValues(PreparedStatement pst, List<String> sqlValues) {
for (int i = 0; i < sqlValues.size(); i++) {
try {
pst.setObject(i + 1, sqlValues.get(i));
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
噢!對了!表的建立sql也提供一下!
CREATE TABLE `spider_zhihu_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`key` varchar(30) DEFAULT NULL,
`name` varchar(30) DEFAULT NULL,
`identity` varchar(100) DEFAULT NULL,
`location` varchar(20) DEFAULT NULL,
`profession` varchar(30) DEFAULT NULL,
`sex` int(2) DEFAULT NULL,
`school` varchar(30) DEFAULT NULL,
`major` varchar(30) DEFAULT NULL,
`recommend` varchar(100) DEFAULT NULL,
`picUrl` varchar(255) DEFAULT NULL,
`agree` int(11) DEFAULT NULL,
`thanks` int(11) DEFAULT NULL,
`ask` int(11) DEFAULT NULL,
`answer` int(11) DEFAULT NULL,
`article` int(11) DEFAULT NULL,
`collection` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=104 DEFAULT CHARSET=utf8;
以上就是全部程式碼。
進行測試。
完美~
你知道可以拿這些資料做什麼呢?
科科,我也不知道。