近日,有朋友向我求助一件小事兒,他在一個短視頻app上看到一個好玩兒的段子,想下載下來,可死活找不到下載的方法。這忙我得幫,少不得就抓包分析了一下這個app,找到了視頻的下載連接,幫他解決了這個小問題。html
由於這個事兒,勾起了我另外一個念頭,這不最近一直想把python爬蟲方面的知識梳理梳理嗎,乾脆藉機行事,正湊着短視頻火熱的勢頭,作一個短視頻的爬蟲好了,中間用到什麼知識就理一理。python
我喜歡把事情說得很直白,若是剛好有初入門的朋友想了解爬蟲的技術,能夠將就看看,或許對你的認識會有提高。若是有高手路過,最好能指點一二,本人不勝感激。mongodb
爬蟲就是一段可以從互聯網上高效獲取數據的程序。數據庫
咱們天天都在從互聯網上獲取數據。當打開瀏覽器訪問百度的時候,咱們就從百度的服務器獲取數據,當拿起手機在線聽歌的時候,咱們就從某個app的服務器上獲取數據。簡單的概括,這些過程均可以描述爲:咱們提交一個Request請求,服務器會返回一個Response數據,應用根據Response來渲染頁面,給咱們展現數據結果。瀏覽器
爬蟲最核心的也是這個過程,提交Requests——〉接受Response。就這樣,很簡單,當咱們在瀏覽器裏打開一個頁面,看到頁面內容的時候,咱們就能夠說這個頁面被咱們採集到了。bash
只不過當咱們真正進行數據爬取時,通常會須要採集大量的頁面,這就須要提交許多的Requests,須要接受許多的Response。數量大了以後,就會涉及到一些比較複雜的處理,好比並發的,好比請求序列,好比去重,好比連接跟蹤,好比數據存儲,等等。因而,隨着問題的延伸和擴展,爬蟲就成爲了一個相對獨立的技術門類。服務器
但它的本質就是對一系列網絡請求和網絡響應的處理。網絡
爬蟲的做用和目的只有一個,獲取網絡數據。咱們知道,互聯網是個數據的海洋,大量的信息漂浮在其中,想把這些資源收歸己用,爬蟲是最經常使用的方式。特別是最近幾年大樹據挖掘技術和機器學習以及知識圖譜等技術的興盛,更是對數據提出了更大的需求。另外也有不少互聯網創業公司,在起步初期自身積累數據較少的時候,也會經過爬蟲快速獲取數據起步。併發
若是你剛剛接觸爬蟲的概念,我建議你暫時不要使用scrapy框架。或者更寬泛的說,若是你剛剛接觸某一個技術門類,我都不建議你直接使用框架,由於框架是對許多基礎技術細節的高級抽象,若是你不瞭解底層實現原理就直接用框架多半會讓你雲裏霧裏迷迷糊糊。app
在入門爬蟲之初,看scrapy的文檔,你會以爲「太複雜了」。當你使用urllib或者Requests開發一個python的爬蟲腳本,並逐個去解決了請求頭封裝、訪問併發、隊列去重、數據清洗等等問題以後,再回過頭來學習scrapy,你會以爲它如此簡潔優美,它能節省你大量的時間,它會爲一些常見的問題提供成熟的解決方案。
這張圖是對scrapy框架的經典描述,一時看不懂沒有關係,用一段時間再回來看。或者把本文讀完再回來看。
在一些書上會把爬蟲的基本抓取流程歸納爲UR2IM,意思是數據爬取的過程是圍繞URL、Request(請求)、Response(響應)、Item(數據項)、MoreUrl(更多的Url)展開的。上圖的綠色箭頭 體現的正是這幾個要素的流轉過程。圖中涉及的四個模塊正是用於處理這幾類對象的:
各個模塊和scrapy引擎之間能夠添加一層或多層中間件,負責對出入該模塊的UR2IM對象進行處理。
參考官方文檔,再也不贅述。官方文檔:https://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/install.html
python的優雅之處在於可以讓開發者專一於業務邏輯,花更少的時間在枯燥的代碼編寫調試上。scrapy無疑完美詮釋了這一精神。
開發爬蟲的通常步驟是:
那麼,咱們一步一步來。
既然是使用scrapy框架,咱們先建立項目:
scrapy startproject DFVideo
緊接着,咱們建立一個爬蟲:
scrapy genspider -t crawl DfVideoSpider eastday.com
這是咱們發如今當前目錄下已經自動生成了一個目錄:DFVideo
目錄下包括如圖文件:
spiders文件夾下,自動生成了名爲DfVideoSpider.py的文件。
爬蟲項目建立以後,咱們來肯定須要爬取的數據。在items.py中編輯:
import scrapy class DfvideoItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() video_url = scrapy.Field()#視頻源url video_title = scrapy.Field()#視頻標題 video_local_path = scrapy.Field()#視頻本地存儲路徑
接下來,咱們須要肯定視頻源的url,這是很關鍵的一步。
如今許多的視頻播放頁面是把視頻連接隱藏起來的,這就使得你們沒法經過右鍵另存爲,防止了視頻別隨意下載。
可是隻要視頻在頁面上播放了,那麼必然是要和視頻源產生數據交互的,因此只要稍微抓下包就可以發現玄機。
這裏咱們使用fiddler抓包分析。
發現其視頻播放頁的連接相似於:video.eastday.com/a/180926221513827264568.html?index3lbt
視頻源的數據連接相似於:mvpc.eastday.com/vyule/20180415/20180415213714776507147_1_06400360.mp4
有了這兩個連接,工做就完成了大半:
在DfVideoSpider.py中編輯
# -*- coding: utf-8 -*- import scrapy from scrapy.loader import ItemLoader from scrapy.loader.processors import MapCompose,Join from DFVideo.items import DfvideoItem from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule import time from os import path import os class DfvideospiderSpider(CrawlSpider): name = 'DfVideoSpider' allowed_domains = ['eastday.com'] start_urls = ['http://video.eastday.com/'] rules = ( Rule(LinkExtractor(allow=r'video.eastday.com/a/\d+.html'), callback='parse_item', follow=True), ) def parse_item(self, response): item = DfvideoItem() try: item["video_url"] = response.xpath('//input[@id="mp4Source"]/@value').extract()[0] item["video_title"] = response.xpath('//meta[@name="description"]/@content').extract()[0] #print(item) item["video_url"] = 'http:' + item['video_url'] yield scrapy.Request(url=item['video_url'], meta=item, callback=self.parse_video) except: pass def parse_video(self, response): i = response.meta file_name = Join()([i['video_title'], '.mp4']) base_dir = path.join(path.curdir, 'VideoDownload') video_local_path = path.join(base_dir, file_name.replace('?', '')) i['video_local_path'] = video_local_path if not os.path.exists(base_dir): os.mkdir(base_dir) with open(video_local_path, "wb") as f: f.write(response.body) yield i
至此,一個簡單但強大的爬蟲便完成了。
若是你但願將視頻的附加數據保存在數據庫,能夠在pipeline.py中進行相應的操做,好比存入mongodb中:
from scrapy import log import pymongo class DfvideoPipeline(object): def __init__(self): self.mongodb = pymongo.MongoClient(host='127.0.0.1', port=27017) self.db = self.mongodb["DongFang"] self.feed_set = self.db["video"] # self.comment_set=self.db[comment_set] self.feed_set.create_index("video_title", unique=1) # self.comment_set.create_index(comment_index,unique=1) def process_item(self, item, spider): try: self.feed_set.update({"video_title": item["video_title"]}, item, upsert=True) except: log.msg(message="dup key: {}".format(item["video_title"]), level=log.INFO) return item def on_close(self): self.mongodb.close()
固然,你須要在setting.py中將pipelines打開:
ITEM_PIPELINES = { 'TouTiaoVideo.pipelines.ToutiaovideoPipeline': 300, }
視頻文件:
今天講了爬蟲的一些基礎的概念,不深也不透,主要是經過一個案例給你們一個直觀的認識。一些細節上的點後續會專門開文細講,喜歡的朋友能夠關注,一塊兒探討。
本文所公佈代碼僅做爲學習交流之用,請勿用於非法用途,負責後果自負。