亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Java中的OkHttp?JSONP爬蟲

 更新時(shí)間:2022年07月18日 10:35:11   作者:胡安民  
一般在java平臺(tái)上,我們會(huì)使用apache?httpclient作為http客戶端,用于發(fā)送?http?請求,并對(duì)響應(yīng)進(jìn)行處理,這篇文章主要介紹了詳解Java中的OkHttp?JSONP爬蟲的相關(guān)資料,需要的朋友可以參考下

什么是JSOUP

JSOUP 是一款Java 的HTML解析器,可直接解析某個(gè)URL地址、HTML文本內(nèi)容。它提供了一套非常省力的API,可通過DOM,CSS以及類似于jQuery的操作方法來取出和操作數(shù)據(jù)。 官網(wǎng)

jsoup實(shí)現(xiàn)了WHATWG HTML5規(guī)范,并將 HTML 解析為與現(xiàn)代瀏覽器相同的 DOM。

  • 從 URL、文件或字符串中抓取和解析HTML
  • 使用 DOM 遍歷或 CSS 選擇器查找和提取數(shù)據(jù)
  • 操作HTML 元素、屬性和文本
  • 根據(jù)安全列表清理用戶提交的內(nèi)容,以防止XSS攻擊
  • 輸出整潔的 HTML

什么是OkHttp

一般在java平臺(tái)上,我們會(huì)使用apache httpclient作為http客戶端,用于發(fā)送 http 請求,并對(duì)響應(yīng)進(jìn)行處理。比如可以使用http客戶端與第三方服務(wù)(如sso服務(wù))進(jìn)行集成,當(dāng)然還可以爬取網(wǎng)上的數(shù)據(jù)等。okhttp與httpclient類似,也是一個(gè)http客戶端,提供了對(duì) http/2 和 spdy 的支持,并提供了連接池,gzip 壓縮和 http 響應(yīng)緩存功能;

okhttp是目前非?;鸬木W(wǎng)絡(luò)庫,它有以下特性:

1.支持http/2,允許所有同一個(gè)主機(jī)地址的請求共享同一個(gè)socket連接

2.連接池減少請求延時(shí)

3.透明的gzip壓縮減少響應(yīng)數(shù)據(jù)的大小

4.緩存響應(yīng)內(nèi)容,避免一些完全重復(fù)的請求

OkHttp教程

爬蟲需要掌握的技術(shù)

  • JSOUP
  • OKHTTP
  • 前端知識(shí)
  • http和https
  • 數(shù)據(jù)存儲(chǔ)(Json、XML ,txt、html, CSV ,Excel , ES ,mysql,redis…)
  • 數(shù)據(jù)分析
  • JavaScript語言
  • 抓包工具fiddler,Wireshark
  • 數(shù)據(jù)清洗
  • 正則表達(dá)式
  • 文件讀寫
  • 多線程

根據(jù)情況可能還不止上面這些,但是會(huì)了上面這些技術(shù)那么可以說爬蟲算是入門

需要的依賴

 <!--        爬蟲-解析html頁面-->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.3</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.10.0</version>
        </dependency>

JSON入門Demo

爬取華北地區(qū),所有省市,一個(gè)星期全部的天氣

按下f12,就就能查看html的結(jié)構(gòu)進(jìn)行分析具體該怎么爬,然后找到對(duì)應(yīng)的標(biāo)簽,之后根據(jù)標(biāo)簽的位置寫出css選擇器

具體詳情代碼如下:

        //拿到網(wǎng)頁的xml
        String doc = OkHttpUtils.builder()
                .url("http://www.weather.com.cn/textFC/hb.shtml")
                .get()
                .sync();

        //時(shí)間,省市,城市,天氣現(xiàn)象
        StringBuilder stringBuilder0=new StringBuilder();
        //標(biāo)題
        StringBuilder stringBuilder = new StringBuilder("時(shí)間,省市,城市,天氣現(xiàn)象,風(fēng)向風(fēng)力,最高氣溫,天氣現(xiàn)象,風(fēng)向風(fēng)力,最低氣溫");
        stringBuilder0.append(stringBuilder).append("\n");
        Document document = Jsoup.parse(doc);//將頁面轉(zhuǎn)換為Document
        //使用css 選擇器
        Elements selecttop = document.select(".day_tabs li");
        //拿到數(shù)據(jù)列表
        Elements select = document.select(".conMidtab");
        for (int i1 = 0; i1 < selecttop.size(); i1++) {
            Element element = selecttop.get(i1);

            String text = element.text();
            //進(jìn)行數(shù)據(jù)清洗,取出時(shí)間
            String time = PatternCommon.cutPatternStr(text, "[\\u4e00-\\u9fa5]*\\((\\S*)\\)", 1).get(1);
            //取和實(shí)際對(duì)應(yīng)的列表
            Element element1 = select.get(i1);
            Elements midtab = element1.select(".conMidtab2");
            for (int i = 0; i < midtab.size(); i++) {
                StringBuilder stringBuilder1 = new StringBuilder();
                //時(shí)間
                stringBuilder1.append(time).append(",");
                Element element2 = midtab.get(i);
                //拿到所有的行
                Elements trs = element2.select("table tr");
                //拿到省市
                Elements select2 = trs.select(".rowspan");
                stringBuilder1.append(select2.text()).append(",");
                //跳過前3行從第4行開始讀取
                for (int i2 = 3; i2 < trs.size()-1; i2++) {
                    StringBuilder stringBuilder2 = new StringBuilder();
                    Element element3 = trs.get(i2);
                    //拿到行下所有列  城市,天氣現(xiàn)象,風(fēng)向風(fēng)力,最高氣溫,天氣現(xiàn)象,風(fēng)向風(fēng)力,最低氣溫
                    Elements td = element3.select("td");
                    for (int i3 = 0; i3 < td.size(); i3++) {
                        Element element4 = td.get(i3);
                        if(i3 == td.size()-2){
                            //最后一個(gè)不需要逗號(hào)
                            stringBuilder2.append(element4.text());
                            break;
                        }
                        stringBuilder2.append(element4.text()).append(",");
                    }
                    StringBuilder stringBuilder3 = new StringBuilder();
                    stringBuilder3.append(stringBuilder1).append(stringBuilder2);
                    stringBuilder0.append(stringBuilder3).append("\n");
                }
            }
        }

        //將內(nèi)容按行寫入到csv文件中
        String absoluteFilePathAndCreate = ResourceFileUtil.getAbsoluteFileOrDirPathAndCreate("/weather.csv");
        ReadWriteFileUtils.writeStrCover(new File(absoluteFilePathAndCreate),stringBuilder0.toString());

JSOUP常用方法

注意: 下面的參數(shù)名稱query和cssQuery 就是css選擇器

Jsoup:

  • Document Jsoup.parse(str); 將字符串HTML轉(zhuǎn)換為Document
  • Connection connect(String url) 創(chuàng)建到URL的新連接。用于獲取和解析HTML頁面
  • Document parse(File in, “UTF-8”) 將文件內(nèi)容解析為HTML。
  • Document parse(InputStream in, “UTF-8”, “”) 讀取輸入流,并將其解析為HTML。

Document :

  • Elements select(css) 使用css選擇器從document中查詢指定元素 ,返回Elements類型
  • String title() 獲取文檔標(biāo)題元素的字符串內(nèi)容。
  • Element head() 文檔頭元素的訪問者
  • Element body() 文檔主體元素的訪問者。

Elements:

  • Elements select(String query) 在此元素列表中查找匹配的元素。
  • Element get(int index) 返回此列表中指定位置的元素
  • String text() 獲取元素的value
  • boolean hasText() 判斷是否有內(nèi)容
  • List<String> eachText() 獲取每個(gè)匹配元素的文本內(nèi)容
  • String html() 獲取所有匹配元素的組合內(nèi)部HTML
  • boolean is(String query) 測試是否有匹配的元素如果有則為true。
  • Elements next() 獲取此列表中每個(gè)元素的下一個(gè)同級(jí)元素
  • Elements next(String query) 獲取此列表中每個(gè)元素的下一個(gè)同級(jí)元素,并通過查詢進(jìn)行篩選。
  • Elements nextAll() 獲取此列表中每個(gè)元素的以下所有元素同級(jí)。
  • Elements nextAll(String query) 獲取此列表中每個(gè)元素的以下所有元素同級(jí),并通過查詢進(jìn)行篩選。
  • Elements prev() 獲取此列表中每個(gè)元素的前一個(gè)元素同級(jí)。
  • Elements prev(String query) 獲取此列表中每個(gè)元素的前一個(gè)元素同級(jí),并通過查詢進(jìn)行篩選。
  • Elements prevAll() 獲取此列表中每個(gè)元素之前的所有同級(jí)元素。
  • Elements prevAll(String query) 獲取此列表中每個(gè)元素之前的所有同級(jí)元素,并通過查詢進(jìn)行篩選。
  • Elements parents() 獲取匹配元素的所有父元素和祖先元素。
  • Element first() 獲取第一個(gè)匹配的元素。
  • Element last() 獲取最后匹配的元素
  • List<FormElement> forms() 從所選元素(如果有)中獲取FormElement表單
  • Elements filter(NodeFilter nodeFilter) 對(duì)每個(gè)選定元素執(zhí)行深度優(yōu)先過濾 (可以控制具體怎么遍歷)
  • Elements traverse(NodeVisitor nodeVisitor) 對(duì)每個(gè)選定元素執(zhí)行深度優(yōu)先遍歷 (一直遍歷到結(jié)束)

Element:

  • Elements parents() 獲取此元素的父元素和祖先元素,直到文檔根。
  • Element parent() 獲取父元素
  • String tagName() 獲取此元素的標(biāo)記名稱
  • boolean isBlock() 測試此元素是否為塊級(jí)元素
  • String id() 獲取此元素的id屬性。
  • Attributes attributes() 獲取元素上所有屬性
  • Element child(int index) 通過該元素的基于0的索引號(hào)獲取該元素的子元素。
  • Elements children() 獲取此元素的子元素列表
  • List<TextNode> textNodes() 獲取此元素的子文本節(jié)點(diǎn)
  • Elements select(String cssQuery) 查找與選擇器CSS查詢匹配的元素
  • Element selectFirst(String cssQuery) 查找與選擇器CSS查詢匹配的第一個(gè)元素
  • boolean is(String cssQuery) 檢查此元素是否與給定的選擇器CSS查詢匹配。
  • Element nextElementSibling() 獲取此元素的下一個(gè)同級(jí)元素
  • Elements siblingElements() 獲取兄弟元素。如果元素沒有同級(jí)元素 則返回空列表
  • String cssSelector() 獲取將唯一選擇此元素的CSS選擇器。(可用于檢索選擇器中元素的CSS路徑)
  • Element previousElementSibling() 獲取此元素的上一個(gè)同級(jí)元素
  • Element firstElementSibling() 獲取此元素的第一個(gè)同級(jí)元素
  • int elementSiblingIndex() 在其元素同級(jí)列表中獲取此元素的列表索引。 如果這是第一個(gè)同級(jí)元素,則返回0。
  • Element lastElementSibling() 獲取此元素的最后一個(gè)同級(jí)元素
  • Elements getElementsByTag(String tagName) 查找具有指定標(biāo)簽名的元素,包括此元素下的元素并遞歸查找。
  • Element getElementById(String id) 按ID查找元素,包括或在此元素下
  • Elements getElementsByClass(String className) 查找具有此類的元素,包括或在該元素下。不區(qū)分大小寫
  • Elements getElementsByAttribute(String key) 查找具有命名屬性集的元素。不區(qū)分大小寫。
  • Elements getElementsByAttributeStarting(String keyPrefix) 查找屬性名稱以提供的前綴開頭的元素。
  • Elements getElementsByAttributeValue(String key, String value) 查找具有具有特定值的屬性的元素。不區(qū)分大小寫。
  • Elements getElementsByAttributeValueNot(String key, String value) 查找沒有此屬性=值的元素。不區(qū)分大小寫。
  • Elements getElementsByAttributeValueStarting(String key, String valuePrefix) 查找屬性以值前綴開頭的元素。不區(qū)分大小寫。
  • Elements getElementsByAttributeValueEnding(String key, String valueSuffix) 查找屬性以值后綴結(jié)尾的元素。不區(qū)分大小寫。
  • Elements getElementsByAttributeValueContaining(String key, String match) 查找具有其值包含匹配字符串的屬性的元素。不區(qū)分大小寫。
  • Elements getElementsByAttributeValueMatching(String key, Pattern pattern) 查找具有值與提供的正則表達(dá)式匹配的屬性的元素。
  • Elements getElementsByAttributeValueMatching(String key, String regex) 查找具有值與提供的正則表達(dá)式匹配的屬性的元素。
  • Elements getElementsByIndexLessThan(int index) 查找同級(jí)索引小于提供的索引的元素。
  • Elements getElementsByIndexGreaterThan(int index) 查找同級(jí)索引大于提供的索引的元素。
  • Elements getElementsByIndexEquals(int index) 查找同級(jí)索引等于提供的索引的元素
  • Elements getElementsContainingText(String searchText) 查找包含指定字符串的元素。文本可以直接出現(xiàn)在元素中,也可以出現(xiàn)在其任何子元素中。(在元素的文本中查找)
  • Elements getElementsContainingOwnText(String searchText) 查找直接包含指定字符串的元素。搜索不區(qū)分大小寫。文本必須直接出現(xiàn)在元素中,而不是其任何子體中 (在元素自己的文本中查找)
  • Elements getElementsMatchingText(Pattern pattern) 查找其文本與提供的正則表達(dá)式匹配的元素
  • Elements getElementsMatchingText(String regex) 查找其文本與提供的正則表達(dá)式匹配的元素
  • Elements getElementsMatchingOwnText(Pattern pattern) 查找其自身文本與提供的正則表達(dá)式匹配的元素。
  • Elements getElementsMatchingOwnText(String regex) 查找其自身文本與提供的正則表達(dá)式匹配的元素。
  • Elements getAllElements() 查找此元素下的所有元素(包括self和children的子元素)
  • String text() 獲取此元素及其所有子元素的組合文本。空白被規(guī)范化和修剪。
  • String wholeText() 獲取該元素所有子元素的(未編碼)文本,包括原始元素中存在的任何換行符和空格。
  • String ownText() 獲取僅由該元素?fù)碛械奈谋荆粺o法獲取所有子級(jí)的組合文本。
  • boolean hasText() 測試這個(gè)元素是否有任何文本內(nèi)容(不僅僅是空白)。 如果元素具有非空白文本內(nèi)容,則為true。
  • String data() 獲取此元素的組合數(shù)據(jù)。例如,數(shù)據(jù)是腳本標(biāo)記的內(nèi)部。請注意,數(shù)據(jù)不是元素的文本。使用text()獲取用戶可見的文本,使用data()獲取腳本、注釋、CSS樣式等的內(nèi)容。
  • String className() 獲取此元素的“class”屬性的文字值,該屬性可能包括多個(gè)類名,用空格分隔。
  • Set<String> classNames() 獲取所有元素的類名
  • boolean hasClass(String className) 測試此元素是否具有類。不區(qū)分大小寫
  • String val() 獲取表單元素的值(input、textarea等)。

使用JSOUP 方式連接

高并發(fā)爬取使用OkHttp,因?yàn)閮?nèi)部做了很多的優(yōu)化,在爬取的頻率很快和多的時(shí)候效率是非常好的, JSOUP內(nèi)部提供了請求方式但效率沒有OkHttp高,下面是封裝好的直接就可以用,但是只支持返回xml/html頁面否則報(bào)錯(cuò),所以盡量使用OkHttp比較靈活,效率還好


import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

/**
 * 簡要描述
 * @Author: huanmin
 * @Date: 2022/7/17 18:47
 * @Version: 1.0
 * @Description: 文件作用詳細(xì)描述....
 * Document execute = JsoupConnect.build("http://www.weather.com.cn/textFC/hb.shtml").getExecute();
 */
public class JsoupConnect {

   private final Connection connect;

    public static   JsoupConnect build(String url) {
        return new JsoupConnect(url);
    }
    public  Document  getExecute() {
        Document document = null;
        try {
            document = connect.get();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return  document;
    }
    public  Document  postExecute() {
        Document document = null;
        try {
            document = connect.get();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return  document;
    }
    
    
    public JsoupConnect(String url) {
        Connection connect1 = Jsoup.connect(url);
        TrustManager[] trustManagers = buildTrustManagers();
        connect1.timeout(30000);//超時(shí)時(shí)間 30秒
        connect1.sslSocketFactory(createSSLSocketFactory(trustManagers));
        connect1.userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
        this.connect =connect1;
    }
    //設(shè)置代理
    //        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8080));
    public  JsoupConnect proxy(Proxy.Type type,String ip,int port) {
        Proxy proxy = new Proxy(type, new InetSocketAddress(ip, port));
        this.connect.proxy(proxy);
        return this;
    }

    public  JsoupConnect cookie(String name, String value){
        connect.cookie(name,value);
        return this;
    }
    public  JsoupConnect header(String name, String value){
        connect.header(name,value);
        return this;
    }

    //get 和 post
    public  JsoupConnect addParameter(String key, String value){
        connect.data(key,value);
        return this;
    }

    /**
     * 生成安全套接字工廠,用于https請求的證書跳過
     *
     * @return
     */

    private SSLSocketFactory createSSLSocketFactory(TrustManager[] trustAllCerts) {
        SSLSocketFactory ssfFactory = null;
        try {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new SecureRandom());
            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ssfFactory;
    }

    private  TrustManager[] buildTrustManagers() {
        return new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }
                }
        };
    }
}

User-Agent(隨機(jī))

User-Agent是Http協(xié)議中的一部分,屬于頭域的組成部分,User Agent也簡稱UA。用較為普通的一點(diǎn)來說,是一種向訪問網(wǎng)站提供你所使用的瀏覽器類型、操作系統(tǒng)及版本、CPU 類型、瀏覽器渲染引擎、瀏覽器語言、瀏覽器插件等信息的標(biāo)識(shí)。UA字符串在每次瀏覽器 HTTP 請求時(shí)發(fā)送到服務(wù)器! ,所以大批量爬蟲的時(shí)候不要一直使用同一個(gè)User-Agent, 要多切換切換,不然就會(huì)識(shí)別到你了給你拉黑

可以利用隨機(jī)的方式來獲取下面的內(nèi)容

Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50
Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11
Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SE 2.X MetaSr 1.0; SE 2.X MetaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)
Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5
Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5
Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5
Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10
UCWEB7.0.2.37/28/999
Openwave/ UCWEB7.0.2.37/28/999
Mozilla/4.0 (compatible; MSIE 6.0; ) Opera/UCWEB7.0.2.37/28/999
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.google.common.io.Resources;

import java.io.File;
import java.net.URL;
import java.util.Collections;
import java.util.List;

/**
 * 簡要描述
 *
 * @Author: huanmin
 * @Date: 2022/7/17 19:51
 * @Version: 1.0
 * @Description: 文件作用詳細(xì)描述....
 */
public class UserAgent {

    private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36";

    private static List<String> userAgents = null;
    static {
        URL url = Resources.getResource("userAgents");
        if(url != null) {
            File file = new File(url.getPath());
            try {
                userAgents = Files.readLines(file, Charsets.UTF_8);
            } catch(Exception ex) {}
        }
    }

    public static String getUserAgent() {
        if(userAgents == null || userAgents.size() == 0) {
            return DEFAULT_USER_AGENT;
        }
        Collections.shuffle(userAgents);
        return userAgents.get(0);
    }

}

后臺(tái)爬蟲的三大問題

后臺(tái)爬蟲在大行其道的時(shí)候,也有著些許棘手的、到目前也沒有什么好的解決方案問題,而歸根結(jié)底,這些問題的根本原因是由于后臺(tái)爬蟲的先天不足導(dǎo)致,在正式討論之前,我們先思考一個(gè)問題,“爬蟲和瀏覽器有什么異同?”

相同點(diǎn): 本質(zhì)上都是通過http/https協(xié)議請求互聯(lián)網(wǎng)數(shù)據(jù)

不同點(diǎn):

  • 爬蟲一般為自動(dòng)化程序,無需用用戶交互,而瀏覽器不是
  • 運(yùn)行場景不同 ,瀏覽器運(yùn)行在客戶端,而爬蟲一般都跑在服務(wù)端
  • 能力不同, 瀏覽器包含渲染引擎、javascript ,而爬蟲一般都不具備這兩者。

了解了這些,我們再來看看后臺(tái)面臨的問題

問題一:交互問題

有些網(wǎng)頁往往需要和用戶進(jìn)行一些交互,進(jìn)而才能走到下一步,比如輸入一個(gè)驗(yàn)證碼,拖動(dòng)一個(gè)滑塊,選幾個(gè)漢字。網(wǎng)站之所以這么做,很多時(shí)候都是為了驗(yàn)證訪問者到底是人還是機(jī)器。

而爬蟲程序遇到這種情況很難處理,傳統(tǒng)的簡單圖片驗(yàn)證碼可以通過圖形處理算法讀出內(nèi)容,但是隨著各種各樣,花樣百出,人神共憤的、變態(tài)的驗(yàn)證碼越來越多(尤其是買火車票時(shí),分分鐘都想爆粗口),這個(gè)問題就越來越嚴(yán)重。

問題二:Javascript 解析問題

如前文所述,javascript可以動(dòng)態(tài)生成dom。目前大多數(shù)網(wǎng)頁屬于動(dòng)態(tài)網(wǎng)頁**(內(nèi)容由javascript動(dòng)態(tài)填充),尤其是在移動(dòng)端,SPA/PWA應(yīng)用越來越流行,網(wǎng)頁中大多數(shù)有用的數(shù)據(jù)都是通過ajax/fetch動(dòng)態(tài)獲**取后然后再由js填充到網(wǎng)頁dom樹中,單純的html靜態(tài)頁面中有用的數(shù)據(jù)很少。

目前主要應(yīng)對(duì)的方案就是對(duì)于js ajax/fetch請求直接請求ajax/fetch的url ,但是還有一些ajax的請求參數(shù)會(huì)依賴一段javascript動(dòng)態(tài)生成,比如一個(gè)請求簽名,再比如用戶登陸時(shí)對(duì)密碼的加密等等。

如果一昧的去用后臺(tái)腳本去干javascript本來做的事,這就要清楚的理解原網(wǎng)頁代碼邏輯,而這不僅非常麻煩,而且會(huì)使你的爬取代碼異常龐大臃腫,但是,更致命的是,有些javascript可以做的事爬蟲程序是很難甚至是不能模仿的,比如有些網(wǎng)站使用拖動(dòng)滑塊到某個(gè)位置的驗(yàn)證碼機(jī)制,這就很難再爬蟲中去模仿。

其實(shí),總結(jié)一些,這些弊端歸根結(jié)底,是因?yàn)榕老x程序并非是瀏覽器,沒有javascript解析引擎所致。針對(duì)這個(gè)問題,目前主要的應(yīng)對(duì)策略就是在爬蟲中引入Javascript 引擎,如PhantomJS,但是又有著明顯的弊端,如服務(wù)器同時(shí)有多個(gè)爬取任務(wù)時(shí),資源占用太大。

還有就是,這些 無窗口的javascript引擎很多時(shí)候使用起來并不能像在瀏覽器環(huán)境中一樣,頁面內(nèi)部發(fā)生跳轉(zhuǎn)時(shí),會(huì)導(dǎo)致流程很難控制。

問題三:IP限制

這是目前對(duì)后臺(tái)爬蟲中最致命的。網(wǎng)站的防火墻會(huì)對(duì)某個(gè)固定ip在某段時(shí)間內(nèi)請求的次數(shù)做限制,如果沒有超過上線則正常返回?cái)?shù)據(jù),超過了,則拒絕請求,如qq 郵箱。

值得說明的是,ip限制有時(shí)并非是專門為了針對(duì)爬蟲的,而大多數(shù)時(shí)候是出于網(wǎng)站安全原因針對(duì)DOS攻擊的防御措施。后臺(tái)爬取時(shí)機(jī)器和ip有限,很容易達(dá)到上線而導(dǎo)致請求被拒絕。目前主要的應(yīng)對(duì)方案是使用代理,這樣一來ip的數(shù)量就會(huì)多一些,但代理ip需要花錢

selenium+phantomjs(維護(hù)中…內(nèi)容重新整理)

selenium+phantomjs組合進(jìn)行爬取,因?yàn)閟elenium封裝了phantomjs,能夠讓我們更方便,更好的使用,節(jié)約時(shí)間和成本。

PhantomJs下載地址: https://phantomjs.org/download.html

      <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.141.59</version>
        </dependency>
        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>phantomjsdriver</artifactId>
            <version>1.4.4</version>
        </dependency>
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
//ssl證書支持
desiredCapabilities.setCapability("acceptSslCerts", true);
//截屏支持,這里不需要
desiredCapabilities.setCapability("takesScreenshot", false);
//css搜索支持
desiredCapabilities.setCapability("cssSelectorsEnabled", true);
//js支持
desiredCapabilities.setJavascriptEnabled(true);
//驅(qū)動(dòng)支持
desiredCapabilities.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
        "G:\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe");
//創(chuàng)建無界面瀏覽器對(duì)象
PhantomJSDriver driver = new PhantomJSDriver(desiredCapabilities);
//這里注意,把窗口的大小調(diào)整為最大,如果不設(shè)置可能會(huì)出現(xiàn)元素不可用的問題
driver.manage().window().maximize();

上述是對(duì)爬蟲實(shí)例對(duì)象設(shè)置請求頭信息,由于我們爬取的網(wǎng)站采用JS+AJAX進(jìn)行渲染頁面,所以需要js支持,必須設(shè)置setJavascriptEnabled(true),否則無法運(yùn)行js代碼,無法正常拿到渲染后的頁面。

//用于設(shè)置phantomjs運(yùn)行器的位置
desiredCapabilities.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,"G:\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe");

現(xiàn)在,我們有了實(shí)例對(duì)象了,可以進(jìn)行網(wǎng)頁爬取了,以CSDN為例,給大家簡單說一下工具的使用。

假設(shè)我們現(xiàn)在需要獲取JAVA模塊的文章(標(biāo)題+鏈接),我們來看看應(yīng)該怎么做。

String ;
PhantomJSDriver driver = create();
//獲取csdn主頁
driver.get(href);
//定位到Java按鈕
WebElement java = driver.findElementByLinkText("Java");
//執(zhí)行點(diǎn)擊
java.click();
//用于定位到Java模塊列表
WebElement feedlist_id = driver.findElementById("feedlist_id");
List<WebElement> liList = feedlist_id.findElements(By.className("clearfix"));
//循環(huán)遍歷li
for (WebElement li : liList) {
    WebElement title = li.findElement(By.className("title"));
    WebElement a = title.findElement(By.tagName("a"));
    System.out.println("標(biāo)題:" + a.getText() + " 鏈接:" + a.getAttribute("href"));
}

常用的如下:

//通過id的方式獲取元素
public WebElement findElementById(String using)
//通過鏈接文本方式獲取單個(gè)元素
public WebElement findElementByLinkText(String using)
//通過標(biāo)簽名方式獲取單個(gè)元素
public WebElement findElementByTagName(String using)
//通過標(biāo)簽名方式獲取多個(gè)元素
public List<WebElement> findElementsByTagName(String using)
//通過name屬性方式獲取單個(gè)元素
public WebElement findElementByName(String using)
//通過name屬性方式獲取多個(gè)元素
public List<WebElement> findElementsByName(String using)
//通過類名方式獲取單個(gè)元素
public WebElement findElementByClassName(String using)
//通過類名方式獲取多個(gè)元素
public List<WebElement> findElementsByClassName(String using)
//通過css選擇器方式獲取單個(gè)元素
public WebElement findElementByCssSelector(String using)
//通過css選擇器方式獲取多個(gè)元素
public List<WebElement> findElementsByCssSelector(String using)
//通過xpath方式獲取單個(gè)元素
public WebElement findElementByXPath(String using)
//通過xpath方式獲取多個(gè)元素
public List<WebElement> findElementsByXPath(String using)
void click(); //觸發(fā)點(diǎn)擊事件
String getAttribute(String name)  //獲取屬性值
String getText()  //標(biāo)簽文本
element.getAttribute("value")  //輸入框value值
clear()用于清空元素的內(nèi)容
sendKeys(CharSequence... keysToSend)用于給輸入框賦值

選擇下拉框元素

Select select = new Select(driver.findElementById("select")); 
//通過索引選擇 
select.selectByIndex(1);
//通過value值獲取 
select.selectByValue("zhangsan")
//通過文本值獲取
select.selectByVisibleText("張三");

單選和復(fù)選

driver.findElementById("radio"); radio.click(); //單選按鈕

復(fù)選框其實(shí)和單選按鈕一樣,都是定位元素,點(diǎn)擊元素,在選擇元素之前,我們可以通過isSelected()來判斷元素是否被選擇,isEnabled()來判斷元素是否被禁用。

表單提交

WebElement form = driver.findElementById("form");
//只能用于表單提交
form.submit();

在某些時(shí)候,有些網(wǎng)站在執(zhí)行的時(shí)候可能會(huì)打開另外一個(gè)窗口,這個(gè)時(shí)候,如果我們想要回到原先的窗口,應(yīng)該怎么辦呢?

//獲取窗口的句柄 
String windowHandle = driver.getWindowHandle(); 
//另外一個(gè)窗口執(zhí)行... 
//另外一個(gè)窗口執(zhí)行結(jié)束后,我們可以通過switchTo()去返回到原先窗口 
driver.switchTo().window(windowHandle);

在某些AJAX請求進(jìn)行渲染的頁面,可能我們不能立即獲取到渲染后的頁面,那么我們就需要進(jìn)行等待,這里支持兩種類型的等待方式:

隱形等待

//針對(duì)全局設(shè)置,所有命令的超時(shí)時(shí)間都是10s,如果超過等待時(shí)間,則拋出異常。
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);  

顯示等待

WebDriverWait webDriverWait = new WebDriverWait(driver, 10);
webDriverWait.until(new ExpectedCondition<WebElement>() {
    @Override
    public WebElement apply(WebDriver webDriver) {
        return webDriver.findElement(By.id("toolber-keyword"));
    }
});

等待某個(gè)元素,最大等待10s,默認(rèn)0.5s為搜索間隔,搜索到元素則停止等待。在使用獲取數(shù)據(jù)的網(wǎng)站中,使用該方式十分方便,若10s都沒有結(jié)果,那么則認(rèn)定系統(tǒng)出現(xiàn)故障。

某些時(shí)候,我們可能通過getText()的方式獲取標(biāo)簽的文本值并不會(huì)生效 ,phantomjs能夠執(zhí)行js語句,這可是一個(gè)好方式,我們可以通過寫js語句來解決大部分問題。 執(zhí)行js語句Object executeScript(String script, Object... args); 該方法可以供我們執(zhí)行js語句,script代表我們的js語句,args代表散列值,接受參數(shù)使用arguments[0]依次來接受。示例如下:
假設(shè)我們想要獲取某個(gè)標(biāo)簽的文本值

第一種方式:driver.executeScript("document.getElementById('blogClick').innerText")

第二種方式:

WebElement blogClick = driver.findElementById("blogClick");
driver.executeScript("arguments[0].innerText",blogClick);

采用爬蟲處理業(yè)務(wù),如果是靜態(tài)網(wǎng)頁還比較好處理,如果是AJAX+JS渲染的動(dòng)態(tài)頁面,在爬取的過程中,會(huì)遇到各種各樣的坑,就需要耐心研究了,到底怎么才能獲取到

到此這篇關(guān)于詳解Java中的OkHttp JSONP爬蟲的文章就介紹到這了,更多相關(guān)java OkHttp JSONP內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Eclipse 安裝 SVN 在線插件教程

    Eclipse 安裝 SVN 在線插件教程

    這篇文章主要介紹了Eclipse 安裝 SVN 在線插件教程的相關(guān)資料,這里對(duì)安裝步驟進(jìn)行了詳細(xì)介紹,需要的朋友可以參考下
    2016-11-11
  • java基礎(chǔ)詳解之?dāng)?shù)據(jù)類型知識(shí)點(diǎn)總結(jié)

    java基礎(chǔ)詳解之?dāng)?shù)據(jù)類型知識(shí)點(diǎn)總結(jié)

    這篇文章主要介紹了java基礎(chǔ)詳解之?dāng)?shù)據(jù)類型知識(shí)點(diǎn)總結(jié),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很大的幫助,需要的朋友可以參考下
    2021-04-04
  • SpringBoot詳解如何整合Redis緩存驗(yàn)證碼

    SpringBoot詳解如何整合Redis緩存驗(yàn)證碼

    本文主要介紹了SpringBoot集成Redis實(shí)現(xiàn)驗(yàn)證碼的緩存簡單案例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • SprintBoot深入淺出講解場景啟動(dòng)器Starter

    SprintBoot深入淺出講解場景啟動(dòng)器Starter

    本篇文章將和大家分享一下 Spring Boot 框架中的 Starters 場景啟動(dòng)器的內(nèi)容,關(guān)于 Starters 具體是用來做什么的,以及在開發(fā) Spring Boot項(xiàng)目前,要如何自定義一個(gè) Starters 場景啟動(dòng)器
    2022-06-06
  • hadoop實(shí)現(xiàn)grep示例分享

    hadoop實(shí)現(xiàn)grep示例分享

    這篇文章主要介紹了hadoop實(shí)現(xiàn)grep示例,可從文檔中提取包含某些字符串的行,需要的朋友可以參考下
    2014-03-03
  • java求兩個(gè)數(shù)中的大數(shù)(實(shí)例講解)

    java求兩個(gè)數(shù)中的大數(shù)(實(shí)例講解)

    下面小編就為大家?guī)硪黄猨ava求兩個(gè)數(shù)中的大數(shù)(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • 淺談Springboot整合RocketMQ使用心得

    淺談Springboot整合RocketMQ使用心得

    本篇文章主要介紹了Springboot整合RocketMQ使用心得,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-01-01
  • Springboot整合Netty實(shí)現(xiàn)RPC服務(wù)器的示例代碼

    Springboot整合Netty實(shí)現(xiàn)RPC服務(wù)器的示例代碼

    這篇文章主要介紹了Springboot整合Netty實(shí)現(xiàn)RPC服務(wù)器的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Java序列化中子類、父類構(gòu)造函數(shù)問題實(shí)例分析

    Java序列化中子類、父類構(gòu)造函數(shù)問題實(shí)例分析

    這篇文章主要介紹了Java序列化中子類、父類構(gòu)造函數(shù)問題,結(jié)合實(shí)例形式分析了java父類與子類構(gòu)造函數(shù)中序列化接口調(diào)用相關(guān)操作技巧與使用注意事項(xiàng),需要的朋友可以參考下
    2019-09-09
  • SpringBoot整合RabbitMQ消息隊(duì)列的完整步驟

    SpringBoot整合RabbitMQ消息隊(duì)列的完整步驟

    這篇文章主要給大家介紹了關(guān)于SpringBoot整合RabbitMQ消息隊(duì)列的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05

最新評(píng)論