1. 程式人生 > >Python爬蟲爬取動態頁面思路+例項(一)

Python爬蟲爬取動態頁面思路+例項(一)

簡介

有時候,我們天真無邪的使用urllib庫或Scrapy下載HTML網頁時會發現,我們要提取的網頁元素並不在我們下載到的HTML之中,儘管它們在瀏覽器裡看起來唾手可得。

這說明我們想要的元素是在我們的某些操作下通過js事件動態生成的。舉個例子,我們在刷QQ空間或者微博評論的時候,一直往下刷,網頁越來越長,內容越來越多,就是這個讓人又愛又恨的動態載入。

爬取動態頁面目前來說有兩種方法

  1. 分析頁面請求(這篇介紹這個)
  2. selenium模擬瀏覽器行為(霸王硬上弓,以後再說)

言歸正傳,下面介紹一下通過分析頁面請求的方法爬取動態載入頁面的思路。中心思想就是找到那個發請求的javascript檔案所發的請求。

舉兩個例子,京東評論和上證股票。

後注:本文的兩個例子都是get請求,可以顯示的在瀏覽器中檢視效果,如果是post請求,需要我們在程式中構造資料,構造方法可以參考我從前的一篇博文Scrapy定向爬蟲教程(六)——分析表單並回帖

京東評論

這是一個比較簡單的例子。

首先我們隨便找一個熱賣的商品,評論比較多。

點進去看看這個頁面現在的狀況

圖一

滾動條給的第一印象感覺這頁彷彿沒多少內容。

鍵盤F12開啟開發者工具,選擇Network選項卡,選擇JS(3月12日補:除JS選項卡還有可能在XHR選項卡中,當然也可以通過其它抓包工具),如下圖

圖二

然後,我們來拖動右側的滾動條,這時就會發現,開發者工具裡出現了新的js請求(還挺多的),不過草草翻譯一下,很容易就能看出來哪個是取評論的,如下圖

圖三

圖四

在瀏覽器中開啟,發現我們想要的資料就在這裡,如下圖

圖五

這整個頁面是一個json格式的資料,對於京東來說,當用戶下拉頁面時,觸發一個js事件,向伺服器傳送上面的請求取資料,然後通過一定的js邏輯把取到的這些json資料填充到HTML頁面當中。對於我們Spider來說,我們要做的就是把這些json資料整理提取。

在實際應用中,當然我們不可能去每個頁面裡找出來這個js發起的請求目標地址,所以我們需要分析這個請求地址的規律,一般情況下規律是比較好找的,因為規律太複雜服務方維護也難。那我們就來看一下京東這個請求:

https://club.jd.com/comment/productPageComments.action
?callback=fetchJSON_comment98vv337&productId=3311073&score=0&sortType=5&page=0&pageSize=10&isShadowSku=0

挺長的一個GET請求,不過引數命名都很規範,產品ID,評論頁碼什麼的,因為我這裡只是舉個例子,我就不一個一個去研究了~

思路有了,就按照正常的爬蟲去寫就好了,發請求,得到響應,解析資料,後續處理等等……

上證股票

這是前段時間一位道友問我的一個問題,感覺還是挺有嚼頭的,比上例要難。

目的是把每一頁的股票資訊都得到,看似很簡單,但是通過檢視原始碼發現,每一頁的連結在原始碼裡是看不到的。如下圖

圖六

ok,又是js動態載入,在原始碼裡不顯示,不過一定躲不過我們的開發者工具,按照上面京東的思路,切到Network、js選項卡,點選頁碼,獲得請求地址,一切行雲流水,如下圖

圖七

然而,當我們複製出這個url,在瀏覽器中開啟的時候,瀏覽器呈現的資訊卻是:

圖八

403錯誤!奇哉怪也!

究其原因,403狀態碼什麼意思呢?意思就是說,本伺服器知道你發來這一串url想找啥呢,但是老子就不給你!

怎麼辦,有辦法~

想想看,為什麼我們在那個頁面上點選第二頁第三頁的時候能夠正常的獲取到資料?為什麼我們直接請求不行?都是同一個瀏覽器呀。

問題就在於,瀏覽器通過上一個頁面發起的請求和單獨發的請求,頭資訊(request header)是不同的,比方說cookie、refer這些欄位,伺服器就通過這些就把我們的請求過濾掉了。

瀏覽器如此,我們的爬蟲也是如此,最後我解決這個問題的方法是給爬蟲設定詳細的請求頭(從我們能請求到的原始頁獲得),包括cookie,refer等,終於成功的得到了返回的json資料。

這份程式碼是用python3的urllib寫的,我只幫他寫了取一頁的資料,邏輯他自己去寫了。如下,看官不妨試試將頭資訊去掉~

import urllib.request

Cookie = "PHPStat_First_Time_10000011=1480428327337; PHPStat_Cookie_Global_User_Id=_ck16112922052713449617789740328; PHPStat_Return_Time_10000011=1480428327337; PHPStat_Main_Website_10000011=_ck16112922052713449617789740328%7C10000011%7C%7C%7C; VISITED_COMPANY_CODE=%5B%22600064%22%5D; VISITED_STOCK_CODE=%5B%22600064%22%5D; seecookie=%5B600064%5D%3A%u5357%u4EAC%u9AD8%u79D1; _trs_uv=ke6m_532_iw3ksw7h; VISITED_MENU=%5B%228451%22%2C%229055%22%2C%229062%22%2C%229729%22%2C%228528%22%5D"

url = "http://query.sse.com.cn/security/stock/getStockListData2.do?&jsonCallBack=jsonpCallback41883&isPagination=true&stockCode=&csrcCode=&areaName=&stockType=1&pageHelp.cacheSize=1&pageHelp.beginPage=3&pageHelp.pageSize=25&pageHelp.pageNo=3&pageHelp.endPage=31&_=1480431103024"

headers = {
    'User-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36',
    'Cookie': Cookie,
    'Connection': 'keep-alive',
    'Accept': '*/*',
    'Accept-Encoding': 'gzip, deflate, sdch',
    'Accept-Language': 'zh-CN,zh;q=0.8',
    'Host': 'query.sse.com.cn',
    'Referer': 'http://www.sse.com.cn/assortment/stock/list/share/'
}

req = urllib.request.Request(url,None,headers)
response = urllib.request.urlopen(req)
the_page = response.read()
print(the_page.decode("utf8"))

結語

還是那句話,通過分析頁面請求的方法爬取動態載入頁面的思路。中心思想就是找到那個發請求的javascript檔案所發的請求。然後利用我們既有的爬蟲知識去構造請求就可以了。

臨近考試,寫作較為匆忙。如果本文有敘述不清或者不正確的地方,還請批評指正。