爬蟲入門到精通-爬蟲之非同步載入(實戰花瓣網)

本文章屬於爬蟲入門到精通系統教程第八講

本次我們會講解兩個知識點

  1. 非同步載入
  2. headers中的Accept

本次我們要抓取的是花瓣網美女照片美女_花瓣,陪你做生活的設計師(發現、採集你喜歡的美女圖片)_花瓣網

本次我們會用到的輔助包

1. scrapy/parsel (假如你用過scrapy,那麼一定不陌生,這就是其中提取器)

Parsel is a library to extract data from HTML and XML using XPath and CSS selectors

簡單來講就是集成了xpath和css,只要你會xpath的話,那麼用法沒有什麼區別

>>> from parsel import Selector>>> sel = Selector(text=u"""<html> <body> <h1>Hello, Parsel!</h1> <ul> <li><a href="http://example.com">Link 1</a></li> <li><a href="http://scrapy.org">Link 2</a></li> </ul </body> </html>""")>>>>>> sel.css(h1::text).extract_first()uHello, Parsel!>>>>>> sel.css(h1::text).re(w+)[uHello, uParsel]>>>>>> for e in sel.css(ul > li): print(e.xpath(.//a/@href).extract_first())http://example.comhttp://scrapy.org

安裝方法: pip install parsel

2. scrapinghub/js2xml

Convert Javascript code to an XML document

簡單來講就是 將JavaScript代碼轉換為xml文檔。然後可以使用xpath從JavaScript中提取數據,不用寫一堆正則了。

>>> import js2xml>>>>>> jscode = """function factorial(n) {... if (n === 0) {... return 1;... }... return n * factorial(n - 1);... }""">>> parsed = js2xml.parse(jscode)>>>>>> parsed.xpath("//funcdecl/@name") # extracts function name[factorial]>>>>>> print js2xml.pretty_print(parsed) # pretty-print generated XML<program> <funcdecl name="factorial"> <parameters> <identifier name="n"/> </parameters> <body> <if> <predicate> <binaryoperation operation="==="> <left> <identifier name="n"/> </left> <right> <number value="0"/> </right> </binaryoperation> </predicate> <then> <block> <return> <number value="1"/> </return> </block> </then> </if> <return> <binaryoperation operation="*"> <left> <identifier name="n"/> </left> <right> <functioncall> <function> <identifier name="factorial"/> </function> <arguments> <binaryoperation operation="-"> <left> <identifier name="n"/> </left> <right> <number value="1"/> </right> </binaryoperation> </arguments> </functioncall> </right> </binaryoperation> </return> </body> </funcdecl></program>>>>

安裝方法: pip install js2xml

開始爬蟲

我們先打開美女_花瓣,陪你做生活的設計師(發現、採集你喜歡的美女圖片)_花瓣網

頁面分析

如果我們想把這裡面所有美女照片抓取下來的話,那麼我們的操作步驟應該是這樣的

  1. 打開首頁的每一個"相框",然後點進去

  1. 獲取所有圖片的鏈接,然後下載下來

程序實現:

用程序實現的話,也是挺簡單的

  1. 獲取首頁所有「相框」的鏈接
  2. 點進去每個鏈接
  3. 獲取詳情頁的所有圖片地址
  4. 下載圖片

代碼:

  1. 獲取首頁所有「相框」的鏈接
    1. 我們打開美女_花瓣,陪你做生活的設計師(發現、採集你喜歡的美女圖片)_花瓣網
    2. 按F12
    3. 點擊如圖所示的位置

    1. 點擊任何一個相框,然後你會看到網頁的源代碼自動會跳到你當前選中的地方

    1. 然後你就可以在這附近找找你想要的鏈接地址(可以看到/pins/1062650100/,我們可以打開這個地址看看,確認下是我們想要找的)

      1. 可以看到圖片是一樣的,說明我們要找的沒錯

      1. 那麼既然我們找到了需要的鏈接,接下來就是用程序定位到這了。
      2. 可以看到鏈接這邊有個class="img x layer-view loaded",那麼我們可以用xpath
      3. //a[@class="img x layer-view loaded"]/@href

來獲取地址了

  1. 用代碼實現:
    1. import requests

      from parsel import Selector

      url = huaban.com/favorite/bea

      headers = {

      User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

      }

      z = requests.get(url,headers=headers)

      print z.status_code

      # 返回200

      # 使用parsel中的Selector 來解析

      sel = Selector(text=z.text)

      print sel.xpath(//a[@class="img x layer-view loaded"]/@href)

      # 發現返回為空

這邊為什麼會返回空呢?不是應該返回所有鏈接的么?

    1. 我們可以查看下網頁源代碼,可以發現的內容都是通過js渲染上去的,所以我們才獲取不到內容(這個可以用js2xml來解析,先放在這裡,到詳情頁再來處理。)

  1. 所謂的非同步載入
    1. 我們還是打開美女_花瓣,陪你做生活的設計師(發現、採集你喜歡的美女圖片)_花瓣網
    2. 可以發現我們把頁面拖動到最下面,會自動載入出新的內容(整個頁面沒有跳轉,這就是所謂的非同步載入。有些網頁是需要手動點擊「載入更多的」,原理都是一樣的)
    3. 獲取非同步載入的請求
      1. 打開F12
      2. 拖動到頁面最下面(有些網站是點擊載入更多)
      3. 注意 我有勾選"xhr"
      4. 可以看到每次頁面到最底部,都會發送一個請求。這個請求就是所謂的非同步載入請求。

      1. 可以看到請求的參數如下:

        j0ga0has:

        max:1062527343

        limit:20

        wfl:1

"j0ga0has"第一個參數不知道是怎麼回事,先放著"max"是 最上面的pin_idlimit 是每次返回的條數有人可能會問,你怎麼知道這個參數是幹嘛的?其實都是試出來的(或者說看出來的)

      1. 如下圖的pin_id,可以發現下一條請求的max就是上一條請求獲取到的最後一個pin_id

      1. 我們查看返回值,發現竟然是json格式的,這樣的話,都不需要我們解析了,那我們找找我們需要的鏈接地址在哪。

      1. 發現鏈接地址就是由pin_id拼接而成的,所以我們只要獲取到這個pin_id就行。

    1. 用程序來實現:
    2. url = huaban.com/favorite/bea

      params = {

      j0ga0hbi:,

      max:1062161596,

      limit:100,

      wfl:1

      }

      z1 = requests.get(url=url,params=params,headers=headers)

      print z1.status_code

      # 返回200

      print z1.json()

      # 報錯

然後發現竟然報錯了。。。

為什麼呢?我們查看請求的時候就是jsno格式的啊

我們列印下源代碼看看

你會看到竟然是"<!DOCTYPE html><html "這樣的,但是我們上面查看請求的時候,明明是如下圖這樣的啊

那麼到底是哪裡出了問題呢?

我們再次查看之前我們看到的非同步請求

可以發現它有幾個 特別的請求頭

指定了格式為json ,那麼我們加上去看看呢

Accept:application/json

X-Request:JSON

X-Requested-With:XMLHttpRequest

    1. headers1 = {

      User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36,

      Accept:application/json,

      X-Request:JSON,

      X-Requested-With:XMLHttpRequest

      }

      z2 = requests.get(url=url,params=params,headers=headers1)

      print z2.content

可以看到返回值和我們之前一樣了。

獲取pin_id

最後只要把pin_id拼接成url就可以了,如果你想要爬取所有的圖片的話,那麼你只需要把最後的pin_id 傳入給max,再請求一次,直到pins為空為止

  1. 在上面我們已經獲取到了所有的詳情頁的地址,那麼我們現在只要獲取到圖片鏈接就行
    1. 隨便打開一個詳情頁花瓣
    2. 查看圖片地址

    1. 複製圖片地址到網頁源代碼裡面找找看
      1. 全部複製,發現沒有找到

      1. 那麼我們複製一部分
        1. 可以看到已經找到了,這邊你也可以用正則表達式,來匹配所有的地址,但是太麻煩了,我們可以用js2xml

    1. 看代碼.

      1. 就這樣,我們已經把圖片地址獲取到了,只需要拼接下即可(記得去下重)

最後再次總結一下

看完本篇文章後,你應該要

  • 能知道如何抓取非同步載入的請求
  • 了解js2xml的用法
  • 了解headers的用法

最後代碼都在 kimg1234/pachong

填一個坑,我在爬蟲入門到精通-網頁的下載 - 知乎專欄留下以下問題

@王東堯

其實解決方法也挺簡單的,就是把content-type這一行注釋掉。

那麼為什麼注釋掉就可以了呢?請仔細研究研究http協議。。。

歡迎關注本人的微信公眾號獲取更多Python爬蟲相關的內容

(可以直接搜索「Python爬蟲分享」)


推薦閱讀:

Flask框架從入門到實戰
PyQGIS開發 -- 地圖控制項交互
一個網站用兩種或以上的後端編程語言會出現什麼情況?為什麼?
[15] Python循環語句(二)
如何用Python和機器學習炒股賺錢?

TAG:Python | 爬虫计算机网络 |