Python 新手入門很多時候都會寫個爬蟲練手,本教程使用 Scrapy 框架,幫你簡單快速實現爬蟲,並將資料儲存至資料庫。在機器學習中資料探勘也是十分重要的,我的資料科學老師曾經說過,好演算法不如好資料。
介紹
Scrapy ,Python 開發的一個快速、高層次的螢幕抓取和 web 抓取框架,用於抓取 web 站點並從頁面中提取結構化的資料。檔案結構清晰,即使是小白也能夠快速上手,總之非常好用?。
XPath ,它是一種用來查詢 XML 文件中節點位置的語言。 XPath 基於 XML 的樹狀結構,有不同型別的節點,包括元素節點,屬性節點和文字節點,提供在資料結構樹中找尋節點的能力。
MySQL 是一種關聯式資料庫管理系統,它的優勢:它是免費的。作者是下載了 MAMP for Mac ,內嵌MySQL
和Apache
。
首先通過 Scrapy 爬取到網頁後, 通過 XPath 來定位指定元素,獲取到你想要的資料,得到資料之後可以將資料存入資料庫( MySQL )。簡單瞭解之後就可以開始編寫你的爬蟲。
*重要:下載並檢視 Demo ,結合本文可以快速實現一個基本爬蟲✌️。
準備工作
安裝 Scrapy (系統預設安裝了 Python)
:
$ pip install Scrapy
複製程式碼
在當前目錄新建工程
$ scrapy startproject yourproject
複製程式碼
新建工程檔案結構如下:
yourproject/
----|scrapy.cfg # 部署配置檔案
|yourproject/ # 工程目錄
|----__init__.py
|----items.py # 專案資料檔案
|----pipelines.py # 專案管道檔案
|----settings.py # 專案設定檔案
|----spiders/ # 我們的爬蟲 目錄
|----__init__.py # 爬蟲主要程式碼在這裡
複製程式碼
簡單的爬蟲主要使用了spiders
、items
、pipelines
這三個檔案:
- spider :爬蟲的主要邏輯。
- items :爬蟲的資料模型。
- pipelines : 爬蟲獲取到的資料的加工工廠,可以進行資料篩選或儲存。
資料模型:items
先看看我們要爬取的網站,這個是 Scrapy 官方 Demo 爬取資料用的網站,我們先用這個來練手。
分析網頁的資訊我們可以看到網頁主體是一個列表,列表每一行都包含可一句引用、作者名字、標籤等資訊。作者名右邊點選(about)可以看到作者的詳細資訊,包括介紹、出生年月日、地點等等。根據上面的資料,我們可以先建立如下資料模型:
items.py
import scrapy
# quote 我們要爬取的主體
class QuoteItem(scrapy.Item):
text = scrapy.Field()
tags = scrapy.Field()
author = scrapy.Field()
next_page = scrapy.Field()
pass
# quote 的作者資訊 對應 QuoteItem.author
class AuthorItem(scrapy.Item):
name = scrapy.Field()
birthday = scrapy.Field()
address = scrapy.Field()
description = scrapy.Field()
pass
複製程式碼
所有的模型必須繼承scrapy.Item
,完成這一步我們就可以開始寫爬蟲的邏輯了。
# 完整的 QuoteItem 資料結構示例
{
text,
tags,
author:{
name,
birthday,
address,
description
}
}
複製程式碼
爬蟲:spider
既然是爬蟲,自然需要去爬取網頁,爬蟲部分的幾個要點:
- 引入你建立的資料模型
- 首先爬蟲類要繼承
scrapy.Spider
。 - 設定爬蟲的名字
name
,啟動爬蟲時要用。 - 將你要爬取的網址放入
start_requests()
,作為爬蟲的起點。 - 爬取的網頁資訊成功後,在的請求響應
parse()
中解析。
spiders/init.py
- 在頂部引入建立的資料模型。
import scrapy
from ScrapySample.items import QuoteItem
from ScrapySample.items import AuthorItem
複製程式碼
- 爬蟲類,
name
->爬蟲名字,allowed_domains
->爬取網頁的白名單。
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = ["quotes.toscrape.com"]
複製程式碼
-
在
start_requests()
中記錄你要爬取的網址。可以只放入一個網址,然後讓爬蟲自己爬取起始網址中下一頁的連結。也可以在這裡把所有需要爬的網址直接放入,比如說
page
一般是從1開始,並且有序的,寫一個for
迴圈可以直接輸入所有頁面的網址。本文使用的是讓爬蟲自己去爬取下一頁網址的方式,所以只寫入了一個起始網址。
def start_requests(self):
urls = [
'http://quotes.toscrape.com/page/1/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
複製程式碼
-
如下程式碼,爬取網頁成功之後,我們要分析網頁結構,找到我們需要的資料。
我們先來看XPath語法,
//div[@class="col-md-8"]/div[@class="quote"
:這是表示查詢 class 為"col-md-8"
的 div 節點下的一個子節點,並且子節點是一個 class 為"quote"
div 節點。如果在當前頁面找到了這樣一個節點,則返回節點資訊,如果沒有找到則返回None
。
def parse(self, response):
# 通過檢視器,找到列表所在的節點
courses = response.xpath('//div[@class="col-md-8"]/div[@class="quote"]')
for course in courses:
# 將資料模型例項化 並從節點中找到資料填入我們的資料模型
item = QuoteItem()
# 輪詢 course 節點下所有 class 為 "text" 的 span 節點,獲取所有匹配到的節點的 text() ,由於獲取到的是列表,我們預設取第一個。
item['text'] = course.xpath('.//span[@class="text"]/text()').extract_first()
item['author'] = course.xpath('.//small[@class="author"]/text()').extract_first()
item['tags'] = course.xpath('.//div[@class="tags"]/a/text()').extract()
# 請求作者詳細資訊
author_url = course.xpath('.//a/@href').extract_first()
# 如果作者介紹的連結不為空 則去請求作者的詳細資訊
if author_url != '':
request = scrapy.Request(url='http://quotes.toscrape.com'+author_url, dont_filter=True, callback=self.authorParse)
# 將我們已經獲取到的 QuoteItem 傳入該請求的回撥函式 authorParse(),在該函式內繼續處理作者相關資料。
request.meta['item'] = item
yield request
# 繼續爬向下一頁 該函式具體實現下面會分析
next_page_request = self.requestNextPage(response)
yield next_page_request
複製程式碼
這段註釋不是很詳細,如果看不懂可能需要補一下相關知識。
-
爬取作者詳細資訊
成功獲取作者詳細資訊 AuthorItem 後並且賦值給 QuoteItem 的屬性
author
,這樣一個完整的引述資訊 QuoteItem 就組裝完成了。
def authorParse(self, response):
# 先獲取從 parse() 傳遞過來的 QuoteItem
item = response.meta['item']
# 通過檢視器,找到作者詳細資訊所在節點
sources = response.xpath('//div[@class="author-details"]')
# 例項化一個作者資訊的資料模型
author_item = AuthorItem()
# 往作者資訊模型填入資料
for source in sources:
author_item['name'] = source.xpath('.//h3[@class="author-title"]/text()').extract_first()
author_item['birthday'] = source.xpath('.//span[@class="author-born-date"]/text()').extract_first()
author_item['address'] = source.xpath('.//span[@class="author-born-location"]/text()').extract_first()
author_item['description'] = source.xpath('.//div[@class="author-description"]/text()').extract_first()
# 最後將作者資訊 author_item 填入 QuoteItem
item['author'] = author_item
# 儲存組裝好的完整資料模型
yield item
複製程式碼
-
爬蟲自己找到出路(下一頁網頁連結)
通過檢視器我們可以找到
下一頁
按鈕元素,找到該節點並提取連結,爬蟲即奔向下一個菜園。
def requestNextPage(self, response):
next_page = response.xpath('.//li[@class="next"]/a/@href').extract_first()
# 判斷下一個是按鈕元素的連結是否存在
if next_page is not None:
if next_page != '':
return scrapy.Request(url='http://quotes.toscrape.com/'+next_page, callback=self.parse)
return None
複製程式碼
爬蟲的主要邏輯到這裡就結束了,我們可以看到,一小段程式碼就可以實現一個簡單的爬蟲。一般主流網頁都針對防爬蟲做了一些處理,實操過程中也許並不會這麼順利,我們可能需要模仿瀏覽器的User-Agent,或者做訪問延時防止請求過於頻繁等等處理。
資料處理:pipelines
pipelines是 Scrapy 用來後續處理的管道,可以同時存在多個,並且可以自定義順序執行,通常用來做資料處理和資料儲存。我們需要在settings.py
檔案中設定需要需要執行的管道和執行順序。
# 在 settings.py 加入下面的程式碼
ITEM_PIPELINES = {
'ScrapySample.pipelines.ScrapySamplePipeline': 300,
}
複製程式碼
在這裡我只使用了一個管道ScrapySamplePipeline
,用來將資料儲存到資料庫當中,後面的數字300
是表示該管道的優先順序,數字越小優先順序越高。
由於我們要儲存資料到資料庫,所以我們需要先在本地搭建起資料庫服務,我這裡用的是MySQL
,如果沒有搭建的小夥伴可以下個 MAMP 免費版本,安裝好傻瓜式操作一鍵啟動Apache
、MySQL
服務。當然,資料庫和表還是要自己建的。
# 在 pipelines.py 中加入資料庫配置資訊
config = {
'host': '127.0.0.1',
'port': 8081,
'user': 'root',
'password': 'root',
'db': 'xietao',
'charset': 'utf8mb4',
'cursorclass': pymysql.cursors.DictCursor,
}
複製程式碼
我們可以在__init__()
函式裡做一些初始化工作,比如說連線資料庫。
然後process_item()
函式是管道處理事件的函式,我們要在這裡將資料儲存入資料庫,我在這個函式裡寫了一些插入資料庫操作。
close_spider()
函式是爬蟲結束工作時候呼叫,我們可以在這裡關閉資料庫。
class ScrapySamplePipeline(object):
def __init__(self):
# 連線資料庫
self.db = sql.connect(**config)
self.cursor = self.db.cursor()
def process_item(self, item, spider):
# 先儲存作者資訊
sql = 'INSERT INTO author (name, birthday, address, detail) VALUES (%s, %s, %s, %s)'
self.cursor.execute(sql, (item['author']['name'], item['author']['birthday'], item['author']['address'], item['author']['description']))
# 獲取作者id
author_id = self.cursor.lastrowid
# 儲存引述資訊
sql = 'INSERT INTO spider (text, tags, author) VALUES (%s, %s, %s)'
self.cursor.execute(sql, (item['text'], ','.join(item['tags']), author_id))
self.db.commit()
# 即將結束爬蟲
def close_spider(self, spider):
self.db.close()
self.cursor.close()
print('close db')
複製程式碼
如果不需要儲存資料庫或者對資料處理的話,pipelines
這部分是可以忽略的。這個時候在命令列切換到工程目錄下,輸入開始執行爬蟲命令:
$ scrapy crawl quotes
複製程式碼
部分不儲存到資料庫的小夥伴可以使用下方命令,將爬取的資料以 Json 格式匯出到該工程目錄下。
$ scrapy crawl quotes -o quotes.json
複製程式碼
最後貼上資料庫資料成功錄入的截圖。
總結
這是作者最開始學習 Python 的時候寫的,有一些不盡人意的地方後面會再調整,寫下本文用意是鞏固知識或是用於以後回顧,同時希望對同樣剛開始學習 Python 的讀者有所幫助。
最後再次貼上Demo ️。