CSDN爬蟲(五)——CSDN用戶(所有)爬取+常用爬蟲正則整理
說明
- 開發環境:jdk1.7+myeclipse10.7+win74bit+mysql5.5+webmagic0.5.2+jsoup1.7.2
- 爬蟲框架:webMagic
- 建議:建議首先閱讀webMagic的文檔,再查看此係列文章,便於理解,快速學習:http://webmagic.io/
- 開發所需jar下載(不包括數據庫操作相關jar包):點我下載
- 該系列文章會省略webMagic文檔已經講解過的相關知識。
概述
- 我們會從CSDN個人中心出發,首先爬取一個用戶的個人信息。然後根據該用戶的好友關係去爬取好友信息。依次類推,爬取所用用戶。
- 爬取CSDN所有用戶是根據“粉絲、關注”去爬取“粉絲、關注”,必然會涉及到“死循環”。到後期肯定會出現大量“髒數據”(重複數據),就要考慮到過濾髒數據的問題。
- 雖然用webMagic框架爬蟲會用到大量的正則表達式,並且爬蟲類的正則在網上也很少能找到資料,但是也是比較固定。
CSDN用戶(所有)爬取代碼預覽
package com.wgyscsf.spider;
import java.util.List;
import org.jsoup.select.Elements;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Html;
import com.wgyscsf.utils.MyStringUtils;
/**
* @author 高遠</n>
* 編寫日期 2016-9-24下午7:25:36</n>
* 郵箱 [email protected]</n>
* 博客 http://blog.csdn.net/wgyscsf</n>
* TODO</n>
*/
public class CsdnMineSpider implements PageProcessor {
private final String TAG = CsdnMineSpider.class.getSimpleName();
private Site site = Site
.me()
.setDomain("my.csdn.net")
.setSleepTime(1000)
// 便於測試,休眠較長時間。
.setUserAgent(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
@Override
public void process(Page page) {
// 列表頁: 這裏進行匹配,匹配出列表頁進行相關處理。
if ((page.getUrl()).regex("http://my.csdn.net/\\w+").match()) {
// 獲取最外層節點
// TODO:應該是get(1),不知道爲什麼
Elements mainElements = page.getHtml().getDocument()
.getElementsByTag("div").get(2).children();
// 個人資料
Elements profileElements = mainElements.get(0).getElementsByTag(
"div");
// 個人技能
Elements skillElements = mainElements.get(1)
.getElementsByTag("div");
// 關係模塊:關注和被關注
Elements relationElements = mainElements.get(2).getElementsByTag(
"div");
// 獲取用戶id
String id_mine = MyStringUtils.getLastSlantContent(skillElements
.get(0).getElementsByTag("a").get(0).attr("href"));
// 開始獲取個人資料
String headImg = profileElements.get(0)
.getElementsByClass("person-photo").get(0)
.getElementsByTag("img").attr("src");
String fansNums = profileElements.get(0)
.getElementsByClass("fans_num").get(0)
.getElementsByTag("b").get(0).text();
String nickname = profileElements.get(0)
.getElementsByClass("person-nick-name").get(0)
.getElementsByTag("span").get(0).text();
// 這裏只能精確到個人資料,沒法繼續分,因爲好多用戶該欄目只填寫部分內容
String personDetail = profileElements.get(0)
.getElementsByClass("person-detail").get(0).text();
// 開始組織個人資料,保存數據操作
System.out.println(TAG + ":用戶id:" + id_mine + ",暱稱:" + nickname
+ ",粉絲:" + fansNums + ",個人資料概述:"
+ personDetail + ",其它信息....");
// 當前爬取頁面信息爬取結束,將當前頁面設置爲“跳過”,下次再加入爬蟲隊列,直接過濾,提高爬取效率。
page.setSkip(true);
// 測試,看是否再被爬取。
page.addTargetRequest("http://my.csdn.net/wgyscsf");
/*
* 核心部分,從關係模塊出發,去遍歷所有相關用戶!
*/
// 開始獲取關注與被關注以及訪客的個人中心。同時加入爬蟲隊列。
String html = relationElements.get(0).html();
List<String> all = new Html(html)
.xpath("//div[@class=\"mod_relations\"]").links().all();
// 加入到爬蟲隊列
page.addTargetRequests(all);
}
}
@Override
public Site getSite() {
return site;
}
public static void main(String[] args) {
Spider.create(new CsdnMineSpider())
.addUrl("http://my.csdn.net/wgyscsf").thread(1)// 便於測試,只開一個線程,正常爬起來,可以開15不成問題。
.pipeline(null)
.run();
}
}
關鍵代碼解釋
- 正則
http://my.csdn.net/\\w+"
表示過濾出以http://my.csdn.net/"
開頭的網址鏈接。不過該正則存在一定的問題,就是隻對網址前面部分進行了限制,並沒有對後面進行限制。只要以http://my.csdn.net/"
開頭的網址全部會被加入到爬蟲隊列,存在大量的“髒數據”網址。比較合理的是:http://my.csdn.net/"
後面可以出現除了“/”的任意字符。正則的準確性直接影響到爬蟲的效率,越準確越好。這裏不再修改。 profileElements.get(0).getElementsByClass("person-detail").get(0).text();
該代碼片段是爲了獲取用戶的個人資料,該資料比較多,包括行業、職業、地區(國、省、市、區等)、姓名等信息。經過分析沒有好的依據對信息進行歸類,這裏只是獲取粗略信息。經過分析,甚至說CSDN官方最開始對該塊沒有進行合理的安排,導致用戶的信息沒有統一的格式。以下代碼片段是該部分的核心代碼,實現了遞歸式的爬取所有用戶。
relationElements
元素來自於mainElements.get(2).getElementsByTag("div");
,屬於“關係模塊”,包括關注的人和被關注的人以及訪客信息。在這個模塊中可以獲取部分其它用戶的id,只不過是部分的,最多隻有6個人信息。CSDN官方沒有提供獲取所有粉絲或者關注者的信息。不過,只要這僅僅的用戶信息,我們就可以爬取所有的用戶。方便的是,順便可以過濾掉“死魚”用戶(沒有相互關係的用戶)。// 開始獲取關注與被關注以及訪客的個人中心。同時加入爬蟲隊列。 String html = relationElements.get(0).html(); List<String> all = new Html(html) .xpath("//div[@class=\"mod_relations\"]").links().all(); // 加入到爬蟲隊列 page.addTargetRequests(all);
需要說明的是,爬取所有用戶,其實不是一定要定位到“關係”模塊。我們甚至可以簡單粗暴的直接獲取
http://my.csdn.net/user_id"
中所用有效鏈接,直接加入到爬蟲隊列。到時候直接通過正則http://my.csdn.net/\\w+"
過濾出有效鏈接即可。代碼如下:// 直接取出該網頁下的所有網址鏈接,簡單粗暴,不用進行判斷。 // 只要在進入爬取用戶信息的時候加正則匹配即可。 // 但是會出現過多的“髒數據”,增加判斷,影響爬取效率。 List<String> all = page.getHtml().links().all(); System.out.println(all);// 測試打印的網址鏈接 // 加入到爬蟲隊列 page.addTargetRequests(all);
正如前文概述中所說,遞歸時的爬取用戶信息,會出現大量已經爬取過的頁面,我們需要“過濾”掉這些信息。核心的代碼是
page.setSkip(true);
,爬取之後,直接設置爲“跳過”,下次爬取會直接跳過該鏈接。webMagic的作者對該方法的解釋如下:/**
- Set whether to skip the result.
- Result which is skipped will not be processed by Pipeline.
* - @param skip whether to skip the result
- @return this
*/
- Set whether to skip the result.
過濾代碼如下:
// 當前爬取頁面信息爬取結束,將當前頁面設置爲“跳過”,下次再加入爬蟲隊列,直接過濾,提高爬取效率。 page.setSkip(true); // 測試,看是否再被爬取。 page.addTargetRequest("http://my.csdn.net/wgyscsf");
爬取結果預覽
- 爬取所有用戶
- 爬取所有
http://my.csdn.net/user_id"
內網址
常用爬蟲正則
http://my.csdn.net/\\w+"
:過濾出所有以http://my.csdn.net/
開頭的網址。^http://blog.csdn.net/((?!/).)*$
:過濾出以http://blog.csdn.net/
開頭,並且後面不能再出現“/”的所有網址鏈接。^http://blog.csdn.net/\\w+/article/list/[0-9]*[1-9][0-9]*$
:過濾出http://blog.csdn.net/
後面是任意字符,並且緊接着/article/list/
,且/article/list/
後面只能是數字的所有網址鏈接。http://blog.csdn.net/\\w+/article/details/\\w+
:效果同上,只是最後允許任意字符,不僅僅限於數字。
測試正則的方式
Pattern pattern = Pattern
.compile("^http://blog.csdn.net/\\w+/article/list/[0-9]*[1-9][0-9]*$");
Matcher matcher = pattern
.matcher("http://blog.csdn.net/wgyscsf/article/list/32423");
boolean b = matcher.matches();
// 當條件滿足時,將返回true,否則返回false
System.out.println(b);
操作代碼(代碼已全部遷移至github,歡迎star)
https://github.com/scsfwgy/WebMagic_CSDN_Demo