整合營銷服務商

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

          免費咨詢熱線:

          一次HTTP請求完整過程之域名解析

          一次HTTP請求完整過程之域名解析

          面試過程總會被問到“HTTP協議如何工作?“,”一次完整的http請求是經歷什么過程“...... 確實此題能衡量程序員的功底,如果你回答非常完整,說明你對網絡請求過程是非常了解的,對大流量和大并發場景你就很清楚如何進行優化,本篇文章從輸入URL到瀏覽器顯示頁面發生了什么這視角大體了解一下,當你在瀏覽器地址欄輸入網址后瀏覽器是怎么把最終的頁面呈現出來的呢?這個過程從程序員理解的角度可以分為以下幾個步驟:

          我先給大家看看整體的請求過程,為能更好地讓讀者明白,作者會分期完整介紹以下過程。

          請求整體過程

          域名解析 -> 發起TCP的3次握手 -> 建立TCP連接后發起http請求 -> 服務器響應http請求->瀏覽器得到html代碼 -> 瀏覽器解析html代碼同時請求html代碼中的資源(如js、css、圖片等) -> 瀏覽器對頁面進行渲染呈現給用戶。

          獲取內容請求

          以上過程大致進行分析細節,以方便大家更加詳細地認識整體的過程,但是有些過程沒有能理解透徹并且過程比較復雜未能提煉通俗易懂語言給大家分析,不過后續會不斷分析給大家的。

          1.域名解析

          我們以www.cnblogs.com為例:請問www.cnblogs.com這個域名的IP地址是多少?

          目的是通過域名地址轉化到資源URL的IP地址,對用戶使用域名是為了方便記憶,但是為了讓計算機理解這個地址還需要把它解析為IP地址,當用戶在地址欄輸入URL中,瀏覽器會首先搜索瀏覽器自身的DNS緩存,先看自身的緩存中是否存在沒有過期對應的條目,如果找到且沒有過期則停止搜索解析到此結束,如果沒有瀏覽器會搜索操作系統的DNS緩存,在操作系統也沒有找到,那么嘗試讀hosts文件,看看里面是否配置對應域名的IP地址,如果在hosts文件中也沒有找到對應的條目,瀏覽器就會發起一次DNS的系統調用,這過程是通過UDP協議向DNS的53端口發起請求遞歸迭代請求,這過程有運營商DNS服務提供給我們,運營商的DNS服務器必須得提供給我們對應域名的IP地址,先向本地配置的首選DNS服務器發起域名解析請求(一般是由電信運營商提供或者各大互聯網廠商提供的DNS服務器)運營商的DNS服務器首先查找自身的緩存,找到對應的條目,且沒有過期,則解析成功。如果沒有找到對應的條目,則運營商的DNS代瀏覽器發起迭代DNS解析請求,它首先是會找根域的DNS的IP地址(這臺DNS服務器都內置13臺根域的DNS的IP地址),找到根域的DNS地址,就會向其發起請求,來一場尋址之旅:

          運營商DNS:請問www.cnblogs.com這個域名的IP地址是多少呢?

          根域DNS:你一個頂級域com域的一個域名,我不知道這個域名的IP地址,但是我知道com域的IP地址,你去找它去問一問呢?

          運營商DNS:請問www.cnblogs.com這個域名的IP地址是多少呢?

          COM域:我不知道www.cnblogs.com這個域名的IP地址,但是我知道cnblogs.com這個域的DNS地址,你去找它去去問一問呢?

          cnblogs.com域名的DNS:這個時候cnblogs.com域的DNS服務器一查,誒,果真在我這里,一般就是由域名注冊商提供的,像萬網,新網等。

          于是就把找到的結果發送給運營商的DNS服務器,這個時候運營商的DNS服務器就拿到了域名對應的IP地址,并返回給操作系統內核,內核又把結果返回給瀏覽器,終于瀏覽器拿到了。

          域名解析流程

          備注:

          瀏覽器:可以使用 chrome://net-internals/#dns 來進行查看

          操作系統:Mac的dns緩存查詢 nslookup www.baidu.com

          • 基本使用
            • 發送請求
            • 解析響應獲
          • 獲取需要的內容
            • 快速獲取鏈接
            • 獲取元素
          • 高級功能
            • JS渲染
            • 自動翻頁(不太好用)
          • 異步
            • 異步渲染JS
            • 異步發送請求

          初識requests_html模塊

          感覺只要學過Python爬蟲的同學應該都知道requests這個庫吧,它在我們的Python爬蟲任務中應該是最常用的一個庫了!今天跟大家分享的這個模塊requests_html,他的作者和前者是同一人!這是一個解析HTML的庫,用起來和requests一樣方便,下面就來介紹一下它!

          • 參考視頻

          使用requests_html

          安裝

          • 依然是那個命令 pip3 install -i https://pypi.doubanio.com/simple requests_html
          • 注意:由于requests_html模塊中使用了異步asynico模塊,所以官方聲明,需要在python3.6以上版本才能正常使用!

          基本使用

          發送請求

          • requests_html發送請求獲取頁面需要先實例化一個HTMLSession對象,然后使用get/post...方法獲取響應,如下列代碼
          #!/usr/bin/env python3
          # coding     : utf-8
          # Author     : xiao qiang
          # 微信公眾號   : xiaoqiangclub
          # Software   : PyCharm
          # File       : test.py
          # Time       : 2021/5/29 7:57
          from requests_html import HTMLSession
          
          if __name__ == '__main__':
              url = 'https://wwww.baidu.com'
              session = HTMLSession()  # 獲取實例化session對象
              r = session.get(url)    # 這里的請求和requests的幾乎一樣!同樣可以根據需要添加headers等參數
          
          • requests_html發送請求的方式和requests中使用session方式發送請求幾乎是一樣的,可以對比參考
          • requests_html同樣可以發送get/post等請求,且可以和requests同樣攜帶headers/data等參數,具體用法參考requests

          解析響應獲

          • 接上,我們需要將獲取的響應解析獲取html頁面,在這里我們同樣可以使用requests中的r.content.decode()等原方法!
          • 但是在requests_html中還提供了更便捷的方法:r.html.html
          • r.html.html實際上是使用了requests_html中的HTML類(負責對HTML進行解析)來進行解析!如下
          #!/usr/bin/env python3
          # coding     : utf-8
          # Author     : xiao qiang
          # 微信公眾號   : xiaoqiangclub
          # Software   : PyCharm
          # File       : test.py
          # Time       : 2021/5/29 7:57
          from requests_html import HTMLSession
          
          if __name__ == '__main__':
              url = 'https://wwww.baidu.com'
              session = HTMLSession()  # 獲取實例化session對象
              r = session.get(url)  # 這里的請求和requests的幾乎一樣!同樣可以根據需要添加headers等參數
          
              # 獲取html頁面
              # html = r.content.decode()  # requests方式
              get_html = r.html.html  # requests_html中的方法
              print(get_html[:15], '...')
          
          • 運行結果(這里只顯示了部分結果?。?/span>

          獲取需要的內容

          快速獲取鏈接

          • requests_html中提供了快速獲取網址鏈接的方法
          • 使用linksabsolute_links兩個屬性分別可以返回HTML對象所包含的所有鏈接和絕對鏈接(均不包含錨點)
          # 快速獲取鏈接
          pprint(r.html.links)  # 獲取html中的鏈接(href屬性)
          pprint(r.html.absolute_links)  # 會自動拼接url生成絕對鏈接
          
          • 部分運行結果如下

          獲取元素

          • requests_html中的HTML對象可以直接使用xpathcss選擇器

          使用xpath

          • requests_html中的HTML對象支持xpath語法,它有以下幾個參數:
          def xpath(self, selector: str, *, clean: bool = False, first: bool = False, _encoding: str = None) -> _XPath:
          - selector,要用的 xpath選擇器;
          - clean,布爾值,如果為True,會清除HTML中style和script標簽;
          - first,布爾值,如果為True,會返回第一個元素,否則會返回滿足條件的元素列表;
          - _encoding,編碼格式。
          
          • 接上面的例子!使用獲取到的響應得到HTML對象r.html
          pprint(r.html.xpath('//li[@class="hotsearch-item odd"]/a'))
          pprint(r.html.xpath('//li[@class="hotsearch-item odd"]/a', first=True).text)
          
          • 運行結果
          • xpath語法

          使用css選擇器(find方法)

          • requests_html中的HTML對象支持css選擇器,它有以下幾個參數:
          def find(self, selector: str = "*", *, containing: _Containing = None, clean: bool = False, first: bool = False, _encoding: str = None) -> _Find:
          - selector,要用的CSS選擇器;
          - clean,布爾值,如果為True,會清除HTML中style和script標簽;
          - containing,如果設置該屬性,只返回包含該屬性文本的標簽;
          - first,布爾值,如果為True,會返回第一個元素,否則會返回滿足條件的元素列表;
          - _encoding,編碼格式。
          
          • 接上面的例子!使用獲取到的響應得到HTML對象r.html
          pprint(r.html.find('a.mnav'))
          pprint(r.html.find('a.mnav', first=True).text)
          
          • 運行結果
          • css選擇器語法
          • 可以使用text屬性來獲取元素的文本內容

          pprint(r.html.find('a.mnav')[0].text)

          • 執行結果
          • 如果要獲取元素的attribute,用attrs屬性

          pprint(r.html.find('a.mnav')[0].attrs)

          • 執行結果
          • 獲取到attrs屬性后,就可以使用字典的相關方法獲取內容了!
          • 最后還可以使用html屬性獲取一個元素的html代碼,如下

          pprint(r.html.find('a.mnav')[0].html)

          • 執行結果

          正則搜索(search、search_all)

          • requests_html除了上面的方式還可以使用search/search_all來直接搜索內容,返回的是一個Result對象/Result對象列表實際上是作者將re正則進行了封裝)!
          def search(self, template: str) -> Result:
          # 只有一個參數
          template: 就是要檢索的內容,這里使用英文狀態的 {} 來獲取內容,有點類似正則里面的 ()
          
          • 使用英文狀態的 {} 來獲取內容,如下
              ret = r.html.find('a.mnav')[0].search('新{}')
              pprint(ret)
              pprint(type(ret))
              pprint(ret[0])
          
          • 執行結果
          • search()獲取到的是第一個匹配的對象,而searchh_all()則是獲取所有匹配的對象,得到的是一個列表,如下

          ret=r.html.find('a.mnav')[0].search_all('新{}')
          pprint(ret)
          pprint(type(ret))
          pprint(ret[0][0])

          • 運行結果
          • 除了對某個元素進行檢索外,還可以直接對html對象進行搜索,如下

          ret=r.html.search_all('百度{}')
          pprint(ret)
          pprint(type(ret))
          pprint(ret[0][0])

          • 運行結果
          • requests_html內容提取的方式這么多,大家可以根據需要和習慣選擇使用!

          search補充

          • 在上面提到的search()/search_all()方法中,我們設定的template參數可以有多個取值(多個{}),得到的結果是一個列表,我們可以遍歷別表進行取值 取值的時候可以通過result[索引]的方式進行獲取對應的數據,如下(示例部分代碼) search_ret=r.html.search_all('<a h{}f="{}"',)
            print(search_ret)
            for ret in search_ret:
            print(ret)
            print(ret[1])
            print('--------------')
            運行結果(部分)
          • 除此之外,我們還可以對取值進行命名,返回的結果是可以使用類似字典(不是字典)[name]的方式取值(不能使用get),如下示例(部分代碼) search_ret=r.html.search_all('<a h{test}f="{url}"',)
            print(search_ret)
            for ret in search_ret:
            print(ret)
            print(ret['name'])
            print('--------------')
          • 運行結果(部分)
          • 以上就是對requests_html模塊search()/search_all()方法的補充內容!

          HTML類

          • requests_html中使用HTML類負責對HTML進行解析
          • HTML類不僅可以解析網絡請求獲取的響應,還可以直接解析html文本,如下
          >>> from requests_html import HTML
          >>> doc = """<a href='https://www.baidu.com'>"""
          
          >>> html = HTML(html=doc)
          >>> html.links
          {'https://www.baidu.com'}
          
          • 還可以直接渲染JS,如下
          # 和上面一段代碼接起來
          >>> script = """
                  () => {
                      return {
                          width: document.documentElement.clientWidth,
                          height: document.documentElement.clientHeight,
                          deviceScaleFactor: window.devicePixelRatio,
                      }
                  }
              """
          >>> val = html.render(script=script, reload=False) # render()方法 后面會講
          
          >>> print(val)
          {'width': 800, 'height': 600, 'deviceScaleFactor': 1}
          
          >>> print(html.html)
          <html><head></head><body><a href="https://www.baidu.com"></a></body></html>
          

          高級功能

          • 前面介紹的是requests_htmlrequests庫的基礎上整合的html解析&數據篩選的功能!
          • 下面要為大家介紹的是requests_html模塊中的一些高級功能:自動渲染JS&智能分頁

          JS渲染

          • 我們在做爬蟲項目的時候會遇到網站的頁面是由js生成的情況!這個時候要么就是自己去一步一步地分析請求,要么就是使用selenium等第三方庫來進行渲染頁面,為了解決這個難題,requests_html模塊中引進了pyppeteer,使用pyppeteer可以像使用selenium一樣實現網站的完整加載!而且pyppeteer是一個異步模塊!效率會更高!
          • requests_html模塊在HTML對象的基礎上使用render()方法重新加載js頁面
          • 注意:在第一次使用render()的時候,系統在用戶目錄(默認是~/.pyppeteer/)中下載一個chromium。下載過程只在第一次執行,以后就可以直接使用chromium來執行任務了。在沒有科學上網的環境下可能下載速度有點慢,請耐心等待...
          • 下面是一個官方示例
          >>> r = session.get('http://python-requests.org/')
          
          >>> r.html.render()
          [W:pyppeteer.chromium_downloader] start chromium download.
          Download may take a few minutes.
          [W:pyppeteer.chromium_downloader] chromium download done.
          [W:pyppeteer.chromium_downloader] chromium extracted to: C:\Users\xxxx\.pyppeteer\local-chromium\571375
          >>> r.html.search('Python 2 will retire in only {months} months!')['months']
          '<time>25</time>'
          
          • requests_html模塊在HTML對象的基礎上使用render()方法重新加載js頁面,它有以下幾個參數:
          def render(self, retries: int = 8, script: str = None, wait: float = 0.2, scrolldown=False, sleep: int = 0, reload: bool = True, timeout: Union[float, int] = 8.0, keep_page: bool = False):
          - retries: 加載頁面失敗的次數
          - script: 頁面上需要執行的JS腳本(可選)
          - wait: 加載頁面前等待的時間(秒),防止超時(可選)
          - scrolldown: 頁面向下滾動的次數(整數)
          - sleep: 在頁面初次渲染之后的等待時間
          - reload: 如果為False,那么頁面不會從瀏覽器中加載,而是從內存中加載,只有設置為True才會在瀏覽器中渲染JS
          - keep_page: 如果為True,允許您使用 r.html.page 訪問瀏覽器頁面
          
          • requests_html還支持異步渲染JS[^1]

          自動翻頁(不太好用)

          • 很多網站會出現翻頁的情況,requests_html模塊的HTML對象中提供了一個next()方法來實現自動翻頁!
          • requests_html模塊在HTML對象的基礎上使用next()方法來實現自動翻頁!它有以下幾個參數:
          def next(self, fetch: bool = False, next_symbol: _NextSymbol = DEFAULT_NEXT_SYMBOL) -> _Next:
          fetch: 一個布爾型參數,默認為False:直接返回下一頁的 url地址;
                 如果設置為True:則直接返回下一頁的 HTML對象
          
          • 這個方法我自己測試了一下,只有一些特定的網站才能實現這個功能,requests_html在的源碼中可以看到,作者通過搜索包含'next', 'more', 'older'字段的a標簽(因為一般情況下我們的下一頁url就是在a標簽下的href屬性中),所以只有滿足了他的條件才會實現這個功能(也就是說HTML頁面不按照這個套路它就無法實現這個功能!),下面是部分源碼
          DEFAULT_NEXT_SYMBOL = ['next', 'more', 'older']
          # next()方法
              def next(self, fetch: bool = False, next_symbol: _NextSymbol = DEFAULT_NEXT_SYMBOL) -> _Next:
                  """Attempts to find the next page, if there is one. If ``fetch``
                  is ``True`` (default), returns :class:`HTML <HTML>` object of
                  next page. If ``fetch`` is ``False``, simply returns the next URL.
          
                  """
          
                  def get_next():
                      candidates = self.find('a', containing=next_symbol) # 尋找 包含字段'next', 'more', 'older' 的a標簽
          
          • 這里我就不做舉例了,大家可以自行去嘗試!

          異步

          • requests_html中還支持了異步功能
          • requests_html是使用了asynico來封裝了一些異步操作,所以這里的一些操作會用到asynico庫相關的一些知識,如果您還不太了解,請自行學習!

          異步渲染JS

          • 前面我們介紹了使用render()方法渲染JS,其實它還支持異步渲染
          • 一般異步渲染使用在我們有多個頁面需要進行渲染的情況下,因為只要在多任務的時候才能體現出異步的高效率,它有以下幾個參數:
          def __init__(self, loop=None, workers=None, mock_browser: bool = True, *args, **kwargs):
          loop: 使用的Asyncio循環。
          workers: 用于執行異步調用的線程數量。如果不傳遞,它將默認為電腦處理器數量乘以5
          
          • 更多的異步使用方法請參考asyncio庫的使用方法,下面是一個官方示例
          >>> async def get_pyclock():
          ...     r = await asession.get('https://pythonclock.org/')
          ...     await r.html.arender()
          ...     return r
          ...
          >>> results = asession.run(get_pyclock, get_pyclock, get_pyclock) # 這里作者將同一個頁面使用異步方式進行了3次渲染,但是實際上使用的時間并不是平時的3倍!可能只是比平時渲染一個頁面多花了一點時間而已!這就是異步的好處!
          
          • 注意:results是一個列表

          異步傳參

          • 這里是后面加的內容,因為我突然想到有時候我們的函數有可能是帶參數的,那么這個時候我們可以使用lambda來進行傳參,看下面示例
          #!/usr/bin/env python
          # -*- encoding: utf-8 -*-                            
          # @Author     : xiao qiang
          # @WeChat     : xiaoqiangclub                              
          # @Software   : PyCharm      
          # @File       : test002.py
          # @Time       : 2021/5/30 19:48
          from requests_html import AsyncHTMLSession
          
          aSession = AsyncHTMLSession()
          
          
          async def test(tt, yy):
              r = await aSession.get('https://www.baidu.com/')
              await r.html.arender()
              print('-{}-{}-'.format(tt, yy))
              return r
          
          
          ret1 = aSession.run(lambda: test('1', 'a'))
          ret2 = aSession.run(lambda: test('2', 'b'))
          ret3 = aSession.run(lambda: test('3', 'c'))
          print(ret1)
          print(ret2)
          print(ret3)
          
          • 注意:這里ret1/ret2/ret3都是列表
          • 運行結果
          • 上面的示例還可以這樣寫

          #!/usr/bin/env python
          # -*- encoding: utf-8 -*-
          # @Author : xiao qiang
          # @WeChat : xiaoqiangclub
          # @Software : PyCharm
          # @File : test002.py
          # @Time : 2021/5/30 19:48
          from requests_html import AsyncHTMLSession
          aSession=AsyncHTMLSession()
          async def test(tt, yy):
          r=await aSession.get('https://www.baidu.com/')
          await r.html.arender()
          print('-{}-{}-'.format(tt, yy))
          return r
          # ret1=aSession.run(lambda: test('1', 'a'))
          # ret2=aSession.run(lambda: test('2', 'b'))
          # ret3=aSession.run(lambda: test('3', 'c'))
          # print(ret1)
          # print(ret2)
          # print(ret3)
          #
          test_dict={
          '1': 'a',
          '2': 'b',
          '3': 'c'
          }
          tasks=[lambda i=i, y=y: test(i, y) for i, y in
          test_dict.items()]
          # lambda傳參誤區參考文章:https://www.jianshu.com/p/58ebd1618556
          ret=aSession.run(*tasks)
          # 注意前面有個 *,不可少!# 參考文章:https://www.jianshu.com/p/58ebd1618556
          print(ret)

          • 這里在使用lambda傳參的時候可能會出現一個錯誤,可以參考文章解決!
          • 運行結果

          異步發送請求

          • 我們在做爬蟲的時候,特別是大型爬蟲的時候,需要對很多頁面進行操作,或者說是需要發送很多請求,也就是需要進行很多IO操作。所以,使用異步發送請求能顯著地提升我們的爬蟲效率!
          • requests_html模塊中,設置了一個AsyncHTMLSession類來實現發送異步請求
          • ,下面是一個官方示例
          >>> from requests_html import AsyncHTMLSession
          >>> asession = AsyncHTMLSession()
          >>> async def get_pythonorg():
          ...     r = await asession.get('https://python.org/')
          ...     return r
          ...
          >>> async def get_reddit():
          ...    r = await asession.get('https://reddit.com/')
          ...    return r
          ...
          >>> async def get_google():
          ...    r = await asession.get('https://google.com/')
          ...    return r
          ...
          >>> results = asession.run(get_pythonorg, get_reddit, get_google)
          >>> results # check the requests all returned a 200 (success) code
          [<Response [200]>, <Response [200]>, <Response [200]>]
          >>> # Each item in the results list is a response object and can be interacted with as such
          >>> for result in results:
          ...     print(result.html.url)
          ...
          https://www.python.org/
          https://www.google.com/
          https://www.reddit.com/
          
          • 上面的示例用到了asynico庫中一些相關的知識,如果您還不太了解,請自行學習!

          總結

          • requests_html模塊requests庫的基礎上封裝了頁面解析數據清理的功能,并且添加了對當前比較流行的異步操作,讓我們在做爬蟲項目(一般項目)的時候無需再去使用多個第三方模塊來實現功能,幾乎是提供了一站式的服務!
          • 所以Python寫爬蟲使用requests_html就對了?。ó斎淮箜椖窟€是首選scrapy,個人愚見!)
          • 更多內容

          視頻講解源碼

          from requests_html import HTMLSession, HTML, AsyncHTMLSession
          from pprint import pprint
          
          
          class DouBanTest:
              def __init__(self):
                  self.start_url = 'https://movie.douban.com/chart'  # 豆瓣電影排行榜url
                  self.js_url = 'https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0'
                  self.session = HTMLSession()  # 實例化session
                  self.aSession = AsyncHTMLSession()  # 實例化異步session
                  self.headers = {
                      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'
                  }
          
              def get_response(self, url):
                  """獲取響應,并返回requests_html中的HTML對象"""
                  r = self.session.get(url, headers=self.headers)
                  # print(r)
          
                  return r.html
          
              # 快速獲取頁面中的url
              def fast_get_urls(self):
                  """快速獲取頁面中的url"""
                  html = self.get_response(self.start_url)
          
                  # HTML的 links屬性 可以快速獲取到頁面中 a標簽中的href屬性
                  urls = html.links
                  # pprint(urls)
          
                  # HTML的 absolute_links屬性 可以快速獲取到頁面中 a標簽中的href屬性,并返回絕對url地址
          
                  absolute_urls = html.absolute_links
                  pprint(absolute_urls)
          
              # 清洗數據(提取數據)
              def get_data_by_xpath(self):
                  """使用xpath獲取數據"""
                  html = self.get_response(self.start_url)
                  a_list = html.xpath('//table//div/a')
                  # pprint(a_list)
          
                  # 提取它的標題和url
                  movies_info = dict()
                  for a in a_list:
                      title = a.text  # 獲取標題(文本)
                      # print(title)
                      movie_url = a.attrs.get('href')  # 使用 attrs 來解析element元素,并獲得一個字典
                      # print(movie_url)
                      # print('-----')
                      movies_info[title] = movie_url
          
                  pprint(movies_info)
          
              # 清洗數據(提取數據)
              def get_data_by_css(self):
                  """使用css獲取數據"""
                  html = self.get_response(self.start_url)
                  a_list = html.find('tr[class="item"] div a')  # 參考 css選擇器 語法
                  # pprint(a_list)
          
                  # 提取它的標題和url
                  movies_info = dict()
                  for a in a_list:
                      title = a.text  # 獲取標題(文本)
                      # print(title)
                      movie_url = a.attrs.get('href')  # 使用 attrs 來解析element元素,并獲得一個字典
                      # print(movie_url)
                      # print('-----')
                      movies_info[title] = movie_url
          
                  pprint(movies_info)
          
              # 清洗數據(提取數據)
              def get_data_by_re(self):
                  """使用css獲取數據"""
                  html = self.get_response(self.start_url)
          
                  # search() 獲取第一條匹配的數據
                  # first_url = html.search('a href="{}"')  # 參數可以參考正則,獲取第一條匹配的數據
                  # pprint(first_url)
          
                  # search_all() 獲取所有滿足條件的數據列表
                  # url_list = html.search_all('a h{}f="{}"')
                  url_list = html.search_all('a h{title}f="{url}"')  # 對取值方式進行命名,返回一個列表
          
                  # pprint(url_list)
                  #
                  # 提取數據
                  for url in url_list:
                      print(url)
                      print(url['title'])  # 使用 result[name] 進行取值
                      print(url['url'])
                      # print(url[0])
                      # print(url[1])
                      print('----------')
          
              # HTML類
              def use_HTML(self):
                  """使用HTML模塊處理文檔"""
                  html_str = '<a class="nbg" href="https://movie.douban.com/subject/3099221/" title="活死人軍團">'
                  html = HTML(html=html_str)
          
                  # links
                  print(html.links)
          
                  # search()
                  print(html.search('href="{}"'))
          
              # 加載JS頁面
              def load_js(self):
                  html = self.get_response(self.js_url)
          
                  # 使用一個 render()方法 來加載js(實際上使用這個pyppeteer)
                  # html.render(wait=3)  # js加載
                  print(html.html)
          
              async def send_requests_ues_async(self, url):
                  """發送異步請求"""
          
                  """獲取響應,并返回requests_html中的HTML對象"""
                  r = await self.aSession.get(url, headers=self.headers)
                  # print(r)
          
                  return r.html
          
              def get_response_by_async(self):
                  url_list = [
                      'https://www.baidu.com',
                      'https://www.qq.com',
                      'https://www.163.com',
                  ]
                  tasks = [lambda url=url: self.send_requests_ues_async(url) for url in url_list]
                  ret = self.aSession.run(*tasks)  # 返回的是一個HTML對象列表
                  # print(ret)
                  # print(ret[0].html)
                  for html in ret:
                      print(html)
          
              async def load_js_use_async(self, url):
                  """異步加載js"""
                  html = await self.send_requests_ues_async(url)
          
                  # 異步加載js
                  await html.arender()
          
                  return html
          
              def get_js_by_async(self):
                  # ret = self.aSession.run(self.load_js_use_async)
                  #
                  # print(ret[0].html)
          
                  url_list = [
                      'https://www.baidu.com',
                      'https://www.qq.com',
                      'https://www.163.com',
                  ]
                  tasks = [lambda url=url: self.load_js_use_async(url) for url in url_list]
                  ret = self.aSession.run(*tasks)  # 返回的是一個HTML對象列表
                  # print(ret)
                  # print(ret[0].html)
                  for html in ret:
                      print(html)
          
          
          if __name__ == '__main__':
              test = DouBanTest()
              # test.get_data_by_xpath()
              # test.get_data_by_css()
              # test.fast_get_urls()
              # test.get_data_by_re()
              # test.use_HTML()
              # test.load_js()
              # test.get_response_by_async()
              test.get_js_by_async()
          
          

          【本文由 "XiaoqiangClub" 發布,2021年6月17日】

          于安全和隱私的原因,web 應用程序不能直接訪問用戶設備上的文件。如果需要讀取一個或多個本地文件,可以通過使用input file和FileReader來實現。在這篇文章中,我們將通過一些例子來看看它是如何工作的。

          文件操作的流程

          獲取文件

          由于瀏覽器中的 JS 無法從用戶的設備訪問本地文件,我們需要為用戶提供一種方法來選擇一個或多個文件供我們使用。這可以通過文件選擇器<input type='fule' />來完成。

          <input type="file" id="fileInput">

          如果想允選擇多個文件,可以添加multiple屬性:

          <input type="file" id="fileInput" multiple>

          我們可以通過change事件來監聽文件的選擇,也可以添加另一個 UI 元素讓用戶顯式地開始對所選文件的處理。

          input file 具有一個files屬性,該屬性是File對象的列表(可能有多個選擇的文件)。

          File對象如下所示:

          讀取文件

          讀取文件,主要使用的是[FileReader][1]類。

          「該對象擁有的屬性:」

          「FileReader.error」 :只讀,一個DOMException,表示在讀取文件時發生的錯誤 。

          「FileReader.readyState」:只讀 表示 FileReader 狀態的數字。取值如下:

          常量名值描述EMPTY0還沒有加載任何數據LOADING1數據正在被加載DONE2已完成全部的讀取請求

          「FileReader.result」:只讀,文件的內容。該屬性僅在讀取操作完成后才有效,數據的格式取決于使用哪個方法來啟動讀取操作。

          「該對象擁有的方法:」

          readAsText(file, encoding):以純文本形式讀取文件,讀取到的文本保存在result屬性中。第二個參數代表編碼格式。

          readAsDataUrl(file):讀取文件并且將文件以數據URI的形式保存在result屬性中。

          readAsBinaryString(file):讀取文件并且把文件以字符串保存在result屬性中。

          readAsArrayBuffer(file):讀取文件并且將一個包含文件內容的ArrayBuffer保存咋result屬性中。

          FileReader.abort():中止讀取操作。在返回時,readyState屬性為DONE。

          「文件讀取的過程是異步操作,在這個過程中提供了三個事件:progress、error、load事件?!?/strong>

          progress:每隔50ms左右,會觸發一次progress事件。

          error:在無法讀取到文件信息的條件下觸發。

          load:在成功加載后就會觸發。

          在下面的示例中,我們將使用readAsText和readAsDataURL方法來顯示文本和圖像文件的內容。

          例一:讀取文本文件

          為了將文件內容顯示為文本,change需要重寫一下:

          首先,我們要確保有一個可以讀取的文件。如果用戶取消或以其他方式關閉文件選擇對話框而不選擇文件,我們就沒有什么要讀取和退出函數。

          然后我們繼續創建一個FileReader。reader的工作是異步的,以避免阻塞主線程和 UI 更新,這在讀取大文件(如視頻)時非常重要。

          reader發出一個'load'事件(例如,類似于Image對象),告訴我們的文件已經讀取完畢。

          reader將文件內容保存在其result屬性中。此屬性中的數據取決于我們使用的讀取文件的方法。在我們的示例中,我們使用readAsText方法讀取文件,因此result將是一個文本字符串。

          例二:顯示本地選擇的圖片

          如果我們想要顯示圖像,將文件讀取為字符串并不是很有用。FileReader有一個readAsDataURL方法,可以將文件讀入一個編碼的字符串,該字符串可以用作<img>元素的源。本例的代碼與前面的代碼基本相同,區別是我們使用readAsDataURL讀取文件并將結果顯示為圖像:

          總結

          1)由于安全和隱私的原因,JavaScript 不能直接訪問本地文件。

          2)可以通過 input 類型為 file 來選擇文件,并對文件進行處理。

          3) file input 具有帶有所選文件的files屬性。

          4) 我們可以使用FileReader來訪問所選文件的內容。


          作者: Martin Splitt 譯者:前端小智 來源:dev

          原文:https://dev.to/g33konaut/reading-local-files-with-javascript-25hn


          主站蜘蛛池模板: 亚洲一区二区三区深夜天堂| 色欲AV蜜桃一区二区三| 精品亚洲一区二区| 日本韩国黄色一区二区三区| 久久蜜桃精品一区二区三区| 久久精品视频一区| 久久久久人妻精品一区蜜桃| 国产免费一区二区三区在线观看| 久久一区二区三区99| 久久国产三级无码一区二区| 久久久久久一区国产精品| 国产精品小黄鸭一区二区三区| 亚洲AV成人精品日韩一区18p| 一区二区三区观看| 国产亚洲自拍一区| 国产一区视频在线| 亚洲AV成人一区二区三区AV| 精品人妻中文av一区二区三区| 亚洲综合一区二区| 国产精品久久无码一区二区三区网 | 亚洲AV无码一区二区一二区| 国产精品久久久久久麻豆一区 | 人妻激情偷乱视频一区二区三区| 色久综合网精品一区二区| 久久国产精品无码一区二区三区 | 久久久91精品国产一区二区| 人妻体内射精一区二区三区 | 国模无码一区二区三区| 国产无吗一区二区三区在线欢| 福利一区二区在线| 一区二区三区影院| 风间由美性色一区二区三区| 中文字幕亚洲综合精品一区| 冲田杏梨AV一区二区三区| 国产在线一区二区三区av| 亚洲一区二区三区在线视频| 无码人妻视频一区二区三区| 色一情一乱一伦一区二区三欧美| 亚洲av无码天堂一区二区三区 | 日韩免费视频一区二区| 国产91大片精品一区在线观看 |