使用 Scrapy 爬取去哪兒網景區資訊
Scrapy 是一個使用 Python 語言開發,為了爬取網站資料,提取結構性資料而編寫的應用框架,它用途廣泛,比如:資料探勘、監測和自動化測試。安裝使用終端命令 pip install Scrapy
即可。
Scrapy 比較吸引人的地方是:我們可以根據需求對其進行修改,它提供了多種型別的爬蟲基類,如:BaseSpider、sitemap 爬蟲等,新版本提供了對 web2.0 爬蟲的支援。
1 Scrapy 介紹
1.1 組成
Scrapy Engine(引擎):負責 Spider、ItemPipeline、Downloader、Scheduler 中間的通訊,訊號、資料傳遞等。
Scheduler(排程器):負責接受引擎傳送過來的 Request 請求,並按照一定的方式進行整理排列、入隊,當引擎需要時,交還給引擎。
Downloader(下載器):負責下載 Scrapy Engine(引擎) 傳送的所有 Requests 請求,並將其獲取到的 Responses 交還給 Scrapy Engine(引擎),由引擎交給 Spider 來處理。
Spider(爬蟲):負責處理所有 Responses,從中解析提取資料,獲取 Item 欄位需要的資料,並將需要跟進的 URL 提交給引擎,再次進入 Scheduler(排程器)。
Item Pipeline(管道):負責處理 Spider 中獲取到的 Item,並進行後期處理,如:詳細解析、過濾、儲存等。
Downloader Middlewares(下載中介軟體):一個可以自定義擴充套件下載功能的元件,如:設定代理、設定請求頭等。
Spider Middlewares(Spider 中介軟體):一個可以自定擴充套件和操作引擎和 Spider 中間通訊的功能元件,如:自定義 request 請求、過濾 response 等。
總的來說就是:Spider
和 Item Pipeline
需要我們自己實現,Downloader Middlewares
和 Spider 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
獲取到響應後,返回給 Spider
,Spider
處理響應,並從中解析提取資料;
7)Spider
將解析的資料經 Scrapy Engine
交給 Item Pipeline
, Item Pipeline
對資料進行後期處理;
8)提取 URL 重新經 Scrapy Engine
交給Scheduler
進行下一個迴圈,直到無 URL 請求結束。
1.3 Scrapy 去重機制
Scrapy 提供了對 request 的去重處理,去重類 RFPDupeFilter
在 dupefilters.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_filter
為 False
開啟去重,為 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®ion=&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 獲取