1. 程式人生 > >python爬蟲實戰:利用scrapy,短短50行程式碼下載整站短視訊

python爬蟲實戰:利用scrapy,短短50行程式碼下載整站短視訊

近日,有朋友向我求助一件小事兒,他在一個短視訊app上看到一個好玩兒的段子,想下載下來,可死活找不到下載的方法。這忙我得幫,少不得就抓包分析了一下這個app,找到了視訊的下載連結,幫他解決了這個小問題。

因為這個事兒,勾起了我另一個念頭,這不最近一直想把python爬蟲方面的知識梳理梳理嗎,乾脆藉機行事,正湊著短視訊火熱的勢頭,做一個短視訊的爬蟲好了,中間用到什麼知識就理一理。

我喜歡把事情說得很直白,如果恰好有初入門的朋友想了解爬蟲的技術,可以將就看看,或許對你的認識會有提升。如果有高手路過,最好能指點一二,本人不勝感激。

一、撕開爬蟲的面紗——爬蟲是什麼,它能做什麼

爬蟲是什麼

爬蟲就是一段能夠從網際網路上高效獲取資料的程式。

我們每天都在從網際網路上獲取資料。當開啟瀏覽器訪問百度的時候,我們就從百度的伺服器獲取資料,當拿起手機線上聽歌的時候,我們就從某個app的伺服器上獲取資料。簡單的歸納,這些過程都可以描述為:我們提交一個Request請求,伺服器會返回一個Response資料,應用根據Response來渲染頁面,給我們展示資料結果。

爬蟲最核心的也是這個過程,提交Requests——〉接受Response。就這樣,很簡單,當我們在瀏覽器裡開啟一個頁面,看到頁面內容的時候,我們就可以說這個頁面被我們採集到了。

只不過當我們真正進行資料爬取時,一般會需要採集大量的頁面,這就需要提交許多的Requests,需要接受許多的Response。數量大了之後,就會涉及到一些比較複雜的處理,比如併發的,比如請求序列,比如去重,比如連結跟蹤,比如資料儲存,等等。於是,隨著問題的延伸和擴充套件,爬蟲就成為了一個相對獨立的技術門類。

但它的本質就是對一系列網路請求和網路響應的處理。

爬蟲能做什麼

爬蟲的作用和目的只有一個,獲取網路資料。我們知道,網際網路是個資料的海洋,大量的資訊漂浮在其中,想把這些資源收歸己用,爬蟲是最常用的方式。特別是最近幾年大樹據挖掘技術和機器學習以及知識圖譜等技術的興盛,更是對資料提出了更大的需求。另外也有很多網際網路創業公司,在起步初期自身積累資料較少的時候,也會通過爬蟲快速獲取資料起步。

二、python爬蟲框架scrapy——爬蟲開發的利器

如果你剛剛接觸爬蟲的概念,我建議你暫時不要使用scrapy框架。或者更寬泛的說,如果你剛剛接觸某一個技術門類,我都不建議你直接使用框架,因為框架是對許多基礎技術細節的高階抽象,如果你不瞭解底層實現原理就直接用框架多半會讓你雲裡霧裡迷迷糊糊。

在入門爬蟲之初,看scrapy的文件,你會覺得“太複雜了”。當你使用urllib或者Requests開發一個python的爬蟲指令碼,並逐個去解決了請求頭封裝、訪問併發、佇列去重、資料清洗等等問題之後,再回過頭來學習scrapy,你會覺得它如此簡潔優美,它能節省你大量的時間,它會為一些常見的問題提供成熟的解決方案。

scrapy資料流程圖

這張圖是對scrapy框架的經典描述,一時看不懂沒有關係,用一段時間再回來看。或者把本文讀完再回來看。

在一些書上會把爬蟲的基本抓取流程概括為UR2IM,意思是資料爬取的過程是圍繞URL、Request(請求)、Response(響應)、Item(資料項)、MoreUrl(更多的Url)展開的。上圖的綠色箭頭 體現的正是這幾個要素的流轉過程。圖中涉及的四個模組正是用於處理這幾類物件的:

  • Spider模組:負責生成Request物件、解析Response物件、輸出Item物件
  • Scheduler模組:負責對Request物件的排程
  • Downloader模組:負責傳送Request請求,接收Response響應
  • ItemPipleline模組:負責資料的處理
  • scrapy Engine負責模組間的通訊

各個模組和scrapy引擎之間可以新增一層或多層中介軟體,負責對出入該模組的UR2IM物件進行處理。

scrapy的安裝

參考官方文件,不再贅述。官方文件:https://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/install.html

三、scrapy實戰:50行程式碼爬取全站短視訊

python的優雅之處在於能夠讓開發者專注於業務邏輯,花更少的時間在枯燥的程式碼編寫除錯上。scrapy無疑完美詮釋了這一精神。

開發爬蟲的一般步驟是:

  • 確定要爬取的資料(item)
  • 找到資料所在頁面的url
  • 找到頁面間的連結關係,確定如何跟蹤(follow)頁面

那麼,我們一步一步來。

既然是使用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,
}

 四、執行結果展示

視訊檔案:

五、最後

今天講了爬蟲的一些基礎的概念,不深也不透,主要是通過一個案例給大家一個直觀的認識。一些細節上的點後續會專門開文細講,喜歡的朋友可以關注,一起探討。

本文所公佈程式碼僅作為學習交流之用,請勿用於非法用途,負責後果自負。