整合營銷服務商

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

          免費咨詢熱線:

          Python網頁解析庫:用requests-html爬取網頁

          . 開始

          Python 中可以進行網頁解析的庫有很多,常見的有 BeautifulSoup 和 lxml 等。在網上玩爬蟲的文章通常都是介紹 BeautifulSoup 這個庫,我平常也是常用這個庫,最近用 Xpath 用得比較多,使用 BeautifulSoup 就不大習慣,很久之前就知道 Reitz 大神出了一個叫 Requests-HTML 的庫,一直沒有興趣看,這回可算歹著機會用一下了。

          使用pip install requests-html安裝,上手和 Reitz 的其他庫一樣,輕松簡單:

          from requests_html import HTMLSession
          session = HTMLSession()
          
          r = session.get('https://www.python.org/jobs/')

          這個庫是在 requests 庫上實現的,r 得到的結果是 Response 對象下面的一個子類,多個一個html 的屬性。所以 requests 庫的響應對象可以進行什么操作,這個 r 也都可以。如果需要解析網頁,直接獲取響應對象的 html 屬性:

          r.html

          2. 原理

          不得不膜拜 Reitz 大神太會組裝技術了。實際上 HTMLSession 是繼承自 requests.Session 這個核心類,然后將 requests.Session 類里的 requests 方法改寫,返回自己的一個 HTMLResponse 對象,這個類又是繼承自 requests.Response,只是多加了一個_from_response 的方法來構造實例:

          class HTMLSession(requests.Session):
              # 重寫 request 方法,返回 HTMLResponse 構造
              def request(self, *args, **kwargs) -> HTMLResponse:
                  r = super(HTMLSession, self).request(*args, **kwargs)
                  return HTMLResponse._from_response(r, self)
          class HTMLResponse(requests.Response):
              # 構造器
              @classmethod
              def _from_response(cls, response, session: Union['HTMLSession', 'AsyncHTMLSession']):
                  html_r = cls(session=session)
                  html_r.__dict__.update(response.__dict__)
                  return html_r

          之后在 HTMLResponse 里定義屬性方法 html,就可以通過 html 屬性訪問了,實現也就是組裝 PyQuery 來干。核心的解析類也大多是使用 PyQuery 和 lxml 來做解析,簡化了名稱,挺討巧的。

          3. 元素定位

          元素定位可以選擇兩種方式:

          css 選擇器

          • css選擇器
          • xpath
          # css 獲取有多少個職位
          jobs = r.html.find("h1.call-to-action")
          # xpath 獲取
          jobs = r.html.xpath("//h1[@class='call-to-action']")

          方法名非常簡單,符合 Python 優雅的風格,這里不妨對這兩種方式簡單的說明:

          4. CSS 簡單規則

          • 標簽名 h1
          • id 使用#id 表示
          • class 使用.class_name 表示
          • 謂語表示:h1[prop=value]

          5. Xpath簡單規則

          • 路徑// 或者 /
          • 標簽名
          • 謂語 [@prop=value]
          • 軸定位名稱::元素名[謂語]

          定位到元素以后勢必要獲取元素里面的內容和屬性相關數據,獲取文本:

          jobs.text
          jobs.full_text

          獲取元素的屬性:

          attrs = jobs.attrs
          value = attrs.get("key")

          還可以通過模式來匹配對應的內容:

          ## 找某些內容匹配
          r.html.search("Python {}")
          r.html.search_all()

          這個功能看起來比較雞肋,可以深入研究優化一下,說不定能在 github 上混個提交。

          6. 人性化操作

          除了一些基礎操作,這個庫還提供了一些人性化的操作。比如一鍵獲取網頁的所有超鏈接,這對于整站爬蟲應該是個福音,URL 管理比較方便:

          r.html.absolute_links
          r.html.links

          內容頁面通常都是分頁的,一次抓取不了太多,這個庫可以獲取分頁信息:

          print(r.html)
          # 比較一下
          for url in r.html:
              print(url)

          結果如下:

          # print(r.html)
          <HTML url='https://www.python.org/jobs/'>
          # for
          <HTML url='https://www.python.org/jobs/'>
          <HTML url='https://www.python.org/jobs/?page=2'>
          <HTML url='https://www.python.org/jobs/?page=3'>
          <HTML url='https://www.python.org/jobs/?page=4'>
          <HTML url='https://www.python.org/jobs/?page=5'>

          通過迭代器實現了智能發現分頁,這個迭代器里面會用一個叫_next 的方法,貼一段源碼感受下:

          def get_next():
              candidates = self.find('a', containing=next_symbol)
          
              for candidate in candidates:
                  if candidate.attrs.get('href'):
                      # Support 'next' rel (e.g. reddit).
                      if 'next' in candidate.attrs.get('rel', []):
                          return candidate.attrs['href']

          通過查找 a 標簽里面是否含有指定的文本來判斷是不是有下一頁,通常我們的下一頁都會通過下一頁 或者加載更多 來引導,他就是利用這個標志來進行判斷。默認的以列表形式存在全局:['next','more','older']。我個人認為這種方式非常不靈活,幾乎沒有擴展性。感興趣的可以往 github 上提交代碼優化。

          7. 加載 js

          也許是考慮到了現在 js 的一些異步加載,這個庫支持 js 運行時,官方說明如下:

          Reloads the response in Chromium, and replaces HTML content
          with an updated version, with JavaScript executed.

          使用非常簡單,直接調用以下方法:

          r.html.render()

          第一次使用的時候會下載 Chromium,不過國內你懂的,自己想辦法去下吧,就不要等它自己下載了。render 函數可以使用 js 腳本來操作頁面,滾動操作單獨做了參數。這對于上拉加載等新式頁面是非常友好的。

          8. 總結

          Reitz 大神設計出來的東西還是一如既往的簡單好用,自己不多做,大多用別人的東西組裝,簡化 api。真是夠人性。不過有的地方還是優化空間,希望有興趣和精力的童鞋去 github 上關注一下這個項目。

          多朋友都聽說過Python的大名,而Python也擁有眾多的爬蟲框架,其中最簡單的莫過于requests-html了。它和著名的網絡請求庫requests是同一個作者,著重于XML數據提取,可以說是最簡單的爬蟲框架了。



          安裝requests-html

          安裝這個類庫非常簡單,直接通過pip就可以安裝了。

          pip install requests-html

          開始使用

          requests-html用起來也十分簡單,下面是一個簡單例子。照例說明一下,第一段引入了HTMLSession用于創建連接,獲取網頁數據。第二段創建連接,獲取了我的簡書用戶頁面。第三段用xpath語法獲取了網頁上的用戶名,最后打印出來。

          from requests_html import HTMLSession
          
          session = HTMLSession()
          response = session.get(
              'https://www.jianshu.com/u/7753478e1554')
          
          username = response.html.xpath(
              '//a[@class="name"]/text()', first=True)
          
          
          print(username)
          

          看起來是不是很簡單?沒錯,確實很簡單,接下來還有一些更加有趣的功能。

          分析網頁

          編寫爬蟲之前還要做一件事情,就是分析網頁的結構。這個工作其實也很簡單,打開你要訪問的網頁,按F12打開開發人員工具,可以看到最左邊有這么一個按鈕。點擊這個按鈕,然后點擊網頁上你想要查看的網頁元素,然后你就可以發現這個元素對應的相關源代碼已經為你定位完畢了。

          定位按鈕

          通過這個功能,我們就可以輕松的分析網頁,然后通過它的結構來編寫爬蟲了。

          提取數據

          上面的response.html即是網頁的根節點HTML節點,在節點對象上可以調用一些方法來檢索數據。最常用的方法是find方法,它通過CSS選擇器來定位數據。對于上面的例子,可以用find方法改寫第三段。

          因為所有查找方法返回的結果都是列表,所以如果你確定只需要查找一個,就將first參數設為真來只返回第一個結果。find方法返回的仍然是一個節點,如果只需要節點的內容,調用其text屬性即可。

          用戶名對應的HTML結構如圖所示。


          代碼如下。

          username = response.html.find('a.name', first=True).text

          除了find方法之外,還可以使用xpath方法用xpath語法來查找節點,正如第一個例子那樣。我個人比較喜歡xpath語法,CSS選擇器雖然更加流行一些,但是寫出來的效果有點怪,不如xpath工整。

          同樣是這個頁面,看看如何獲取我的簡書的個人簡介。網頁代碼如圖所示。


          代碼如下。

          description = response.html.xpath(
              '//div[@class="description"]/div[@class="js-intro"]/text()', first=True)

          CSS選擇器和XPATH語法都不是本篇的主要內容,如果你這方面不太熟悉,最好去看一下相關的教程。當然如果大家有什么疑問的話,也可以提出來。假如大家想看的話,我也可以專門寫一篇文章介紹一下這些語法知識。


          渲染網頁

          有些網頁利用了前后端分離技術開發的,需要瀏覽器渲染才能完整顯示。如果用爬蟲去看的話,只能顯示一部分內容。這時候就需要瀏覽器渲染頁面,才能獲取完整的頁面。用requests-html的話,這個過程非常簡單。

          首先先來看看一個需要渲染網頁的例子。下面的代碼訪問了我的簡書用戶頁面,然后嘗試獲取我的所有文章。但是如果你運行這個例子的話,就會發現只能獲取前幾項。因為簡書的頁面正是一個典型的需要瀏覽器渲染的頁面,爬蟲獲取到的網頁是不完整的。

          from requests_html import HTMLSession
          
          session = HTMLSession()
          headers = {
              'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.119 Safari/537.36'
          }
          
          url = 'https://www.jianshu.com/u/7753478e1554'
          r = session.get(url, headers=headers)
          
          for a in r.html.xpath('//ul[@class="note-list"]/li/div[@class="content"]/a[@class="title"]'):
              title = a.text
              link = f'https://www.jianshu.com{a.attrs["href"]}'
              print(f'《{title}》,{link}')
          

          那么如何渲染網頁來獲取完整的結果呢?其實非常簡單,在查詢HTML節點之前,調用render函數即可。


          render函數來使用瀏覽器渲染


          原理也非常簡單,第一次調用render的時候,requests-html會在本地下載一個chromium瀏覽器,用它來渲染網頁。如此一來,我們就可以獲取到渲染之后的頁面了。


          但是對于簡書這個例子來說還是有些問題,因為如果你在瀏覽器里打開這個網頁的話,會發現一些文章在瀏覽器下滑頁面的時候才開始渲染。不過聰慧的作者早就考慮到這種情況了,render函數支持下滑的參數,設定之后,就會模擬瀏覽器下滑操作,從而解決了這個問題。

          r.html.render(scrolldown=50, sleep=0.2)

          節點對象

          不論上面的r.html還是find/xpath函數返回的結果,它們都是節點對象。除了上面介紹的幾個提取數據的方法以外,節點對象還有以下一些屬性,在我們提取數據的時候也有很大作用。



          相較于專業的爬蟲框架scrapy,或者僅用于解析XML的解析庫BeautifulSoup。requests-html可以是說恰到好處,它沒有前者那么難學,也不像后者還需要搭配HTTP請求庫才能使用。如果你手頭需要臨時抓取幾個網頁,那么requests-html就是你最好的選擇。

          、Requests簡介

          urllib庫參考:Python 爬蟲之urllib庫

          Requests是用python語言基于urllib編寫的,該模塊主要用來發 送 HTTP 請求,requests 模塊比 urllib 模塊更簡潔,是學習 python 爬蟲的較好的http請求模塊。

          不是 python 的內置庫,如果沒有安裝,可以安裝先。

          pip install requests


          二、各種請求方式

          requests里提供個各種請求方式,每次調用 requests 請求之后,會返回一個 response 對象,該對象包含了具體的響應信息。響應信息如下:

          • 屬性或方法 :說明
          • apparent_encoding :編碼方式
          • close():關閉與服務器的連接
          • content:返回響應的內容,以字節為單位
          • cookies:返回一個 CookieJar 對象,包含了從服務器發回的 cookie
          • elapsed :返回一個 timedelta 對象,包含了從發送請求到響應到達之間經過的時間量,可以用于測試響應速度。比如 r.elapsed.microseconds 表示響應到達需要多少微秒
          • encoding:解碼 r.text 的編碼方式
          • headers :返回響應頭,字典格式
          • history :返回包含請求歷史的響應對象列表(url)
          • is_permanent_redirect :如果響應是永久重定向的 url,則返回 True,否則返回 False
          • is_redirect:如果響應被重定向,則返回 True,否則返回 False
          • iter_content():迭代響應
          • iter_lines():迭代響應的行
          • json():返回結果的 JSON 對象 (結果需要以 JSON 格式編寫的,否則會引發錯誤)
          • links:返回響應的解析頭鏈接
          • next:返回重定向鏈中下一個請求的 PreparedRequest 對象
          • ok:檢查 "status_code" 的值,如果小于400,則返回 True,如果不小于 400,則返回 False
          • raise_for_status() : 如果發生錯誤,方法返回一個 HTTPError 對象
          • reason:響應狀態的描述,比如 "Not Found" 或 "OK"
          • request:返回請求此響應的請求對象
          • status_code:返回 http 的狀態碼,比如 404 和 200(200 是 OK,404 是 Not Found)
          • text:返回響應的內容,unicode 類型數據
          • url:返回響應的 URL


          1、基本Get 請求

          import requests
          
          x = requests.get('http://www.baidu.com')
          print(x.status_code)
          print(x.reason)
          print(x.apparent_encoding)
          print(x.text)

          執行結果:

          200                                                                                                                                                                                                      
          OK                                                                              
          utf-8                                                                           
          <!DOCTYPE html>                                                                 
          <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8>.....

          請求 json 數據文件,返回 json 內容:

          通過params參數傳遞一個字典內容,從而直接構造url

          import requests
          params = {
              'name': 'zhangsan',
              'age': 100
          }
          x = requests.get('http://httpbin.org/get',params=params)
          print(x.url)
          print(x.json())

          執行結果:

          http://httpbin.org/get?name=zhangsan&age=100                                                                                                                                                             
          {'args': {'age': '100', 'name': 'zhangsan'}, 'headers': {'...

          抓取二進制數據

          在上面的例子中,我們抓取的是網站的一個頁面,實際上它返回的是一個 HTML 文檔。如果想抓取圖片、音頻、視頻等文件,需要用到content,這樣獲取的數據是二進制數據。

          如抓取百度logo圖片:

          import requests
          
          x = requests.get('https://www.baidu.com/img/flexible/logo/pc/result.png')
          
          print(x.content)

          執行結果:

          b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xca\x00\

          得到的是一串二進制的亂碼,如果想得到圖片,直接將其保存到本地即可。

          import requests
          
          x = requests.get('https://www.baidu.com/img/flexible/logo/pc/result.png')
          
          with open('baidulogo.png','wb') as f:
              f.write(x.content)

          執行后,在本地目錄,可以看到圖片已經下載

          添加headers

          有些網頁,必須需要headers才能訪問。這時只需要將參賽傳到 headers參數中即可,如下所示:

          import requests
          
          headers = {
              'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
          }
          
          x = requests.get('https://baidu.com/', headers=headers)
          
          print(x.text)

          2、基本POST請求

          在爬蟲中,另外一個比較常見的請求方式就是 POST 請求,跟GET用法差不多。

          post() 方法可以發送 POST 請求到指定 url,一般格式如下:

          requests.post(url, data={key: value}, json={key: value}, args)
          • url 請求 url。
          • data 參數為要發送到指定 url 的字典、元組列表、字節或文件對象。
          • json 參數為要發送到指定 url 的 JSON 對象。
          • args 為其他參數,比如 cookies、headers、verify等。
          import requests
          
          data = {'name': 'zhangsan', 'age': '100'}
          x = requests.post("http://httpbin.org/post", data=data)
          print(x.text)

          執行結果:

          {                                                                                                                                                                                                        
            "args": {},                                                                   
            "data": "",                                                                   
            "files": {},                                                                  
            "form": {                                                                     
              "age": "100",                                                                                                                                                                                        
              "name": "zhangsan"                                                          
            },                                                                                                                                                                                                     
            "headers": {                                                                                                                                                                                           
              "Accept": "*/*",                                                                                                                                                                                     
              "Accept-Encoding": "gzip, deflate",                                                                                                                                                                  
              "Content-Length": "21",
              ....

          3、其他請求

          其他請求可以自行測試,差不多用法。

          r = requests.put('https://httpbin.org/put', data = {'key':'value'})
          
          r = requests.delete('https://httpbin.org/delete')
          
          r = requests.head('https://httpbin.org/get')
          
          r = requests.options('https://httpbin.org/get')

          三、requests 方法

          requests 方法如下表:

          • 方法:描述
          • delete(url, args):發送 DELETE 請求到指定 url
          • get(url, params, args):發送 GET 請求到指定 url
          • head(url, args):發送 HEAD 請求到指定 url
          • patch(url, data, args):發送 PATCH 請求到指定 url
          • post(url, data, json, args):發送 POST 請求到指定 url
          • put(url, data, args):發送 PUT 請求到指定 url
          • request(method, url, args):向指定的 url 發送指定的請求方法
          import requests
          
          # 發送請求
          x = requests.request('get', 'https://httpbin.org/')
          
          # 返回網頁內容
          print(x.status_code)

          執行結果:

          200

          設置請求頭:

          # 導入 requests 包
          import requests
          
           
          kw = {'s':'python 教程'}
          
          # 設置請求頭
          headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
           
          # params 接收一個字典或者字符串的查詢參數,字典類型自動轉換為url編碼,不需要urlencode()
          response = requests.get("https://httpbin.org", params = kw, headers = headers)
          
          # 查看響應狀態碼
          print (response.status_code)
          
          # 查看響應頭部字符編碼
          print (response.encoding)
          
          # 查看完整url地址
          print (response.url)
          
          # 查看響應內容,response.text 返回的是Unicode格式的數據
          print(response.text)

          執行結果:

           200                                                                                                                                                                                                      
           utf-8                                                                           
           https://httpbin.org/?s=python+%E6%95%99%E7%A8%8B                                
           <!DOCTYPE html>                                                                 
           <html lang="en">                                                                
                                                                                           
           <head>                                                                          
               <meta charset="UTF-8">                                                      
               <title>httpbin.org</title>                                                  
               <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700"
                  rel="stylesheet"> 
                  ....


          四、requests其他高級用法

          1、文件上傳

          實現方法和其他參數類似,也是構造一個字典然后通過files參數傳遞。

          import requests
          files= {"files":open("git.jpeg","rb")}
          response = requests.post("http://httpbin.org/post",files=files)
          print(response.text)

          2、獲取cookie

          import requests
          
          response = requests.get("http://www.baidu.com")
          print(response.cookies)
          
          for key,value in response.cookies.items():
              print(key+"="+value)

          3、會話維持

          cookie的一個作用就是可以用于模擬登陸,做會話維持。

          import requests
          s = requests.Session()
          s.get("http://httpbin.org/cookies/set/number/123456")
          response = s.get("http://httpbin.org/cookies")
          print(response.text)

          4、證書驗證

          現在的很多網站都是https的方式訪問,所以這個時候就涉及到證書的問題,有時遇到:requests.exceptions.SSLError: HTTPSConnectionPool 錯誤。

          import requests
          from requests.packages import urllib3
          urllib3.disable_warnings()
          response = requests.get("https://httpbin.org",verify=False)
          print(response.status_code)

          當然,我們也可以指定一個本地證書用作客戶端證書,這可以是單個文件(包含密鑰和證書)或一個包含兩個文件路徑的元組:

          import requests
          
          response = requests.get('https://httpbin.org/', cert=('/path/server.crt', '/path/server.key'))
          print(response.status_code)

          上面的代碼是演示實例,我們需要有 crt 和 key 文件,并且指定它們的路徑。另外注意,本地私有證書的 key 必須是解密狀態,加密狀態的 key是不支持的。

          5、代理設置

          import requests
          
          from requests.auth import HTTPBasicAuth
          
          response = requests.get("http://192.168.152.100:9001/",auth=HTTPBasicAuth("user","123"))
          print(response.status_code)

          還有一種方式

          import requests
          response = requests.get("http://192.168.152.100:9001/",auth=("user","123"))
          print(response.status_code)

          6、超時設置

          在某些網絡情況不好的情況下,服務器可能很久才會響應甚至無法響應,此時就需要設置超時來避免無限的等待。在 requests 中,我們可以通過 timeout 參數來設置超時時間,這個時間是發出請求到服務器返回的整個過程所用的時間,即連接和讀取兩個過程,單位為秒。當然,也可以分別設置連接和讀取各自的超時時間:

          import requests
          
          r = requests.get('https://httpbin.org/get', timeout=1)
          print(r.status_code)
          r = requests.get('https://httpbin.org/get', timeout=(5, 30))

          當然,如果不設置 timeout 或者將其設置為 None ,則表示不會進行超時處理,即會永久等待下去。


          主站蜘蛛池模板: 色噜噜一区二区三区| 综合久久一区二区三区 | 无码国产精品一区二区免费式直播 | 亚洲视频一区网站| 91久久精品国产免费一区| 99无码人妻一区二区三区免费| 色综合久久一区二区三区| 亚洲第一区在线观看| 精品久久一区二区三区| 一区二区三区在线观看| 波多野结衣一区二区三区88 | 日本一区二区在线| 中文字幕无线码一区2020青青| 国产日韩精品一区二区在线观看播放 | 日韩人妻精品无码一区二区三区 | 果冻传媒一区二区天美传媒| 97av麻豆蜜桃一区二区| 日本在线视频一区| 亚洲视频一区在线| 一区二区三区四区精品| 久久久国产精品亚洲一区| 波多野结衣一区在线观看| 无码国产精成人午夜视频一区二区 | 亚洲av鲁丝一区二区三区| 欧美人妻一区黄a片| 国产一区二区三区免费观在线| 69久久精品无码一区二区| 黄桃AV无码免费一区二区三区| 日本激情一区二区三区| 一区二区三区日本视频| 天堂va在线高清一区| 精品一区二区久久| 精品免费AV一区二区三区| 精品福利一区二区三区免费视频| 人妻无码一区二区三区| 精品久久久中文字幕一区| 日韩在线不卡免费视频一区 | 无码少妇一区二区性色AV| 视频在线一区二区三区| 亚州AV综合色区无码一区| 久久国产一区二区三区|