整合營銷服務商

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

          免費咨詢熱線:

          在 ASP.NET CORE 中使用 SESSION

          ession 是保存用戶和 Web 應用的會話狀態的一種方法,ASP.NET Core 提供了一個用于管理會話狀態的中間件。在本文中我將會簡單介紹一下 ASP.NET Core 中的 Session 的使用方法。

          安裝配置 Session

          nuget 添加引用 Microsoft.AspNetCore.Session

          ession 是基于 IDistributedCache 構建的,所以必須引用一種 IDistributedCache 的實現,ASP.NET Core 提供了多種 IDistributedCache 的實現 (Redis、SQL Server、In-memory)

          In-memory

          services.AddDistributedMemoryCache();
          services.AddSession();

          SQL Server

          nuget 添加引用 Microsoft.Extensions.Caching.SqlServer

          SqlServerCache實現允許分布式緩存使用SQL Server數據庫作為其后備存儲。要創建SQL Server表,您可以使用sql-cache工具,該工具將使用您指定的名稱和模式創建一個表。

          要使用sql-cache工具,請添加SqlConfig.Tools.csproj文件的<ItemGroup>元素并運行dotnet恢復。

          <ItemGroup>
            <DotNetCliToolReference Include="Microsoft.Extensions.Caching.SqlConfig.Tools" Version="1.0.0-msbuild3-final" />
          </ItemGroup>

          通過運行以下命令來測試SqlConfig.Tools

          C:\DistCacheSample\src\DistCacheSample>dotnet sql-cache create --help

          sql-cache工具將顯示用法,選項和命令幫助,現在你可以創建表到sql server中,運行“sql-cache create”命令:

          C:\DistCacheSample\src\DistCacheSample>dotnet sql-cache create "Data Source=(localdb)\v11.0;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache
             info: Microsoft.Extensions.Caching.SqlConfig.Tools.Program[0]
                 Table and index were created successfully.

          創建的表格具有以下架構:

          注意的ConnectionString(以及可選地,SchemaNameTableName)通常應該被存儲的源控制(如UserSecrets)以外,因為它們可能包含憑證。

          像所有的緩存實現一樣,你的應用程序應該使用一個實例來獲取和設置緩存值IDistributedCache,而不是SqlServerCache。該示例SqlServerCacheProduction環境中實現(因此已配置ConfigureProductionServices)。

          // Microsoft SQL Server implementation of IDistributedCache.
          // Note that this would require setting up the session state database.
          services.AddDistributedSqlServerCache(o =>
          {
            o.ConnectionString = "Server=.;Database=ASPNET5SessionState;Trusted_Connection=True;";
            o.SchemaName = "dbo";
            o.TableName = "Sessions";
          });
          services.AddSession();

          Redis

          nuget 添加引用 Microsoft.Extensions.Caching.Redis

          Redis是一款開源的內存數據存儲,通常用作分布式緩存。您可以在本地使用它,并且可以為Azure托管的ASP.NET Core應用程序配置Azure Redis緩存。您的ASP.NET Core應用程序使用RedisDistributedCache實例配置緩存實施。

          您可以ConfigureServices通過請求一個實例IDistributedCache(參見上面的代碼)來配置Redis實現并在您的應用代碼中訪問它。

          在示例代碼中,RedisCache當為服務器配置Staging環境時使用實現。因此該ConfigureStagingServices方法配置RedisCache:

          services.AddDistributedRedisCache(options =>  {  options.Configuration = "localhost";  options.InstanceName = "SampleInstance";  });

          接著在 Startup.cs 的 Config 方法中配置使用 Session 中間件,所有中間件的配置順序非常重要,必須在 UseSession 調用后才能訪問 Session 。

          // 必須在 UseMvc 之前調用
          app.UseSession();
          
          app.UseMvc(routes =>
          {
              routes.MapRoute(
                  name: "default",
                  template: "{controller=Home}/{action=Index}/{id?}");
          });

          在 AddSession 和 UseSession 方法中可以傳入一個 SessionOptions 參數,通過該參數可以設置 Session 的 Cookie name, Cookie path 等信息。

          配置完成后,就可以使用 Session 保存數據了。

          具體實現redis實現 https://www.cnblogs.com/liuxiaoji/p/9259747.html

          使用 Session 存儲數據

          Session 安裝配置好就可以通過 HttpContext.Session 來保存和讀取數據了。由于 Session 是基于 IDistributedCache 構建的,因此 Session 只能存儲 byte[] 數據,這樣使用起來很不方便,好在有很多擴展方法可以用來直接讀取和保存 string、int 等類型數據。

          一個 Session 使用的簡單示例:

          public IActionResult Index()
          {
              HttpContext.Session.SetString("SessionStartedTime", "Session started time:" + DateTime.Now.ToString());
              return View();
          }
          
          public IActionResult About()
          {
              ViewData["CurrentTime"] = "Current time:" + DateTime.Now.ToString();
              ViewData["SessionStartedTime"] = HttpContext.Session.GetString("SessionStartedTime");
          
              return View();
          }

          或者設置一個擴展類也可以直接將實體類序列化成json存儲

          TTP是無狀態協議,這意味著每次客戶端檢索網頁時,都要單獨打開一個服務器連接,因此服務器不會記錄下先前客戶端請求的任何信息。

          有三種方法來維持客戶端與服務器的會話:


          Cookies

          網絡服務器可以指定一個唯一的session ID作為cookie來代表每個客戶端,用來識別這個客戶端接下來的請求。

          這可能不是一種有效的方式,因為很多時候瀏覽器并不一定支持cookie,所以我們不建議使用這種方法來維持會話。


          隱藏表單域

          一個網絡服務器可以發送一個隱藏的HTML表單域和一個唯一的session ID,就像下面這樣:

          <input type="hidden" name="sessionid" value="12345">

          這個條目意味著,當表單被提交時,指定的名稱和值將會自動包含在GET或POST數據中。每當瀏覽器發送一個請求,session_id的值就可以用來保存不同瀏覽器的軌跡。

          這種方式可能是一種有效的方式,但點擊<A HREF>標簽中的超鏈接時不會產生表單提交事件,因此隱藏表單域也不支持通用會話跟蹤。


          重寫URL

          您可以在每個URL后面添加一些額外的數據來區分會話,服務器能夠根據這些數據來關聯session標識符。

          舉例來說,http://w3cschool.cc/file.htm;sessionid=12345, session標識符為sessionid=12345,服務器可以用這個數據來識別客戶端。

          相比而言,重寫URL是更好的方式來,就算瀏覽器不支持cookies也能工作,但缺點是您必須為每個URL動態指定session ID,就算這是個簡單的HTML頁面。


          session對象

          除了以上幾種方法外,JSP利用servlet提供的HttpSession接口來識別一個用戶,存儲這個用戶的所有訪問信息。

          默認情況下,JSP允許會話跟蹤,一個新的HttpSession對象將會自動地為新的客戶端實例化。禁止會話跟蹤需要顯式地關掉它,通過將page指令中session屬性值設為false來實現,就像下面這樣:

          <%@ page session="false" %>

          JSP引擎將隱含的session對象暴露給開發者。由于提供了session對象,開發者就可以方便地存儲或檢索數據。

          下表列出了session對象的一些重要方法:

          S.N.方法 & 描述
          1public Object getAttribute(String name)返回session對象中與指定名稱綁定的對象,如果不存在則返回null
          2public Enumeration getAttributeNames()返回session對象中所有的對象名稱
          3public long getCreationTime()返回session對象被創建的時間, 以毫秒為單位,從1970年1月1號凌晨開始算起
          4public String getId()返回session對象的ID
          5public long getLastAccessedTime()返回客戶端最后訪問的時間,以毫秒為單位,從1970年1月1號凌晨開始算起
          6public int getMaxInactiveInterval()返回最大時間間隔,以秒為單位,servlet 容器將會在這段時間內保持會話打開
          7public void invalidate()將session無效化,解綁任何與該session綁定的對象
          8public boolean isNew()返回是否為一個新的客戶端,或者客戶端是否拒絕加入session
          9public void removeAttribute(String name)移除session中指定名稱的對象
          10public void setAttribute(String name, Object value) 使用指定的名稱和值來產生一個對象并綁定到session中
          11public void setMaxInactiveInterval(int interval)用來指定時間,以秒為單位,servlet容器將會在這段時間內保持會話有效

          JSP Session應用

          這個例子描述了如何使用HttpSession對象來獲取創建時間和最后一次訪問時間。我們將會為request對象關聯一個新的session對象,如果這個對象尚未存在的話。

          <%@ page language="java" contentType="text/html; charset=UTF-8"
           pageEncoding="UTF-8"%><%@ page import="java.io.*,java.util.*" %><%
           // 獲取session創建時間
           Date createTime = new Date(session.getCreationTime());
           // 獲取最后訪問頁面的時間
           Date lastAccessTime = new Date(session.getLastAccessedTime());
           String title = "再次訪問菜鳥教程實例";
           Integer visitCount = new Integer(0);
           String visitCountKey = new String("visitCount");
           String userIDKey = new String("userID");
           String userID = new String("ABCD");
           // 檢測網頁是否由新的訪問用戶
           if (session.isNew()){
           title = "訪問菜鳥教程實例";
           session.setAttribute(userIDKey, userID);
           session.setAttribute(visitCountKey, visitCount);
           } else { visitCount = (Integer)session.getAttribute(visitCountKey); visitCount += 1; userID = (String)session.getAttribute(userIDKey); session.setAttribute(visitCountKey, visitCount);
           }%><html><head><title>Session 跟蹤</title></head><body><h1>Session 跟蹤</h1><table border="1" align="center"> <tr bgcolor="#949494">
           <th>Session 信息</th>
           <th>值</th></tr> <tr>
           <td>id</td>
           <td><% out.print( session.getId()); %></td></tr> <tr>
           <td>創建時間</td>
           <td><% out.print(createTime); %></td></tr> <tr>
           <td>最后訪問時間</td>
           <td><% out.print(lastAccessTime); %></td></tr> <tr>
           <td>用戶 ID</td>
           <td><% out.print(userID); %></td></tr> <tr>
           <td>訪問次數</td>
           <td><% out.print(visitCount); %></td></tr> </table> </body></html>

          試著訪問 http://localhost:8080/testjsp/main.jsp ,第一次運行時將會得到如下結果:

          再次訪問,將會得到如下結果:


          刪除Session數據

          當處理完一個用戶的會話數據后,您可以有如下選擇:

          • 移除一個特定的屬性:

            調用public void removeAttribute(String name) 方法來移除指定的屬性。

          • 刪除整個會話:

            調用public void invalidate() 方法來使整個session無效。

          • 設置會話有效期:

            調用 public void setMaxInactiveInterval(int interval) 方法來設置session超時。

          • 登出用戶:

            支持servlet2.4版本的服務器,可以調用 logout()方法來登出用戶,并且使所有相關的session無效。

          • 配置web.xml文件:

            如果使用的是Tomcat,可以向下面這樣配置web.xml文件:

           <session-config>
           <session-timeout>15</session-timeout>
           </session-config>

          超時以分鐘為單位,Tomcat中的默認的超時時間是30分鐘。

          Servlet中的getMaxInactiveInterval( ) 方法以秒為單位返回超時時間。如果在web.xml中配置的是15分鐘,則getMaxInactiveInterval( ) 方法將會返回900。

          • 基本使用
            • 發送請求
            • 解析響應獲
          • 獲取需要的內容
            • 快速獲取鏈接
            • 獲取元素
          • 高級功能
            • 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,個人愚見?。?/span>
          • 更多內容

          視頻講解源碼

          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日】


          主站蜘蛛池模板: 不卡一区二区在线| 久久精品道一区二区三区| 国产成人一区二区三区精品久久| 国产成人精品无码一区二区| 中文字幕精品一区二区三区视频| 日韩精品一区二区午夜成人版| 夜色阁亚洲一区二区三区| 午夜视频久久久久一区 | 视频一区二区精品的福利| 国产福利一区视频| 国产成人精品无码一区二区| 日本一区二区视频| 一本色道久久综合一区| 亚洲视频免费一区| 一区二区乱子伦在线播放| 亚洲一区二区无码偷拍| 无码人妻精品一区二| 精品亚洲A∨无码一区二区三区| 国产一区二区三区不卡在线看| 高清国产AV一区二区三区| 午夜视频久久久久一区 | 国产亚洲一区二区精品| 亚洲第一区香蕉_国产a| 四虎永久在线精品免费一区二区| 国产91一区二区在线播放不卡 | 香蕉一区二区三区观| 老湿机一区午夜精品免费福利| 伊人久久大香线蕉AV一区二区| 一区二区三区无码视频免费福利 | 在线观看午夜亚洲一区| 日韩最新视频一区二区三| 国产亚洲综合一区二区三区| 国模无码人体一区二区| 国产产一区二区三区久久毛片国语 | 日韩一区二区久久久久久| 日本一区二区三区四区视频| 激情内射亚洲一区二区三区| 国产精品亚洲不卡一区二区三区 | 国产小仙女视频一区二区三区| 国产成人高清视频一区二区| 欧洲精品免费一区二区三区|