使用 Scrapy 爬取去哪兒網景區信息

Scrapy 是一個使用 Python 語言開發,爲了爬取網站數據,提取結構性數據而編寫的應用框架,它用途普遍,好比:數據挖掘、監測和自動化測試。安裝使用終端命令 pip install Scrapy 便可。css

Scrapy 比較吸引人的地方是:咱們能夠根據需求對其進行修改,它提供了多種類型的爬蟲基類,如:BaseSpider、sitemap 爬蟲等,新版本提供了對 web2.0 爬蟲的支持。html

1 Scrapy 介紹

1.1 組成

  • Scrapy Engine(引擎):負責 Spider、ItemPipeline、Downloader、Scheduler 中間的通信,信號、數據傳遞等。python

  • Scheduler(調度器):負責接受引擎發送過來的 Request 請求,並按照必定的方式進行整理排列、入隊,當引擎須要時,交還給引擎。web

  • Downloader(下載器):負責下載 Scrapy Engine(引擎) 發送的全部 Requests 請求,並將其獲取到的 Responses 交還給 Scrapy Engine(引擎),由引擎交給 Spider 來處理。服務器

  • Spider(爬蟲):負責處理全部 Responses,從中解析提取數據,獲取 Item 字段須要的數據,並將須要跟進的 URL 提交給引擎,再次進入 Scheduler(調度器)。框架

  • Item Pipeline(管道):負責處理 Spider 中獲取到的 Item,並進行後期處理,如:詳細解析、過濾、存儲等。dom

  • Downloader Middlewares(下載中間件):一個能夠自定義擴展下載功能的組件,如:設置代理、設置請求頭等。scrapy

  • Spider Middlewares(Spider 中間件):一個能夠自定擴展和操做引擎和 Spider 中間通訊的功能組件,如:自定義 request 請求、過濾 response 等。ide

總的來講就是:SpiderItem Pipeline 須要咱們本身實現,Downloader MiddlewaresSpider Middlewares 咱們能夠根據需求自定義。測試

1.2 流程梳理

1)Spider 將須要發送請求的 URL 交給 Scrapy Engine 交給調度器;

2)Scrapy Engine 將請求 URL 轉給 Scheduler

3)Scheduler 對請求進行排序整理等處理後返回給 Scrapy Engine

4)Scrapy Engine 拿到請求後經過 Middlewares 發送給 Downloader

5)Downloader 向互聯網發送請求,在獲取到響應後,又通過 Middlewares 發送給 Scrapy Engine

6)Scrapy Engine 獲取到響應後,返回給 SpiderSpider 處理響應,並從中解析提取數據;

7)Spider 將解析的數據經 Scrapy Engine 交給 Item PipelineItem Pipeline 對數據進行後期處理;

8)提取 URL 從新經 Scrapy Engine 交給Scheduler 進行下一個循環,直到無 URL 請求結束。

1.3 Scrapy 去重機制

Scrapy 提供了對 request 的去重處理,去重類 RFPDupeFilterdupefilters.py 文件中,路徑爲:Python安裝目錄\Lib\site-packages\scrapy ,該類裏面有個方法 request_seen 方法,源碼以下:

def request_seen(self, request):
    # 計算 request 的指紋
    fp = self.request_fingerprint(request)
    # 判斷指紋是否已經存在
    if fp in self.fingerprints:
        # 存在
        return True
    # 不存在,加入到指紋集合中
    self.fingerprints.add(fp)
    if self.file:
        self.file.write(fp + os.linesep)

它在 Scheduler 接受請求的時候被調用,進而調用 request_fingerprint 方法(爲 request 生成一個指紋),源碼以下:

def request_fingerprint(request, include_headers=None):
    if include_headers:
        include_headers = tuple(to_bytes(h.lower())
                                 for h in sorted(include_headers))
    cache = _fingerprint_cache.setdefault(request, {})
    if include_headers not in cache:
        fp = hashlib.sha1()
        fp.update(to_bytes(request.method))
        fp.update(to_bytes(canonicalize_url(request.url)))
        fp.update(request.body or b'')
        if include_headers:
            for hdr in include_headers:
                if hdr in request.headers:
                    fp.update(hdr)
                    for v in request.headers.getlist(hdr):
                        fp.update(v)
        cache[include_headers] = fp.hexdigest()
    return cache[include_headers]

在上面代碼中咱們能夠看到

fp = hashlib.sha1()
...
cache[include_headers] = fp.hexdigest()

它爲每個傳遞過來的 URL 生成一個固定長度的惟一的哈希值。再看一下 __init__ 方法,源碼以下:

def __init__(self, path=None, debug=False):
    self.file = None
    self.fingerprints = set()
    self.logdupes = True
    self.debug = debug
    self.logger = logging.getLogger(__name__)
    if path:
        self.file = open(os.path.join(path, 'requests.seen'), 'a+')
        self.file.seek(0)
        self.fingerprints.update(x.rstrip() for x in self.file)

咱們能夠看到裏面有 self.fingerprints = set() 這段代碼,就是經過 set 集合的特色(set 不容許有重複值)進行去重。

去重經過 dont_filter 參數設置,如圖所示

dont_filterFalse 開啓去重,爲 True 不去重。

2 實現過程

製做 Scrapy 爬蟲需以下四步:

  • 建立項目 :建立一個爬蟲項目
  • 明確目標 :明確你想要抓取的目標(編寫 items.py)
  • 製做爬蟲 :製做爬蟲開始爬取網頁(編寫 xxspider.py)
  • 存儲內容 :設計管道存儲爬取內容(編寫pipelines.py)

咱們以爬取去哪兒網北京景區信息爲例,如圖所示:

2.1 建立項目

在咱們須要新建項目的目錄,使用終端命令 scrapy startproject 項目名 建立項目,我建立的目錄結構如圖所示:

  • spiders 存放爬蟲的文件
  • items.py 定義數據類型
  • middleware.py 存放中間件
  • piplines.py 存放數據的有關操做
  • settings.py 配置文件
  • scrapy.cfg 總的控制文件

2.2 定義 Item

Item 是保存爬取數據的容器,使用的方法和字典差很少。咱們計劃提取的信息包括:area(區域)、sight(景點)、level(等級)、price(價格),在 items.py 定義信息,源碼以下:

import scrapy

class TicketspiderItem(scrapy.Item):
    area = scrapy.Field()
    sight = scrapy.Field()
    level = scrapy.Field()
    price = scrapy.Field()
    pass

2.3 爬蟲實現

在 spiders 目錄下使用終端命令 scrapy genspider 文件名 要爬取的網址 建立爬蟲文件,而後對其修改及編寫爬取的具體實現,源碼以下:

import scrapy
from ticketSpider.items import TicketspiderItem

class QunarSpider(scrapy.Spider):
    name = 'qunar'
    allowed_domains = ['piao.qunar.com']
    start_urls = ['https://piao.qunar.com/ticket/list.htm?keyword=%E5%8C%97%E4%BA%AC&region=&from=mpl_search_suggest']

    def parse(self, response):
        sight_items = response.css('#search-list .sight_item')
        for sight_item in sight_items:
            item = TicketspiderItem()
            item['area'] = sight_item.css('::attr(data-districts)').extract_first()
            item['sight'] = sight_item.css('::attr(data-sight-name)').extract_first()
            item['level'] = sight_item.css('.level::text').extract_first()
            item['price'] = sight_item.css('.sight_item_price em::text').extract_first()
            yield item
        # 翻頁
        next_url = response.css('.next::attr(href)').extract_first()
        if next_url:
            next_url = "https://piao.qunar.com" + next_url
            yield scrapy.Request(
                next_url,
                callback=self.parse
            )

簡單介紹一下:

  • name:爬蟲名
  • allowed_domains:容許爬取的域名
  • atart_urls:爬取網站初始請求的 url(可定義多個)
  • parse 方法:解析網頁的方法
  • response 參數:請求網頁後返回的內容

yield

在上面的代碼中咱們看到有個 yield,簡單說一下,yield 是一個關鍵字,做用和 return 差很少,差異在於 yield 返回的是一個生成器(在 Python 中,一邊循環一邊計算的機制,稱爲生成器),它的做用是:有利於減少服務器資源,在列表中全部數據存入內存,而生成器至關於一種方法而不是具體的信息,佔用內存小。

爬蟲假裝

一般須要對爬蟲進行一些假裝,關於爬蟲假裝可經過【Python 爬蟲(一):爬蟲假裝】作一下簡單瞭解,這裏咱們使用一個最簡單的方法處理一下。

  • 使用終端命令 pip install scrapy-fake-useragent 安裝
  • 在 settings.py 文件中添加以下代碼:
DOWNLOADER_MIDDLEWARES = {
    # 關閉默認方法
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None, 
    # 開啓
    'scrapy_fake_useragent.middleware.RandomUserAgentMiddleware': 400, 
}

2.4 保存數據

咱們將數據保存到本地的 csv 文件中,csv 具體操做能夠參考:CSV 文件讀寫,下面看一下具體實現。

首先,在 pipelines.py 中編寫實現,源碼以下:

import csv

class TicketspiderPipeline(object):
    def __init__(self):
        self.f = open('ticker.csv', 'w', encoding='utf-8', newline='')
        self.fieldnames = ['area', 'sight', 'level', 'price']
        self.writer = csv.DictWriter(self.f, fieldnames=self.fieldnames)
        self.writer.writeheader()
    def process_item(self, item, spider):
        self.writer.writerow(item)
        return item

    def close(self, spider):
        self.f.close()

而後,將 settings.py 文件中以下代碼:

ITEM_PIPELINES = {
    'ticketSpider.pipelines.TicketspiderPipeline': 300,
}

放開便可。

2.5 運行

咱們在 settings.py 的同級目錄下建立運行文件,名字自定義,放入以下代碼:

from scrapy.cmdline import execute
execute('scrapy crawl 爬蟲名'.split())

這個爬蟲名就是咱們以前在爬蟲文件中的 name 屬性值,最後在 Pycharm 運行該文件便可。

參考:

http://www.scrapyd.cn/doc/
https://www.liaoxuefeng.com/wiki/897692888725344/923029685138624

完整代碼請關注文末公衆號,後臺回覆qs獲取。

相關文章
相關標籤/搜索