<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          前后端分離有什么了不起,手把手教你用Python爬下來!

          共 12352字,需瀏覽 25分鐘

           ·

          2021-01-15 15:06


          大家好,我是早起。

          本文一個詳細的爬蟲進階教程,里面包含了很詳細的思考和試錯過程,如果你對學爬蟲是認真的,建議認真看。

          我們要抓取下面這個網(wǎng)站上的所有圖書列表:

          https://www.epubit.com/books

          1) 探索研究

          創(chuàng)建一個新的python文件,寫入如下代碼:

          import requests
          url = 'https://www.epubit.com/books'
          res = requests.get(url)
          print(res.text)

          運行發(fā)現(xiàn)打印結(jié)果如下:

          這里面根本沒有圖書的信息。但使用瀏覽器檢查器可以看到圖書的信息:

          我們碰到了一個基于前后端分離的網(wǎng)站,或者說一個用JavaScript獲取數(shù)據(jù)的網(wǎng)站。這種網(wǎng)站的數(shù)據(jù)流程是這樣的:

          • 初次請求只返回了網(wǎng)頁的基本框架,并沒有數(shù)據(jù)。就是前面截圖看到那樣。
          • 但網(wǎng)頁的基本框架中包含JavaScript的代碼,這段代碼會再發(fā)起一次或者多次請求獲取數(shù)據(jù)。我們稱為后續(xù)請求。

          為了抓取這樣的網(wǎng)站,有兩個辦法:

          1. 分析出后續(xù)請求的地址和參數(shù),寫代碼發(fā)起同樣的后續(xù)請求。
          2. 使用模擬瀏覽器技術,比如selenium。這種技術可以自動發(fā)起后續(xù)請求獲取數(shù)據(jù)。

          2) 分析后續(xù)請求

          打開谷歌瀏覽器的檢查器,按圖中的指示操作:

          1. 點擊Network,這里可以查看瀏覽器發(fā)送的所有網(wǎng)絡請求。
          2. XHR,查看瀏覽器用JavaScript發(fā)送的請求。
          3. 下面可以看到很多請求。我們要一個個看過去找到包含商品列表的請求。

          再來理解一下瀏覽器打開一個網(wǎng)頁的過程,一般并不是一個請求返回了所有的內(nèi)容,而是包含多個步驟:

          1. 第一個請求獲得HTML文件,里面可能包含文字,數(shù)據(jù),圖片的地址,樣式表地址等。HTML文件中并沒有直接包含圖片。
          2. 瀏覽器根據(jù)HTML中的鏈接,再次發(fā)送請求,讀取圖片,樣式表,基于JavaScript的數(shù)據(jù)等。

          所以我們看到有這么不同類型的請求:XHR, JS,CSS,Img,F(xiàn)ont, Doc等。

          我們爬取的網(wǎng)站發(fā)送了很多個XHR請求,分別用來請求圖書列表,網(wǎng)頁的菜單,廣告信息,頁腳信息等。我們要從這些請求中找出圖書的請求。

          具體操作步驟如圖:

          1. 在左邊選中請求
          2. 在右邊選擇Response
          3. 下面可以看到這個請求返回的數(shù)據(jù),從數(shù)據(jù)可以判斷是否包含圖書信息。

          Javascript請求返回的格式通常是JSON格式,這是一種JavaScript的數(shù)據(jù)格式,里面包含用冒號隔開的一對對數(shù)據(jù),比較容易看懂。JSON很像Python中的字典。

          在眾多的請求中,可以根據(jù)請求的名字大致判斷,提高效率。比如上圖中getUBookList看起來就像是獲取圖書列表。點開查看,返回的果然是圖書列表。

          請記住這個鏈接的地址和格式,后面要用到:

          https://www.epubit.com/pubcloud/content/front/portal/getUbookList?page=1&row=20&=&startPrice=&endPrice=&tagId= 分析一下,可以看到:

          1. 網(wǎng)址是:https://www.epubit.com/pubcloud/content/front/portal/getUbookList
          2. page=1表示第1頁,我們可以依次傳入2,3,4等等。
          3. row=20表示每一頁有20本書
          4. startPrice和endPrice表示價格條件,他們的值都是空,表示不設定價格限制。

          3) 使用postman測試猜想

          為了驗證這個設想打開谷歌瀏覽器,在地址欄中輸入以下網(wǎng)址:

          https://www.epubit.com/pubcloud/content/front/portal/getUbookList?page=1&row=20&=&startPrice=&endPrice=&tagId=

          可是得到了如下的返回結(jié)果:

          {
              "code""-7",
              "data"null,
              "msg""系統(tǒng)臨時開小差,請稍后再試~",
              "success"false
          }

          這并不是系統(tǒng)出了問題,而是系統(tǒng)檢測到我們是非正常的請求,拒絕給我們返回數(shù)據(jù)。

          這說明除了發(fā)送這個URL,還需要給服務器傳送額外的信息,這些信息叫做Header,翻譯成中文是請求頭的意思。

          在下圖中可以看到正常的請求中包含了多個請求頭:

          1. 選中要查看的請求
          2. 在右邊選Headers
          3. 往下翻,可以看到Request Headers,下面就是一項項數(shù)據(jù):
            • Accept: application/json, text/plain, /
            • Accept-Encoding:gzip, deflate, br
            • ....

          為了讓服務器正常處理請求,我們要模擬正常的請求,也添加相應的header。如果給的Header也都一樣,服務器根本不可能識別出我們是爬蟲。后面我們會學習如何在發(fā)送請求時添加header。

          但通常服務器并不會檢查所有的Header,可能只要添加一兩個關鍵Header就可以騙服務器給我們數(shù)據(jù)了。但我們要一個個測試那些Header是必須的。

          在瀏覽器中無法添加Header,為了發(fā)送帶Header的HTTP請求,我們要使用另一個軟件叫做Postman。這是一個API開發(fā)者和爬蟲工程師最常使用的工具之一。

          首先在postman的官網(wǎng)下載:www.postman.com。根據(jù)指示一步步安裝軟件,中間沒有額外的設置。

          打開postman后可以看到如下界面:

          1. 在最上面點擊加號,可以添加一個新的請求
          2. 中間填寫請求的URL
          3. 點Headers進入Headers的設置界面,添加Header。

          這些Header的名字和值可以在檢查器中復制過來。如果自己拼寫,注意千萬不要寫錯。

          我們來了解一下幾個常見的header:

          • User-Agent: 這個Header表示請求者是誰,一般是一個包括詳細版本信息的瀏覽器的名字,比如:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36

            如果爬蟲不添加這個Header,服務器一下就能識別出這是不正常請求,可以予以拒絕。當然,是否拒絕取決于程序員的代碼邏輯。

          • Cookie: 如果一個網(wǎng)站需要登錄,登錄的信息就保存在Cookie中。服務器通過這個Header判定是否登陸了,登陸的是誰。

            假設我們要自動在京東商城下單,我們可以先人工登錄,復制Cookie的值,用Python發(fā)送請求并包含這個Cookie,這樣服務器就認為我們已經(jīng)登陸過了,允許我們下單或做其他操作。如果在程序中加上計時的功能,指定具體下單的時間點,這就是秒殺程序。這是爬取需要登錄的網(wǎng)站的一種常用方法。

          • Accept:指瀏覽器接受什么格式的數(shù)據(jù),比如**application/json, text/plain, */***是指接受JSON,文本數(shù)據(jù),或者任何數(shù)據(jù)。

          • Origin-Domain: 是指請求者來自那個域名,這個例子中是:www.epubit.com

          關于更多的HTTP的Header,可以在網(wǎng)上搜索HTTP Headers學習。

          我一個個添加常用的Header,但服務器一直不返回數(shù)據(jù),直到添加了Origin-Domain這個Header。這說明這個Header是必備條件。

          網(wǎng)頁的后臺程序有可能不檢查Header,也有可能檢查一個Header,也有可能檢查多個Header,這都需要我們嘗試才能知道。

          既然Origin-Domain是關鍵,也許后臺程序只檢查這一個Header,我們通過左邊的選擇框去掉其他的Header,只保留Origin-Domain,請求仍然成功,這說明后臺只檢查了這一個Header:

          然后修改地址欄中的page參數(shù),獲取其他的頁,比如截圖中修改成了3,再發(fā)送請求,發(fā)現(xiàn)服務器返回了新的數(shù)據(jù)(其他的20本書)。這樣我們的請求過程就成功了。

          4) 寫抓取程序

          開發(fā)爬蟲,主要的時間是分析,一旦分析清楚了,爬取代碼并不復雜:

          import requests

          def get_page(page=1):
              '''抓取指定頁的數(shù)據(jù),默認是第1頁'''
              # 使用page動態(tài)拼接URL
              url = f'https://www.epubit.com/pubcloud/content/front/portal/getUbookList?page={page}&row=20&=&startPrice=&endPrice=&tagId='
              headers = {'Origin-Domain''www.epubit.com'}
              # 請求的時候同時傳入headers
              res = requests.get(url, headers=headers) 
              print(res.text)

          get_page(5)

          這里我們測試了抓取第5頁的數(shù)據(jù),比對打印出的JSON數(shù)據(jù)和網(wǎng)頁上的第5頁數(shù)據(jù),結(jié)果是匹配的。

          現(xiàn)在我們?nèi)シ治鯦SON的數(shù)據(jù)結(jié)構(gòu),再來完善這個程序。

          5) 分析JSON數(shù)據(jù)

          JSON就像Python中的字典,用大括號存放數(shù)據(jù),用冒號分割鍵和值。下面是省略的JSON數(shù)據(jù):

          {
              "code""0",
              "data": {
                  "current"1//第一頁
                  "pages"144//一共幾頁
                  "records": [  //很多本書的信息放在方括號中
                      {
                          "authors""[美] 史蒂芬·普拉達(Stephen Prata)",  //作者
                          "code""UB7209840d845c9"//代碼
                          "collectCount"416//喜歡數(shù)
                          "commentCount"64//評論數(shù)
                          "discountPrice"0//折扣價
                          "downebookFlag""N",
                          "fileType""",
                          ...
                      },
                      {
                          "authors""笨叔",
                          "code""UB7263761464b35",
                          "collectCount"21,
                          "commentCount"3,
                          "discountPrice"0,
                          "downebookFlag""N",
                          "fileType""",
                          ...
                      },
                      ...
                  ],
                  "size"20,
                  "total"2871
              },
              "msg""成功",
              "success"true
          }

          我們來學習一下這個JSON格式:

          1. 最外面是一個大括號,里面包含了code, data, msg, success四塊信息。這個格式是開發(fā)這個網(wǎng)頁的程序員自己設計的,不同的網(wǎng)頁可能不同。
          2. 其中code, msg和sucess表示請求的狀態(tài)碼,請求返回的提示,請求是否成功。而真正的數(shù)據(jù)都在data中。
          3. data的冒號后面是一個大括號,表示一個數(shù)據(jù)對象。里面包含了當前頁數(shù)(current),總頁數(shù)(pages),書的信息(records)等。
          4. records表示很多本書,所以它用一個方括號表示,方括號里面又有很多大括號包起來的數(shù)據(jù)對象,每個大括號表示一本書。
          {
              "authors""[美] 史蒂芬·普拉達(Stephen Prata)"//書名
              "code""UB7209840d845c9"//代碼
              "collectCount"416//喜歡數(shù)
              "commentCount"64,  //評論數(shù)
              "discountPrice"0,  //折扣0,表示沒有折扣
              ...
              "forSaleCount"3,  //在售數(shù)量
              ...
              "logo""https://cdn.ptpress.cn/pubcloud/bookImg/A20190961/20200701F892C57D.jpg",
              "name""C++ Primer Plus 第6版 中文版"//書名
              ...
              "price"100.30,  //價格
              ...
          }

          每本書的信息有很多個字段,這里省略掉了很多字段,給重要的信息添加了注釋。

          6) 完成程序

          現(xiàn)在來完善上面的程序,從JSON中解析出我們要的數(shù)據(jù),為了簡化,我們只抓取:書名,作者,編號和價格。

          程序框架:

          import requests
          import json
          import time 
          class Book:
              # --省略--
          def get_page(page=1):
              # --省略--
              books = parse_book(res.text)
              return books
          def parse_book(json_text):
              #--省略--

          all_books = []
          for i in range(110):
              print(f'======抓取第{i}頁======')
              books = get_page(i)
              for b in books:
                  print(b)
              all_books.extend(books)
              print('抓完一頁,休息5秒鐘...')
              time.sleep(5)
          1. 定義了Book類來表示一本書
          2. 添加了parse_book函數(shù)負責解析數(shù)據(jù),返回包含當前頁的20本書的list
          3. 最下面使用for循環(huán)抓取數(shù)據(jù),并放到一個大的列表中,range中添加要抓取的頁數(shù)。通過前面的分析可以知道一共有幾頁。
          4. 抓取完一頁后,一定要sleep幾秒,一是防止給網(wǎng)站帶來太大壓力,二是防止網(wǎng)站會封鎖你的IP,是為他好,也是為了自己好。
          5. 把抓來的信息保存到文件中的代碼,請自行完成。

          下面來看看,被省略掉的部分:

          Book類:

          class Book:
              def __init__(self, name, code, author, price):
                  self.name = name
                  self.code = code
                  self.author = author
                  self.price = price

              def __str__(self):
                  return f'書名:{self.name},作者:{self.author},價格:{self.price},編號:{self.code}'

          下面是__str__函數(shù)是一個魔法函數(shù),當我們使用print打印一個Book對象的時候,Python會自動調(diào)用這個函數(shù)。

          parse_book函數(shù):

          import json

          def parse_book(json_text):
              '''根據(jù)返回的JSON字符串,解析書的列表'''
              books = []
              # 把JSON字符串轉(zhuǎn)成一個字典dict類
              book_json = json.loads(json_text)
              records = book_json['data']['records']
              for r in records:
                  author = r['authors']
                  name = r['name']
                  code = r['code']
                  price = r['price']
                  book = Book(name, code, author, price)
                  books.append(book)
              return books
          1. 在最上面import了json模塊,這是Python自帶的,不用安裝
          2. 關鍵的代碼就是使用json把抓來的JSON字符串轉(zhuǎn)成字典,剩下的是對字典的操作,就很容易理解了。

          抓取基于JavaScript的網(wǎng)頁,復雜主要在于分析過程,一旦分析完成了,抓取的代碼比HTML的頁面還要更簡單清爽。


          -END-


          有幸入選CSDN博客之星評選,希望各位大佬點擊閱讀原文幫忙來一票,你一票,我一票,早起明天就出道

          瀏覽 65
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青青草国产亚洲精品久久 | 亚洲人妖在线 | 中文字幕人妻乱码 | 亚洲性爱在线 | 精品亲子伦一区二区三区 |