1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 12352字,需瀏覽 25分鐘

         ·

        2021-01-15 15:06


        大家好,我是早起。

        本文一個(gè)詳細(xì)的爬蟲(chóng)進(jìn)階教程,里面包含了很詳細(xì)的思考和試錯(cuò)過(guò)程,如果你對(duì)學(xué)爬蟲(chóng)是認(rèn)真的,建議認(rèn)真看。

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

        https://www.epubit.com/books

        1) 探索研究

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

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

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

        這里面根本沒(méi)有圖書(shū)的信息。但使用瀏覽器檢查器可以看到圖書(shū)的信息:

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

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

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

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

        2) 分析后續(xù)請(qǐng)求

        打開(kāi)谷歌瀏覽器的檢查器,按圖中的指示操作:

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

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

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

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

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

        具體操作步驟如圖:

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

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

        在眾多的請(qǐng)求中,可以根據(jù)請(qǐng)求的名字大致判斷,提高效率。比如上圖中g(shù)etUBookList看起來(lái)就像是獲取圖書(shū)列表。點(diǎn)開(kāi)查看,返回的果然是圖書(shū)列表。

        請(qǐng)記住這個(gè)鏈接的地址和格式,后面要用到:

        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頁(yè),我們可以依次傳入2,3,4等等。
        3. row=20表示每一頁(yè)有20本書(shū)
        4. startPrice和endPrice表示價(jià)格條件,他們的值都是空,表示不設(shè)定價(jià)格限制。

        3) 使用postman測(cè)試猜想

        為了驗(yàn)證這個(gè)設(shè)想打開(kāi)谷歌瀏覽器,在地址欄中輸入以下網(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)臨時(shí)開(kāi)小差,請(qǐng)稍后再試~",
            "success"false
        }

        這并不是系統(tǒng)出了問(wèn)題,而是系統(tǒng)檢測(cè)到我們是非正常的請(qǐng)求,拒絕給我們返回?cái)?shù)據(jù)。

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

        在下圖中可以看到正常的請(qǐng)求中包含了多個(gè)請(qǐng)求頭:

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

        為了讓服務(wù)器正常處理請(qǐng)求,我們要模擬正常的請(qǐng)求,也添加相應(yīng)的header。如果給的Header也都一樣,服務(wù)器根本不可能識(shí)別出我們是爬蟲(chóng)。后面我們會(huì)學(xué)習(xí)如何在發(fā)送請(qǐng)求時(shí)添加header。

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

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

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

        打開(kāi)postman后可以看到如下界面:

        1. 在最上面點(diǎn)擊加號(hào),可以添加一個(gè)新的請(qǐng)求
        2. 中間填寫(xiě)請(qǐng)求的URL
        3. 點(diǎn)Headers進(jìn)入Headers的設(shè)置界面,添加Header。

        這些Header的名字和值可以在檢查器中復(fù)制過(guò)來(lái)。如果自己拼寫(xiě),注意千萬(wàn)不要寫(xiě)錯(cuò)。

        我們來(lái)了解一下幾個(gè)常見(jiàn)的header:

        • User-Agent: 這個(gè)Header表示請(qǐng)求者是誰(shuí),一般是一個(gè)包括詳細(xì)版本信息的瀏覽器的名字,比如: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

          如果爬蟲(chóng)不添加這個(gè)Header,服務(wù)器一下就能識(shí)別出這是不正常請(qǐng)求,可以予以拒絕。當(dāng)然,是否拒絕取決于程序員的代碼邏輯。

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

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

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

        • Origin-Domain: 是指請(qǐng)求者來(lái)自那個(gè)域名,這個(gè)例子中是:www.epubit.com

        關(guān)于更多的HTTP的Header,可以在網(wǎng)上搜索HTTP Headers學(xué)習(xí)。

        我一個(gè)個(gè)添加常用的Header,但服務(wù)器一直不返回?cái)?shù)據(jù),直到添加了Origin-Domain這個(gè)Header。這說(shuō)明這個(gè)Header是必備條件。

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

        既然Origin-Domain是關(guān)鍵,也許后臺(tái)程序只檢查這一個(gè)Header,我們通過(guò)左邊的選擇框去掉其他的Header,只保留Origin-Domain,請(qǐng)求仍然成功,這說(shuō)明后臺(tái)只檢查了這一個(gè)Header:

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

        4) 寫(xiě)抓取程序

        開(kāi)發(fā)爬蟲(chóng),主要的時(shí)間是分析,一旦分析清楚了,爬取代碼并不復(fù)雜:

        import requests

        def get_page(page=1):
            '''抓取指定頁(yè)的數(shù)據(jù),默認(rèn)是第1頁(yè)'''
            # 使用page動(dòng)態(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'}
            # 請(qǐng)求的時(shí)候同時(shí)傳入headers
            res = requests.get(url, headers=headers) 
            print(res.text)

        get_page(5)

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

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

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

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

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

        我們來(lái)學(xué)習(xí)一下這個(gè)JSON格式:

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

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

        6) 完成程序

        現(xiàn)在來(lái)完善上面的程序,從JSON中解析出我們要的數(shù)據(jù),為了簡(jiǎn)化,我們只抓?。簳?shū)名,作者,編號(hào)和價(jià)格。

        程序框架:

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

        下面來(lái)看看,被省略掉的部分:

        Book類(lèi):

        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'書(shū)名:{self.name},作者:{self.author},價(jià)格:{self.price},編號(hào):{self.code}'

        下面是__str__函數(shù)是一個(gè)魔法函數(shù),當(dāng)我們使用print打印一個(gè)Book對(duì)象的時(shí)候,Python會(huì)自動(dòng)調(diào)用這個(gè)函數(shù)。

        parse_book函數(shù):

        import json

        def parse_book(json_text):
            '''根據(jù)返回的JSON字符串,解析書(shū)的列表'''
            books = []
            # 把JSON字符串轉(zhuǎn)成一個(gè)字典dict類(lèi)
            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. 關(guān)鍵的代碼就是使用json把抓來(lái)的JSON字符串轉(zhuǎn)成字典,剩下的是對(duì)字典的操作,就很容易理解了。

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


        -END-


        有幸入選CSDN博客之星評(píng)選,希望各位大佬點(diǎn)擊閱讀原文幫忙來(lái)一票,你一票,我一票,早起明天就出道

        瀏覽 66
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            操屄视屏 | 超碰AⅤ | 成人碰碰免费视频 | 天天色天天综合 | 久热精品视频6 | 国产免费黄色电影 | 国产综合久久777777麻豆 | 国产69精品久久久久久久久久久 | www.激情小说.com | 91麻豆精品国产91久久久资源速度 |