爬蟲入門到精通-爬蟲之非同步載入(實戰花瓣網)
本文章屬於爬蟲入門到精通系統教程第八講
本次我們會講解兩個知識點
- 非同步載入
- 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
開始爬蟲
我們先打開美女_花瓣,陪你做生活的設計師(發現、採集你喜歡的美女圖片)_花瓣網
頁面分析
如果我們想把這裡面所有美女照片抓取下來的話,那麼我們的操作步驟應該是這樣的
- 打開首頁的每一個"相框",然後點進去
- 獲取所有圖片的鏈接,然後下載下來
程序實現:
用程序實現的話,也是挺簡單的
- 獲取首頁所有「相框」的鏈接
- 點進去每個鏈接
- 獲取詳情頁的所有圖片地址
- 下載圖片
代碼:
- 獲取首頁所有「相框」的鏈接
- 我們打開美女_花瓣,陪你做生活的設計師(發現、採集你喜歡的美女圖片)_花瓣網
- 按F12
- 點擊如圖所示的位置
- 點擊任何一個相框,然後你會看到網頁的源代碼自動會跳到你當前選中的地方
- 然後你就可以在這附近找找你想要的鏈接地址(可以看到/pins/1062650100/,我們可以打開這個地址看看,確認下是我們想要找的)
- 可以看到圖片是一樣的,說明我們要找的沒錯
- 那麼既然我們找到了需要的鏈接,接下來就是用程序定位到這了。
- 可以看到鏈接這邊有個class="img x layer-view loaded",那麼我們可以用xpath
- //a[@class="img x layer-view loaded"]/@href
來獲取地址了
- 用代碼實現:
- import requestsfrom parsel import Selectorurl = http://huaban.com/favorite/beauty/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)# 發現返回為空
這邊為什麼會返回空呢?不是應該返回所有鏈接的么?
- 我們可以查看下網頁源代碼,可以發現的內容都是通過js渲染上去的,所以我們才獲取不到內容(這個可以用js2xml來解析,先放在這裡,到詳情頁再來處理。)
- 所謂的非同步載入
- 我們還是打開美女_花瓣,陪你做生活的設計師(發現、採集你喜歡的美女圖片)_花瓣網
- 可以發現我們把頁面拖動到最下面,會自動載入出新的內容(整個頁面沒有跳轉,這就是所謂的非同步載入。有些網頁是需要手動點擊「載入更多的」,原理都是一樣的)
- 獲取非同步載入的請求
- 打開F12
- 拖動到頁面最下面(有些網站是點擊載入更多)
- 注意 我有勾選"xhr"
- 可以看到每次頁面到最底部,都會發送一個請求。這個請求就是所謂的非同步載入請求。
- 可以看到請求的參數如下:j0ga0has:max:1062527343
limit:20
wfl:1
"j0ga0has"第一個參數不知道是怎麼回事,先放著"max"是 最上面的pin_idlimit 是每次返回的條數有人可能會問,你怎麼知道這個參數是幹嘛的?其實都是試出來的(或者說看出來的)
- 如下圖的pin_id,可以發現下一條請求的max就是上一條請求獲取到的最後一個pin_id
- 我們查看返回值,發現竟然是json格式的,這樣的話,都不需要我們解析了,那我們找找我們需要的鏈接地址在哪。
- 發現鏈接地址就是由pin_id拼接而成的,所以我們只要獲取到這個pin_id就行。
- 用程序來實現:
- url = http://huaban.com/favorite/beauty/params = { j0ga0hbi:, max:1062161596, limit:100,
wfl:1
}z1 = requests.get(url=url,params=params,headers=headers)print z1.status_code# 返回200print z1.json()# 報錯
然後發現竟然報錯了。。。
為什麼呢?我們查看請求的時候就是jsno格式的啊 我們列印下源代碼看看你會看到竟然是"<!DOCTYPE html><html "這樣的,但是我們上面查看請求的時候,明明是如下圖這樣的啊
那麼到底是哪裡出了問題呢?
我們再次查看之前我們看到的非同步請求可以發現它有幾個 特別的請求頭指定了格式為json ,那麼我們加上去看看呢Accept:application/jsonX-Request:JSONX-Requested-With:XMLHttpRequest- 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為空為止
- 在上面我們已經獲取到了所有的詳情頁的地址,那麼我們現在只要獲取到圖片鏈接就行
- 隨便打開一個詳情頁花瓣
- 查看圖片地址
- 複製圖片地址到網頁源代碼裡面找找看
- 全部複製,發現沒有找到
- 那麼我們複製一部分
- 可以看到已經找到了,這邊你也可以用正則表達式,來匹配所有的地址,但是太麻煩了,我們可以用js2xml
- 看代碼.
- 就這樣,我們已經把圖片地址獲取到了,只需要拼接下即可(記得去下重)
最後再次總結一下
看完本篇文章後,你應該要
- 能知道如何抓取非同步載入的請求
- 了解js2xml的用法
- 了解headers的用法
最後代碼都在 kimg1234/pachong
填一個坑,我在爬蟲入門到精通-網頁的下載 - 知乎專欄留下以下問題
@王東堯
其實解決方法也挺簡單的,就是把content-type這一行注釋掉。
那麼為什麼注釋掉就可以了呢?請仔細研究研究http協議。。。
歡迎關注本人的微信公眾號獲取更多Python爬蟲相關的內容
(可以直接搜索「Python爬蟲分享」)
推薦閱讀:
※Flask框架從入門到實戰
※PyQGIS開發 -- 地圖控制項交互
※一個網站用兩種或以上的後端編程語言會出現什麼情況?為什麼?
※[15] Python循環語句(二)
※如何用Python和機器學習炒股賺錢?