banner
bladedragon

bladedragon

scrapy爬蟲入門

現在說到爬蟲,大家都會或多或少地將 python 和爬蟲聯繫在一起,歸根到底,是因為 python 豐富的生態和靈活簡單的語法。同時基於 python 存在有幾個強大的爬蟲框架,極大地降低了爬蟲的難度,提高了編寫程序的效率。最近我也體驗了一下 scrapy,算是做一個入門的記錄吧

簡介#

Scrapy 是一個用於爬網網站和提取結構化數據的應用程序框架,可用於各種有用的應用程序,例如數據挖掘,信息處理或歷史檔案。

—— 翻譯自官網

推薦直接查看官網,裡面甚至有完整的 scrapy 教程,簡直是深入學習框架的必備選擇!

安裝#

在安裝 scrapy 框架的時候,網上有許多方法,針對 python 的環境不同,可能存在奇奇怪怪的錯誤,這裡我基於 python3 環境,預裝了 pip,親測一遍過

預裝環境

Windows 10 + python 3.7.0 + pip20.1 +virtualenv

同時請開啟一個新的 virtual 環境來保證不會出現其他包的依賴衝突

前期安裝組件

  • lxml
  • pyOpenSSL
  • Twisted
  • PyWin32

安裝 lxml

直接pip安裝即可。這是python一個HTMLXML解析庫,即使不用框架也是經常使用的

pip3 install lxml

安裝 PyWin32

官网下载对应版本的安装包双击安装即可 [pywin32]([https://sourceforge.net/projects/pywin32/files/pywin32/Build%20221/](https://sourceforge.net/projects/pywin32/files/pywin32/Build 221/))

安裝剩餘組件

這裡首先要介紹wheel

wheelpython的一個打包格式,以前python主流的打包格式是.egg文件,但現在*.whl文件也變得流行起來。

wheel其實上是 python 上一種壓縮打包的組件,有點類似於 zip 之類的,但在本文中你只要知道通過 wheel 文件格式文件你可以快速將一個庫安裝到你的python環境中

安裝其實也很簡單

pip3 install wheel

這樣你的python環境就支持.whl文件格式的安裝啦

接下來的步驟就是到各個官網上下載各組件的whl格式,注意要和你的 python 環境匹配

安裝

pip3 install pyOpenSSL-19.1.0-py2.py3-none-any.whl 
  • Twisted注意要和你的 python 版本對應

像我的環境就是

pip3 install Twisted-20.3.0-cp37-cp37m-win_amd64.whl

安裝 scrapy

所有依賴包安裝成功後直接 pip 安裝 scrapy 就不會有問題啦

pip3 install Scrapy

組件介紹#

首先創建項目

在你想要放置爬蟲項目的文件夾運行,xxx 就是你的項目名

scrapy startproject xxx

順便記錄一下一些基本的操作

  • 創建項目:scrapy startproject xxx
  • 進入項目:cd xxx #進入某個文件夾下
  • 創建爬蟲:scrapy genspider xxx(爬蟲名) xxx.com (爬取域)
  • 生成文件:scrapy crawl xxx -o xxx.json (生成某種類型的文件)
  • 運行爬蟲:scrapy crawl XXX
  • 列出所有爬蟲:scrapy list
  • 獲得配置信息:scrapy settings [options]

創建完成後你可以看到文件夾下多了這些內容

image

讓我們一個個介紹這些組件(spider_demo是你的爬蟲項目名)

  • scrapy.cfg: 項目的配置文件 (在項目文件夾的平行目錄下)
  • spider_demo/spiders/: 放置spider代碼的目錄. (放爬蟲的地方)也是你放爬蟲具體邏輯的地方
  • spider_demo/items.py: 項目中的item文件.(創建容器的地方,也是定義最終爬蟲得到的結果格式的地方)
  • spider_demo/pipelines.py: 項目中的pipelines文件.(實現數據的清洗、存儲和驗證)
  • spider_demo/settings.py: 項目的設置文件.(爬蟲的具體配置,包括啟動某個中間件,啟動關閉某個功能等)
  • spider_demo/middlewares.py: 定義項目的下載器中間件和爬蟲中間件

感覺是不是還有點蒙圈?接下來簡單介紹一下scrapy運行的原理,這樣相信就能更理解這些組件的作用了


官網的流程圖

崎 architecture 的建築

Scrapy是由執行引擎控制執行的

  1. Spider發起請求給Engine
  2. Engine安排請求Scheduler和接受下一個爬取請求
  3. Scheduler返回下一個請求
  4. Engine將請求通過Downloader Middlewares發送給Downloader
  5. Downloader爬取網頁並將返回結果通過Downloader Middlewares發送回Engine
  6. 引擎接受響應並通過Spider Middleware轉發給Spider處理
  7. Spider parse()方法對獲取到的response進行處理,解析出items或者請求,將解析出來的items或請求,返回給Engine
  8. Engineitems 發送到Item Pipline, 將請求發送到Scheduler
  9. 重複步驟 1 直到沒有新的請求

總結一下上面步驟出現的組件

组件名组件功能
Engine框架核心,負責整體的數據和信號的調度框架實現
Scheduler一個存放請求的隊列框架實現
Downloader執行具體下載任務的單元框架實現
Spider處理下載得到的響應結果,提取需要的是數據(具體的業務邏輯)自己實現
Item Pipline處理最終得到的數據,如進行持久化操作自己實現
Downloader MIddlewares在正式進行下載任務之前,可以進行一些自定義處理。比如設置請求頭,設置代理自己實現
Spider Midderwares自定義請求和過濾響應自己實現

相信這一套組合拳下來應該能對這個框架有了基本的認識,接下來就通過實戰來強化一下記憶吧

簡單應用#

這次是根據網上通過爬取貓眼電影的排行榜做的一個demo,以後有時間再換一個更加複雜的demo

實現目標是爬取電影排行榜上的片名、分數和排名,同時將結果以json的格式保存在一個.json文件中

image

首先你要確定你需要爬取哪些數據,將你需要的數據記錄到容器中,在item.py中進行編寫:

import scrapy

#這裡我們需要排名、標題、收藏人數、上映時間和分數
class SpiderDemoItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    index = scrapy.Field()
    title = scrapy.Field()
    star = scrapy.Field()
    releasetime = scrapy.Field()
    score = scrapy.Field()

接下來在Spiders文件夾下新建一個爬蟲文件,例如我新建了一個MoyanSpider.py文件

import scrapy
from spider_demo.items import SpiderDemoItem


class MaoyanSpider(scrapy.Spider):
    #這是爬蟲啟動的名稱,之後啟動爬蟲就需要用到這個名稱
    name = "maoyan"
    #可以爬取的域名可選列表
    allowed_domains = ["maoyan.com"]
    #目標爬取的網址
    start_urls = [
        "http://maoyan.com/board/7/", 
        "http://maoyan.com/board/4/",
    ]

    #處理已經下載的頁面
    def parse(self, response):
        
        dl = response.css(".board-wrapper dd")
        #通過解析得到具體的數據存到容器中
        for dd in dl:
            item = SpiderDemoItem()
            item["index"] = dd.css(".board-index::text").extract_first()
            item["title"] = dd.css(".name a::text").extract_first()
            item["star"] = dd.css(".star::text").extract_first()
            item["releasetime"] = dd.css(".releasetime::text").extract_first()
            score = dd.css('.integer::text').extract_first()
            if score is not None:
                item["score"] = score  + dd.css('.fraction::text').extract_first()
            else:
                item["score"] = 0

            #通過yield將結果返回
            yield item

這裡提一下,scrapy支持各種類型的解析,你可以使用 python 常見的三大解析庫進行解析,但框架也提供了一種自己的解析方式(Selector

  • 選擇器
  • Xpath

這裡不詳細敘述,以後有時間再細聊

同時我們需要在setting,py中對配置稍微進行修改 (setting.py中有許多默認配置,這裡只展示修改的部分)

#如果沒有自動生成UA,就需要手動定義,但是每次爬取都是同樣的UA容易出現驗證操作,因此後面還會介紹一種隨機生成UA的方法
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'

#允許機器人協議,關於機器人協議的具體內容可以自行上網查找
ROBOTSTXT_OBEY = False

這樣,基本就完成了一個簡單的爬蟲,只需要執行scrapy crawl maoyan(最後一個是你的爬蟲名)

image

之後還有兩個要點,一個是項目的持久化,一個是隨機化User-Agent

先看持久化,這裡簡單起見就示範將爬蟲數據以json格式導出

這裡需要修改pipline.py,至於為什麼,相信看了之前的組件介紹應該能明白

import json
import codecs


class SpiderDemoPipeline:
    def process_item(self, item, spider):
        return item


class JsonPipline(object):
    def __init__(self):
        print("打開文件,準備寫入....")
        self.file = codecs.open("maoyan.json", "wb", encoding='utf-8')

    def process_item(self, item, spider):
        print("準備寫入...")
        line = json.dumps(dict(item), ensure_ascii=False) + "\n"
        self.file.write(line)
        return item

    def close_spider(self, spider):
        print("寫入完畢,關閉文件")
        self.file.close

然後在setting.py中開啟自定義的pipline

ITEM_PIPELINES = {
    #    'spider_demo.pipelines.SpiderDemoPipeline': 300,
    'spider_demo.pipelines.JsonPipline': 200
}

至於隨機化 UA,先說明添加 UA 的原理

scrapy首先會讀取setting.py裡面關於 UA 的設置,然後經過middleware,如果沒有進行自定義操作,就會將配置中的 UA 添加到請求頭中。因此,想要實現隨機化 UA, 實際上就可以在發起網頁請求之前,在Download Middleware上做文章。

這裡在middleware.py上做了修改,引入了第三方包fake_useragent

from scrapy import signals
import random
from fake_useragent import UserAgent


class RandomUserAgentMiddleware(object):
    # 隨機更換user-agent
    def __init__(self, crawler):
        super(RandomUserAgentMiddleware, self).__init__()
        self.ua = UserAgent()
        self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random")

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def process_request(self, request, spider):
        def get_ua():
            return getattr(self.ua, self.ua_type)

        request.headers.setdefault('User-Agent', get_ua())

同時在setting.py上做修改

DOWNLOADER_MIDDLEWARES = {
    'spider_demo.middlewares.SpiderDemoDownloaderMiddleware': 543,
     'spider_demo.middlewares.RandomUserAgentMiddleware': 400,
    'scrapy.downloadermiddleware.useragent.UserAgentMiddleware': None,
}

# 隨機選擇UA
#這個是自己設置的,依賴於fake-useragent
RANDOM_UA_TYPE = 'random'

至此,一個簡單的爬蟲應用就實現了!

可以看見UA發生了變化

image

同時生成了一個maoyan.json文件

json 文件

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。