基於Java的網頁爬蟲實踐


本文項目san-spider源碼地址
https://github.com/lufei222/san-spider.git


爬蟲概念

1、爬蟲基本概念

爬蟲的概念 :網絡爬蟲(又稱爲網頁蜘蛛,網絡機器人,在FOAF社區中間,更經常的稱爲網頁追逐者),是一種按照一定的規則,自動地抓取萬維網信息的程序或者腳本。這是百度百科對爬蟲的定義,其實,說簡單點,爬蟲就是利用寫好的程序自動的提取網頁的信息。

2、爬蟲的分類
通用爬蟲:通用爬蟲是搜索引擎(Baidu、Google、Yahoo等)“抓取系統”的重要組成部分。主要目的是將互聯網上的網頁下載到本地,形成一個互聯網內容的鏡像備份。 簡單來講就是儘可能的;把互聯網上的所有的網頁下載下來,放到本地服務器裏形成備分,在對這些網頁做相關處理(提取關鍵字、去掉廣告),最後提供一個用戶檢索接口。
聚焦爬蟲:聚焦爬蟲是根據指定的需求抓取網絡上指定的數據。例如:獲取豆瓣上電影的名稱和影評,而不是獲取整張頁面中所有的數據值。
增量式爬蟲:增量式是用來檢測網站數據更新的情況,且可以將網站更新的數據進行爬取。

3、爬蟲的價值
抓取互聯網上的數據,爲我所用,有了大量的數據,就如同有了一個數據銀行一樣,下一步做的就是如何將這些爬取的數據產品化,商業化。
在這裏插入圖片描述


願景

開源的爬蟲框架已經很多了,有各種語言(比如:python、java)實現的,有單機的,還有大型分佈式的,多達上百種,詳情可見:
開源中國網絡爬蟲框架列表
33款可用來抓數據的開源爬蟲軟件工具
爬蟲項目經驗小結
github上有哪些優秀的java爬蟲項目

我們的要求也不高:

  • 社區豐富,用戶量多,多人用的項目少踩坑。
  • 文檔要全,上手快。
  • 安裝和集成使用簡單。
  • 代碼語法簡潔
  • 爬蟲的主要功能要有,比如:支持請求頭部設置,支持代理,支持多線程,url自動去重複,html解析方便(至少要能支持css選擇器,xpath選擇器,正則表達式等常見的解析方式)

選定好一款爬蟲開源框架後,就要考慮自己的業務特點,設計自己的項目架構了,大多數用爬蟲的人,基本需求其實是類似的。

最終一般的爬蟲項目都是這樣的操作:

  1. 將目標網站的頁面儘可能快速的扒下來

  2. 然後解析出有用的內容

  3. 落地存儲到db、緩存

稍微成熟爬蟲開源框架基本上都已經實現了第一步 。
根據實際業務規則解析完了以後,如何落地、保持更新網站變更策略,都需要我們去考慮。


爬蟲框架選型

可以參考 開源網絡爬蟲框架應該怎麼選?
考慮選型的時候主要有以下參考項:

  • 支持多線程?
  • 爬蟲能用代理麼?
  • 爬蟲會爬取重複數據麼?
  • 爬蟲能爬取ajax生成的信息麼?
  • 爬蟲怎麼抽取網頁信息
  • 爬蟲怎麼保存網頁的信息
  • 爬蟲速度如何?
  • 報錯容易定位修改嗎?

上面說的爬蟲,基本可以分3類:

  • 分佈式爬蟲:Nutch…
  • JAVA單機爬蟲:Crawler4j、WebMagic、WebCollector、Gecco、Jsoup、Htmlunit
  • 非JAVA單機爬蟲:Scrapy…

分佈式爬蟲

Nucth:
優點:分佈式抓取,存儲和索引,有hadoop支持,第三方插件豐富
缺點:使用上手難,用Nutch進行爬蟲的二次開發,爬蟲的編寫和調試所需的時間,往往是單機爬蟲所需的十倍時間不止。

單機爬蟲

對於單機爬蟲框架,日常開發中佔用時間多的地方就是網頁內容解析,所以首先要介紹下優秀的HTML網頁解析器 :Jsoup和Htmlunit和神器Selenium

  • Jousp: 一款非常流行的Java的HTML解析器,主要用來對HTML解析。也可以進行Http請求網頁爬取源碼。
  • Htmlunit:它不僅僅是一個HTML解析器。這是一個真正的“無GUI瀏覽器”和HTML單元測試工具。
  • Selenium:Selenium是基於Web應用的驗收測試工具集合,直接運行在瀏覽器中,通過一系列命令來模擬用戶操作,Selenium可以將這些命令轉化成實際的HTTP請求在瀏覽器中運行 ,獲取到網頁信息和Cookie等信息

上面列舉的單機爬蟲中,Gecco基於註解方式實現,官方demo無法運行,體驗太差,首先排除不考慮。

對於其他幾個,功能都很豐富,且都在持續更新中。

name Github Star 文檔豐富度 使用項目數 網絡博文豐富度(10)
Crawler4j 3.9k 5 199 6
WebMagic 9.1k 非常齊全 586 7.5
WebCollector 2.6k 7 72 7.5

從以上幾個指標來看,都很優秀
基於我實際項目運行對比情,WebMagic文檔豐富上手快,demo項目多,所以當前日常使用WebMagic。


非Java單機爬蟲

主要說 Python 爬蟲,以Scrapy爲首,對比Java主要優勢在於

  • Python的語法簡潔 ,入門簡單,節點解析簡潔高效,比如:Python 可以用 30 行代碼,完成 Java 50 行代碼乾的任務。Python 寫代碼的確快。
  • 爬蟲用戶量大、社區活躍度大。
  • Github基於Python實踐的項目基數巨大,對於大多數網站,基本有參考,拿來修改即用。

綜上對比:
分佈式爬蟲Nucth有點大材小用,開發效率也不高,暫時不打算考慮。
在日常Java項目中,我會首選WebMagic,而當需要花費大量時間精力去做爬蟲工作的時候,我會選擇Python的Scrapy。


爬蟲和反爬蟲

如何應對網站反爬蟲策略?如何高效地爬大量數據?
爬蟲突破封禁的6種常見方法
常見反爬蟲機制與應對方法
爬蟲與反爬蟲的博弈

爬蟲和反爬蟲都是一直在進步的,下面列舉一些常見的爬與反爬運用涉及的相關知識點

  • 設置User-Agent
  • 設置Cookie
  • 訪問頻率限制
  • 代理IP或者分佈式爬蟲:
  • 構造合理的HTTP請求頭

網頁節點的解析方式

CSS選擇器 + Xpath + 正則表達式整理
xpath表達式


Jsoup、WebCollector、Htmlunit解析實例

Jsoup源碼
WebCollector源碼
接下來大篇幅主要介紹Webmagic,因爲先簡要介紹下其他解析器。
使HttpClient、Jsoup、Htmlunit爬取網頁實例代碼源碼:
LagouMulSpider.java

package demo;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.List;
import java.util.Random;

/**
 * lagou多種不同方式實現的爬蟲
 * 測試htmlunit、jsoup、httpclient直接爬取
 */
public class LagouMulSpider {

    static Random random = new Random();
    private static String LAGOU_URL = "https://www.lagou.com/zhaopin/ceo/1/?filterOption=3&sid=f1937baf1115438c9ea9aee62836a985";


    public static void main(String[] args) throws IOException, InterruptedException {
        //直接httpclient爬取lagou網頁,會提示存在惡意訪問行爲被攔截,
        testHttpClient();
        testJsoup();
        testHtmlunitLagou();
        testHtmlunitBaidu();
    }
    private static void testJsoup() throws IOException, InterruptedException {
        System.out.println("************************testJsoup************************");
        for(int i=0;i<2;i++) {
            Document doc = Jsoup.connect(LAGOU_URL).get();
            Elements newsHeadlines = doc.select(".pager_container");
            System.out.println(newsHeadlines);
            Thread.sleep(200);
        }
    }


    private static void testHttpClient() {
        try {
            System.out.println("************************testHttpClient************************");
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(LAGOU_URL);

            CloseableHttpResponse response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            String content = EntityUtils.toString(entity, "utf-8");
            response.close();
            Jsoup.parse(content);
            Document doc = Jsoup.parse(content);
            Elements elements = doc.getElementsByTag("title");
            System.out.println(elements);
        }catch (Exception e){
            System.out.println(e.getCause());
        }
    }

    private static BrowserVersion getRandomBrowserVersion(){
        int i = random.nextInt(5);
        BrowserVersion browserVersion = BrowserVersion.getDefault();
        switch (i){
            case 1:  browserVersion = BrowserVersion.CHROME  ;break;
            case 2:  browserVersion = BrowserVersion.FIREFOX  ;break;
            case 3:  browserVersion = BrowserVersion.FIREFOX_68  ;break;
            case 4:  browserVersion = BrowserVersion.BEST_SUPPORTED  ;break;
            case 5:  browserVersion = BrowserVersion.INTERNET_EXPLORER  ;break;
            default: ;
        }
        return browserVersion;
    }

    /**
     * 測試循環獲取lagou的頁面是否也是五次限制,還是真的能像瀏覽器一樣正常訪問.發現其實是一樣限流了,超過32秒才能繼續訪問
     * @throws IOException
     * @throws InterruptedException
     */
    private static void testHtmlunitLagou() throws IOException, InterruptedException {
        System.out.println("************************testHtmlunitLagou************************");
        for(int i=0;i<2;i++){
            //創建一個webclient
            WebClient webClient = new WebClient(getRandomBrowserVersion());
            //htmlunit 對css和javascript的支持不好,所以請關閉之
            webClient.getOptions().setJavaScriptEnabled(false);
            webClient.getOptions().setCssEnabled(false);
            //獲取頁面
            HtmlPage page = webClient.getPage(LAGOU_URL);
            List<Object> byXPath = page.getByXPath("//div[@class='pager_container']//text()");
            System.out.println(byXPath);
            //關閉webclient
            webClient.close();
        }
    }
    private static void testHtmlunitBaidu() throws IOException {
        System.out.println("************************testHtmlunitBaidu************************");
        String str;
        //創建一個webclient
        WebClient webClient = new WebClient();
        //htmlunit 對css和javascript的支持不好,所以請關閉之
        webClient.getOptions().setJavaScriptEnabled(false);
        webClient.getOptions().setCssEnabled(false);
        //獲取頁面
        HtmlPage page = webClient.getPage("http://www.baidu.com/");
        //獲取頁面的TITLE
        str = page.getTitleText();
        System.out.println(str);
        webClient.close();
    }
}

WebCollector解析實例


WebMagic的介紹及使用

Java爬蟲框架WebMagic的介紹及使用(定時任務、代理)
官方文檔
Java爬蟲框架WebMagic入門

1、WebMagic框架簡介

PageProcessor、Scheduler、Downloader和Pipeline,對應爬蟲生命週期中的處理、管理、下載和持久化等功能,都是Spider中的屬性,爬蟲框架通過Spider啓動和管理。

WebMagic總體架構圖如下:
在這裏插入圖片描述

2、四大組件

  • PageProcessor 負責解析頁面,抽取有用信息,以及發現新的鏈接。需要自己定義。
  • Scheduler 負責管理待抓取的URL,以及一些去重的工作。一般無需自己定製Scheduler。
  • Pipeline 負責抽取結果的處理,包括計算、持久化到文件、數據庫等。
  • Downloader 負責從互聯網上下載頁面,以便後續處理。一般無需自己實現。

3、用於數據流轉的對象
Request 是對URL地址的一層封裝,一個Request對應一個URL地址。
Page 代表了從Downloader下載到的一個頁面——可能是HTML,也可能是JSON或者其他文本格式的內容。
ResultItems 相當於一個Map,它保存PageProcessor處理的結果,供Pipeline使用。


WebMagic+Selenium自動化登錄爬蟲實踐

WebMagic其他 demo
WebMagic實例 GiteeAutoLoginSpider.java

視頻展示效果如下
https://share.weiyun.com/liXqrw51
https://weibo.com/tv/v/J7HTMa0Zu?fid=1034:4518366212194347

package demo;
 
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
 
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.pipeline.ConsolePipeline;
import us.codecraft.webmagic.pipeline.FilePipeline;
import us.codecraft.webmagic.processor.PageProcessor;


/**
 * gitee自動化登錄獲取私有項目
 */
public class GiteeAutoLoginSpider implements PageProcessor {

	private static String GITEE_NAME = System.getenv("GITEE_NAME");
	private static String GITEE_USERNAME = System.getenv("GITEE_USERNAME");
	private static String GITEE_PASSWORD = System.getenv("GITEE_PASSWORD");
	private static String GITEE_URL = "https://gitee.com/login";


	private Site site = Site.me().setRetryTimes(3).setSleepTime(1000).setTimeOut(10000);

	// 用來存儲cookie信息
	private Set<Cookie> cookies = new HashSet<>();

	@Override
	public Site getSite() {
		// 將獲取到的cookie信息添加到webmagic中
		for (Cookie cookie : cookies) {
			site.addCookie(cookie.getName(), cookie.getValue());
		}
		return site;
	}

	/**
	 * 解析網頁節點具體業務邏輯
	 * @param page
	 */
	@Override
	public void process(Page page) {

		System.out.println("開始解析");
		String  tabName = page.getHtml().xpath("//a[@class='item f-bold']//allText()").get();
		System.out.println(tabName);
		List<String> projects = page.getHtml().xpath("//span[@class='project-title']//allText()").all();
		List<String> privateProject = projects.stream().filter(x -> x.contains("san")).distinct().collect(Collectors.toList());
		System.out.println(privateProject);
		page.putField("gitee project ", privateProject);

	}
	
    /**
	 * 登錄獲取cookie的操作
	 * 
     * 使用selenium+chromedriver驅動完成自動登錄gitee獲取cookie的操作
     * 對於大多數網站可以直接獲得cookie
     * 對於大型的驗證比較多的網站,會比較麻煩,建議可以百度 或者 github參照其他項目的selenium自動登錄實現
     * 在自動登錄實現不可行的時候,更快的方式是直接瀏覽器登錄手動複製cookie,以便後續登錄之後的操作繼續正常進行
     */
	public void login() {
		// 登陸
		System.setProperty("webdriver.chrome.driver", "D:/chromedriver/chromedriver.exe"); // 註冊驅動
		WebDriver driver = new ChromeDriver();
		driver.get(GITEE_URL);// 打開網址
		// 防止頁面未能及時加載出來而設置一段時間延遲
		try {
			Thread.sleep(1000);
			// 設置用戶名密碼
			driver.findElement(By.id("user_login")).sendKeys(GITEE_USERNAME); // 用戶名
			driver.findElement(By.id("user_password")).sendKeys(GITEE_PASSWORD); // 密碼
			// 模擬點擊
			driver.findElement(By.name("commit")).click();
			// 防止頁面未能及時加載出來而設置一段時間延遲
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 獲取cookie信息
		cookies = driver.manage().getCookies();
		System.out.println("cookie " + cookies);

		driver.close();

	}
 
	public static void main(String[] args) {
 
		String url = "https://gitee.com/"+GITEE_NAME+"/dashboard/projects?scope=private&&sort="; // 地址
		GiteeAutoLoginSpider dome = new GiteeAutoLoginSpider();
        // 登陸
		dome.login();
		Spider.create(dome)
				.addUrl(url)
				//輸出內容到控制檯
				.addPipeline(new ConsolePipeline())
				//輸出內容到文件
				.addPipeline(new FilePipeline("D:\\webmagic\\gitee"))
				.run();

	}
}

結論和參考

經過項目的經驗時間,其實上面列舉的功能都很強大,如果只是Java項目當中由於一些需求需要使用,那麼其實Jsoup或者Htmlunit足矣,要有代理、多線程、去重、頭部設置、自動登錄等,則根據自己需要引入WebMagic或Selenium等,參照着Github上面的豐富的爬蟲項目肯定能完成自己的需求。
如果你需要投入大量時間精力在爬蟲上面的話,建議直接用Python的Scrapy,已有開源項目足矣讓你在爬蟲工作上游刃有餘。

本文項目san-spider源碼地址
https://github.com/lufei222/san-spider.git


參考

口罩地址Python
htmlunit 爬蟲案例
實例二 htmlunit
解決htmlunit的webclient對象在多線程環境下的共享問題
高級爬蟲進階:HtmlUnit+多線線程+消息隊列快速抓取大量信息數據
Java爬蟲,爬取京東、天貓、淘寶、阿里巴巴、蘇寧、國美、考拉電商數據
基於webmagic的爬蟲項目經驗小結
CSS選擇器 + Xpath + 正則表達式整理
在開源中國爬蟲分類的軟件
雪球網的爬蟲
今日頭條相關的httpunit
Nutch、heritrix、crawler4j優缺點
關於webmagic的說明文檔
基於Webmagic的Java爬蟲(四)爬取動態列表頁內容
基於webmagic的理財產品分頁
java爬蟲Gecco工具抓取新聞實例
爬蟲京東
java爬蟲事例
github上有哪些優秀的java爬蟲項目
https://www.zhihu.com/question/31427895
https://www.52pojie.cn/thread-1068214-1-1.html
使用WebMagic多線程爬取圖+httpClient多線程下載圖片
拉勾網爬取(WebMagic+Selenium+ChromeDriver)
https://blog.csdn.net/weixin_43719622/article/details/102784141
https://github.com/Yangtze-Innovation/Search-Job-Platfom/tree/CourageHe/2-WebMagic/4-WebMagicSelenimu
第一次用webmagic寫爬蟲
webmagic簡書
爬取動態頁面模擬登錄
WebMagic 實現爬蟲入門教程
爬取頁面需要登陸纔可爬取,這種怎麼解決
不能登錄的常見問題1
不能登錄的常見問題2
webmagic框架圖
WebMagic實現分佈式抓取以及斷點抓取,爬蟲主要運行時間消耗是請求網頁時的io阻塞,所以開啓多線程,讓不同請求的等待同時進行,可以大大提高爬蟲運行效率
多線程爬蟲圖
Gather Platform 聚集收集平臺
基於Crawler4j + jsoup實現蟲
selenium介紹
crawler4j簡介
htmlunit HtmlUnit的使用
webcollector簡介
Java開源爬蟲框架WebCollector爬取CSDN博客
爬取微信公衆號
Java開源爬蟲框架WebCollector爬取搜索引擎
爬蟲與反爬蟲

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章