整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Java 網絡爬蟲,該怎么學?

          Java 網絡爬蟲,該怎么學?
           來源:公眾號平頭哥的技術博文 , 
          作者 平頭哥
          

          起網絡爬蟲,大家想起的估計都是 Python ,誠然爬蟲已經是 Python 的代名詞之一,相比 Java 來說就要遜色不少。有不少人都不知道 Java 可以做網絡爬蟲,其實 Java 也能做網絡爬蟲而且還能做的非常好,在開源社區中有不少優秀的 Java 網絡爬蟲框架,例如 webmagic 。我的第一份正式工作就是使用 webmagic 編寫數據采集程序,當時參與了一個輿情分析系統的開發,這里面涉及到了大量網站的新聞采集,我們就使用了 webmagic 進行采集程序的編寫,由于當時不知道其設計原理,使用起來還是走了不少彎路,其實 webmagic 的設計借鑒了 Scrapy ,所以它也能像 Scrapy 一樣強大,關于 webmagic 框架我們將在后續展開詳細的討論。

          在后面的幾年工作中,也參與了好幾個爬蟲項目,但是大多數都是使用 Python ,拋開語言不談,爬蟲也是有一套思想的。這些年寫爬蟲程序,對我個人的技術成長幫助非常大,因為在爬蟲的過程中,會遇到各種各樣的問題,其實做網絡爬蟲還是非常考驗技術的,除了保證自己的采集程序可用之外,還會遇到被爬網站各種奇奇怪怪的問題,比如整個 HTML 頁面有沒一個 class 或者 id 屬性,你要在這種頁面提取表格數據,并且做到優雅的提取,這就是非常考驗你的想象力以及技術啦。非常有幸在剛入行的時候就接觸到了網絡爬蟲這一塊,它加快了我對互聯網的理解和認知,擴寬了我的視野。

          這幾年來網絡爬蟲比較火,如果你想學習 Java 網絡爬蟲,我根據我自己的經驗總結了一下,想入門學習 Java 網絡爬蟲需要知道的四點基礎知識。

          1、有 “道德” 的爬蟲

          我為什么會把這一點放在最前面呢?因為我覺得這一點比較重要,什么叫有 “道德” 的爬蟲呢?就是遵循被爬服務器的規則,不去影響被爬服務器的正常運行,不把被爬服務搞垮,這就是有 “道德” 的爬蟲。

          經常有人討論的一個問題就是爬蟲合法嗎?知乎一下你看到的將是這樣的

          答案千千萬,在這眾多答案中,我個人比較贊同下面的這個回答

          爬蟲作為一種計算機技術就決定了它的中立性,因此爬蟲本身在法律上并不被禁止,但是利用爬蟲技術獲取數據這一行為是具有違法甚至是犯罪的風險的。所謂具體問題具體分析,正如水果刀本身在法律上并不被禁止使用,但是用來捅人,就不被法律所容忍了。

          爬蟲違不違法?取決于你做的事情違不違法,網絡爬蟲的本質是什么?網絡爬蟲的本質是用機器代替人工去訪問頁面。我查看公開的新聞肯定不犯法,所以我去采集公開在互聯網上的新聞也不犯法,就像各大搜索引擎網站一樣,別的網站巴不得別搜索引擎的蜘蛛抓取到。另一種恰恰相反的情況是去采集別人隱私的數據,你自己去查看別人的隱私信息這就是一種違法的行為,所以用程序去采集也是違法的,這就像答案中所說的水果刀本身不違法,但是用來捅人就違法啦。

          要做到有 “道德” 的爬蟲,Robots 協議是你必須需要了解的,下面是 Robots 協議的百度百科

          在很多網站中會申明 Robots 協議告訴你哪些頁面是可以抓取的,哪些頁面是不能抓取的,當然 Robots 協議只是一種約定,就像公交車上的座位一樣標明著老弱病殘專座,你去坐了也不違法。

          除了協議之外,我們在采集行為上也需要克制,在 『數據安全管理辦法(征求意見稿)』的第二章第十六條指出:

          網絡運營者采取自動化手段訪問收集網站數據,不得妨礙網站正常運行;此類行為嚴重影響網站運行,如自動化訪問收集流量超過網站日均流量三分之一,網站要求停止自動化訪問收集時,應當停止。

          這條規定指出了爬蟲程序不得妨礙網站正常運行,如果你使用爬蟲程序把網站搞垮了,真正的訪問者就不能訪問該網站了,這是一種非常不道德的行為。應該杜絕這種行為。

          除了數據的采集,在數據的使用上同樣需要注意,我們即使在得到授權的情況下采集了個人信息數據,也千萬不要去出賣個人數據,這個是法律特別指出禁止的,參見:

          根據《最高人民法院 最高人民檢察院關于辦理侵犯公民個人信息刑事案件適用法律若干問題的解釋》第五條規定,對“情節嚴重”的解釋:
          • (1)非法獲取、出售或者提供行蹤軌跡信息、通信內容、征信信息、財產信息五十條以上的;
          • (2)非法獲取、出售或者提供住宿信息、通信記錄、健康生理信息、交易信息等其他可能影響人身、財產安全的公民個人信息五百條以上的;
          • (3)非法獲取、出售或者提供第三項、第四項規定以外的公民個人信息五千條以上的便構成“侵犯公民個人信息罪”所要求的“情節嚴重”。
          • 此外,未經被收集者同意,即使是將合法收集的公民個人信息向他人提供的,也屬于刑法第二百五十三條之一規定的“提供公民個人信息”,可能構成犯罪。

          2、學會分析 Http 請求

          我們每一次與服務端的交互都是通過 Http 協議,當然也有不是 Http 協議的,這個能不能采集我就不知道啦,沒有采集過,所以我們只談論 Http 協議,在 Web 網頁中分析 Http 協議還是比較簡單,我們以百度檢索一條新聞為例

          我們打開 F12 調試工具,點擊 NetWork 查看版能查看到所有的請求,找到我們地址欄中的鏈接,主鏈接一般存在 NetWork 最上面一條鏈接

          在右邊headers查看欄中,我們能夠看到這次請求所需要的參數,在這里我們需要特別注意 Request Headers 和 Query String Parameters 這兩個選項欄。

          Request Headers 表示的是該次 Http 請求所需要的請求頭的參數,有一些網站會根據請求頭來屏蔽爬蟲,所以里面的參數還是需要了解一下的,請求頭參數中大部分參數都是公用的, User-Agent 和 Cookie 這兩個參數使用比較頻繁, User-Agent 標識瀏覽器請求頭,Cookie 存放的是用戶登錄憑證。

          Query String Parameters 表示該次 Http 請求的請求參數,對于post 請求來說這個還是非常重要的,因為在這里可以查看到請求參數,對我們模擬登陸等 Post 請求非常有用。

          上面是網頁版的 HTTP 請求的鏈接分析,如果需要采集 APP 里面的數據就需要借助模擬器了,因為 APP 里沒有調試工具,所以只能借助模擬器,使用較多的模擬器工具有如下兩種,有興趣的可以執行研究。

          • fiddler
          • wireshark

          3、學會 HTML 頁面解析

          我們采集的頁面都是 HTML 頁面,我們需要在 HTML 頁面中獲取我們需要的信息,這里面就涉及到了 HTML 頁面解析,也就是 DOM 節點解析,這一點是重中之重,如果你不會這一點就像魔術師沒有道具一樣,只能干瞪眼啦。例如下面這個 HTML 頁面

          我們需要獲取標題 “java user-agent 判斷是否電腦訪問” ,我們先通過 F12 檢查元素

          標題所在的 span 標簽我已經在圖中框出來啦,我們該如何解析這個節點信息呢?方法有千千萬萬,經常使用的選擇器應該是 CSS 選擇器 和 XPath ,如果你還不知道這兩種選擇器,可以點擊下方鏈接學習了解一下:

          CSS 選擇器參考手冊:https://www.w3school.com.cn/cssref/css_selectors.asp

          XPath 教程:https://www.w3school.com.cn/xpath/xpath_syntax.asp

          使用 CSS 選擇器解析的寫法為:#wgt-ask > h1 > span

          使用 XPath 解析的寫法為://span[@class="wgt-ask"]

          這樣就獲取到了 span 的節點,值需要取出 text 就好了,對于 CSS 選擇器 和 XPath 除了自己編寫之外,我們還可以借助瀏覽器來幫我們完成,例如 chrome 瀏覽器

          只需要選中對應的節點,右鍵找到 Copy ,它提供了幾種獲取該節點的解析方式,具體的如上圖所示,Copy selector 對應的就是 Css 選擇器,Copy XPath 對應的是 XPath,這個功能還是非常有用的。

          4、了解反爬蟲策略

          因為現在爬蟲非常泛濫,很多網站都會有反爬蟲機制,來過濾掉爬蟲程序,以便保證網站的可以用,這也是非常有必要的手段,畢竟如果網站不能使用了,就沒有利益可談啦。反爬蟲的手段非常多,我們來看看幾種常見的反爬蟲手段。

          基于 Headers 的反爬蟲機制

          這是一種比較常見的反爬蟲機制,網站通過檢查 Request Headers 中的 User-Agent 、Referer 參數,來判斷該程序是不是爬蟲程序。要繞過這種機制就比較簡單,我們只需要在網頁中先查看該網站所需要的 User-Agent 、Referer 參數的值,然后在爬蟲程序的 Request Headers 設置好這些參數就好啦。

          基于用戶行為的反爬蟲機制

          這也是一種常見的反爬蟲機制,最常用的就是 IP 訪問限制,一個 IP 在一段時間內只被允許訪問多少次,如果超過這個頻次的話就會被認為是爬蟲程序,比如豆瓣電影就會通過 IP 限制。

          對于這種機制的話,我們可以通過設置代理 IP 來解決這個問題,我們只需要從代理ip網站上獲取一批代理ip,在請求的時候通過設置代理 IP 即可。

          除了 IP 限制之外,還會有基于你每次的訪問時間間隔,如果你每次訪問的時間間隔都是固定的,也可能會被認為是爬蟲程序。要繞過這個限制就是在請求的時候,時間間隔設置不一樣,比例這次休眠 1 分鐘,下次 30 秒。

          基于動態頁面的反爬蟲機制

          有很多網站,我們需要采集的數據是通過 Ajax 請求的或者通過 JavaScript生成的,對于這種網站是比較蛋疼的,繞過這種機制,我們有兩種辦法,一種是借助輔助工具,例如 Selenium 等工具獲取渲染完成的頁面。第二種方式就是反向思維法,我們通過獲取到請求數據的 AJAX 鏈接,直接訪問該鏈接獲取數據。

          以上就是爬蟲的一些基本知識,主要介紹了網絡爬蟲的使用工具和反爬蟲策略,這些東西在后續對我們的爬蟲學習會有所幫助,由于這幾年斷斷續續的寫過幾個爬蟲項目,使用 Java 爬蟲也是在前期,后期都是用 Python,最近突然間對 Java 爬蟲又感興趣了,所以準備寫一個爬蟲系列博文,重新梳理一下 Java 網絡爬蟲,算是對 Java 爬蟲的一個總結,如果能幫助到想利用 Java 做網絡爬蟲的小伙伴,那就更棒啦。Java 網絡爬蟲預計會有六篇文章的篇幅,從簡單到復雜,一步一步深入,內容涉及到了我這些年爬蟲所遇到的所有問題。下面是模擬的六篇文章介紹。

          1、網絡爬蟲,原來這么簡單

          這一篇是網絡爬蟲的入門,會使用 Jsoup 和 HttpClient 兩種方式獲取到頁面,然后利用選擇器解析得到數據。最后你會收獲到爬蟲就是一條 http 請求,就是這么簡單。

          2、網頁采集遇到登錄問題,我該怎么辦?

          這一章節簡單的聊一聊獲取需要登錄的數據,以獲取豆瓣個人信息為例,從手動設置 cookies 和模擬登陸這兩種方式簡單的聊一聊這類問題。

          3、網頁采集遇到數據 Ajax 異步加載,我該怎么辦?

          這一章節簡單的聊一聊異步數據的問題,以網易新聞為例,從利用 htmlunit 工具獲取渲染完頁面和反向思維直接獲取到 Ajax 請求連接獲取數據兩種方式,簡單的聊一下這類問題的處理方式。

          4、網頁采集 IP 被封,我該怎么辦?

          IP 訪問被限制這應該是常見的事情,以豆瓣電影為例,主要以設置代理IP為中心,簡單的聊一聊 IP 被限制的解決辦法,還會簡單的聊一下如何搭建自己的ip代理服務。

          5、網絡采集性能太差,我該怎么辦?

          有時候對爬蟲程序的性能有要求,這種單線程的方式可能就行不通了,我們可能就需要多線程甚至是分布式的爬蟲程序啦,所以這一篇主要聊一聊多線程爬蟲以及分布式爬蟲架構方案。

          6、開源爬蟲框架 webmagic 使用案例解析

          以前用 webmagic 做過一次爬蟲,但是那個時候我似懂非懂的,沒有很好的理解 webmagic 框架,經過這幾年的經歷,我現在對這個框架有了全新的認識,所以想按照 webmagic 的規范搭建一個簡單的 demo來體驗 webmagic 的強大之處。

          篇文章不介紹xpath庫的安裝和原理,只歸納總結xpath的所有用法

          一、基本規則

          1. 常用表達式規則



          2. 多屬性匹配運算符介紹



          二、基本用法

          1. 初始化對象

          html_doc="""

          <html>

          <head><title>The Dormouse's story</title></head>

          <body>

          <p class="title"><b>The Dormouse's story</b></p>

          <ul class="list" id="list-1">

          <li class="element"><a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>Foo</li>

          <li class="element">Bar</li>

          <li class="element">Jay</li>

          </ul>

          <ul class="list two" id="list-2">

          <li class="element"><a href="http://example.com/tillie" class="parent" id="link3">Tillie</a>evan</li>

          <li class="element">jane</li>

          <li class="element">summer</li>

          </ul>

          <p class="story">Once upon a time there were three little sisters; and their names were

          <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,

          <a href="http://example.com/lacie" class="child" id="link2">Lacie</a> and

          <a href="http://example.com/tillie" class="parent" id="link3">Tillie</a>;

          and they lived at the bottom of a well.</p>

          <p class="story">End...</p>

          </body>

          </html>

          """

          html=etree.HTML(html_doc ) # html文本初始化

          # html=etree.parse('./example.html', etree.HTMLParser()) # html文件初始化

          2. 補全HTML代碼

          print(etree.tostring(html).decode('utf-8'))

          3. 文本獲取

          print(html.xpath('//a[@class="parent"]/text()')) # 獲取所有a節點內class等于'parent'的文本,返回一個列表

          4. 屬性獲取

          print(html.xpath('//ul/@class')) # 獲取所有ul節點內的class值,返回一個列表

          print(html.xpath('//ul/attribute::*')) # 獲取所有ul節點內的所有屬性值,返回一個列表

          5. 屬性匹配

          # 屬性匹配

          print(html.xpath('//ul[@class="list"]')) # 獲取所有class等于'list'的ul節點,返回一個列表

          # 屬性多值匹配

          print(html.xpath('//ul[contains(@class, "two")]')) # 獲取所有class包含'two'的ul節點,返回一個列表

          # 多屬性匹配

          print(html.xpath('//ul[contains(@class, "two") and @id="list-2"]')) # 滿足上面的情況再加上id等于'list-2',返回一個列表

          6. 獲取所有節點

          print(html.xpath('//*')) # 獲取所有節點,返回一個列表

          print(html.xpath('//li')) # 獲取所有的li節點,返回一個列表

          7. 獲取父 / 祖先節點

          print(html.xpath('//li/parent::*')) # 獲取所有li節點的直接父節點,返回一個列表

          print(html.xpath('//li/..')) # 用法同上

          print(html.xpath('//li/ancestor::*')) # 獲取所有li節點的祖先節點,返回一個列表

          print(html.xpath('//li/ancestor::ul')) # 獲取所有li節點的ul祖先節點,返回一個列表

          8. 獲取子 / 子孫節點

          print(html.xpath('//ul/child::*')) # 獲取所有ul節點內的直接子節點,返回一個列表

          print(html.xpath('//ul/child::li')) # 獲取所有ul節點內的li直接子節點,返回一個列表

          print(html.xpath('//ul/li')) # 用法同上

          print(html.xpath('//ul/descendant::*')) # 獲取所有ul節點內的子孫節點,返回一個列表

          print(html.xpath('//ul/descendant::a')) # 獲取所有ul節點內的a子孫節點,返回一個列表

          print(html.xpath('//ul//a')) # 用法同上

          9. 獲取兄弟 / 后續節點

          print(html.xpath('//li[1]/following-sibling::*')) # 獲取所有li[1]節點之后的兄弟節點,返回一個列表

          print(html.xpath('//li[1]/following::*')) # 獲取所有li[1]節點的后續節點,返回一個列表

          print(html.xpath('//li[1]/following::*[2]')) # 獲取所有li[1]節點后的第二個節點,返回一個列表

          10. 按序選擇(正序位置是從1開始,last()-2 代表倒數第三個位置,因為last()是最后一個)

          print(html.xpath('//ul/li[1]')) # 獲取所有ul節點內的第一個li節點,返回一個列表

          print(html.xpath('//ul/li[last()]')) # 獲取所有ul節點內的最后一個li節點,返回一個列表

          print(html.xpath('//ul/li[last()-2]')) # 獲取所有ul節點內的倒數第三個li節點,返回一個列表

          print(html.xpath('//ul/li[position()<3]')) # 獲取所有ul節點內位置小于3的li節點,返回一個列表

          一章講了selenium提供了多種元素定位方法,今天學習元素定位之link定位:
          find_element_by_xpath

          前幾章學習的幾種定位方法相對來說比較簡單,通常在一個頁面當中每一個元素都有一個唯一id和name屬性值,我們可以通過它們的屬性值來找到它們;但是實際項目中并非想象的這般美好,有時候一個元素并沒有id、name屬性,或者頁面上多個元素的id和name屬性值相同,又或者每一次刷新和頁面,id值都會隨機變化,這些情況下,我們如何來定位元素呢?

            下面介紹Xpath與CSS定位,與前面介紹的幾種定位方式相比,他們提供了靈活的定位策略,可以通過不同的方式定位想到的元素。

            Xpath是一種在XML文檔中定位元素的語言。因為HTML可以看作XML的一種實現,所以selenium用戶可以使用這種強大的語言在web應用中定位元素。

          有代碼示例都以百度首頁為例,且都是使用Chrome瀏覽器打開網頁,在百度首頁時候用Chrome自帶控制臺查看百度輸入框的HTML代碼,可以使用F12或右鍵檢查打開控制臺,找到百度輸入框的THML代碼,并使用鼠標右鍵選擇Copy Xpath路徑,如下圖:



          代碼如下圖:


          運行結果如下:




          根據上圖結果可以看出我們在輸入框輸入了selenium但是沒有任何查詢信息,這是因為我們沒有做回車操作

           絕對路徑定位

            Xpath有很多種定位策略,最簡單直觀的就是寫出元素的絕對路徑。如果扔把一個元素看作一個人的話,假設這個人沒有任何屬性特征(手機號、姓名、身份證),但這個人一定存在于某個地理位置,如XX省XX市XX區XX路XX號。對于頁面上的元素而言也會有這樣一個絕對地址。

          參考baidu.html前端工具所展示的代碼,我們可以通過的方式找到百度輸入框和搜索按鈕

            find_element_by_xpath(“/html/body/div/div[2]/div/div/div/from/span/input”)

            find_element_by_xpath(“/html/body/div/div[2]/div/div/div/from/span[2]/input”)

            find_element_by_xpath()方法使用XPath語言來定位元素。XPath主要用標簽的層級關系來定位元素的絕對路徑,最外層為html語言。在boby文本,一級一級查找,如果一個層級下有多個相同的標簽名,那么就按上下順序確定是第幾個,例如div[2]表示當前層級下的第二個的第二個div標簽。

            利用元素屬性定位

            除了使用絕對路徑外,XPath也可以使用元素的屬性值來定位。同樣的百度輸入框和搜索按鈕為例:

            find_element_by_xpath(“//inout[@id=’kw’]”)
            find_element_by_xpath(“//inout[@id=’su’]”)

            //表示當前頁面某個目錄下,input表示定位元素的標簽名,[@id=’kw’]表示這個元素的id屬性值等于kw。下面通過name和class屬性值來定位。

            find_element_by_xpath(“//inout[@name=wd]”)

            find_element_by_xpath(“//inout[@class=’s_ipt’]”)

            find_element_by_xpath(“//inout*[@class=’bg s_btn’]”)

            如果不想指定標簽名,則也可以用星號(*)代替。當然,使用XPath不局限于id、name和class這三個屬性值,元素的任意屬性值都可以使用,只要它能唯一的標識一個元素。

            find_element_by_xpath(“//inout[@maxlength=’100’]”)

            find_element_by_xpath(“//inout[@autocomplete=’off’]”)

            find_element_by_xpath(“//inout[@type=’submit’]”)

            層級與屬性結合

            如果一個元素本身沒有可以唯一標識這個元素的屬性值,那么我們可以找其上一級元素,如果它的上一級元素有可以唯一標識屬性的值,也可以拿來使用。還是以百度輸入框為例,加入百度輸入框本身沒有可利用的屬性值,那么我們可以找它的上一級屬性。例如“小明”剛出生的時候沒有名字,沒有上戶口,那么親朋好友來找“小明”時可以先找到小明的爸爸,因為他爸爸是有很多屬性特征的,找到了小明的爸爸后,就可以找到小明了。通過XPath描述如下:

            find_element_by_xpath("//span[@class=’bg s_ipt_wr’]/input")

            span[@class=’bg s_ipt_wr’]通過class屬性定位到父級元素,后面/input就表示父級元素下面的子元素。如果父級元素沒有可利用的屬性值,那么可以繼續向上查找“爺爺”元素。

            find_element_by_xpath("//form[@id='form’]/span/input")

            find_element_by_xpath("//form[@id='form’]/span[2]/input")

            我們可以通過這種方法一級一級地向上查找,知道找到最外層的<html>標簽,這就是一個絕對路徑了。

            使用邏輯運算符

            如果一個屬性不能唯一地區分一個元素,我們還可以使用邏輯運算符連接多個屬性來查找元素。

            <input id=’kw’ class=’su’ name=’ie’>

            <input id=’kw’ class=’aa’ name=’ie’>

            <input id=’bb’ class=’su’ name=’ie’>

            如上面的三行元素,假如我們現在要定位第一行元素,如果使用id將會與第二行元素重名,如果使用class將會與第三行元素重名。如果同時使用id和class就會唯一地標識這個元素,這個時候就可以通過邏輯運算符“and”來連接兩個條件。

            find_element_by_xpath(“//input[@id=’kw’ and @class=’su’/span/input]”)

            當然,我們也可以用“and”連接更多的屬性來唯一地標識一個元素。


          主站蜘蛛池模板: 97人妻无码一区二区精品免费| 亚洲国产高清在线精品一区| 日本一区二区三区免费高清| 性色av一区二区三区夜夜嗨| 伊人激情AV一区二区三区| 午夜性色一区二区三区不卡视频 | 亚洲综合av永久无码精品一区二区| 日韩精品无码一区二区三区| 伊人精品视频一区二区三区| 色婷婷AV一区二区三区浪潮| 日韩精品无码中文字幕一区二区| 国精品无码一区二区三区在线 | 国产色情一区二区三区在线播放| 岛国无码av不卡一区二区| 日韩人妻精品一区二区三区视频| 亚洲Av永久无码精品一区二区 | 国产福利一区二区三区视频在线 | 无码午夜人妻一区二区三区不卡视频 | 中文字幕一区在线播放| 亚洲一区无码中文字幕| 国内精品视频一区二区三区八戒| 久久AAAA片一区二区| 在线播放一区二区| 中文字幕一区二区三区有限公司 | 国产精品第一区揄拍| A国产一区二区免费入口| 亚洲AV成人一区二区三区在线看| 福利片福利一区二区三区| 亚洲视频在线观看一区| 亚洲一区视频在线播放| 亚洲熟妇AV一区二区三区宅男| 成人国产一区二区三区| 国产无人区一区二区三区| 国产内射在线激情一区| 国产一区精品视频| 亚洲香蕉久久一区二区| 国产精品高清一区二区三区| 波多野结衣电影区一区二区三区 | 无码精品人妻一区二区三区中| 国产精品无码一区二区在线观一 | chinese国产一区二区|