Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 高清国产一区二区三区,国内精品久久久久久久试看,国产亚洲精品线观看77

          整合營銷服務商

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

          免費咨詢熱線:

          JS 解密和混淆破解

          JS 解密和混淆破解

          們已經學到很多反爬機制以及相應的反反爬策略。使用那些手段,其實已經完全可以完成絕大多數的爬蟲任務。但是,還是有極個別的情況下,會出現諸如 JS 加密和 JS 混淆之類的高深反爬機制。

          如果不幸遇到這種反爬機制,一個明智之舉是給站長點個贊,然后恭恭敬敬選擇放棄,去別的地方找數據。

          當然,還是那句話,我們可以選擇不爬,但是對付 JS 加密和 JS 混淆的方法卻不可以不會。

          這里就以中國空氣質量在線檢測平臺為例,介紹 JS 加密和 JS 混淆的實現和破解方法。

          要爬取的網站:https://www.aqistudy.cn/html/city_detail.html

          這個網站正在升級,所以頁面無法正常顯示。這也意味著這個網站本身的 JS 解密是有問題的(如果沒問題就能顯示了),所以最后我們并不能完全解析出數據來。雖然如此,這個網站仍然是學習 JS 加密和 JS 混淆的相當不錯的平臺。

          閑話少說,開始干活!

          首先瀏覽器打開網頁,并打開調試臺的抓包工具。修改查詢條件(城市的名稱 + 時間范圍),然后點擊查詢按鈕,捕獲點擊按鈕后發起請求對應的數據包。點擊查詢按鈕后,并沒有刷新頁面,顯然發起的是 ajax 請求。該請求就會將指定查詢條件對應的數據加載到當前頁面中(我們要爬取的數據就是該 ajax 請求請求到的數據)。

          分析捕獲到的數據包

          • 提取出請求的 url:https://www.aqistudy.cn/apinew/aqistudyapi.php
          • 請求方式:post
          • 請求參數:d: 動態變化一組數據(且加密)
          • 響應數據:是加密的密文數據

          該數據包請求到的是密文數據,為何在前臺頁面顯示的卻是原文數據呢?

          原來,在請求請求到密文數據后,前臺接受到密文數據后使用指定的解密操作(JS 函數)對密文數據進行了解密操作,然后將原文數據顯示在了前臺頁面。

          接下來的工作流程:

          首先先處理動態變化的請求參數,動態獲取該參數的話,就可以攜帶該參數進行請求發送,將請求到的密文數據捕獲到。

        1. 將捕獲到的密文數據找到對應的解密函數對其進行解密即可。
        2. 【重點】需要找到點擊查詢按鈕后對應的 ajax 請求代碼,從這組代碼中就可以破解動態變化的請求參數和加密的響應數據對應的相關操作。
        3. 找 ajax 請求對應的代碼,分析代碼獲取參數 d 的生成方式和加密的響應數據的解密操作。直接在頁面中,并沒有辦法直接找到發送 ajax 請求的函數的,因為它以及被封裝到別的文件中了。我們可以基于火狐瀏覽器定位查詢按鈕綁定的點擊事件。
        4. 抽絲剝繭,首先從 getData 函數實現中找尋 ajax 請求對應的代碼。在該函數的實現中沒有找到 ajax 代碼,但是發現了另外兩個函數的調用,getAQIData()getWeatherData()。ajax 代碼一定是存在于這兩個函數實現內部。

          另外,這里記住一個參數,type==’HOUR‘,它的含義是查詢時間是以小時為單位。這個參數我們后來會用到。

          接下來我們就去分析 getAQIData()getWeatherData(),爭取能夠找到 ajax 代碼。

          我們找到這兩個函數的定義位置,還是沒有找到 ajax 請求代碼。不過我們卻發現它們同時調用了另外一個函數,getServerData(method,param,func,0.5)。它的參數的值可以為:

          • method 可以是 ‘GETCITYWEATHER’ 或者 ‘GETDETAIL’
          • params 的值是 {city, type, startTime, endTime},也就是查詢條件
          • func 是一個匿名函數,看樣子是在處理數據。

          下一步當然就要找 getServerData 函數了,看看那個函數里面有沒有我們一致想要的發送 ajax 請求的代碼。

          我們嘗試著在頁面中搜索,卻找不到這個函數。很顯然,它是被封裝到其他 js 文件中了。這時,我們可以基于抓包工具做全局搜索。

          好消息是,我們順利找到了 getServerData 函數!壞消息是,這貨長得一點也不像是函數。

          這是因為,這段 JS 函數代碼被加密的。這種加密的方式,我們稱為 JS 混淆。

          JS 混淆,也就是對核心的 JS 代碼進行加密。

          JS 反混淆,則是對 JS 加密代碼進行解密。

          接下來我們要做的,就是 JS 反混淆,讓這段我們看不懂的東西,顯現出廬山真面目。

          我們用的方法十分簡單粗暴,也就是暴力破解。使用這個網站就可以實現對 JS 混淆的暴力破解:https://www.bm8.com.cn/jsConfusion/

          將 getServerData 函數所在的那一整行代碼都復制過來,粘貼到這個網址的文本輸入框中,然后點擊 開始格式化 即可:

          終于,我們看到了 getServerData 的代碼,并且在其中發現了發送 ajax 的請求:

          function getServerData(method, object, callback, period) {
              const key=hex_md5(method + JSON.stringify(object));
              const data=getDataFromLocalStorage(key, period);
              if (!data) {
                  var param=getParam(method, object);
                  $.ajax({
                      url: '../apinew/aqistudyapi.php',
                      data: {
                          d: param
                      },
                      type: "post",
                      success: function (data) {
                          data=decodeData(data);
                          obj=JSON.parse(data);
                          if (obj.success) {
                              if (period > 0) {
                                  obj.result.time=new Date().getTime();
                                  localStorageUtil.save(key, obj.result)
                              }
                              callback(obj.result)
                          } else {
                              console.log(obj.errcode, obj.errmsg)
                          }
                      }
                  })
              } else {
                  callback(data)
              }
          }

          從這段代碼中,我們不難得出下面這幾個信息:

          • ajax 請求成功后獲得到的 data 是加密的響應數據(就是我們最開始通過抓包工具看到的那一串神秘的相應字符串),通過 decodeData(data) 函數,可以將加密的數據解密成我們需要的明文數據。
          • 發送請求時攜帶的參數,也就是 d 對應的值 param 是通過 getParam(method, object) 函數返回動的態變化的請求參數。這兩個參數我們前面也分析過:參數 method 可以是 ‘GETCITYWEATHER’ 或者 ‘GETDETAIL’參數 object 則為 {city, type, startTime, endTime},是我們的查詢條件我們當然還可以繼續最終下去,刨根問題找到它們究竟是通過什么方式進行加密和解密的。然后,使用 Python 代碼,重復這個加密和解密的過程,完成請求數據的生成和響應數據的解析過程。

          但是我們并不打算這么做。因為再繼續深挖下去,難度將會陡然增加。此時我們已經很疲憊了,如果繼續下去恐怕要瘋掉。而且,JavaScript 和 Python 畢竟是兩種語言,它們之間的方法和各種包都不相同。JavaScript 能實現的,Python 未必能夠輕松完成。所以重新寫一個加密和解密的腳本,并不是明智之舉。

          更好的解決方案是,我們提供請求的明文數據,通過網站自己的 JS 代碼進行加密,得到加密的請求參數。使用這個參數,我們發送請求給服務端。拿到加密的響應數據后,再通過網站的 JS 代碼進行解密。

          也就是說,我們接下來需要做的就是要調用兩個 JS 函數 decodeData 和 getParam,并拿到返回結果即可。

          現在的問題是,在 Python 程序中如何調用 JS 函數呢?

          這就涉及到一個新的概念:JS 逆向。JS 逆向,也就是在 Python 中調用 JS 函數代碼。

          能夠實現 JS 逆向的方式有兩種:

          1. 手動將 JS 函數改寫稱為 Python 函數并執行。
          2. 這種方法我剛剛談過了,并不現實。因為 JS 能實現的,Python 未必能夠輕易實現。而且畢竟還要重寫函數,比較麻煩。
          3. 使用固定模塊,實現自動逆向(推薦)。
          4. 一個很好用的實現 JS 逆向的 Python 庫 是 PyExecJS。
          5. PyExecJS 庫用來實現模擬 JavaScript 代碼執行獲取動態加密的請求參數,然后再將加密的響應數據帶入 decodeData 進行解密即可。
          6. PyExecJS 需要在本機安裝好 nodejs 的環境。
          7. PyExecJS 的安裝:
          pip install PyExecJS

          接下來,我們就可以生成加密的請求數據了。

          首先,把我們解析出來的那串代碼保存到本地,比如名為 code.js 的文件中。在里面我們補充一個函數,比如名字叫 getPostParamCode,用來發起我們的數據請求。之所以這樣做是因為使用 PyExecJS 調用 JS 函數時,傳入的參數只能是字符串。而 getParam 方法的參數需要用到 JS 的自定義對象。

          我們只需在 code.js 中加上下面的代碼即可:

          function getPostParamCode(method, type, city, start_time, end_time) {
              var param={};
              param.type=type;
              param.city=city;
              param.start_time=start_time;
              param.end_time=end_time;
              return getParam(method, param)
          }

          然后,使用 PyExecJS 調用里面的 getParam 方法,將我們的請求數據加密:

          # 模擬執行decodeData的js函數對加密響應數據進行解密
          import execjs
          import requests
          
          node=execjs.get()
          
          # 請求參數
          method='GETCITYWEATHER'
          type='HOUR'
          city='北京'
          start_time='2020-03-20 00:00:00'
          end_time='2020-03-25 00:00:00'
          
          # 編譯js代碼
          file='code.js'    # js代碼的路徑
          ctx=node.compile(open(file, encoding='utf-8').read())
          
          # 將請求數據加密
          encode_js=f'getPostParamCode("{method}", "{type}", "{city}", "{start_time}", "{end_time}")'
          params=ctx.eval(encode_js)
          
          # 使用加密的參數,發起post請求
          url='https://www.aqistudy.cn/apinew/aqistudyapi.php'
          response_text=requests.post(url, data={'d': params}).text
          
          # 將響應數據解密
          decode_js=f'decodeData("{response_text}")'
          decrypted_data=ctx.eval(decode_js)    # 如果順利,返回的將是解密后的原文數據
          print(decrypted_data)    # 執行會報錯:目前頁面中沒有數據。解密函數只是針對頁面中原始的數據進行解密。

          自此,我們完成了 JS 加密和 JS 混淆的處理。這里我們總結一下這幾個概念:

          • JS 加密,也就是通過 JS 代碼,將數據進行加密處理,將明文數據變成密文數據。如果不能將其解密,密文數據將毫無用處。
          • JS 解密:通過 JS 代碼,將加密的數據解密,也就是將密文數據解析成明文數據。JS 解密是 JS 加密的逆過程。
          • JS 混淆:將 JS 代碼(比如 JS 函數)本身進行加密。
          • JS 反混淆:將加密了的 JS 代碼解密成常規的 JS 代碼。通常直接使用暴力破解即可。
          • JS 逆向(重要):通過 Python 代碼調用 JS 的函數。

          附,ajax 請求的各個數據的含義:

          頭條號每天堅持更新原創干貨技術文章,歡迎關注本頭條號"Linux學習教程",公眾號名稱“Linux入門學習教程"。

          如需學習視頻,請復制以下信息到手機瀏覽器或電腦瀏覽器上:

          zcwyou.com

          1. 前言

          隨著互聯網活動的增加,網絡攻擊變得更加復雜和具有挑戰性。

          毫無疑問,互聯網用戶的數據安全意識已經成倍增長。如果你是站長或博主,保護用戶的敏感數據和隱私不受網絡罪犯的惡意侵害就成了你的首要責任。

          在這里,SSL證書在加強您的網站安全性方面起著最有效和最關鍵的作用。因此,我們很有必要了解SSL的基本知識。

          https vs http


          2. SSL是什么?

          安全套接字層(SSL)是一種安全協議,為互聯網上傳輸的數據提供加密,并對網絡服務器進行身份驗證。當您提交任何敏感資料時,SSL會對您的數據進行加密,以確保數據得到充分保護和安全,并且只有預期的收件人才能理解。SSL證書是由證書頒發機構頒發給網站的數字證書,它保證了用戶的web瀏覽器和web服務器之間的所有信息都是加密的。因此,SSL證書保護您的數據免受惡意竊取或破壞,如竊聽、中間人攻擊等。

          一旦安裝SSL證書,網站協議將從HTTP轉移到安全的HTTPS。此外,您可以從瀏覽器中看到一個信任的標識,一個掛鎖,被添加到您的網站的URL中。如上圖所示。這可以確保訪問者使用安全連接進行通信。SSL證書增加了網站的用戶體驗,并幫助提高其在谷歌搜索引擎結果頁面上的排名。它還可以驗證網站的真實性。但是你怎么檢查這些信息呢?

          ssl原理


          3. 如何訪問SSL證書包含的信息?

          在瀏覽器上單擊地址欄中網址前面的掛鎖,可以查看SSL證書信息。它還包含關于網站身份的信息。以下是SSL證書的主要內容:

          • 為其頒發證書的“域名”
          • 頒發證書的組織、人員或機構
          • 頒發證書的證書頒發機構的名稱
          • 簽發CA的數字簽名
          • 與之關聯的子域
          • 證書簽發日期
          • 證書的有效期

          web瀏覽器與web服務器通信,并使用該數據文件驗證網站的身份和SSL證書的狀態。

          4. SSL證書如何保護用戶數據和隱私?

          SSL證書安裝在您的web服務器上后,它會以公鑰和私鑰的形式提供一個可識別的數字識別號碼,用于服務器的認證。這些只不過是一長串任意生成的數字。它還允許服務器對用戶和服務器之間交換的敏感信息進行加密和解密。

          • 當一個訪問者訪問您的網站時,用戶web瀏覽器將嘗試驗證您網站的SSL證書的有效性,以及通過一個稱為握手的過程驗證您的服務器。
          • 然后,web服務器發送其SSL證書的副本以及服務器的公鑰。
          • 瀏覽器根據受信任的ca列表、其到期日期和其真實性檢查SSL證書。
          • 一旦瀏覽器確信SSL證書是有效的,并且您的服務器已經過身份驗證,它將此指示給web服務器。
          • 此后,一個數字簽名的確認被發送回瀏覽器,以建立一個安全的加密路徑,用于在web服務器和用戶之間的信息傳輸。
          • 如果瀏覽器發現您的SSL證書無效,則會向用戶顯示一個錯誤信息“您的連接不是可信的”,這將導致您的訪問者立即離開您的網站。

          根據您的安全需求和預算,您可以從Internet上許多著名的證書頒發機構(CAs)提供的各種選擇中購買SSL證書。因此,即使你預算有限,也要確保你的網站安全,使用有效的、便宜的SSL證書來贏得用戶的信任,保護你的品牌聲譽。因此,我們要了解不同類型的SSL證書。

          SSL證書如何保護用戶數據和隱私


          5. SSL證書的類型

          各種驗證級別

          SSL證書的類型



          證書頒發機構(CA)根據不同的驗證級別需求頒發各種SSL證書。因此,SSL證書可以根據這些不同的驗證級別過程進行分類,如下所示:

          5.1 域驗證(DV) SSL證書

          DV SSL證書申請驗證過程非常簡單,幾乎不需要復雜的申請流程。因此,在SSL市場上可用的所有其他類型中,它需要的時間最短。DV SSL證書廣泛用于非金融交易或用戶敏感數據的小型網站。它們是最便宜的選擇,也被廣泛用于保護博客。它只能提供基礎的認證。

          5.2 組織驗證(OV) SSL證書

          CA需要進行域驗證,并驗證所有者公司的真實性后,才能簽發OV SSL證書。OV SSL證書顯示公司的身份信息,可通過訪問證書上的信息查看。該公司的詳細信息顯示在“主題標簽”中,如下面的例子所示:

          ssl certification


          中型企業比較普遍使用OV SSL證書,其發放時間比DV SSL證書長。由于證書所有者公司的驗證過程需要更多的時間,所以比DV SSL證書的費用更高。

          5.3 擴展驗證(EV) SSL證書

          EV SSL證書與最高程度的信任和聲譽相關聯。EV SSL證書必須經過以下嚴格的認證后才能簽發給組織。非常嚴格和徹底的背景驗證是簽發驗證程序的一部分,如其地址、目前的運營狀況、法律狀況等。因此,與DV和OV SSL證書相比,此SSL證書的簽發時間最長,成本也更高。該公司的名稱顯示在地址欄,因此可以驗證。可信任的標識,網站Logo在網站上的展示增加了用戶的信任。EV SSL證書廣泛應用于銀行、大型金融機構、電子商務網站等。

          6. 不同數量的域/子域

          使用不同類型SSL證書確保網站安全的另一個決定因素是需要保護的域和子域的數量,它們如下:

          6.1 單域證書

          單域SSL證書為您的網站保護一個域或子域。

          為了保護多個域和子域,購買具有成本效益的通配符SSL證書或多域證書提供了更好的投資回報和更好的時間管理,而不是為每個域和子域使用單一域證書。

          6.2 通配符SSL證書

          通配符SSL證書僅使用一個證書就可以保護主域和其所有子域。通配符證書使用格式為*.example.com的通用名稱,因此它將使用單個證書來保護多個子域。如果考慮到預算問題,建議購買并安裝通配符SSL證書,該證書將提供與較昂貴證書相同的加密級別。

          6.3 多域/SAN SSL證書

          多域或SAN(主題可選名稱)SSL證書僅使用一個證書就可以保護多個域/子域。您可以添加和修改Subject Alternative Name字段,并輕松保護不同域和子域之間的多個名稱。例如,只需一個多域(SAN) SSL證書。

          6.4 統一通信證書(UCC)

          統一通信證書(UCC)只不過是多域SSL證書,早期設計用于保護Microsoft Exchange和Live Communications服務器。現在,它被用來保護任何多個域名,只需一個證書。UCC證書具有組織驗證功能,還可以用作EV SSL證書,為用戶提供最高程度的信任和安全性。

          7. 結論

          總之,我們可以說,要贏得用戶的信任,并將你的在線業務或博客帶到新的成功高度,關注用戶數據的安全是至關重要的。要做到這一點,沒有比使用廉價的SSL來保護你的網站或博客更好的方法了,它可以幫助你瞄準你的安全需求和預算。


          如果喜歡本文,歡迎轉發。本文已同步至博客站,尊重原創,轉載時請在正文中附帶以下鏈接:https://www.linuxrumen.com/rmxx/1994.html

          在這里我就不再一一介紹每個步驟的具體操作了,因為在爬取老版數據的時候都已經講得非常清楚了,所以在這里我只會在重點上講述這個是這么實現的,如果想要看具體步驟請先去看我的文章內容,里面有非常詳細的介紹以及是怎么找到加密js代碼和api接口。

          私信小編01即可獲取大量Python學習資料


          58同城網站分析

          58同城的數據爬取非常簡單,唯一有點難的就是字體的加密,除此之外其他的數據用xpath即可獲取。

          想爬取不同地方的直接訪問鏈接即可:


          數據在鏈接中,直接請求獲取即可。


          字體加密破解

          既然是字體加密那么就先把字體尋找出來,尋找簡單,在開發者工具中的分類找到Font,然后搜索這個鏈接進行查找。


          已經找到這個字體了,他是在請求頁面的時候返回的,然后他還是個base64的,只需要轉換一下再保存就可以了。

          請求鏈接獲取字體

          import requests
          from lxml import etree
          
          def get_data():
              url="https://bj.58.com/chuzu/?PGTID=0d200001-0000-11e9-58e6-a658f219b27c&ClickID=1"
              headers={
                  'authority': 'bj.58.com',
                  'method': 'GET',
                  'path': '/chuzu/?PGTID=0d200001-0000-11e9-58e6-a658f219b27c&ClickID=1',
                  'scheme': 'https',
                  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
                  'accept-encoding': 'gzip, deflate, br',
                  'accept-language': 'zh-CN,zh;q=0.9',
                  'cache-control': 'max-age=0',
                  'cookie': 'f=n; commontopbar_new_city_info=1%7C%E5%8C%97%E4%BA%AC%7Cbj; commontopbar_ipcity=bj%7C%E5%8C%97%E4%BA%AC%7C0; userid360_xml=C0E9739B2022549506AFBC01231A1DAA; time_create=1606640420140; xxzl_cid=f4a781439d9247f393d0a1629bec00df; xzuid=e0e5ea78-ac5a-491b-819d-a869ab37a7a7; xxzl_deviceid=2G3xFS3qwOviMHxtC%2FVEituhpmiI%2FJ%2BAmJ08cPBulZSe7LcSgT98WgFcyNDbzMXJ; id58=c5/nfF+bz1xVS0tAA7tjAg==; 58tj_uuid=116f1ed0-7c25-477e-8887-be3602fa2389; new_uv=1; utm_source=; spm=; init_refer=https%253A%252F%252Fbj.58.com%252Fchuzu%252Fsub%252Fpn70%252F%253Fpagetype%253Dditie%2526PGTID%253D0d3090a7-0000-1b87-3e2e-c6efe8d19973%2526ClickID%253D2; wmda_uuid=13712f08f0e555f110b1b2684ce9d709; wmda_new_uuid=1; wmda_session_id_11187958619315=1604046685879-d3ad7e5f-77f6-29d7; wmda_visited_projects=%3B11187958619315; als=0; f=n; new_session=0',
                  'sec-fetch-dest': 'document',
                  'sec-fetch-mode': 'navigate',
                  'sec-fetch-site': 'none',
                  'sec-fetch-user': '?1',
                  'upgrade-insecure-requests': '1',
                  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
              }
              response=requests.get(url=url, headers=headers)
              return response.text
          
          def get_font(data):
              html=etree.HTML(data)
              script=html.xpath('//script[2]/text()')[0]
              ttf=re.findall(".*?src:url\(\'data:application/font-ttf;charset=utf-8;base64,(.*?)\'\).*?",script,re.S)[0]
              with open('fangchan-secret.ttf','wb') as f:
                  f.write(base64.b64decode(ttf))
          
          if __name__=='__main__':
              data=get_data()
              get_font(data)

          解析字體字符

          import base64
          from fontTools.ttLib import TTFont
          import re
          import requests
          from lxml import etree
          
          def get_data():
              url="https://bj.58.com/chuzu/?PGTID=0d200001-0000-11e9-58e6-a658f219b27c&ClickID=1"
              headers={
                  'authority': 'bj.58.com',
                  'method': 'GET',
                  'path': '/chuzu/?PGTID=0d200001-0000-11e9-58e6-a658f219b27c&ClickID=1',
                  'scheme': 'https',
                  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
                  'accept-encoding': 'gzip, deflate, br',
                  'accept-language': 'zh-CN,zh;q=0.9',
                  'cache-control': 'max-age=0',
                  'cookie': 'f=n; commontopbar_new_city_info=1%7C%E5%8C%97%E4%BA%AC%7Cbj; commontopbar_ipcity=bj%7C%E5%8C%97%E4%BA%AC%7C0; userid360_xml=C0E9739B2022549506AFBC01231A1DAA; time_create=1606640420140; xxzl_cid=f4a781439d9247f393d0a1629bec00df; xzuid=e0e5ea78-ac5a-491b-819d-a869ab37a7a7; xxzl_deviceid=2G3xFS3qwOviMHxtC%2FVEituhpmiI%2FJ%2BAmJ08cPBulZSe7LcSgT98WgFcyNDbzMXJ; id58=c5/nfF+bz1xVS0tAA7tjAg==; 58tj_uuid=116f1ed0-7c25-477e-8887-be3602fa2389; new_uv=1; utm_source=; spm=; init_refer=https%253A%252F%252Fbj.58.com%252Fchuzu%252Fsub%252Fpn70%252F%253Fpagetype%253Dditie%2526PGTID%253D0d3090a7-0000-1b87-3e2e-c6efe8d19973%2526ClickID%253D2; wmda_uuid=13712f08f0e555f110b1b2684ce9d709; wmda_new_uuid=1; wmda_session_id_11187958619315=1604046685879-d3ad7e5f-77f6-29d7; wmda_visited_projects=%3B11187958619315; als=0; f=n; new_session=0',
                  'sec-fetch-dest': 'document',
                  'sec-fetch-mode': 'navigate',
                  'sec-fetch-site': 'none',
                  'sec-fetch-user': '?1',
                  'upgrade-insecure-requests': '1',
                  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
              }
              response=requests.get(url=url, headers=headers)
              return response.text
          
          def get_font(data):
              html=etree.HTML(data)
              script=html.xpath('//script[2]/text()')[0]
              ttf=re.findall(".*?src:url\(\'data:application/font-ttf;charset=utf-8;base64,(.*?)\'\).*?",script,re.S)[0]
              with open('fangchan-secret.ttf','wb') as f:
                  f.write(base64.b64decode(ttf))
          
          def parse_font():
              font=TTFont('fangchan-secret.ttf')
              bestcmap=font['cmap'].getBestCmap()
              newmap=dict()
              for key in bestcmap.keys():
                  value=int(re.search(r'(\d+)', bestcmap[key]).group(1)) - 1
                  key=hex(key)
                  newmap[key]=value
              print(newmap)
          
          if __name__=='__main__':
              data=get_data()
              get_font(data)
              parse_font()


          我們發現字體編號和之前的不符合,比如:
          0x9476=7,而這里的是2,這是什么原因呢?是因為他的字體是動態生成的,每次返回的數字編號對應的值都是不同的,但是不影響我們代碼的正常運行與結果。

          import base64
          from fontTools.ttLib import TTFont
          import re
          import requests
          from lxml import etree
          
          def get_data():
              url="https://bj.58.com/chuzu/?PGTID=0d200001-0000-11e9-58e6-a658f219b27c&ClickID=1"
              headers={
                  'authority': 'bj.58.com',
                  'method': 'GET',
                  'path': '/chuzu/?PGTID=0d200001-0000-11e9-58e6-a658f219b27c&ClickID=1',
                  'scheme': 'https',
                  'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
                  'accept-encoding': 'gzip, deflate, br',
                  'accept-language': 'zh-CN,zh;q=0.9',
                  'cache-control': 'max-age=0',
                  'cookie': 'f=n; commontopbar_new_city_info=1%7C%E5%8C%97%E4%BA%AC%7Cbj; commontopbar_ipcity=bj%7C%E5%8C%97%E4%BA%AC%7C0; userid360_xml=C0E9739B2022549506AFBC01231A1DAA; time_create=1606640420140; xxzl_cid=f4a781439d9247f393d0a1629bec00df; xzuid=e0e5ea78-ac5a-491b-819d-a869ab37a7a7; xxzl_deviceid=2G3xFS3qwOviMHxtC%2FVEituhpmiI%2FJ%2BAmJ08cPBulZSe7LcSgT98WgFcyNDbzMXJ; id58=c5/nfF+bz1xVS0tAA7tjAg==; 58tj_uuid=116f1ed0-7c25-477e-8887-be3602fa2389; new_uv=1; utm_source=; spm=; init_refer=https%253A%252F%252Fbj.58.com%252Fchuzu%252Fsub%252Fpn70%252F%253Fpagetype%253Dditie%2526PGTID%253D0d3090a7-0000-1b87-3e2e-c6efe8d19973%2526ClickID%253D2; wmda_uuid=13712f08f0e555f110b1b2684ce9d709; wmda_new_uuid=1; wmda_session_id_11187958619315=1604046685879-d3ad7e5f-77f6-29d7; wmda_visited_projects=%3B11187958619315; als=0; f=n; new_session=0',
                  'sec-fetch-dest': 'document',
                  'sec-fetch-mode': 'navigate',
                  'sec-fetch-site': 'none',
                  'sec-fetch-user': '?1',
                  'upgrade-insecure-requests': '1',
                  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
              }
              response=requests.get(url=url, headers=headers)
              return response.text
          
          def get_font(data):
              html=etree.HTML(data)
              script=html.xpath('//script[2]/text()')[0]
              ttf=re.findall(".*?src:url\(\'data:application/font-ttf;charset=utf-8;base64,(.*?)\'\).*?",script,re.S)[0]
              with open('fangchan-secret.ttf','wb') as f:
                  f.write(base64.b64decode(ttf))
          
          def parse_font():
              font=TTFont('fangchan-secret.ttf')
              bestcmap=font['cmap'].getBestCmap()
              newmap=dict()
              for key in bestcmap.keys():
                  value=int(re.search(r'(\d+)', bestcmap[key]).group(1)) - 1
                  key=hex(key)
                  newmap[key]=value
              return newmap
          
          def parse_data(data,newmap):
              for key,value in newmap.items():
                  key_=key.replace('0x','&#x') + ';'
                  if key_ in data:
                      data=data.replace(key_,str(value))
              html=etree.HTML(data)
              house_list=html.xpath('//ul[@class="house-list"]/li')[:-1]
              for house in house_list:
                  room=house.xpath('.//p[@class="room"]/text()')[0]
                  money=house.xpath('.//b[@class="strongbox"]/text()')[0]
                  print(room,money)
          
          if __name__=='__main__':
              data=get_data()
              get_font(data)
              newmap=parse_font()
              parse_data(data,newmap)

          聲明:本文僅供學習交流使用,請勿用于商業用途,違者后果自負。


          主站蜘蛛池模板: 亚洲日韩精品一区二区三区无码 | 亚洲国产精品无码第一区二区三区| 无码人妻一区二区三区在线视频| 无码日韩AV一区二区三区| 久久综合九九亚洲一区| 日本免费精品一区二区三区| 国产成人欧美一区二区三区| 精品国产一区二区三区香蕉 | 一区二区三区亚洲视频| 日本精品一区二区在线播放| 精品国产aⅴ无码一区二区| 一区二区三区亚洲| 亚洲AV色香蕉一区二区| 久久精品免费一区二区三区| 肥臀熟女一区二区三区 | 欧美日本精品一区二区三区| 国产精品一区二区久久| 3D动漫精品啪啪一区二区下载| 久久久老熟女一区二区三区| 无码精品人妻一区二区三区漫画 | 亚洲AV美女一区二区三区| 国内精品无码一区二区三区| 亚洲夜夜欢A∨一区二区三区| 亚洲一区AV无码少妇电影☆| 无码人妻久久久一区二区三区| 一区二区视频在线观看| 亚洲男女一区二区三区| 亚洲av无码一区二区三区天堂| 亚洲一区二区三区国产精华液| 日本一区二区三区免费高清| 国产精品视频无圣光一区| 丝袜美腿高跟呻吟高潮一区| 国产精品亚洲综合一区| 夜夜嗨AV一区二区三区| 精品亚洲A∨无码一区二区三区| 一区二区不卡在线| 福利一区在线视频| 任你躁国产自任一区二区三区| 精品乱人伦一区二区三区| 亚洲国产精品一区二区久久| 亚洲AV无码片一区二区三区|