578
Python
Python爬取B站,鹹魚的正確 GET 姿勢
分析目標
我們選定 B站的 動畫區 進行測試,打開後我們發現有好多好多圖....
但當我們使用 F12 查看這些圖片的時候,發現並沒有圖片的地址...
這就是目前大多網站使用的 Ajax 技術動態加載數據的鍋,可遇到這種情況這麼辦呢?別急別急,我們知道這些圖片的地址一定是需要加載的,而目前常見WEB傳輸數據的基本就是方式 XML 和 Json (其實是我就知道這兩種...),那好我們去看看請求的 XML 和 Json 文件。
以下省略查找過程....
這個是 MAD·AMV 最新動態的 Json 文件,利用上麵相同的方法,我們找到 3D區、短片·配音區、綜合區 以及 右邊排行部分 的相應 json 地址。
找到 Json 數據後,我們需要開始分析如何才能從中拿到 圖片地址了
好在 Chrome 瀏覽器提供了一個 Preview 功能,自動幫我們整理好 數據,如下
這樣就很清晰啦,我們隻需要一層層解析拿到 pic 即可。於是我們這樣寫:
我們利用 requests 內置的 json 解碼器,很不幸,報錯:
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
它提示說:解碼 Json 數據的時候出了問題,可能是在 第一行 第一列,咦?好奇怪,剛才不是用瀏覽器看過結構了嗎,沒毛病啊,怎麼還在報錯:Σ(  ̄ ̄||)
別急別急,我們先看看原始的 Json 數據長啥樣?用 瀏覽器打開上麵的 json 鏈接就可以了。
(/TДT)/ 前麵的那些字母是幹嘛的呀,為什麼還有括號啊!
所以我們知道了 Json 解析錯誤 的原因啦:後麵在處理的時候把前麵的這部分刪掉就好啦,另外我們也發現了archives這個關鍵字,我們在上一張圖看見過的哦,有印象嗎?啊,你說沒有呀,沒關係,這裏你隻需要記著它是一個 List 的數據類型就好了呀!
開碼開碼
先看看 解析部分怎麼寫:
獲取 Json 數據
解析 Json 數據,並保存其中的 所有的圖片鏈接
注意去重
importjson
importrequests
importre
classBiliBiliSpider(object):def__init__(self):self._images = [] self._oldImages = []# 保存已經獲取過的 圖片地址
def_getImages(self, url):content = requests.get(url).text dic = json.loads(re.match(r ^([a-zA-Z0-9_(]+)(.*)(\))$ , content).group(2)) data = dic[ data ]
# 判斷是 動態區 和 還是 評論區的數據if archives indata: final = data[ archives ]
else: final = data
foriinrange(len(final)): image = final[i][ pic ]
ifimagenotinself._oldImages:# 去重self._images.append(image) self._oldImages.append(image)
defgetResult(self, url):self._getImages(url) temp = self._images self._images = []
returntemp
OK,接下來寫下載器:
獲取需要下載的 圖片地址
進行下載
我這裏是用數字作為圖片的名字保存起來的,數字用一個 count.txt 文件保存,為了好看一點吧...
importrequests
classBiliBiliDownloader(object):
def__init__(self):
self._images = []
defaddNewImages(self, images):
forimageinimages:
self._images.append(image)
# 獲取後綴名defgetFinName(self, url):ifurl[-4:].find( . ) >-1: fin = url[-4:]
else: fin = url[-5:]
returnfin
defimageDownload(self):
withopen( count.txt , rb )asf: temp = int(f.read().decode( utf-8 ))
forurlinself._images: name = images/ + str(temp) + self.getFinName(url)print( Downloading {}... .format(name)) r = requests.get(url)
withopen(name, wb )asf: f.write(r.content) temp +=1self._images = []
withopen( count.txt , wb )asf: f.write(str(temp).encode( utf-8 ))
最後是調度器:
為了防止被當作是 惡意訪問 從而被封 IP,這裏我們選擇犧牲時間,取巧使用 sleep(x) ,讓其等待一段時間之後再發出請求。
fromBiliBiliSpiderimportBiliBiliSpider
fromBiliBiliDownloaderimportBiliBiliDownloader
importtime
classBiliBiliMain(object):def__init__(self):self._spider = BiliBiliSpider() self.downloader = BiliBiliDownloader()
defCrawl(self, url):images = self._spider.getResult(url) self.downloader.addNewImages(images) self.downloader.imageDownload()
if__name__ == __main__ :
# 添加相應位置的 json 文件json_url_MAD = https://xxx... json_url_MAD_rank = https://xxx... ... json_url_list = [] json_url_list.append(json_url_MAD) json_url_list.append(...) ...
bilibili = BiliBiliMain()
# 設置一個標誌位,循環爬取多少次之後自動退出。flag =1whileTrue:
forurlinjson_url_list: bilibili.Crawl(url) time.sleep(30)# 防止被當作是惡意請求。。。time.sleep(100)# 防止被當作是惡意請求。。。flag +=1ifflag >15:
break
運行效果:
總結:
你們可能會有疑問,你這個,根本沒有代理、沒有混淆IP防止反爬、也沒有模擬 Ajax 請求動態抓取雲雲~
那我可以很負責的告訴你,你!走錯地方了!你要找的技術貼出門右拐!( ̄へ ̄)
關於取巧
我們恰巧使用的是 B站的 Ajax 技術,隻要哪個視頻有了最新評論(或者是一下其它的條件),就會使用 Ajax 將最新的數據取出來。就像下麵這樣:
所以可能在訪問人數多的時候,更新越快,越有可能獲得更多不同的圖片啦!之後你就可以在吃飯的時候,把它掛起,然後吃飯回來就會發現有好多好多的圖片!(=・ω・=)
關於以後
之後會陸續的更新文章,希望能夠找到小夥伴一起學習呀
關注+轉發
感謝大家
最後更新:2017-10-08 22:39:28