电竞比分网-中国电竞赛事及体育赛事平台

分享

實戰(zhàn)|Python輕松實現(xiàn)動態(tài)網(wǎng)頁爬蟲(附詳細源碼)

 閑野之家 2022-02-18
項目背景


事情是這樣的,前幾天我公眾號寫了篇爬蟲入門的實戰(zhàn)文章,叫做《實戰(zhàn)|手把手教你用Python爬蟲(附詳細源碼)》。發(fā)出去不到一天,一名從業(yè)10年的王律師找到了我,我雖然同意了他的微信申請,但內(nèi)心是按奈不住的慌張。

圖片

簡單交流了下,原來他在自學(xué)爬蟲,但他發(fā)現(xiàn)翻頁的時候,url一直不變。其實他爬取的是較高難度的網(wǎng)頁,也就是這次要詳細介紹的動態(tài)網(wǎng)頁。一向樂于助人的J哥自然會給他指明方向,以最短的時間從青銅走向白銀。


AJAX動態(tài)加載網(wǎng)頁



什么是動態(tài)網(wǎng)頁

J哥一向注重理論與實踐相結(jié)合,知其然也要知其所以然,才能以不變應(yīng)萬變。

所謂的動態(tài)網(wǎng)頁,是指跟靜態(tài)網(wǎng)頁相對的一種網(wǎng)頁編程技術(shù)。靜態(tài)網(wǎng)頁,隨著html代碼的生成,頁面的內(nèi)容和顯示效果就基本上不會發(fā)生變化了——除非你修改頁面代碼。而動態(tài)網(wǎng)頁則不然,頁面代碼雖然沒有變,但是顯示的內(nèi)容卻是可以隨著時間、環(huán)境或者數(shù)據(jù)庫操作的結(jié)果而發(fā)生改變的?!獊碓窗俣劝倏?/span>

動態(tài)網(wǎng)頁具有減少工作量、內(nèi)容更新快、可完成功能多等特點,被很多公司所采用,比如狗東、某寶、某瓣、某乎等等。


什么是AJAX
隨著人們對動態(tài)網(wǎng)頁加載速度的要求越來越高,AJAX技術(shù)應(yīng)運而生并成為許多站點的首選。AJAX是一種用于創(chuàng)建快速動態(tài)網(wǎng)頁的技術(shù),通過在后臺與服務(wù)器進行少量數(shù)據(jù)交換,使網(wǎng)頁實現(xiàn)異步更新。這意味著在不重新加載整個網(wǎng)頁的情況下,可以對網(wǎng)頁的某部分進行更新。

如何爬取AJAX動態(tài)加載網(wǎng)頁

1. 解析接口

只要是有數(shù)據(jù)發(fā)送過來,那肯定是有發(fā)送到服務(wù)器的請求的吧。我們只需找出它悄悄加載出的頁面的真實請求即可。特點:爬取速度快,爬取的數(shù)據(jù)干凈,有些網(wǎng)站解析難度較大。

2. Selenium 

selenium是什么呢?它本來是個自動化測試工具,但是被廣泛的用戶拿去爬蟲了。它是一個工具,這個工具可以用代碼操作瀏覽器,比如控制瀏覽器的下滑、模擬鼠標(biāo)點擊等。特點:代碼較簡單,爬取速度慢,容易被封ip。



項目實操


怎么說了那么多理論,說實話J哥也不想那么啰嗦??墒前桑@些東西經(jīng)常會被問到,干脆直接寫下來,下次還有人問就直接把這篇文章發(fā)給他,一勞永逸!

圖片

OK,咱們繼續(xù)回到王律師的part。王律師作為一名資深律師,深知研究法院歷年公示的開庭信息、執(zhí)行信息等對于提升業(yè)務(wù)能力具有重要作用。于是,他興高采烈地打開了一個法院信息公示網(wǎng)頁。

長這樣:

圖片

然后,他根據(jù)J哥之前寫的爬蟲入門文章去爬數(shù)據(jù),成功提取了第一頁,內(nèi)心無比之激動。

圖片

緊接著,他加了個for循環(huán),想著花個幾分鐘時間把此網(wǎng)站2164頁共計32457條開庭公告數(shù)據(jù)提取到excel里。

然后,也就沒有然后了。各位看了前面的理論部分應(yīng)該也知道了,他這是 AJAX動態(tài)加載的網(wǎng)頁。無論你怎么點擊下一頁,url是不會變化的。你不信我點給你看看,左上角的url像山一樣矗立在那:

圖片


解析接口

既然如此,那我們就開啟爬蟲的正確姿勢吧,先用解析接口的方法來寫爬蟲。

首先,找到真實請求。右鍵檢查,點擊Network,選中XHR,刷新網(wǎng)頁,選擇Name列表中的jsp文件。沒錯,就這么簡單,真實請求就藏在里面。

圖片

我們再仔細看看這個jsp,這簡直是個寶啊。有真實請求url,有請求方法post,有Headers,還有Form Data,而From Data表示給url傳遞的參數(shù),通過改變參數(shù),咱們就可以獲得數(shù)據(jù)!為了安全,我把自個Cookie打了個馬賽克,機智的朋友也許發(fā)現(xiàn)了我順帶給自個打了個廣告。

圖片

我們再仔細看看這些參數(shù),pagesnum參數(shù)不就是代表頁數(shù)嘛!王律師頓悟,原來他心心念念的翻頁在這里!跨過千山萬水終于找到你!我們嘗試點擊翻頁,發(fā)現(xiàn)只有pagesnum參數(shù)會變化。

圖片

既然發(fā)現(xiàn)了它,那就趕緊抓住它。J哥以迅雷不及掩耳勢打開PyCharm,導(dǎo)入了爬蟲所需的庫。

1from urllib.parse import urlencode
2import csv
3import random
4import requests
5import traceback
6from time import sleep
7from lxml import etree    #lxml為第三方網(wǎng)頁解析庫,強大且速度快

構(gòu)造真實請求,添加Headers。這里J哥沒有貼自己的User-Agent和Cookie,主要是一向膽小甚微的J哥害怕啊。

 1base_url = 'http://www.hshfy./shfy/gweb2017/ktgg_search_content.jsp?'  #這里要換成對應(yīng)Ajax請求中的鏈接
2
3headers = {
4    'Connection''keep-alive',
5    'Accept''*/*',
6    'X-Requested-With''XMLHttpRequest',
7    'User-Agent''你的User-Agent',
8    'Origin''http://www.hshfy.',
9    'Referer''http://www.hshfy./shfy/gweb2017/ktgg_search.jsp?zd=splc',
10    'Accept-Language''zh-CN,zh;q=0.9',
11    'Content-Type''application/x-www-form-urlencoded',
12    'Cookie''你的Cookie'
13}

構(gòu)建get_page函數(shù),自變量為page,也就是頁數(shù)。以字典類型創(chuàng)建表單data,用post方式去請求網(wǎng)頁數(shù)據(jù)。這里要注意要對返回的數(shù)據(jù)解碼,編碼為'gbk',否則返回的數(shù)據(jù)會亂碼!另外我還加了異常處理優(yōu)化了下,以防意外發(fā)生。

1def get_page(page):
2    n = 3
3    while True:
4        try:
5            sleep(random.uniform(12))  # 隨機出現(xiàn)1-2之間的數(shù),包含小數(shù)
6            data = {
7                'yzm''yxAH',
8                'ft':'',
9                'ktrqks''2020-05-22',
10                'ktrqjs''2020-06-22',
11                'spc':'',
12                'yg':'',
13                'bg':'',
14                'ah':'',
15                'pagesnum': page
16            }
17            url = base_url   urlencode(data)
18            print(url)
19            try:
20                response = requests.request('POST',url, headers = headers)
21                #print(response)
22                if response.status_code == 200:
23                    re = response.content.decode('gbk')
24                    # print(re)
25                    return re  # 解析內(nèi)容
26            except requests.ConnectionError as e:
27                print('Error', e.args)  # 輸出異常信息
28        except (TimeoutError, Exception):
29            n -= 1
30            if n == 0:
31                print('請求3次均失敗,放棄此url請求,檢查請求條件')
32                return
33            else:
34                print('請求失敗,重新請求')
35                continue

構(gòu)建parse_page函數(shù),對返回的網(wǎng)頁數(shù)據(jù)進行解析,用Xpath提取所有字段內(nèi)容,保存為csv格式。有人會問為啥J哥這么喜歡用Xpath,因為簡單好用啊?。?!這么簡單的網(wǎng)頁結(jié)構(gòu)搞個正則大法裝x,J哥我做不到啊。

 1def parse_page(html):
2    try:
3        parse = etree.HTML(html)  # 解析網(wǎng)頁
4        items = parse.xpath('//*[@id='report']/tbody/tr')
5        for item in items[1:]:
6            item = {
7                'a'''.join(item.xpath('./td[1]/font/text()')).strip(),
8                'b'''.join(item.xpath('./td[2]/font/text()')).strip(),
9                'c'''.join(item.xpath('./td[3]/text()')).strip(),
10                'd'''.join(item.xpath('./td[4]/text()')).strip(),
11                'e'''.join(item.xpath('./td[5]/text()')).strip(),
12                'f'''.join(item.xpath('./td[6]/div/text()')).strip(),
13                'g'''.join(item.xpath('./td[7]/div/text()')).strip(),
14                'h'''.join(item.xpath('./td[8]/text()')).strip(),
15                'i'''.join(item.xpath('./td[9]/text()')).strip()
16            }
17            #print(item)
18            try:
19                with open('./law.csv''a', encoding='utf_8_sig', newline=''as fp:
20                    # 'a'為追加模式(添加)
21                    # utf_8_sig格式導(dǎo)出csv不亂碼
22                    fieldnames = ['a''b''c''d''e','f','g','h','i']
23                    writer = csv.DictWriter(fp,fieldnames)
24                    writer.writerow(item)
25            except Exception:
26                print(traceback.print_exc())  #代替print e 來輸出詳細的異常信息
27    except Exception:
28        print(traceback.print_exc())

最后,遍歷一下頁數(shù),調(diào)用一下函數(shù)。OK,搞定!

1    for page in range(1,5):  #這里設(shè)置想要爬取的頁數(shù)
2        html = get_page(page)
3        #print(html)
4        print('第'   str(page)   '頁提取完成')

我們來看一下最終效果:

圖片


Selenium
好學(xué)的朋友可能還想看看Selenium是如何來爬AJAX動態(tài)加載網(wǎng)頁的,J哥自然會滿足你的好奇心。于是趕緊新建了一個py文件,準(zhǔn)備趁勢追擊,用Selenium把這網(wǎng)站爬下來。

首先,把相關(guān)庫導(dǎo)進來。

1from lxml import etree
2import time
3from selenium import webdriver
4from selenium. webdriver.support.wait import WebDriverWait
5from selenium.webdriver.support import expected_conditions as EC
6from selenium.webdriver.common.by import By

然后,用chromedriver驅(qū)動打開這個網(wǎng)站。

1def main():
2    # 爬取首頁url
3    url = 'http://www.hshfy./shfy/gweb2017/flws_list.jsp?ajlb=aYWpsYj3D8crCz'
4    # 定義谷歌webdriver
5    driver = webdriver.Chrome('./chromedriver')
6    driver.maximize_window()  # 將瀏覽器最大化
7    driver.get(url)
于是,驚喜的發(fā)現(xiàn),報錯了。J哥憑借著六級英語的詞匯儲量,居然看懂了!大概意思是我的驅(qū)動和瀏覽器的版本不匹配,只支持79版本的瀏覽器。

圖片

J哥很郁悶,因為我以前爬蟲用Selenium沒出現(xiàn)過這種問題啊。J哥不甘心,于是打開了谷歌瀏覽器看了下版本號。

圖片

我丟!都更新到81版本啦!既然這樣,那就請好奇的小伙伴等J哥設(shè)置好瀏覽器自動更新、重新下載最新驅(qū)動后,下次再來聽窩講Selenium爬蟲吧,記得關(guān)注此公眾號,精彩不錯過哦~


結(jié) 語


總結(jié)一下,對于AJAX動態(tài)加載網(wǎng)頁爬蟲,一般就兩種方式:解析接口;Selenium。J哥推薦解析接口的方式,如果解析的是json數(shù)據(jù),就更好爬了。實在沒轍了再用Selenium吧。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多